### Setup PHPantom with Neovim (nvim-lspconfig) Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/SETUP.md Enable PHPantom as a language server in Neovim using the `nvim-lspconfig` plugin. ```lua require('lspconfig').phpantom.setup({}) ``` -------------------------------- ### PHPantom TOML Configuration Example Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/SETUP.md Example of a `.phpantom.toml` file showing settings for PHP version override, diagnostic toggles, and indexing strategy. ```toml [php] # Override the detected PHP version (default: inferred from composer.json, or 8.5). # version = "8.5" [diagnostics] # Report member access on subjects whose type could not be resolved. # Useful for discovering gaps in type coverage. Off by default. # unresolved-member-access = true [indexing] # How PHPantom discovers classes across the workspace. # "composer" (default) - use Composer classmap, self-scan on fallback # "self" - always self-scan, ignore Composer classmap # "none" - no proactive scanning, Composer classmap only # strategy = "composer" ``` -------------------------------- ### Completion Assertion Example Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/tests/fixtures/README.md Use `// expect:` or `// expect_absent:` to assert completion results. `// expect:` checks if any completion label starts with the given string. ```php // test: constructor argument infers template type // feature: completion // ignore: needs todo.md ยง2 (function-level @template) // expect: bar( --- ``` -------------------------------- ### Install PHPantom LSP via Homebrew Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/SETUP.md Use this command to install the PHPantom LSP on macOS and Linux systems with Homebrew. ```bash brew install phpantom-lsp ``` -------------------------------- ### Setup PHPantom with Neovim (Built-in LSP) Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/SETUP.md Configure PHPantom with Neovim's built-in LSP client. This method does not require external plugins. ```lua vim.lsp.config['phpantom'] = { cmd = { 'phpantom_lsp' }, filetypes = { 'php' }, root_markers = { 'composer.json', '.git' }, } vim.lsp.enable('phpantom') ``` -------------------------------- ### Install PHPantom LSP via Cargo Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/SETUP.md Install the PHPantom LSP using Cargo, Rust's package manager. Ensure you have the Rust toolchain installed. ```bash cargo install phpantom_lsp --locked ``` -------------------------------- ### GitHub Actions CI Integration for PHPantom Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/CLI.md Example of a GitHub Actions workflow to integrate PHPantom into a CI pipeline. It checks out the code, installs Composer dependencies (excluding dev), installs PHPantom, and then runs an analysis on the `src/` directory, failing the build if warnings are found. ```yaml name: Type Coverage on: [push, pull_request] jobs: phpantom: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 # Use --no-dev to catch production code that depends on dev-only # packages (e.g. calling PHPUnit classes from application code). - name: Install Composer dependencies run: composer install --no-interaction --prefer-dist --no-dev - name: Install PHPantom run: | curl -sL https://github.com/AJenbo/phpantom_lsp/releases/download/0.6.0/phpantom_lsp-x86_64-unknown-linux-gnu.tar.gz | tar xz chmod +x phpantom_lsp - name: Check type coverage run: ./phpantom_lsp analyze --severity warning --no-colour src/ ``` -------------------------------- ### Example Output of Fixes Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/CLI.md Illustrates the typical output when the `fix` command applies changes, showing the lines affected and the rules applied. ```text ------ ------------------------------------------- Line src/Service/UserService.php ------ ------------------------------------------- 5 Unused import 'App\Models\LegacyUser' ๐Ÿ”ง unused_import 6 Unused import 'App\Support\OldHelper' ๐Ÿ”ง unused_import ------ ------------------------------------------- [FIXED] Applied 2 fixes across 1 file ``` -------------------------------- ### Example Analyze Output Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/CLI.md Illustrates the PHPStan-style table format for reporting diagnostics, including line numbers, messages, and diagnostic identifiers. ```text ------ ------------------------------------------- Line src/Service/UserService.php ------ ------------------------------------------- 15 Unknown class 'App\Models\LegacyUser'. ๐Ÿชช unknown_class 42 Call to undefined method Post::archive(). ๐Ÿชช unknown_member ------ ------------------------------------------- ``` -------------------------------- ### Fixture Header Directives Example Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/tests/fixtures/README.md Illustrates how to use plain comments within the header section of a fixture file, which are silently ignored by the runner. ```php // test: guard clause narrows type after early return // feature: completion // This is adapted from phpactor if-statement/type_after_return.test // expect: barMethod( ``` -------------------------------- ### Start PHPantom LSP Server via TCP Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/CLI.md Use the `--tcp` flag to start the LSP server listening on a specific TCP port. This is useful for debugging or connecting with network-aware clients. The server exits when the client disconnects. ```sh phpantom_lsp --tcp 9257 # listen on 127.0.0.1:9257 phpantom_lsp --tcp 127.0.0.1:9257 # same, explicit host phpantom_lsp --tcp 0.0.0.0:9257 # listen on all interfaces phpantom_lsp --tcp 0 # OS picks a free port ``` -------------------------------- ### Signature Help Assertion Example Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/tests/fixtures/README.md Assert signature help details using `// expect_sig_label:`, `// expect_sig_active:`, and `// expect_sig_param:`. At least one signature help assertion is required. ```php // expect_sig_label: (string $name, int $age): void // expect_sig_active: 0 // expect_sig_param: string $name // expect_sig_param: int $age ``` -------------------------------- ### Install Project-Level Stubs via Composer Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/todo/external-stubs.md Include stub packages as dev dependencies in your project's composer.json file for zero-config integration. ```json { "require-dev": { "jetbrains/phpstorm-stubs": "^2025.3", "php-stubs/wordpress-stubs": "^6.0", "php-stubs/acf-pro-stubs": "^6.0" } } ``` -------------------------------- ### Example Output of Dry Run Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/CLI.md Shows the output of a dry run, indicating which fixes would be applied without making any changes to the files. ```text ------ ------------------------------------------- Line src/Service/UserService.php ------ ------------------------------------------- 5 Unused import 'App\Models\LegacyUser' ๐Ÿ”ง unused_import ------ ------------------------------------------- [DRY RUN] 1 fix in 1 file (not applied) ``` -------------------------------- ### Flattened Facade Method Signature Example Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/todo/laravel.md This illustrates the problem where template parameters are flattened in the generated PHPDoc for facades, leading to less precise type resolution. ```php @method static mixed make(string $abstract) ``` -------------------------------- ### Definition Assertion Example Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/tests/fixtures/README.md Use `// expect_definition:` to specify the expected definition location. It can be relative to the cursor file (`self:line`) or an absolute path (`file:line`). ```php // expect_definition: self:12 // expect_definition: src/Foo.php:5 ``` -------------------------------- ### PHP 8.4 Asymmetric Visibility Example Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/todo/type-inference.md Illustrates asymmetric visibility for plain and promoted properties introduced in PHP 8.4 and extended in PHP 8.5. This allows different visibility modifiers for reading and writing a property. ```php class Settings { public private(set) string $name; public function __construct( public protected(set) int $retries = 3, ) {} } ``` -------------------------------- ### Manually Connect to PHPantom LSP TCP Server Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/CLI.md For manual testing, you can start the PHPantom LSP server with the `--tcp` flag in one terminal and then use a tool like `nc` (netcat) in another terminal to connect to the specified address and port, sending JSON-RPC messages. ```sh # In one terminal, start the server: phpantom_lsp --tcp 9257 # In another terminal, connect and send JSON-RPC: nc 127.0.0.1 9257 ``` -------------------------------- ### Pathological test file performance breakdown Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/todo/performance.md Example output showing the performance breakdown for a specific file, `PurchaseFileService.php`. This format, generated during Phase 2 of analysis, provides timings for different aspects like fast, class resolution, memory, function execution, and unresolved symbols. ```text โฑ 63.2s src/core/Purchase/Services/PurchaseFileService.php [fast=1ms cls=40ms mem=23696ms fn=12ms unres=16781ms arg=22568ms impl=0ms depr=54ms] ``` -------------------------------- ### IDE Initialization Options for Stubs Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/todo/external-stubs.md IDE extensions communicate the path to bundled stubs via initializationOptions in the LSP initialize request. ```json { "initializationOptions": { "stubs": { "path": "/path/to/bundled/stubs" } } } ``` -------------------------------- ### Build the Release Binary Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/BUILDING.md Compiles the project in release mode to create an optimized binary. ```bash cargo build --release # build the binary ``` -------------------------------- ### Init Functions: Populating fqn_uri_index Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/ARCHITECTURE.md Covers the initialization process where `fqn_uri_index` is populated from various sources like Composer classmaps, Drupal scans, PSR-0, and phar archives. All paths are converted to URIs during this phase. ```text **Init functions**: populate `fqn_uri_index` from Composer classmap, Drupal scans, PSR-0, phar archives, or full workspace scans. All paths are converted to URI strings at init time. ``` -------------------------------- ### Generate Default .phpantom.toml Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/SETUP.md Run this command in your project's root directory to create a `.phpantom.toml` file with all options documented and commented out. ```bash phpantom_lsp init ``` -------------------------------- ### Run the Full Test Suite Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/BUILDING.md Executes all tests defined for the project. ```bash cargo test ``` -------------------------------- ### Ignore Fixture Example Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/tests/fixtures/README.md Use `// ignore:` to mark a test as ignored, providing a reason. This is useful for tests covering planned features. ```php // ignore: needs todo.md ยง2 (function-level @template) ``` -------------------------------- ### Attribute Constructor Signature Help Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/todo/signature-help.md Demonstrates where signature help should trigger within an attribute argument list. This helps in identifying the parameters for the attribute's constructor. ```php #[Route('/users', methods: ['GET'])] // ^ signature help here showing Route::__construct params ``` -------------------------------- ### Base Resolution Flowchart Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/ARCHITECTURE.md Illustrates the process of merging members during base resolution, starting with own members, then traits, and finally the parent class chain. ```text ClassInfo (own members) โ”‚ โ”œโ”€โ”€ 1. Merge used traits (via `use TraitName;`) โ”‚ โ””โ”€โ”€ Recursively follows trait composition and parent_class chains โ”‚ โ””โ”€โ”€ 2. Walk the extends chain (parent_class) โ””โ”€โ”€ For each parent: merge its traits, then its public/protected members ``` -------------------------------- ### Example of Laravel Facade Method Signature Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/todo/laravel.md This shows the expected PHPDoc signature for a facade method with template parameters, which is preferred for better type resolution. ```php /** * @template T of object * @param class-string $abstract * @return T */ public function make($abstract) { ... } ``` -------------------------------- ### Build PHPantom LSP from Source Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/SETUP.md Compile the PHPantom LSP from its source code. The release binary will be located in `target/release/phpantom_lsp`. ```bash cargo build --release # Binary is at target/release/phpantom_lsp ``` -------------------------------- ### Analyze Entire Project Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/CLI.md Scan the entire PHP project for diagnostics. This is the default behavior when no path is specified. ```bash phpantom_lsp analyze ``` -------------------------------- ### Function Lookup Flowchart Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/ARCHITECTURE.md Illustrates the multi-phase lookup process for `find_or_load_function`, showing how it checks global functions, byte-level scans, autoload files, and embedded stubs before failing. ```text find_or_load_function(["str_contains", "App\\str_contains"]) โ”‚ โ”œโ”€โ”€ Phase 1: global_functions (user code + cached stubs) โ”‚ Checks all candidate names against the global_functions map. โ”‚ Keys are always FQNs: "array_map" for global functions, โ”‚ "Illuminate\Support\enum_value" for namespaced ones. โ”‚ No short-name fallback entries are stored. โ”‚ โ†“ miss โ”‚ โ”œโ”€โ”€ Phase 1.5: autoload_function_index (byte-level scan) โ”‚ Checks candidate names against autoload_function_index โ”‚ (function FQN โ†’ file path on disk). Populated by the โ”‚ find_symbols byte-level scan for both Composer projects โ”‚ (autoload_files.php) and non-Composer projects (workspace โ”‚ full-scan). โ”‚ When found: โ”‚ 1. Reads the file from disk. โ”‚ 2. Calls update_ast to get full FunctionInfo + ClassInfo. โ”‚ 3. Results are cached in global_functions (so future โ”‚ lookups hit Phase 1). โ”‚ 4. Returns the matching FunctionInfo. โ”‚ โ†“ miss โ”‚ โ”œโ”€โ”€ Phase 1.75: Last-resort autoload file parse โ”‚ The byte-level scanner misses functions inside conditional โ”‚ blocks (e.g. `if (! function_exists(...))` guards). As a โ”‚ safety net, lazily parses each known autoload file path โ”‚ (stored in autoload_file_paths) via update_ast until the โ”‚ function is found. Skips files already in uri_classes_index. Each โ”‚ file is parsed at most once; subsequent lookups hit Phase 1. โ”‚ โ†“ miss โ”‚ โ”œโ”€โ”€ Phase 2: Embedded PHP stubs โ”‚ Looks up each candidate name in stub_function_index. โ”‚ When found: โ”‚ 1. Parses the entire stub file (extracting all FunctionInfo). โ”‚ 2. Caches ALL functions from that file into global_functions โ”‚ under phpantom-stub-fn:// URIs (FQN keys only). โ”‚ 3. Also caches any classes defined in the same stub file into โ”‚ uri_classes_index (so return type references can be resolved). โ”‚ 4. Returns the matching FunctionInfo. โ”‚ โ†“ miss โ”‚ โ””โ”€โ”€ None ``` -------------------------------- ### PHPStan @method Tag with Template Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/todo/laravel.md Example of a PHPStan `@method` tag supporting template parameters using the `` syntax. This allows for richer static analysis of generic methods on facades. ```php /** * @method static T make(class-string $abstract) */ class App extends Facade { ... } ``` -------------------------------- ### PHP 8.4 Property Hooks Example Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/todo/type-inference.md Demonstrates the syntax for defining getter and setter hooks on a class property in PHP 8.4. This allows custom logic for property access and modification. ```php class User { public string $name { get => strtoupper($this->name); set => trim($value); } } ``` -------------------------------- ### Single File Fixture Structure Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/tests/fixtures/README.md Demonstrates the basic structure of a single-file fixture, including header directives and the PHP body with a cursor marker. ```php // test: human-readable description // feature: completion // expect: bar( --- <> ``` -------------------------------- ### Multi-File Fixture Structure Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/tests/fixtures/README.md Shows the structure for multi-file fixtures, using '=== path ===' delimiters to separate files. Exactly one file must contain the cursor marker '<>'. ```php // test: cross-file PSR-4 completion // feature: completion // expect: doWork( --- === src/Helper.php === <> } } ``` -------------------------------- ### Configure .phpantom.toml Stub Paths Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/todo/external-stubs.md Specify custom stub directories in the .phpantom.toml file. Paths are resolved relative to the workspace root unless absolute. ```toml [stubs] paths = [ "./stubs", "/opt/company/php-stubs", ] ``` -------------------------------- ### Model ID Type Hinting with HasUuids/HasUlids Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/todo/laravel.md Illustrates how models using `HasUuids` or `HasUlids` traits should have their primary key (`$id`) typed as `string` instead of the default `int`. This example shows the expected type assertion. ```php assertType('string', $uuidModel->id) ``` -------------------------------- ### Symbol Resolution Flow: find_or_load_class Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/ARCHITECTURE.md Illustrates the sequential phases `find_or_load_class` uses to resolve a class name, starting from a cache hit to PSR-4 resolution and finally embedded stubs. Returns as soon as a phase succeeds. ```text find_or_load_class("Iterator") โ”‚ โ”œโ”€โ”€ Phase 0: fqn_class_index (O(1) hash hit) โ”‚ Direct lookup by FQN. This is where cached results from โ”‚ previous phases are found on subsequent lookups. โ”‚ โ†“ miss โ”‚ โ”œโ”€โ”€ Phase 1: fqn_uri_index (FQN โ†’ file URI) โ”‚ Direct hash lookup in the unified FQN-to-URI index, populated โ”‚ at init time from Composer's classmap, the self-scanner, and โ”‚ phar archives. Covers all classes (including vendor) that don't โ”‚ need PSR-4 path computation. โ”‚ โ†“ miss โ”‚ โ”œโ”€โ”€ Phase 2: PSR-4 resolution โ”‚ Uses PSR-4 mappings from composer.json to locate the file on disk. โ”‚ These mappings only cover user code (vendor PSR-4 is not loaded). โ”‚ Example: "App\Models\User" โ†’ workspace/src/Models/User.php โ”‚ Reads, parses, resolves names, caches in uri_classes_index. โ”‚ โ†“ miss โ”‚ โ”œโ”€โ”€ Phase 3: Embedded PHP stubs โ”‚ Looks up the class name in the compiled-in stub index โ”‚ (from phpstorm-stubs). Parses the stub PHP source, caches โ”‚ in uri_classes_index under a phpantom-stub:// URI. โ”‚ โ†“ miss โ”‚ โ””โ”€โ”€ None ``` -------------------------------- ### Hover Assertion Example Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/tests/fixtures/README.md Supports two hover modes: cursor hover using `// expect:` and symbol hover using `// expect_hover:`. Cursor hover checks if the hover content contains expected substrings. ```php // expect: // expect_hover: symbol => substring ``` -------------------------------- ### Go-to-Implementation Scanning Phases Source: https://github.com/phpantom-dev/phpantom_lsp/blob/main/docs/ARCHITECTURE.md Illustrates the five progressive phases used by find_implementors to locate concrete classes implementing an interface or extending an abstract class. Each phase expands the search scope. ```text find_implementors("Cacheable", "App\Contracts\Cacheable") โ”‚ โ”œโ”€โ”€ Phase 1: uri_classes_index (already-parsed classes) โ”‚ Iterates every ClassInfo in every file already in memory. โ”‚ Checks interfaces list and parent_class chain against the target. โ”‚ โ†“ continue โ”‚ โ”œโ”€โ”€ Phase 2: fqn_uri_index (FQN โ†’ URI entries not yet covered) โ”‚ Loads classes via class_loader for entries not seen in Phase 1. โ”‚ โ†“ continue โ”‚ โ”œโ”€โ”€ Phase 3: fqn_uri_index files (string pre-filter โ†’ parse) โ”‚ Collects unique file paths from fqn_uri_index. โ”‚ Skips files already in uri_classes_index. โ”‚ Reads each file's raw source and checks contains(target_short). โ”‚ Only matching files are parsed via parse_and_cache_file. โ”‚ Every class in a parsed file is checked (not just the looked-up FQN). โ”‚ โ†“ continue โ”‚ โ”œโ”€โ”€ Phase 4: embedded stubs (string pre-filter โ†’ lazy parse) โ”‚ Checks each stub's static source string for contains(target_short). โ”‚ Matching stubs are loaded via class_loader (parsed and cached). โ”‚ โ†“ continue โ”‚ โ”œโ”€โ”€ Phase 5: PSR-4 directory walk (user code only) โ”‚ Recursively collects all .php files under every PSR-4 root. โ”‚ Skips files already covered by fqn_uri_index (Phase 3) or uri_classes_index. โ”‚ Reads raw source, applies the same string pre-filter. โ”‚ Matching files are parsed via parse_and_cache_file. โ”‚ Discovers classes missing from fqn_uri_index. โ”‚ โ†“ done โ”‚ โ””โ”€โ”€ Vec (concrete implementors only) ```