|
1 | | -# PHPantomLSP |
| 1 | +# PHPantom |
2 | 2 |
|
3 | | -A fast, lightweight PHP language server that stays out of your way. Using only a few MB of RAM regardless of project size and fully usable in milliseconds without requiring high-end hardware. |
| 3 | +A fast, lightweight PHP language server written in Rust. Uses only a few MB of RAM regardless of project size and is fully responsive in milliseconds. No indexing phase, no background workers, no waiting. |
4 | 4 |
|
5 | | -> **Note:** This project is in active development. |
| 5 | +> [!NOTE] |
| 6 | +> PHPantom is in active development. Completion and go-to-definition are solid and used daily. More LSP features (hover, signature help, find references) are on the roadmap. |
6 | 7 |
|
7 | 8 | ## Features |
8 | 9 |
|
9 | | -### Completion |
10 | | - |
11 | | -- **Instance members** via `->` — methods, properties, constructor-promoted properties |
12 | | -- **Static members** via `::` — static methods, static properties, constants, enum cases |
13 | | -- **`parent::`** — inherited and overridden members (excludes private) |
14 | | -- **Traits and interfaces** — trait members appear on the using class; interface contracts are resolved |
15 | | -- **`@mixin` classes** — members from `@mixin` annotations are included |
16 | | -- **Magic members** — `@property`, `@property-read`, `@method` from PHPDoc |
17 | | -- **Method chaining** — return types are followed through arbitrarily long chains |
18 | | -- **Null-safe chaining** — `?->` is handled identically to `->` |
19 | | -- **Full signatures** in completion labels (parameters, types, return type) |
20 | | -- Magic methods (`__construct`, `__call`, etc.) are only suggested interally |
21 | | -- **Named argument completion** — when typing inside call parentheses, parameter names are suggested with a trailing `:`. |
22 | | -- **PHPDoc tag completion** — context-aware suggestions inside `/** … */` blocks. |
23 | | -- **Variable name completion** — typing `$` suggests variables that are in scope. |
24 | | -- **Class name completion** — unqualified and partially-qualified class names are completed from the current file |
25 | | -- **Function completion** — standalone functions are completed |
26 | | -- **Constant completion** — constants defined via `define()` are offered alongside class names |
27 | | -- **Deprecated detection** — members, classes, and functions marked with `@deprecated` in their PHPDoc are shown with strikethrough in the completion list |
28 | | - |
29 | | -<img width="683" height="339" alt="image" src="https://github.com/user-attachments/assets/65e8220d-5d94-466f-aea7-2f239a8d4b19" /> |
30 | | - |
31 | | -### Go to Definition |
32 | | - |
33 | | -- **Classes, interfaces, traits, enums** — same-file and cross-file |
34 | | -- **Methods, properties, constants** — resolves through inheritance, traits, and mixins |
35 | | -- **Standalone functions** — including PHP built-ins via embedded stubs |
36 | | -- **Variables** — jumps to the most recent assignment or declaration (assignment, parameter, `foreach`, `catch`, `static`/`global`) |
37 | | -- **Property type definition** — when the cursor is on a property or parameter at its declaration site, jumps to the class definition of its type hint |
38 | | -- **Namespace resolution** — fully-qualified, partially-qualified, and aliased names via `use ... as ...` |
39 | | - |
40 | | -### Type Resolution |
41 | | - |
42 | | -PHPantom infers variable types from assignments, parameter hints, return types, and PHPDoc annotations, then uses them to power both completion and go-to-definition. |
43 | | - |
44 | | -- **Union types** (`A|B`) and **intersection types** (`A&B`), including PHP 8.2 DNF types like `(A&B)|C` |
45 | | -- **Conditional return types** — PHPStan-style `@return ($param is class-string<T> ? T : mixed)`, used by patterns like `app(User::class)->getEmail()` |
46 | | -- **`@var` overrides** — inline `/** @var Type $var */` docblocks refine variable types |
47 | | -- **Ambiguous variables** — when a variable is assigned different types in conditional branches, all candidates are offered |
48 | | - |
49 | | -### Type Narrowing |
50 | | - |
51 | | -Completion results adapt to runtime type checks. PHPantomLSP narrows union types in both the positive and inverse branches of `if`/`else`, `while`, and `match(true)`: |
52 | | - |
53 | | -- `instanceof` and negated `!instanceof` |
54 | | -- `is_a($var, ClassName::class)` |
55 | | -- `get_class($var) === ClassName::class` and `$var::class === ClassName::class` (including `!==` and reversed operand order) |
56 | | -- `assert($var instanceof ClassName)` |
57 | | -- **Custom assertion functions** via `@phpstan-assert` / `@psalm-assert` annotations: |
58 | | - - `@phpstan-assert Type $param` — unconditional narrowing after the call |
59 | | - - `@phpstan-assert-if-true Type $param` — narrows in the then-branch |
60 | | - - `@phpstan-assert-if-false Type $param` — narrows in the else-branch |
61 | | - |
62 | | -### Composer & Project Awareness |
63 | | - |
64 | | -- Parses `composer.json` for PSR-4, classmap, and file autoload mappings |
65 | | -- Resolves cross-file class lookups on demand |
66 | | -- Discovers classes and functions from `require_once` files |
67 | | - |
68 | | -> **Note:** |
69 | | -> - Run `composer install -o` (or `composer dump-autoload -o`) to generate autoload files needed for full class completion. |
70 | | -> - If your project doesn’t use Composer, you can create a minimal `composer.json` to generate a classmap: |
| 10 | +PHPantom focuses on completion and go-to-definition and aims to do them really well. Here's where it stands: |
| 11 | + |
| 12 | +| | PHPantom | Intelephense | PHP Tools | Phpactor | PHPStorm | |
| 13 | +|---|---|---|---|---|---| |
| 14 | +| Completion | ✅ | ✅ | ✅ | ✅ | ✅ | |
| 15 | +| Go-to-definition | ✅ | ✅ | ✅ | ✅ | ✅ | |
| 16 | +| `@mixin` completion | ✅ | 💰 | ✅ | ✅ | ✅ | |
| 17 | +| `@phpstan-assert` narrowing | ✅ | ❌ | ✅ | ❌ | ⚠️ partial | |
| 18 | +| Conditional return types | ✅ | ❌ | ✅ | ❌ | ✅ | |
| 19 | +| Array shape inference | ✅ | ❌ | ✅ | ❌ | ✅ | |
| 20 | +| Object shape completion | ✅ | ❌ | ✅ | ❌ | ✅ | |
| 21 | +| `@phpstan-type` aliases | ✅ | ❌ | ✅ | ❌ | ✅ | |
| 22 | +| Hover | ❌ | ✅ | ✅ | ✅ | ✅ | |
| 23 | +| Signature help | ❌ | ✅ | ✅ | ✅ | ✅ | |
| 24 | +| Find references | ❌ | ✅ | ✅ | ✅ | ✅ | |
| 25 | +| Diagnostics | ❌ | ✅ | ✅ | ⚠️ limited | ✅ | |
| 26 | +| Rename / refactoring | ❌ | 💰 | ✅ | ✅ | ✅ | |
| 27 | +| Time to ready | **10 ms** | 1 min 25 s | 3 min 17 s | 15 min 39 s | 19 min 38 s | |
| 28 | +| RAM usage | **7 MB** | 520 MB | 3.9 GB | 498 MB | 2.0 GB | |
| 29 | +| Disk cache | **0** | 45 MB | 0 | 4.1 GB | 551 MB | |
| 30 | + |
| 31 | +<sub>Performance measured on a production codebase: 21K PHP files, 1.5M lines of code (vendor + application).</sub> |
| 32 | + |
| 33 | +## Context-Aware Intelligence |
| 34 | + |
| 35 | +- **Smart PHPDoc completion.** `@throws` detects uncaught exceptions in the method body, including those propagated from called methods. `@param` pre-fills with the name and type from the signature. Tags are filtered to context: `@var` only in property docblocks, `@param` only when there are undocumented parameters. Already-documented tags aren't suggested again. |
| 36 | +- **Array shape inference from code.** `$config = ['host' => 'localhost', 'port' => 3306]` offers key completion with no annotation. Incremental `$config['key'] = ...` assignments extend the shape. Nested access chains resolve through shapes and generics. `array_filter`, `array_map`, `array_pop`, `current`, etc. preserve the element type instead of losing it to `array`. |
| 37 | +- **Guard clause stacking.** Early return narrows subsequent code. Multiple guards stack to whittle a union down. Works in ternaries, `match(true)`, with `is_a()`, `assert()`. |
| 38 | +- **Generic collection foreach.** Iterating `Collection<User>`, `Generator<int, Item>`, or a class with `@implements IteratorAggregate<int, User>` resolves the loop variable to the element type. Keys too. |
| 39 | +- **Generics.** Class-level `@template` with substitution through inheritance (`@extends Base<User>`). |
| 40 | +- **Everything else you'd expect.** `foreach`, `clone`, `$arr[] = new Foo()`, destructuring with named keys, chained method calls in assignments. |
| 41 | + |
| 42 | +## Project Awareness |
| 43 | + |
| 44 | +PHPantom understands Composer projects out of the box: |
| 45 | + |
| 46 | +- **PSR-4 autoloading.** Resolves classes across files on demand. |
| 47 | +- **Classmap and file autoloading.** `autoload_classmap.php` and `autoload_files.php`. |
| 48 | +- **Embedded PHP stubs** from [phpstorm-stubs](https://github.com/JetBrains/phpstorm-stubs) bundled in the binary, no runtime downloads needed. |
| 49 | +- **`require_once` discovery.** Functions from required files are available for completion. |
| 50 | + |
| 51 | +> [!IMPORTANT] |
| 52 | +> Run `composer install -o` (or `composer dump-autoload -o`) in your project to generate the optimized autoload files PHPantom needs for cross-file class resolution. |
71 | 53 | > |
72 | | -> ```json |
73 | | -> { |
74 | | -> "autoload": { |
75 | | -> "classmap": ["src/"] |
76 | | -> } |
77 | | -> } |
78 | | -> ``` |
79 | | -> |
80 | | -> Then run: |
81 | | -> ```bash |
82 | | -> composer dump-autoload -o |
83 | | -> ``` |
84 | | -
|
85 | | -## Building |
86 | | -
|
87 | | -The PHP stubs are managed as a Composer dependency in `stubs/`. Install them before building: |
88 | | -
|
89 | | -```bash |
90 | | -# Install the PHP stubs (requires Composer) |
91 | | -composer install |
92 | | -
|
93 | | -# Build |
94 | | -cargo build |
95 | | -
|
96 | | -# or for a release build |
97 | | -cargo build --release |
98 | | -``` |
99 | | -
|
100 | | -> **Note:** The build will succeed without `composer install`, but the resulting binary won't know about built-in PHP symbols like `Iterator`, `Countable`, `UnitEnum`, etc. Always run `composer install` first for a fully functional build. |
101 | | -
|
102 | | -After updating stubs (`composer update`), just rebuild — the `build.rs` script watches `composer.lock` and re-embeds everything automatically. |
103 | | - |
104 | | -For more details on how symbol resolution and stub loading work, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md). |
105 | | - |
106 | | -## Testing |
107 | | - |
108 | | -Run the test suite: |
| 54 | +> If your project doesn't use Composer, you can create a minimal `composer.json`: |
| 55 | +> ```json |
| 56 | +> { "autoload": { "classmap": ["src/"] } } |
| 57 | +> ``` |
| 58 | +> Then run `composer dump-autoload -o`. |
109 | 59 |
|
110 | | -```bash |
111 | | -cargo test |
112 | | -``` |
| 60 | +## Getting Started |
113 | 61 |
|
114 | | -### Manual LSP Testing |
| 62 | +See **[docs/SETUP.md](docs/SETUP.md)** for editor-specific installation instructions (Zed, Neovim, and other editors). |
115 | 63 |
|
116 | | -The included `test_lsp.sh` script sends JSON-RPC messages to the server over stdin/stdout, exercising the full LSP protocol flow (initialize, open file, hover, completion, shutdown): |
| 64 | +## Building from Source |
117 | 65 |
|
118 | | -```bash |
119 | | -./test_lsp.sh |
120 | | -``` |
| 66 | +See **[docs/BUILDING.md](docs/BUILDING.md)** for build, test, and debug instructions. |
121 | 67 |
|
122 | | -This is useful for verifying end-to-end behavior outside of an editor. |
123 | | - |
124 | | -### Debugging |
125 | | - |
126 | | -Enable logging by setting the `RUST_LOG` environment variable: |
127 | | - |
128 | | -```bash |
129 | | -RUST_LOG=debug cargo run 2>phpantom.log |
130 | | -``` |
131 | | - |
132 | | -Logs are written to stderr, so redirect as needed. |
133 | | - |
134 | | -## Editor Integration |
135 | | - |
136 | | -PHPantomLSP communicates over stdin/stdout. Point your editor's LSP client at the binary: |
137 | | - |
138 | | -- **Path:** `target/release/phpantom_lsp` (after `cargo build --release`) |
139 | | - |
140 | | -### Zed |
141 | | - |
142 | | -Install it as a dev extension from the `zed-extension/` directory in this repo: |
| 68 | +## Contributing |
143 | 69 |
|
144 | | -1. Open Zed |
145 | | -2. Open the Extensions panel |
146 | | -3. Click **Install Dev Extension** |
147 | | -4. Select the `zed-extension/` directory |
| 70 | +See **[docs/CONTRIBUTING.md](docs/CONTRIBUTING.md)**. |
148 | 71 |
|
149 | | -The extension automatically downloads the correct pre-built binary from GitHub releases for your platform. If you'd prefer to use a locally built binary, ensure `phpantom_lsp` is on your `PATH` and the extension will use it instead. |
| 72 | +## Architecture |
150 | 73 |
|
151 | | -To configure PHPantom LSP as the default PHP language server in Zed, add the following to your Zed settings (`settings.json`): |
| 74 | +For details on how symbol resolution, stub loading, and inheritance merging work, see **[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)**. |
152 | 75 |
|
153 | | -```json |
154 | | -{ |
155 | | - "languages": { |
156 | | - "PHP": { |
157 | | - "language_servers": ["phpantom_lsp", "!intelephense", "!phpactor", "!phptools", "..."] |
158 | | - } |
159 | | - } |
160 | | -} |
161 | | -``` |
| 76 | +## Acknowledgements |
162 | 77 |
|
163 | | -## Contributing |
| 78 | +PHPantom stands on the shoulders of: |
164 | 79 |
|
165 | | -See [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md). |
| 80 | +- **[Mago](https://github.com/carthage-software/mago):** the PHP parser that powers all of PHPantom's AST analysis. |
| 81 | +- **[PHPStan](https://phpstan.org/)** and **[Psalm](https://psalm.dev/):** whose combined work on static analysis for PHP transformed the language's type ecosystem. Generics, array shapes, conditional return types, assertion annotations: these tools pushed each other forward and pushed the community toward rigorous PHPDoc annotations that make a language server like this possible. PHPantom's author cut his teeth on PHPStan, which is why `@phpstan-*` annotations are a first-class citizen here. |
| 82 | +- **[JetBrains phpstorm-stubs](https://github.com/JetBrains/phpstorm-stubs):** type information for the entire PHP standard library, embedded directly into the binary. |
166 | 83 |
|
167 | 84 | ## License |
168 | 85 |
|
169 | | -MIT - see [LICENSE](LICENSE). |
| 86 | +MIT. See [LICENSE](LICENSE). |
0 commit comments