fix(php-extractor): capture file-scope calls + map include/require as imports (#367)#400
Open
tirth8205 wants to merge 1 commit into
Open
Conversation
… imports (Egonex-AI#367) Two bugs in the PHP extractor caused ~0% call-graph + import coverage on procedural (page-style) PHP: Bug A: extractCallGraph() only recorded calls when functionStack.length > 0, silently dropping every function_call_expression / member_call_expression / scoped_call_expression at file scope. Top-level calls (the norm in procedural PHP) now flow into the graph attributed to a synthetic "<file>" caller so file→callee edges are preserved. Bug B: extractStructure() only mapped namespace_use_declaration ("use") to imports, leaving include / include_once / require / require_once expressions invisible. These now emit import entries with the path captured. Plain string literals resolve to their content; __DIR__ / __FILE__ concatenations are reconstructed by walking the binary_expression and preserving the magic constant verbatim so the consumer can resolve it relative to the current file. Non-resolvable arguments (variables, etc.) still emit an edge with the raw argument text so the dependency isn't silently lost. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes two extractor bugs that caused ~0% call-graph + import coverage on procedural (page-style) PHP — the dominant style outside Composer/PSR-4 projects (#367).
Bug A — top-level calls dropped.
extractCallGraph()only recorded a call whenfunctionStack.length > 0, so anyfunction_call_expression/member_call_expression/scoped_call_expressionat file scope produced no entry. On procedural PHP, that meant the call graph was empty.Fix: when
functionStackis empty, attribute the call to a synthetic<file>caller (a stable, language-neutral identifier) so file→callee edges are still emitted.Bug B —
include/requirenot mapped.extractStructure()only looked atnamespace_use_declarationfor imports.include,include_once,require, andrequire_once— the file→file dependency mechanism for non-namespaced / legacy PHP — were ignored.Fix: recognise
expression_statementchildren whose inner node isinclude_expression/include_once_expression/require_expression/require_once_expression. Walk the path argument:'config.php',"lib/util.php") → captured as-is.__DIR__ . '/helpers.php') → reconstructed by recursing intobinary_expressionand preserving__DIR__/__FILE__verbatim so the consumer can resolve relative to the current file.Test plan
All 35 tests in
php-extractor.test.tspass (29 pre-existing + 6 new).New tests cover:
attributes top-level calls to the synthetic <file> caller (#367)—main(),$obj->run(),Helper::go()at file scope all captured under<file>.mixes file-scope and in-function calls in a single file (#367)—<file>-scopebootstrap()+handleRequest()and in-functionprocess_input()both appear with correct callers.captures include/require with a plain string literal as imports (#367)— all four variants (include,include_once,require,require_once) produce import entries.resolves __DIR__ . '/path' concatenation in require_once (#367)— produces source"__DIR__/helpers.php".captures include/require alongside use statements (#367)— coexists withnamespace_use_declarationextraction.records non-resolvable include path as raw text (#367)—include $path;still emits an entry.Existing behaviour (in-function calls, instance/static method calls,
useimports, grouped/aliaseduse, block-scoped namespaces, return types, exports) is preserved — the original test that asserted top-level calls were silently dropped was updated to reflect the new (correct) behaviour.Verification commands run:
pnpm test --root understand-anything-plugin/packages/core php-extractor→ 35/35 pass, 0 type errors (--typecheck).pnpm lint→ clean.Closes #367.