| 1643 | /// (without quotes). |
| 1644 | #[inline] |
| 1645 | fn read_define_name<'a>(content: &'a [u8], i: &mut usize) -> Option<&'a str> { |
| 1646 | let len = content.len(); |
| 1647 | if *i >= len { |
| 1648 | return None; |
| 1649 | } |
| 1650 | let quote = content[*i]; |
| 1651 | if quote != b'\'' && quote != b'"' { |
| 1652 | return None; |
| 1653 | } |
| 1654 | *i += 1; // skip opening quote |
| 1655 | let start = *i; |
| 1656 | while *i < len && content[*i] != quote { |
| 1657 | if content[*i] == b'\\' && *i + 1 < len { |
| 1658 | let next = content[*i + 1]; |
| 1659 | if next == quote || next == b'\\' { |
| 1660 | // Escaped quote or escaped backslash — the name |
| 1661 | // contains a real escape sequence, which is unusual |
| 1662 | // for constant names. Bail out. |
| 1663 | return None; |
| 1664 | } |
| 1665 | // A bare backslash (e.g. namespace separator in |
| 1666 | // 'App\Config\DB_HOST') is literal in single-quoted |
| 1667 | // strings and safe to include. |
| 1668 | } |
| 1669 | *i += 1; |
| 1670 | } |
| 1671 | if *i >= len { |
| 1672 | return None; |
| 1673 | } |
| 1674 | let name = &content[start..*i]; |
| 1675 | *i += 1; // skip closing quote |
| 1676 | std::str::from_utf8(name).ok() |
| 1677 | } |
| 1678 | |
| 1679 | /// Read a class/interface/trait/enum name after the keyword. |
| 1680 | /// |