Framework-Aware PHP Parsing Capabilities in code-review-graph: A Deep Dive into Laravel-Aware Static Analysis
code-review-graph provides framework-aware PHP parsing that combines generic PHP construct analysis with Composer PSR-4 resolution, Blade template edge detection, and an evidence-gated Laravel post-pass that creates Route-to-controller and Eloquent relationship edges only when provable framework semantics exist.
Static analysis tools for PHP often stop at syntax trees, but code-review-graph (from tirth8205/code-review-graph) treats PHP as a first-class citizen with deep framework integration. Its parser recognizes that modern PHP codebases rely on Composer autoloading, Laravel's routing and ORM conventions, and Blade templating—then weaves these into a unified graph representation.
Core PHP Construct Analysis
The parser indexes fundamental PHP language features as distinct graph nodes. In code_review_graph/parser.py, the _resolve_php_scoped_calls method handles trait usage, enum definitions, object-creation expressions with new, and base-class extension clauses.
This ensures every type reference creates traceable edges in the code graph, not just anonymous syntax nodes.
Composer PSR-4 Resolution
Class-to-file mapping follows Composer's longest-prefix rule with multi-directory support and result caching.
The CodeParser._resolve_module_to_file method takes a fully-qualified class name and resolves it against the repository's composer.json PSR-4 mappings. Search is bounded to the repository root to prevent external leakage.
from pathlib import Path
from code_review_graph.parser import CodeParser
# Composer PSR-4 resolution in action
parser = CodeParser(repo_root=Path("/my/repo"))
qualified = parser._resolve_module_to_file(
"App\\Service\\Mailer", # Fully-qualified class name
"/my/repo/src/Mailer.php", # Current file (used for relative look-ups)
"php"
)
# qualified → "/my/repo/src/Service/Mailer.php"
Multi-directory mappings are supported, and results are cached to avoid repeated filesystem traversal. The resolution logic lives within parser.py and is invoked from the scoped-call resolver at line 11050 and surrounding context.
Blade Template Reference Detection
The parser recognizes Laravel's Blade templating through extension-based language detection. When processing .blade.php files, it identifies @include, @extends, and similar directives while deliberately ignoring commented or escaped variants.
These create directed edges from the template file to its referenced views, enabling end-to-end flow analysis from PHP controllers through to view layers.
from pathlib import Path
from code_review_graph.parser import CodeParser
# Blade template reference detection
blade_path = Path("resources/views/welcome.blade.php")
nodes, edges = CodeParser().parse_file(blade_path)
# An edge is created from the Blade file to any included view (@include('partials.foo'))
The blade-specific logic is gated by detect_language handling of the "blade" extension, ensuring this analysis only fires for appropriate file types.
Laravel Semantic Post-Pass
After generic PHP analysis completes, a dedicated Laravel post-pass inspects nodes for explicit framework evidence. This is evidence-gated analysis: edges are created only when provable Laravel semantics exist.
The post-pass triggers on:
Route::definitions (Route::get(),Route::post(), etc.)- Eloquent model method calls (
hasMany(),belongsTo(), etc.) - Imported Laravel facades or known framework receivers
Evidence Requirements
The resolve_target function (lines L11194-L11242 within _resolve_php_scoped_calls) requires at least one of:
- Fully-qualified class name
- Explicit
useimport statement selforstaticreference within the same class- Matching namespace resolution
If none match, the call remains unresolved rather than guessed.
from pathlib import Path
from code_review_graph.parser import CodeParser
# Parse a single PHP file and see the generic PHP nodes/edges
php_file = Path("src/Order/Queue/Mailer.php")
nodes, edges = CodeParser().parse_file(php_file)
# Resolve a Laravel route definition (evidence-gated)
store = CodeParser().parse_file(Path("routes/web.php"))[1]
# edges now contains Route→Controller edges if the route points to a real controller
Edge Types Generated
When evidence is sufficient, the parser creates:
- Route-to-controller edges:
Route::get('/users', [UserController::class, 'index'])becomes a directed edge from the route definition to the controller method - Eloquent relationship edges:
User::hasMany(Post::class)creates edges between model classes based on ORM semantics
The Laravel branch is clearly demarcated in parser.py by the comment "PHP / Laravel semantic constructs" and subsequent resolution logic within _resolve_php_scoped_calls.
File Organization and Implementation
| File | Purpose |
|---|---|
code_review_graph/parser.py |
Central implementation; _resolve_php_scoped_calls contains both generic PHP and Laravel-specific logic |
docs/FEATURES.md |
High-level capability documentation |
docs/superpowers/specs/2026-07-17-php-laravel-parser-design.md |
Design specification for the Laravel post-pass and evidence-gated edge creation |
tests/test_php_laravel.py |
Validation suite covering Composer resolution, Blade detection, and Laravel edge generation |
Summary
- Generic PHP constructs (traits, enums,
newexpressions, base classes) are indexed as graph nodes via_resolve_php_scoped_calls - Composer PSR-4 resolution maps class names to files using longest-prefix matching with caching, implemented in
_resolve_module_to_file - Blade template edges connect views to their includes through extension-gated analysis
- Laravel semantic post-pass creates Route-to-controller and Eloquent relationship edges only when explicit framework evidence exists
- Evidence-gated resolution prevents false positives by requiring qualified classes, imports, or self/static references
Frequently Asked Questions
How does code-review-graph handle PSR-4 autoloading with multiple source directories?
The CodeParser._resolve_module_to_file method supports multi-directory mappings defined in composer.json. It applies the longest-prefix matching rule to determine the correct file path, caches results for performance, and constrains searches to the repository root to maintain boundary safety.
What prevents the Laravel parser from creating false-positive edges?
The parser implements evidence-gated analysis through the resolve_target function (lines L11194-L11242). It requires at least one of: fully-qualified class name, explicit use import, self/static reference, or matching namespace. Without this evidence, calls remain unresolved rather than guessed.
Does Blade template analysis require a full Laravel installation?
No. Blade detection operates on file extension (.blade.php) and directive pattern matching within the generic PHP parser infrastructure. The detect_language function gates Blade-specific logic, and the analysis processes template files independently of runtime Laravel availability.
Where is the Laravel-specific parsing logic located?
All PHP-related parsing, including the Laravel post-pass, resides in CodeParser._resolve_php_scoped_calls within code_review_graph/parser.py. The Laravel branch follows a comment marker "PHP / Laravel semantic constructs" and implements framework-aware edge creation after generic PHP analysis completes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →