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 use import statement
  • self or static reference 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, new expressions, 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →