feat(php): capture use FQN/alias/kind metadata on imports edges (#19) - #29
Merged
Conversation
PHP `imports` edges carried no metadata: `_import_php` kept only
`raw.split("\\")[-1]`, so the FQN, the alias and the `function`/`const`
kind of every `use` statement were discarded at capture time. That is the
fact base #16's decisive-refusal resolver needs, and it already existed —
correctly parsed, group-use and aliases included — but only as a
function-local map inside `_resolve_php_type_references`.
Extract that parser (`_record_use_clause` plus the
`namespace_use_declaration` walk) into three shared helpers in
resolution.py and have both consumers call them. `_import_php` is
dispatched per `namespace_use_clause` and never sees the declaration, so
`_php_use_clause_context` recovers the group-use prefix and the
declaration-level `function`/`const` keyword from the parent node; the
edge then carries `target_fqn`, `alias` and `use_kind`, mirroring
`_import_csharp`.
Strictly metadata-only. The edge target stays keyed on the imported short
name, `_PHP_CONFIG.import_types` stays `namespace_use_clause` (so a trait
`use` inside a class body — a `use_declaration` — is still not an import),
and `_resolve_php_type_references` keeps its exact behavior: it passes
`apply_declaration_kind=False` because it has only ever honored a
clause-level `function`/`const` keyword, never the declaration-level one a
group `use function A\{f, g};` carries. Verified beyond the suite by
diffing full extract() output (metadata stripped) over a PHP corpus
covering every `use` form plus the existing fixtures — byte-identical
before and after.
Tests go through the public extract() seam: plain, aliased, group and
aliased-group use, leading-backslash normalization, and edge targets
unchanged. The `use function` / `use const` exclusion is asserted where it
is observable — a same-file `use Vendor\Sdk\Base;` control re-points its
supertype reference onto the FQN stub while the `use function` /
`use const` names must not, so they stay on the bare stub.
Suite: 4032 passed / 36 skipped (baseline 4024 / 36 plus 8 new).
…ture-19 * origin/v8: fix(extract): scope member-call resolvers to the sources they own (#10) fix(php): recognize PHP 8.2 DNF property and promoted-param types (#9) fix(php): refuse the same-file bare-name edge for union/intersection receivers (#9) fix(php): emit indirect_call for first-class callables (#15)
This was referenced Aug 5, 2026
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.
Fixes #19 (spec #18, ticket 1/5 — foundation for the #16 fix).
What
PHP
importsedges now carryuse_kind/alias/target_fqnmetadata, mirroring_import_csharp. The already-correctuse-parser from_resolve_php_type_referenceswas extracted into shared helpers (_php_use_clause_fact,_php_use_clause_context,_php_use_declaration_facts) consumed by both the resolution pass and the capture path, replacing_import_php's lossyraw.split("\\")[-1]. Strictly metadata-only: no resolver behavior change,_PHP_CONFIG.import_typesuntouched, edge targets unchanged.No-behavior-change evidence
extract()output with metadata stripped, before vs after, over a corpus covering plain / aliased / group / aliased-group /function/const/ group-function / leading-backslashuse, traituse, inheritance, and member calls: 59 nodes / 86 edges, byte-identical.d40d006) was triple-verified: both parent-diff directions byte-identical, plus an independent patch-reconstruction ofextract.pymatching by sha256.Discovered, filed separately
#26 — group-form
use function A\{f, g};/use constput the keyword on the declaration, not the clause, so those names enter the class-name map today (pre-existing, reproduced at102ae79). Deliberately preserved bit-for-bit here viaapply_declaration_kind=False; the new metadata reports the correct kind. #26 must land before #21, since a wrongly claimed short name would become a wrongly decisive refusal.Notes for #21/#22
use_kindvocabulary isclass/function/constwithaliasas a separate key (unlike C#'susing_kind == "alias") — gate onuse_kind == "class"._resolve_php_type_referencesre-pointsimportsedges but leaves metadata untouched, sometadata.target_fqnis the reliable read, not the target node's label.Test evidence
🤖 Generated with Claude Code