The **PSR-4 scanner**: a single-pass byte-level scanner that extracts fully-qualified class, interface, trait, and enum names from PHP source bytes. This is the classes-only scanner used by the PSR-4 directory walker and vendor package scanner. For a scanner that also extracts functions and constants, see [`find_symbols`] (the full-scan). Skips comments, strings, heredocs, and nowdocs inline wi
(content: &[u8])
| 1256 | /// Skips comments, strings, heredocs, and nowdocs inline without |
| 1257 | /// allocating a separate "cleaned" buffer. |
| 1258 | pub fn find_classes(content: &[u8]) -> Vec<String> { |
| 1259 | // Quick rejection — use SIMD to check if any class-like keywords exist |
| 1260 | if !has_class_keyword(content) { |
| 1261 | return Vec::new(); |
| 1262 | } |
| 1263 | |
| 1264 | let mut classes = Vec::with_capacity(4); |
| 1265 | let mut namespace = String::new(); |
| 1266 | let len = content.len(); |
| 1267 | let mut i = 0; |
| 1268 | |
| 1269 | // State flags |
| 1270 | let mut in_line_comment = false; |
| 1271 | let mut in_block_comment = false; |
| 1272 | let mut in_single_string = false; |
| 1273 | let mut in_double_string = false; |
| 1274 | let mut in_heredoc = false; |
| 1275 | let mut heredoc_id: &[u8] = &[]; |
| 1276 | |
| 1277 | while i < len { |
| 1278 | // ── Skip: line comment (memchr to newline) ────────────────── |
| 1279 | if in_line_comment { |
| 1280 | if let Some(pos) = memchr(b'\n', &content[i..]) { |
| 1281 | i += pos + 1; |
| 1282 | } else { |
| 1283 | break; |
| 1284 | } |
| 1285 | in_line_comment = false; |
| 1286 | continue; |
| 1287 | } |
| 1288 | |
| 1289 | // ── Skip: block comment (memchr to '*') ───────────────────── |
| 1290 | if in_block_comment { |
| 1291 | if let Some(pos) = memchr(b'*', &content[i..]) { |
| 1292 | i += pos; |
| 1293 | if i + 1 < len && content[i + 1] == b'/' { |
| 1294 | in_block_comment = false; |
| 1295 | i += 2; |
| 1296 | } else { |
| 1297 | i += 1; |
| 1298 | } |
| 1299 | } else { |
| 1300 | break; |
| 1301 | } |
| 1302 | continue; |
| 1303 | } |
| 1304 | |
| 1305 | // ── Skip: single-quoted string (memchr to '\'' or '\\') ──── |
| 1306 | if in_single_string { |
| 1307 | match memchr2_single_string(&content[i..]) { |
| 1308 | Some((offset, b'\\')) => { |
| 1309 | i += offset + 2; |
| 1310 | } |
| 1311 | Some((offset, _)) => { |
| 1312 | i += offset + 1; |
| 1313 | in_single_string = false; |
| 1314 | } |
| 1315 | None => break, |