Why
PHP carries the repo's only LIMITATION: comment (parsers/call_resolver.py:1173): CGR qualifies PHP functions by file path and does not track namespaces, so cross-file call resolution in any namespaced codebase (i.e. all modern PHP) is systematically wrong. Total current PHP depth is a 62-LOC handler (parsers/handlers/php.py) plus three import helpers in import_processor.py:3648-3703 — no parsers/php/ package, no type inference.
Plan — three stages
Stage 1 (pure tree-sitter): namespace-aware qualification
- A real
parsers/php/ package with a proper handler: parse namespace_definition, use / group-use / aliases, and build namespace-aware qns (Vendor\Pkg\Sub\func style mapped onto CGR's dotted qn scheme).
- Resolve calls through the current namespace, imported aliases, and the global fallback rule (unqualified function calls fall back to the global namespace — PHP's actual lookup order).
- This alone retires the
LIMITATION: comment and is the prerequisite for everything below.
Stage 2: php-parser NameResolver facts
nikic/php-parser's NameResolver visitor emits the fully-qualified name of every symbol occurrence — compiler-grade naming for the cost of a php binary. A small bundled PHP tool (seed: evals/oracles/php_oracle/) walks the resolved AST and emits LanguageFrontend facts: resolved call sites (including ClassName::method, new, instanceof), external proofs for symbols outside the repo, INHERITS/IMPLEMENTS by resolved name.
Stage 3 (opt-in): PHPStan collector
For framework-heavy code (Laravel facades, Symfony containers) where names alone can't resolve dynamic dispatch, a PHPStan collector rule can export inferred types and call targets. Requires composer install, so strictly opt-in and last.
Tests
Fixture PHP repo per stage: namespaced functions with use aliases and global fallback (stage 1); trait usage, static calls, interface implementation (stage 2). The existing php_l1/php_retrieval evals should show measurable precision gains after stage 1 alone.
Stages 2+ depend on the LanguageFrontend protocol issue; stage 1 does not.
Why
PHP carries the repo's only
LIMITATION:comment (parsers/call_resolver.py:1173): CGR qualifies PHP functions by file path and does not track namespaces, so cross-file call resolution in any namespaced codebase (i.e. all modern PHP) is systematically wrong. Total current PHP depth is a 62-LOC handler (parsers/handlers/php.py) plus three import helpers inimport_processor.py:3648-3703— noparsers/php/package, no type inference.Plan — three stages
Stage 1 (pure tree-sitter): namespace-aware qualification
parsers/php/package with a proper handler: parsenamespace_definition,use/ group-use/ aliases, and build namespace-aware qns (Vendor\Pkg\Sub\funcstyle mapped onto CGR's dotted qn scheme).LIMITATION:comment and is the prerequisite for everything below.Stage 2: php-parser
NameResolverfactsnikic/php-parser's
NameResolvervisitor emits the fully-qualified name of every symbol occurrence — compiler-grade naming for the cost of aphpbinary. A small bundled PHP tool (seed:evals/oracles/php_oracle/) walks the resolved AST and emitsLanguageFrontendfacts: resolved call sites (includingClassName::method,new, instanceof), external proofs for symbols outside the repo, INHERITS/IMPLEMENTS by resolved name.Stage 3 (opt-in): PHPStan collector
For framework-heavy code (Laravel facades, Symfony containers) where names alone can't resolve dynamic dispatch, a PHPStan collector rule can export inferred types and call targets. Requires
composer install, so strictly opt-in and last.Tests
Fixture PHP repo per stage: namespaced functions with
usealiases and global fallback (stage 1); trait usage, static calls, interface implementation (stage 2). The existingphp_l1/php_retrievalevals should show measurable precision gains after stage 1 alone.Stages 2+ depend on the
LanguageFrontendprotocol issue; stage 1 does not.