phpantom_lsp 0.2.0

Fast PHP language server with deep type intelligence. Generics, Laravel, PHPStan annotations. Ready in an instant.
Documentation
# PHPantomLSP

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.

> **Note:** This project is in active development.

## Features

### Completion

- **Instance members** via `->` — methods, properties, constructor-promoted properties
- **Static members** via `::` — static methods, static properties, constants, enum cases
- **`parent::`** — inherited and overridden members (excludes private)
- **Traits and interfaces** — trait members appear on the using class; interface contracts are resolved
- **`@mixin` classes** — members from `@mixin` annotations are included
- **Magic members**`@property`, `@property-read`, `@method` from PHPDoc
- **Method chaining** — return types are followed through arbitrarily long chains
- **Null-safe chaining**`?->` is handled identically to `->`
- **Full signatures** in completion labels (parameters, types, return type)
- Magic methods (`__construct`, `__call`, etc.) are only suggested interally
- **Named argument completion** — when typing inside call parentheses, parameter names are suggested with a trailing `:`.
- **PHPDoc tag completion** — context-aware suggestions inside `/** … */` blocks.
- **Variable name completion** — typing `$` suggests variables that are in scope.
- **Class name completion** — unqualified and partially-qualified class names are completed from the current file
- **Function completion** — standalone functions are completed
- **Constant completion** — constants defined via `define()` are offered alongside class names
- **Deprecated detection** — members, classes, and functions marked with `@deprecated` in their PHPDoc are shown with strikethrough in the completion list

<img width="683" height="339" alt="image" src="https://github.com/user-attachments/assets/65e8220d-5d94-466f-aea7-2f239a8d4b19" />

### Go to Definition

- **Classes, interfaces, traits, enums** — same-file and cross-file
- **Methods, properties, constants** — resolves through inheritance, traits, and mixins
- **Standalone functions** — including PHP built-ins via embedded stubs
- **Variables** — jumps to the most recent assignment or declaration (assignment, parameter, `foreach`, `catch`, `static`/`global`)
- **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
- **Namespace resolution** — fully-qualified, partially-qualified, and aliased names via `use ... as ...`

### Type Resolution

PHPantom infers variable types from assignments, parameter hints, return types, and PHPDoc annotations, then uses them to power both completion and go-to-definition.

- **Union types** (`A|B`) and **intersection types** (`A&B`), including PHP 8.2 DNF types like `(A&B)|C`
- **Conditional return types** — PHPStan-style `@return ($param is class-string<T> ? T : mixed)`, used by patterns like `app(User::class)->getEmail()`
- **`@var` overrides** — inline `/** @var Type $var */` docblocks refine variable types
- **Ambiguous variables** — when a variable is assigned different types in conditional branches, all candidates are offered

### Type Narrowing

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)`:

- `instanceof` and negated `!instanceof`
- `is_a($var, ClassName::class)`
- `get_class($var) === ClassName::class` and `$var::class === ClassName::class` (including `!==` and reversed operand order)
- `assert($var instanceof ClassName)`
- **Custom assertion functions** via `@phpstan-assert` / `@psalm-assert` annotations:
  - `@phpstan-assert Type $param` — unconditional narrowing after the call
  - `@phpstan-assert-if-true Type $param` — narrows in the then-branch
  - `@phpstan-assert-if-false Type $param` — narrows in the else-branch

### Composer & Project Awareness

- Parses `composer.json` for PSR-4, classmap, and file autoload mappings
- Resolves cross-file class lookups on demand
- Discovers classes and functions from `require_once` files

> **Note:**
> - Run `composer install -o` (or `composer dump-autoload -o`) to generate autoload files needed for full class completion.
> - If your project doesn’t use Composer, you can create a minimal `composer.json` to generate a classmap:
>
>   ```json
>   {
>     "autoload": {
>       "classmap": ["src/"]
>     }
>   }
>   ```
>
>   Then run:
>   ```bash
>   composer dump-autoload -o
>   ```

## Building

The PHP stubs are managed as a Composer dependency in `stubs/`. Install them before building:

```bash
# Install the PHP stubs (requires Composer)
composer install

# Build
cargo build

# or for a release build
cargo build --release
```

> **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.

After updating stubs (`composer update`), just rebuild — the `build.rs` script watches `composer.lock` and re-embeds everything automatically.

For more details on how symbol resolution and stub loading work, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).

## Testing

Run the test suite:

```bash
cargo test
```

### Manual LSP Testing

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):

```bash
./test_lsp.sh
```

This is useful for verifying end-to-end behavior outside of an editor.

### Debugging

Enable logging by setting the `RUST_LOG` environment variable:

```bash
RUST_LOG=debug cargo run 2>phpantom.log
```

Logs are written to stderr, so redirect as needed.

## Editor Integration

PHPantomLSP communicates over stdin/stdout. Point your editor's LSP client at the binary:

- **Path:** `target/release/phpantom_lsp` (after `cargo build --release`)

### Zed

Install it as a dev extension from the `zed-extension/` directory in this repo:

1. Open Zed
2. Open the Extensions panel
3. Click **Install Dev Extension**
4. Select the `zed-extension/` directory

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.

To configure PHPantom LSP as the default PHP language server in Zed, add the following to your Zed settings (`settings.json`):

```json
{
  "languages": {
    "PHP": {
      "language_servers": ["phpantom_lsp", "!intelephense", "!phpactor", "!phptools", "..."]
    }
  }
}
```

## Contributing

See [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md).

## License

MIT - see [LICENSE](LICENSE).