Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
92e0dc1
feat(php): Add scalable reference CodeLens
sidux Aug 26, 2026
0ccf3f5
fix(indexing): Reuse workspace index for annotations
sidux Aug 27, 2026
be8c516
fix(indexing): Batch member reference counts
sidux Aug 27, 2026
4176e9e
fix(indexing): Reuse forward scopes for member counts
sidux Aug 27, 2026
5757191
fix(indexing): Coalesce member count refreshes
sidux Aug 27, 2026
e1daa32
fix(indexing): Cache semantic member targets
sidux Aug 27, 2026
f4bea42
fix(references): keep member batching framework-neutral
sidux Aug 27, 2026
d06fdfe
feat(navigation): Navigate PHP symbols in YAML and XML
sidux Aug 25, 2026
66ef462
feat(navigation): Index YAML and XML PHP references
sidux Aug 26, 2026
dc6652f
feat(frameworks): Add Symfony and Doctrine resource navigation
sidux Jul 6, 2026
63cd4fa
fix(symfony): Preserve framework CodeLens destinations
sidux Aug 5, 2026
1f54594
fix(doctrine): Bound reverse relationship CodeLens
sidux Aug 27, 2026
5354811
fix(navigation): Preserve Symfony resource fallbacks
sidux Aug 27, 2026
7c90214
fix(doctrine): Cache repository mapping index
sidux Aug 27, 2026
2c60084
fix(indexing): Index framework reference lookups
sidux Aug 27, 2026
6106c78
test(frameworks): avoid redundant URI allocations
sidux Aug 27, 2026
af3df00
feat(symfony): Index PHP configuration resources
sidux Jul 29, 2026
ff40130
feat(symfony): Add service container intelligence
sidux Jul 29, 2026
fd29607
fix(symfony): Handle Unicode in PHP resource scans
sidux Jul 29, 2026
086d08a
feat(symfony): Add route intelligence
sidux Jul 29, 2026
46ca1b9
feat(symfony): Add Twig template intelligence
sidux Jul 29, 2026
0995004
feat(symfony): Add translation intelligence
sidux Jul 29, 2026
ba8db30
feat(php): Map transparent proxies to real classes
sidux Aug 25, 2026
58a0ac0
feat(php): add call hierarchy
sidux Aug 30, 2026
b15472c
feat(symfony): Navigate event publishers and listeners
sidux Aug 25, 2026
22e6a6b
feat(symfony): Add events and Messenger intelligence
sidux Jul 29, 2026
845866b
perf(symfony): index Messenger CodeLens mappings
sidux Aug 27, 2026
596c503
feat(symfony): expose events in call hierarchy
sidux Aug 30, 2026
8894f02
feat(symfony): Resolve configured ExpressionLanguage strings
sidux Aug 25, 2026
bc45de5
feat(symfony): Add forms, validation, and config schemas
sidux Jul 29, 2026
7490c3e
docs(symfony): Add framework playground
sidux Jul 29, 2026
c63d943
test(symfony): Fix all-target lint
sidux Jul 29, 2026
3e51c1e
docs(release): Correct unreleased feature notes
sidux Aug 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ jobs:
- run: find examples/php -name '*.php' -print0 | xargs -0 -n1 php -l
- run: php -d zend.assertions=1 examples/php/scaffolding/assertions.php
- run: php -l examples/laravel/app/Demo.php
- run: find examples/symfony/src examples/symfony/config -name '*.php' -exec php -l {} \;

benchmark:
name: Benchmark
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ PHPantom focuses on deep type intelligence. Here's how it compares:
| Closure parameter inference | ✅ | 🚧 | 🚧 | 🚧 | 🚧 |
| Laravel | ✅ | ❌ | 🚧 | ❌ | 🚧 |
| Blade templates | 🚧 | ❌ | ✅ | ❌ | 🚧 |
| Symfony / Twig / Doctrine | ✅ | 🚧 | 🚧 | 🚧 | 🧩 |
| Other frameworks<sup>4</sup> | 🚧 | 🚧 | 🚧 | 🚧 | 🧩 |
| **Refactoring** | | | | | |
| Rename | ✅ | 🔒 | 🔒 | ✅ | ✅ |
Expand All @@ -60,14 +61,14 @@ PHPantom focuses on deep type intelligence. Here's how it compares:
<sup>1</sup> Completion, hover, signature help, go-to-definition, find references, diagnostics, document symbols.<br>
<sup>2</sup> Auto-import, go-to implementation / type-definition, smart select, folding ranges, formatting, code lens, inlay hints, type hierarchy, document links.<br>
<sup>3</sup> Implement interface methods, extract method/function, extract/inline variable, generate constructor, generate getter/setter.<br>
<sup>4</sup> CakePHP, non-Composer WordPress, Symfony, Behat, PHPUnit, and Prophecy, and Twig.<br>
<sup>4</sup> CakePHP, non-Composer WordPress, Behat, PHPUnit, and Prophecy.<br>
<sup>5</sup> Convert between arrow functions and closures, and switch statements to match expressions.<br>
Performance measured on a production codebase: 21K PHP files, 1.5M lines of code (vendor + application). Time to ready is CPU time consumed until full type intelligence is available on a cold start (first index); tools with a disk cache launch faster on subsequent starts.
</sub>
</p>

