MCPcopy Create free account
hub / github.com/PHPantom-dev/phpantom_lsp / find_symbols

Function find_symbols

src/classmap_scanner.rs:830–1246  ·  view source on GitHub ↗

The **full-scan**: a single-pass byte-level scanner that extracts fully-qualified class, function, and constant names from PHP source bytes. This is the extended version of [`find_classes`] (the PSR-4 scanner) that also recognises `function` declarations, `define()` calls, and top-level `const` statements. It is used for both non-Composer projects (full workspace scan) and Composer autoload file

(content: &[u8])

Source from the content-addressed store, hash-verified

828/// projects (full workspace scan) and Composer autoload files
829/// (`autoload_files.php` and their `require_once` chains).
830pub fn find_symbols(content: &[u8]) -> ScanResult {
831 // Quick rejection — if the file has none of the relevant keywords
832 // we can bail immediately.
833 if !has_any_keyword(content) {
834 return ScanResult::default();
835 }
836
837 let mut result = ScanResult::default();
838 let mut namespace = String::new();
839 let len = content.len();
840 let mut i = 0;
841
842 // Brace depth tracking for top-level `const` detection.
843 // Depth 0 = top-level, depth 1 = inside a class/namespace block.
844 let mut brace_depth: u32 = 0;
845 // Whether we are inside a braced namespace block.
846 let mut in_braced_namespace = false;
847 // The brace depth at which the current namespace was opened.
848 // `const` declarations at this depth (or depth 0 outside braced
849 // namespaces) are top-level.
850 let mut namespace_brace_depth: u32 = 0;
851
852 // State flags
853 let mut in_line_comment = false;
854 let mut in_block_comment = false;
855 let mut in_single_string = false;
856 let mut in_double_string = false;
857 let mut in_heredoc = false;
858 let mut heredoc_id: &[u8] = &[];
859
860 while i < len {
861 // ── Skip: line comment (memchr to newline) ──────────────────
862 if in_line_comment {
863 if let Some(pos) = memchr(b'\n', &content[i..]) {
864 i += pos + 1;
865 } else {
866 break; // rest of file is a comment
867 }
868 in_line_comment = false;
869 continue;
870 }
871
872 // ── Skip: block comment (memchr to '*') ─────────────────────
873 if in_block_comment {
874 if let Some(pos) = memchr(b'*', &content[i..]) {
875 i += pos;
876 if i + 1 < len && content[i + 1] == b'/' {
877 in_block_comment = false;
878 i += 2;
879 } else {
880 i += 1;
881 }
882 } else {
883 break; // unclosed block comment
884 }
885 continue;
886 }
887

Callers 15

scan_file_fullFunction · 0.85
scan_files_parallel_fullFunction · 0.85
symbols_simple_functionFunction · 0.85
use_const_not_capturedFunction · 0.85
symbols_top_level_constFunction · 0.85
symbols_namespaced_constFunction · 0.85

Calls 12

has_any_keywordFunction · 0.85
memchr2_single_stringFunction · 0.85
memchr2_double_stringFunction · 0.85
is_keyword_boundaryFunction · 0.85
read_nameFunction · 0.85
is_preceded_by_useFunction · 0.85
read_define_nameFunction · 0.85
filterMethod · 0.80
iterMethod · 0.80
pushMethod · 0.80
mapMethod · 0.45
is_emptyMethod · 0.45

Tested by 15

symbols_simple_functionFunction · 0.68
use_const_not_capturedFunction · 0.68
symbols_top_level_constFunction · 0.68
symbols_namespaced_constFunction · 0.68
symbols_mixed_fileFunction · 0.68