feat(php): php projects support added
This commit is contained in:
126
.agents/designs/php-support.md
Normal file
126
.agents/designs/php-support.md
Normal file
@@ -0,0 +1,126 @@
|
||||
PHP Language Support for Belief Map
|
||||
=====================================
|
||||
|
||||
Context and motivation
|
||||
----------------------
|
||||
The belief map previously supported Python and TypeScript/TSX. PHP projects
|
||||
(Laravel, Symfony, legacy codebases) had no graph representation, meaning
|
||||
agents querying the belief map would get zero results for `.php` files --
|
||||
even in monorepos containing both TS and PHP services.
|
||||
|
||||
This design adds PHP as a first-class language alongside Python and TS,
|
||||
covering the same entity/edge model: classes, interfaces, traits, enums,
|
||||
imports, heritage, trait usage, and constructor dependency injection.
|
||||
|
||||
|
||||
Implementation approach
|
||||
-----------------------
|
||||
**tree-sitter-php** is the primary parser, mirroring the TypeScript approach.
|
||||
A regex fallback (`parse_php_regex`) ensures PHP files remain visible even
|
||||
when `tree-sitter-php` is not installed, at the cost of reduced precision.
|
||||
|
||||
**PSR-4 autoload resolution** reads `composer.json` (`autoload.psr-4` and
|
||||
`autoload-dev.psr-4`) to map FQCNs in `use` statements to file paths. This
|
||||
mirrors the TypeScript `tsconfig.json` path-alias resolver. When no
|
||||
`composer.json` is found, a walk-up heuristic converts namespace separators
|
||||
to path separators (like the Python resolver).
|
||||
|
||||
**Constructor DI extraction** mirrors the NestJS heuristic from the TS
|
||||
parser: typed `__construct` parameters with PascalCase type-hints are
|
||||
treated as service dependencies, producing `calls-api` edges. This works
|
||||
for Symfony autowiring and Laravel service container injection.
|
||||
|
||||
**Trait usage** (`use TraitX;` inside a class body) is a PHP-specific
|
||||
relationship with no direct equivalent in Python or TS. Traits are
|
||||
registered as abstract providers (like interfaces), and classes that use
|
||||
them get `calls-api` edges to the trait's defining module.
|
||||
|
||||
**LSP enrichment** uses `phpactor language-server` (JSON-RPC over stdio),
|
||||
the open-source PHP language server. It integrates with the existing
|
||||
`LspClient` infrastructure without changes to the transport layer.
|
||||
|
||||
|
||||
Supported entities
|
||||
------------------
|
||||
| PHP construct | Entity kind | sexp tag | Notes |
|
||||
|---------------|-------------|----------|-------|
|
||||
| `class Foo` | class | `cls` | abstract modifier, heritage |
|
||||
| `interface Foo` | interface | `ifc` | auto-abstract |
|
||||
| `trait Foo` | trait | `trt` | auto-abstract; usable via `use` |
|
||||
| `enum Foo` (PHP 8.1) | enum | `enm` | backed enums (`: string/int`) |
|
||||
| `function foo()` | function | `fn` | top-level functions only |
|
||||
| `#[Attribute]` | decorator | `:deco` | PHP 8 attributes on classes/fns |
|
||||
| `class { use TraitX; }` | trait usage | edge | `calls-api` to trait module |
|
||||
| `__construct(Foo $f)` | DI dependency | edge | `calls-api` to service module |
|
||||
|
||||
|
||||
Edge types for PHP
|
||||
------------------
|
||||
Same model as other languages:
|
||||
- `imports` -- resolved via PSR-4 from `use` statements
|
||||
- `calls-api` -- implements/extends + trait usage + constructor DI
|
||||
- `data-flow` -- to modules with validation (Symfony Validator, Laravel)
|
||||
- `refs` -- entity-level cross-file references
|
||||
- `calls` (LSP only) -- precise function-to-function call edges
|
||||
|
||||
|
||||
Skipped / out of scope (v1)
|
||||
---------------------------
|
||||
- **Blade templates** (`.blade.php`) -- skipped like `.d.ts`
|
||||
- **Anonymous classes** (`new class { ... }`) -- no name, no ID
|
||||
- **Trait conflict resolution** (`insteadof` / `as`) -- edges on all used traits
|
||||
- **Dynamic imports** (`include`, `require`) -- not tracked
|
||||
- **Runtime service discovery** -- no container introspection
|
||||
- **`use function` / `use const`** -- tracked as imports but not as entity refs
|
||||
|
||||
|
||||
File discovery
|
||||
--------------
|
||||
- `.php` extension (excluding `.blade.php`)
|
||||
- `vendor/` and `var/` directories are skipped (composer deps, Symfony cache)
|
||||
- `bootstrap/` and `storage/` are NOT skipped (may contain real source)
|
||||
- `composer.json` defines PSR-4 autoload rules and LSP project boundaries
|
||||
|
||||
|
||||
Fallback behavior
|
||||
-----------------
|
||||
When `tree-sitter-php` is not installed:
|
||||
- `parse_php_regex` handles classes, interfaces, traits, enums (with heritage)
|
||||
- No PHP 8 attribute extraction
|
||||
- No constructor DI extraction
|
||||
- Method extraction is best-effort (regex within a window)
|
||||
- Trait usage inside class bodies IS detected (regex)
|
||||
- The graph is still usable; precision improves when `tree-sitter-php` is added
|
||||
|
||||
When `phpactor` is not installed:
|
||||
- `--lsp` mode skips PHP projects with a warning
|
||||
- AST-based edges (imports, calls-api, refs) are unaffected
|
||||
- Only `calls` (LSP call-hierarchy) edges are missing
|
||||
|
||||
|
||||
Acceptance criteria
|
||||
-------------------
|
||||
1. Given a Laravel/Symfony project with `composer.json`, when running
|
||||
`build_belief_map.py`, then `(node ... php ...)` facts are emitted for
|
||||
each `.php` file with correct PSR-4-resolved `imports` edges.
|
||||
|
||||
2. Given a class implementing an interface, when parsing, then a
|
||||
`calls-api` edge connects them.
|
||||
|
||||
3. Given a class using a trait (`use TraitX;`), when parsing, then a
|
||||
`calls-api` edge connects the class module to the trait module.
|
||||
|
||||
4. Given a class with constructor type-hints, when parsing, then
|
||||
`calls-api` edges connect the class to each dependency module.
|
||||
|
||||
5. Given PHP 8 attributes (`#[AsService]`), when parsing with
|
||||
tree-sitter-php, then the attribute name appears in `:deco`.
|
||||
|
||||
6. Given `composer.json` with `autoload.psr-4`, when resolving imports,
|
||||
then `use App\Service\Foo` resolves to `src/Service/Foo.php`.
|
||||
|
||||
7. Given `tree-sitter-php` is not installed, when parsing, then
|
||||
`parse_php_regex` produces visible entities (reduced precision).
|
||||
|
||||
8. `belief_search.py analyze <php-class-id>` returns boundary/deps/rdeps
|
||||
without modifications to the query engine.
|
||||
Reference in New Issue
Block a user