> [!TIP]
> **Want to verify?** Open [`examples/php/`](examples/php/) in your editor and trigger completion at the marked locations in `completion.php`. It exercises every type intelligence feature in the table, including edge cases where tools diverge. For Laravel specifically, open [`examples/laravel/`](examples/laravel/) — a standalone project with real Eloquent models, config, routes, views, and translations that exercises Eloquent property resolution, query builder chaining, scopes, custom collections, and go-to-definition for config keys, route names, and translation strings.
> **Want to verify?** Open [`examples/php/`](examples/php/) in your editor and trigger completion at the marked locations in `completion.php`. It exercises every type intelligence feature in the table, including edge cases where tools diverge. Framework playgrounds live in [`examples/laravel/`](examples/laravel/) and [`examples/symfony/`](examples/symfony/).

## Context-Aware Intelligence

Expand All @@ -77,6 +78,7 @@ Performance measured on a production codebase: 21K PHP files, 1.5M lines of code
- **Conditional return types.** PHPStan-style conditional `@return` types resolve to the concrete branch at each call site.
- **Type aliases and shapes.** `@phpstan-type`, `@phpstan-import-type`, and `object{...}` shapes all resolve through to completions.
- **Laravel.** Eloquent relationships, scopes, accessors, casts, and Builder chains resolve end-to-end. Macros behave like real methods. Container strings like `app('cache')` resolve to the bound class, `auth()->user()` resolves to your configured model, authorization strings resolve to the gate definition or policy method that declares them, and query string compleation on both relation and column names. Blade templates get completion, hover, go-to-definition, and diagnostics through virtual PHP preprocessing. No ide-helper or database access required.
- **Symfony.** Container configuration, routes, Twig templates, translations, events, Messenger, forms, validation mappings, Doctrine metadata, and local configuration schemas participate in completion, navigation, references, rename, diagnostics, and code lenses across PHP, YAML, XML, XLIFF, and Twig.
- **Everything else you'd expect.** Generics, type narrowing, named arguments, destructuring, first-class callables, anonymous classes, `@deprecated` detection, and namespace segment drilling.

