code-review-graph version
2.3.6 (run as the MCP server). Also reproduced on the 2.3.2 CLI; the root cause
is present in both.
Operating system
macOS 15 (Darwin 24.5.0), Apple Silicon.
Python version
Python 3.14.4
AI platform
Claude Code (MCP server over stdio).
Output of code-review-graph status
From the minimal reproducer below:
Nodes: 6
Edges: 5
Files: 3
Languages: php
Steps to reproduce
- Create a minimal PSR-4 PHP project and build the graph:
mkdir -p repro/src/App/Domain/Entity repro/src/App/Service repro/src/App/Api
cd repro && git init -q
printf '<?php\nnamespace App\\Domain\\Entity;\nclass Job {}\n' > src/App/Domain/Entity/Job.php
printf '<?php\nnamespace App\\Service;\nuse App\\Domain\\Entity\\Job;\nclass MatchService {}\n' > src/App/Service/MatchService.php
printf '<?php\nnamespace App\\Api;\nuse App\\Domain\\Entity\\Job;\nclass JobController {}\n' > src/App/Api/JobController.php
git add -A
code-review-graph build
- Ask for the importers of the
Job class (via the MCP query_graph tool, or
the package API):
from code_review_graph.tools.query import query_graph
query_graph("importers_of", "<abs>/src/App/Domain/Entity/Job.php", repo_root="<abs>")
Expected vs actual behavior
Expected: importers_of(Job.php) returns the two files that
use App\Domain\Entity\Job; (MatchService.php and JobController.php).
Equivalently, the IMPORTS_FROM edges should target the resolved path of
Job.php.
Actual: importers_of returns 0 results. Inspecting the graph shows the
IMPORTS_FROM edge targets are the raw use statement text instead of a
resolved file path or FQN:
src/App/Api/JobController.php -> use App\Domain\Entity\Job;
src/App/Service/MatchService.php -> use App\Domain\Entity\Job;
importers_of(Job.php) -> Found 0 result(s)
The same defect makes tests_for and inheritors_of return nothing for PHP
classes, hides PHP importers from the upstream side of get_impact_radius, and
(because resolve_bare_call_targets uses IMPORTS_FROM targets to disambiguate
cross-file CALLS) degrades PHP call resolution too. On a real Symfony/PHP
codebase this meant a core entity imported by 452 files showed 0 importers.
Additional context
Root cause: _extract_import in parser.py has a per-language branch for
Python, JS/TS, Go, Rust, Java, C#, Scala, Dart, Julia and others, but none for
PHP, so PHP use statements fall through to the raw-text fallback
(imports.append(text)) and store the entire statement as the edge target.
There is also no PHP case in _do_resolve_module (Java resolves import a.b.C;
to the class file; PHP needs the analogous mapping).
A fix is proposed in #573: a PHP branch in _extract_import (recording the FQN,
handling as aliases, grouped use A\{B, C}, and use function / use const)
plus a PHP branch in _do_resolve_module that resolves the FQN to an absolute
.php path by walking up from the importing file, mirroring the Java resolver.
code-review-graph version
2.3.6 (run as the MCP server). Also reproduced on the 2.3.2 CLI; the root cause
is present in both.
Operating system
macOS 15 (Darwin 24.5.0), Apple Silicon.
Python version
Python 3.14.4
AI platform
Claude Code (MCP server over stdio).
Output of
code-review-graph statusFrom the minimal reproducer below:
Steps to reproduce
Jobclass (via the MCPquery_graphtool, orthe package API):
Expected vs actual behavior
Expected:
importers_of(Job.php)returns the two files thatuse App\Domain\Entity\Job;(MatchService.phpandJobController.php).Equivalently, the
IMPORTS_FROMedges should target the resolved path ofJob.php.Actual:
importers_ofreturns 0 results. Inspecting the graph shows theIMPORTS_FROMedge targets are the rawusestatement text instead of aresolved file path or FQN:
The same defect makes
tests_forandinheritors_ofreturn nothing for PHPclasses, hides PHP importers from the upstream side of
get_impact_radius, and(because
resolve_bare_call_targetsusesIMPORTS_FROMtargets to disambiguatecross-file
CALLS) degrades PHP call resolution too. On a real Symfony/PHPcodebase this meant a core entity imported by 452 files showed 0 importers.
Additional context
Root cause:
_extract_importinparser.pyhas a per-language branch forPython, JS/TS, Go, Rust, Java, C#, Scala, Dart, Julia and others, but none for
PHP, so PHP
usestatements fall through to the raw-text fallback(
imports.append(text)) and store the entire statement as the edge target.There is also no PHP case in
_do_resolve_module(Java resolvesimport a.b.C;to the class file; PHP needs the analogous mapping).
A fix is proposed in #573: a PHP branch in
_extract_import(recording the FQN,handling
asaliases, groupeduse A\{B, C}, anduse function/use const)plus a PHP branch in
_do_resolve_modulethat resolves the FQN to an absolute.phppath by walking up from the importing file, mirroring the Java resolver.