## Project Awareness
Expand Down
286 changes: 286 additions & 0 deletions config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,31 @@
"type": "string",
"description": "Override the detected PHP version (e.g. \"8.3\"). When unset, PHPantom infers from composer.json's platform or require.php.",
"pattern": "^\\d+\\.\\d+(\\.\\d+)?$"
},
"proxies": {
"type": "array",
"description": "Generated transparent-proxy discovery rules. Matching subclasses keep their PHP type, while project metadata is attributed to their real parent class.",
"items": {
"type": "object",
"properties": {
"paths": {
"type": "array",
"description": "Workspace-relative PHP files, directories, or glob patterns to scan for generated proxy subclasses.",
"items": {
"type": "string"
}
},
"marker-interface": {
"type": "string",
"description": "Fully-qualified interface that a generated subclass must directly implement to be treated as a transparent proxy."
}
},
"required": [
"paths",
"marker-interface"
]
},
"default": []
}
}
},
Expand Down Expand Up @@ -147,6 +172,267 @@
}
}
},
"symfony": {
"type": "object",
"description": "Symfony runtime metadata recovered statically from compiled containers and configured PHP attributes.",
"properties": {
"container": {
"type": "object",
"description": "Controls static compiled-container discovery. Container PHP is read as text and is never executed.",
"properties": {
"enabled": {
"type": "boolean",
"description": "Read Symfony compiled-container metadata.",
"default": true
},
"environment": {
"type": "string",
"description": "Cache environment used by automatic var/cache/<environment> discovery.",
"default": "dev"
},
"paths": {
"type": "array",
"description": "Optional workspace-relative compiled-container files, directories, or glob patterns. The newest useful match wins.",
"items": {
"type": "string"
}
}
}
},
"events": {
"type": "object",
"description": "Configures event-name matching and attribute adapters. Exact listener wiring comes from the compiled container.",
"properties": {
"ignored-prefixes": {
"type": "array",
"description": "Prefixes removed before event names are compared.",
"items": {
"type": "string"
}
},
"ignored-suffixes": {
"type": "array",
"description": "Suffixes removed before event names are compared.",
"items": {
"type": "string"
}
},
"publishers": {
"type": "array",
"description": "Rules that derive Symfony event publishers from PHP method attributes.",
"items": {
"type": "object",
"properties": {
"attribute": {
"type": "string",
"description": "Fully-qualified publisher attribute class."
},
"name-argument": {
"type": "string",
"description": "Named argument containing an explicit event name."
},
"name-position": {
"type": "integer",
"description": "Zero-based positional fallback for the explicit event-name argument.",
"minimum": 0
},
"dispatch-argument": {
"type": "string",
"description": "Named argument containing dispatch enum cases."
},
"dispatch-position": {
"type": "integer",
"description": "Zero-based positional fallback for the dispatch argument.",
"minimum": 0
},
"default-dispatch": {
"type": "array",
"description": "Dispatch names used when the attribute omits its dispatch argument.",
"items": {
"type": "string"
}
},
"dispatch-cases": {
"type": "object",
"description": "Map from PHP enum case name to the event-name dispatch segment.",
"additionalProperties": {
"type": "string"
}
},
"name-template": {
"type": "string",
"description": "Derived event-name template. Supports {dispatch}, {class}, {class_snake}, {method}, {method_snake}, {method_suffix}, and {method_suffix_snake}."
},
"explicit-name-template": {
"type": "string",
"description": "Template used for explicit names. Supports the same placeholders plus {name}.",
"default": "{name}"
},
"default-methods": {
"type": "array",
"description": "Method names that omit the method suffix.",
"items": {
"type": "string"
}
},
"skip": {
"type": "array",
"description": "Conditions that omit one derived dispatch when another attribute argument is set.",
"items": {
"type": "object",
"properties": {
"dispatch": {
"type": "string"
},
"argument": {
"type": "string"
},
"position": {
"type": "integer",
"minimum": 0
}
},
"required": [
"dispatch",
"argument"
]
}
}
},
"required": [
"attribute",
"name-template"
]
}
},
"subscribers": {
"type": "array",
"description": "Optional rules that expose event names written in subscriber method attributes before or without a compiled container.",
"items": {
"type": "object",
"properties": {
"attribute": {
"type": "string",
"description": "Fully-qualified subscriber attribute class."
},
"name-argument": {
"type": "string"
},
"name-position": {
"type": "integer",
"minimum": 0
},
"transport-argument": {
"type": "string"
},
"transport-position": {
"type": "integer",
"minimum": 0
},
"transport-cases": {
"type": "object",
"description": "Map from PHP enum case name to an event-name suffix.",
"additionalProperties": {
"type": "string"
}
}
},
"required": [
"attribute"
]
}
}
}
},
"expression-language": {
"type": "object",
"description": "Maps configured PHP attribute and constructor arguments to Symfony ExpressionLanguage strings and their PHP type contracts.",
"properties": {
"attributes": {
"type": "array",
"description": "Method-attribute arguments that contain an expression string or an array of expression strings.",
"items": {
"type": "object",
"properties": {
"attribute": {
"type": "string",
"description": "Fully-qualified PHP method attribute class."
},
"argument": {
"type": "string",
"description": "Named argument containing expressions."
},
"position": {
"type": "integer",
"description": "Zero-based positional fallback for the expression argument.",
"minimum": 0
},
"method-parameters": {
"type": "boolean",
"description": "Bind expression roots to method parameters with the same name.",
"default": false
},
"bindings": {
"type": "object",
"description": "Map expression roots to return, parameter:<name-or-index>, or class:<FQN> type sources.",
"additionalProperties": {
"type": "string"
}
}
},
"required": [
"attribute"
]
}
},
"constructors": {
"type": "array",
"description": "Expression object constructors nested inside PHP method attributes.",
"items": {
"type": "object",
"properties": {
"class": {
"type": "string",
"description": "Fully-qualified expression object class."
},
"argument": {
"type": "string",
"description": "Named constructor argument containing the expression."
},
"position": {
"type": "integer",
"description": "Zero-based positional fallback for the constructor argument.",
"minimum": 0
},
"inside-attribute-prefixes": {
"type": "array",
"description": "Only match constructors nested in attributes whose FQN starts with one of these prefixes. An empty list matches any attribute.",
"items": {
"type": "string"
}
},
"method-parameters": {
"type": "boolean",
"description": "Bind expression roots to method parameters with the same name.",
"default": false
},
"bindings": {
"type": "object",
"description": "Map expression roots to return, parameter:<name-or-index>, or class:<FQN> type sources.",
"additionalProperties": {
"type": "string"
}
}
},
"required": [
"class"
]
}
}
}
}
}
},
"formatting": {
"type": "object",
"description": "Controls the formatting strategy. PHPantom ships a built-in formatter (PER-CS 2.0 style). Projects with php-cs-fixer or PHP_CodeSniffer in composer.json require-dev automatically use those tools instead. Explicit configuration here always takes priority.",
Expand Down
Loading
Loading