phpantom_lsp

PHPantom-devMIT活跃维护

用 Rust 写的高性能 PHP 语言服务器,提供泛型、Laravel 和 PHPStan 注解的深度类型推断,启动即用,适合在 Neovim、VS Code 等编辑器里获得快速准确的代码补全与跳转。

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

0.11.1发布于 2026-10-07 04:25 UTC1,283star +1 / 7 天80fork同步于 2026-11-10 11:49 UTC

怎么上手

由 README 提炼,执行前请确认
cargo install phpantom_lsp
硬件要求按 README 说明

README 未说明显存或 GPU 要求;性能测试显示在 5.1K PHP 文件的生产级 Laravel 代码库上常驻内存约 578 MB。

下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

用 Rust 写的快速 PHP 语言服务器,专注深度类型推断,适合想要秒开且内存占用低的 PHP 工程师

用 Rust 写的高性能 PHP 语言服务器,提供泛型、Laravel 和 PHPStan 注解的深度类型推断,启动即用,适合在 Neovim、VS Code 等编辑器里获得快速准确的代码补全与跳转。

核心能力

  • 用 Rust 实现,后台索引,缓存命中后约 2 秒就绪
  • 支持泛型与 @template、数组形状、条件返回类型等 PHPDoc 高级类型
  • 内置 Laravel、Symfony、WordPress、Drupal、Yii 3、PHPUnit 与 Pest 框架支持
  • 在服务器内集成 PHPStan、PHPCS、PHPMD、Mago 并附带快速修复
  • 提供重命名、提取与内联、生成构造函数与访问器、现代化语法改写

容易踩的坑

WordPress 的核心函数与类需要先通过 Composer 安装 php-stubs/wordpress-stubs 才能获得类型;框架支持依赖项目自身已有的代码,无需额外下载或配置;README 未给出编辑器端的具体接入配置,需参考官方文档。

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本0.11.1
  1. 0.11.1最新正式版2026-10-07 · 2026-10-07 04:25 UTC

    PHPantom 0.11.1

    Highlights

    New editor features

    • Unreachable code is dimmed. Code that can never run, such as statements after a return or throw, is greyed out the way an unused import is (@petrovo-as).

    Laravel

    • Translations across locales. Navigation, completion and hover work across JSON and PHP language files and every locale, and a missing key gets a quick fix when its PHP group file exists (@shuvroroy).
    • Package translations. Paths registered with loadTranslationsFrom() now support completion, navigation, hover and key diagnostics (@shuvroroy).
    • Config keys set by a package's provider. A key set with config([...]) or Config::set() is known, so auth('sanctum') is no longer reported as an unknown guard.

    Blade

    • Fewer false syntax errors. An Alpine or Vue :attr binding on a plain HTML tag is no longer parsed as PHP, and a directive newer than your Laravel is plain text, so a JSON-LD "@context" key no longer produces syntax errors before Laravel 11.
    • @break and @continue keep their condition. @break($done) and @continue($skip) now only jump when the condition holds.
    • @use imports. The first import no longer breaks a template that opens with a block, unused imports are dimmed and removed by phpantom_lsp fix, and a group wrapped over several lines no longer shifts the lines below it.
    • Stable analyze output. Blade diagnostics no longer vary between runs and now match what the editor shows.

    Type checking

    • match (true) arms know the arms above them. default => strtoupper($name) no longer reports $name as possibly null (@phcorp, #464).
    • Type checks narrow properties. is_resource($this->stream) narrows a resource|null property the way it narrows a variable (@phcorp, #466).
    • Integer range() calls are lists of integers. array_map(fn (int $i) => …, range(0, $n - 1)) no longer reports that the callback is passed int|float (@phcorp, #467).
    • More accurate narrowing. in_array() only narrows by what the list proves, match arms narrow the way the equivalent if does, and a ternary to the right of && or || sees what the left side proved.
    • Unsealed array shapes are checked. A type such as array{foo: int, ...} no longer accepts any non-empty array, and the entries it lists must fit the typed array it is passed to.

    Code actions

    • Safer class moves. In the editor and with phpantom_lsp move, moves that would pull other declarations into the new namespace, or break a file with several namespace blocks, are refused, and the rest touch only their own block.
    • Imports respect each namespace block. In a file with several namespace blocks, new imports go into the block that needs them, and an import only another block uses is dimmed as unused.
    • Member actions appear at column 0. Actions on a property or method, such as getter/setter and visibility, are now offered with the cursor at column 0 or on a whole-line selection.

    Configuration and indexing

    • PHP_CodeSniffer only runs where you use it. A vendor/bin/phpcs installed by another package no longer produces warnings unless squizlabs/php_codesniffer is in require-dev, a ruleset file exists, or .phpantom.toml sets [phpcs] standard.
    • laravel-ide-helper files are skipped. _ide_helper.php and _ide_helper_models.php are no longer indexed, since PHPantom resolves facades and models natively; list them with a leading ! in [indexing] exclude to index them anyway.
    • Files outside the project no longer break indexing. A PHP file opened from elsewhere, or a change in another workspace folder, no longer stops the server when the configuration changes.

    Performance

    • Blade indexing. Templates with no DocBlock declaring their variables are indexed much faster, with the work spread across every core.
    • Long scripts and large classes. A long file of t
  2. 0.11.02026-10-06 · 2026-10-06 01:12 UTC
    查看更新说明

    Highlights

    New command-line tools

    • phpantom_lsp format formats a whole project's PHP and Blade files with the same formatter your editor uses. --check makes it a CI gate.
    • phpantom_lsp move moves a class or a whole namespace. It updates declarations, imports, references, and PSR-4 paths, and lists any leftover mentions it couldn't rewrite. (@calebdw)
    • analyze accepts multiple paths, so a pre-commit hook can pass only the files that changed.
    • phpantom_lsp init is interactive. It asks about the settings people change most often and writes a .phpantom.toml with only what you changed.

    New editor features

    • Call hierarchy. See who calls a function or method, and what it calls. (@sidux)
    • Reference and implementation CodeLens. Clickable counts above declarations open the references or implementations list. (@sidux)
    • Convert qualified names to imports, for one name or for a whole namespace at once, with aliases added automatically when names collide. (@calebdw)
    • Navigate to PHP classes from YAML and XML files. Ctrl+Click a class name or Class::member in config files to open it. (@sidux)
    • PHPUnit data providers and @depends are navigable. Renaming a provider method also updates the attributes and annotations that point to it.
    • PHPMD integration. Mess Detector findings appear in the editor using your project's existing config.

    Blade

    • Blade templates can be formatted. Optionally the PHP inside them is formatted too, and Pint is used if your project relies on it.
    • Document outline and folding for .blade.php files.
    • Custom directives registered with Blade::directive() / Blade::if() are recognised.
    • "Create missing view" quick fix.
    • Diagnostics for unbalanced component tags.
    • Named slots are scoped correctly, and anonymous components without @props get attribute completion.

    Laravel

    • builder-of, collection-of, factory-of, and relation-of PHPDoc types resolve to the model's real Eloquent classes, including custom builders and dotted relation paths. They also work inside Closure(…) parameter types. (@calebdw)
    • view-string parameters complete template names and report unknown views. (@calebdw)
    • Storage disks and other configured service names (log channels, cache stores, auth guards, and more) get completion, hover, go-to-definition, references, and typo diagnostics. (@shuvroroy)
    • UUID/ULID primary keys are typed as string. (@shuvroroy)
    • Custom pivot accessors (->as('participation')) are understood. (@shuvroroy, #381)

    Configuration and indexing

    • Exclude paths and add PHP file extensions with [indexing] in .phpantom.toml, for example Drupal's .module and .inc. (@syntlyx)
    • VS Code and Cursor users get this automatically from files.exclude and files.associations, and changes apply without a restart.
    • Symlinked directories are indexed with the rest of the project. (@liudashuang, #383)
    • Nullable-only argument mismatches can be downgraded to warnings. (@iz-ahmad)

    Performance

    • Find References and the reference CodeLens are much faster on large projects. Searches run in parallel, skip files that can't match, and reuse earlier results. Opening a large Laravel controller went from about 13 s of background work to well under a second on repeat visits.
    • Hover and completion no longer slow down in long functions with many local variables (4.3 s → 0.8 s on one test file).
    • Analysis of very large files is faster (2.1 s → 0.7 s on php-parser's generated parser).
    • Editing a file and searching for workspace symbols do much less work.

    Other

    • New semantic-export library feature for headless tools that need declarations, references, and diagnostics without running an LSP. (@aaaaaandrew)
    • Bundled mago toolchain updated to 1.47.5. (@nguyentranchung)
    • Extract interface is only offered in editors that can cre
  3. 0.10.02026-08-20 · 2026-08-20 01:22 UTC
    查看更新说明

    What's Changed

    Added

    Whole-project analysis
    • Full workspace indexing. PHPantom now parses every PHP file in your project in the background after startup by default, building complete symbol data and a cross-file reference index. Find References, Rename, Go to Implementation, and Type Hierarchy resolve against the whole project instead of only the files you have opened, and scan only the files known to reference the symbol. Lighter modes remain available for projects that prefer a smaller footprint. Contributed by @sidux in https://github.com/PHPantom-dev/phpantom_lsp/pull/186.
    • Workspace-wide diagnostics. Problems can now be surfaced across the whole project rather than only in open files. Set workspace = true under [diagnostics] in .phpantom.toml and, once startup and the background index finish, diagnostics run over every file and stream into the editor's problems panel as they are found, so issues in files you have not opened are already visible when you navigate to them. Configured external tools (PHPStan, PHPCS, Mago) also run once over the whole project afterwards. Both passes are deferred until after startup so they never slow down the time it takes the editor to become usable. It is off by default: a project-wide sweep is real work on every session, and it is worth asking for rather than paying for unasked.
    Blade templates
    • A Blade template's variables come from a declared priority chain. What a template has in scope is now resolved rather than guessed at. A @bladestan-signature docblock is the template's contract; @props and @aware fill in what it leaves out; a component's own class, and Livewire's $this, supply their members; View::share() and View::composer() registrations in your service providers are read wherever they are written; a layout's declarations reach every template that extends it; and anything still undeclared is inferred from the call sites that render the template. Completion, hover, go-to-definition, and undefined-variable diagnostics all read the same set, alongside the variables Blade itself injects ($attributes, $slot, $componentName, $errors, $loop). Each source only fills in what the ones above it did not declare, so a template that documents its own contract keeps it. Closes #296.
    • Every way of rendering a Blade template is a render site. view(), View::make(), Route::view(), Response::view(), the view factory's first(), renderWhen(), renderUnless(), and renderEach(), a mailable's new Content(view: …) and $this->view(), and Blade's own @include family, @extends, and @each all navigate, hover, complete, and hand the template the data they pass. A render is recognised by its receiver's type rather than by how it is spelled, so a view factory injected into a constructor and a mailable held in a local count too, and a data argument that names nothing (view('page', $data), ->with($extra), array_merge(…)) is read off its type when that type is a single array shape. Contributed by @shuvroroy (#337).
    • A view() call is checked against the Blade template's contract. A template that declares what it needs now holds its callers to it, the same way a function call is held to a signature: a variable the render does not pass, one whose type the declaration does not accept, and a key nothing in the template reads are each reported where the mistake is. This is the editor half of the rule Bladestan (the PHPStan extension for Blade) enforces in CI, so one annotation produces the same errors live while typing and on the build. A template that declares nothing is not checked at all, which is what makes this opt-in, and the checks stand down wherever the data stops being readable. A declaration that widens what its layout declared is reported on the template that writes it rather than at every call site.
    • Blade components are first-class. <x-alert>, <x-forms.date-picker>, and `<livewir
  4. 0.9.02026-07-19 · 2026-07-19 23:24 UTC
    查看更新说明

    Added

    • Macro hover shows origin and inferred return types. Hovering on a macro method call now displays a "macro" indicator instead of the generic "virtual" label, distinguishing ::macro() registrations from @method/@mixin synthesized members. When the closure has no explicit return type hint, the return type is inferred from the closure body and shown with an "(inferred)" annotation. Bare $this / self / static returns preserve their keyword form, and method chains like $this->transform(...) use the last method's declared return type directly, preserving $this, static, and generic parameters that the general resolver would flatten to a bare class name. Regular (non-macro) methods with inferred return types also show the "(inferred)" annotation on hover. Contributed by @calebdw.
    • Return type mismatch diagnostics (type_mismatch_return). Functions and methods with a declared return type are now checked against their return statements. Incompatible return values are flagged as errors. Void functions returning a value and bare return; in non-void functions are also flagged. Generators (functions using yield) are skipped. Uses the same conservative is_type_compatible policy as argument type checking to avoid false positives. Contributed by @calebdw.
    • Property type assignment diagnostics (type_mismatch_property). Assignments to typed properties ($this->prop = expr and self::$prop = expr) are checked against the declared property type. Incompatible values are flagged as errors. Only plain = assignments are checked; compound operators (+=, .=, etc.) are skipped. Untyped and mixed properties are not flagged. Contributed by @calebdw.
    • Conditional return types keep an intersection with the matched class. A @return ($x is class-string<T> ? T&SomeInterface : SomeInterface) annotation now resolves the matched branch to the concrete class intersected with the interface, instead of collapsing it to the bare class. Mock factories such as Mockery's mock(Foo::class) and Laravel's $this->mock(Foo::class) therefore resolve to Foo&MockInterface, so their members complete and assigning the result to a Foo-typed property or returning it from a Foo&MockInterface method no longer reports a spurious type mismatch.
    • PSR-4 mismatch diagnostics and rename-based moves. Files now warn when the declared namespace or primary class name does not match the PSR-4 path or filename, with quick fixes to correct them. Renaming a class from its declaration now opens the full FQCN so you can move it between namespaces in one step, and renaming a namespace can rewrite multiple segments at once while moving PSR-4 directories and updating references across the project. Contributed by @calebdw.
    • Case-sensitive autoloading diagnostic. A class reference whose casing differs from the class's actual declaration is now flagged, with a quick fix to correct it. This catches the bug where code loads on a case-insensitive filesystem (macOS, Windows) but fails with a class-not-found error on Linux, because PSR-4 maps the name to a file path and path lookups are case-sensitive there. It covers use imports and inline references to autoloaded classes; built-in classes and same-file references, which never reach the autoloader, are left alone.
    • Completion candidates ranked by dependency provenance. Class, function, and constant completions are now sorted by origin tier: project code first, then core/stub symbols, then explicit Composer dependencies (require / require-dev), then transitive vendor dependencies last. The provenance is inferred from composer.json and installed.json during indexing. Contributed by @calebdw.
    • analyze and fix work without composer.json. Both commands now treat a directory that has no composer.json (a WordPress site, a legacy codebase) as a plain PHP project: classes are indexed by scanning the tree and files are discovered by walking the roo
  5. 0.8.02026-05-14 · 2026-05-14 12:23 UTC
    查看更新说明

    Added

    • Blade template support. Completion, hover, go-to-definition, diagnostics, semantic tokens, and inlay hints work inside .blade.php files. Contributed by @MingJen in https://github.com/AJenbo/phpantom_lsp/pull/100.
    • Blade keyword highlighting. Blade directives, echo delimiters, PHP keywords, cast types, comments, and PHPDoc tags inside .blade.php files now receive semantic tokens for proper syntax coloring.
    • Blade view directive navigation. Go-to-definition works on view names inside Blade directives (@include, @extends, @includeIf, @includeWhen, @includeUnless, @includeFirst, @component, @each), jumping to the referenced template file.
    • Replace FQCN with import. A refactoring code action on any fully-qualified class name (\Foo\Bar) inserts a use statement and replaces all occurrences of the same FQCN throughout the file with the short name. Detects existing imports and short-name conflicts. A separate "Replace all FQCNs with imports" action appears when the file contains multiple distinct FQCNs, replacing all of them at once (skipping those with import conflicts).
    • Broader type narrowing. instanceof, type-guard functions, in_array() strict mode, assert(), @phpstan-assert-if-true/-if-false, and compound &&/|| conditions now narrow types in if/else branches, guard clauses, while-loop bodies, ternary expressions, and match(true) arms.
    • Argument type mismatch diagnostics. Flags function and method calls where an argument's resolved type is incompatible with the declared parameter type.
    • Invalid class-like kind diagnostics. Flags class-like names used in positions where their kind is guaranteed to fail at runtime: new on abstract classes, interfaces, traits, or enums; extends on a final class, interface, or trait; implements with a non-interface; trait use with a non-trait; instanceof with a trait; catch with a non-Throwable type; and traits in type-hint positions.
    • Unused variable diagnostics. Variables assigned but never read are flagged with hint severity and rendered as dimmed text. Variables named $_ or prefixed with $_ are exempt.
    • Mago diagnostic proxy. Mago lint and analyze diagnostics are surfaced as LSP diagnostics with quick-fix code actions. Configurable under [mago] in .phpantom.toml.
    • Laravel Pint formatting. Projects with laravel/pint in require-dev automatically use Pint for formatting via stdin. Configurable under [formatting] in .phpantom.toml with pint = "path" or pint = "" to disable.
    • PHPCS diagnostic proxy. PHP_CodeSniffer violations are surfaced as LSP diagnostics with severity mapping. Configurable under [phpcs] in .phpantom.toml.
    • Return type inference from method bodies. Methods without a declared return type or @return docblock now have their return type inferred from return statements, improving completion, hover, and diagnostics for untyped code.
    • Closure and arrow function parameter inference. Untyped closure parameters are inferred from the enclosing call's callable signature, including through method chains that return static. Generic type substitution flows through to inferred parameters.
    • Closure and arrow function inlay hints. When a closure or arrow function is passed to a callable-typed parameter, inlay hints show inferred parameter types and the return type derived from the enclosing callable signature.
    • Generics. @mixin tags referencing a template parameter now resolve through the template bound. new $var() where $var is class-string<T> resolves to T. SPL collection classes now carry @template parameters so iteration methods resolve to concrete type arguments.
    • Namespace renaming. Renaming a namespace segment updates all declarations, use statements, and fully-qualified references across the workspace. When a PSR-4 autoload mapping exists, the corresponding directory is moved automatically.

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    PHPantom

    A fast, lightweight PHP language server written in Rust. Ready in seconds, keeps memory use modest even on large codebases, and stays responsive throughout. Indexing happens in the background, so you're never left waiting on it.

    Check out the documentation to get started.

    Features

    PHPantom focuses on deep type intelligence. Here's how it compares:

    PHPantomIntelephensePHP ToolsPhpactorPHPStorm
    Core LSP features1✅✅✅✅✅
    Advanced navigation2✅🚧🔒🚧🚧✅
    Editor extras3✅🚧🔒🚧🚧✅
    Diagnostics
    Built-in diagnostics4✅🚧🔒✅🚧✅
    External analyzer integration5✅❌❌🚧🚧
    Type Intelligence
    Generics / @template✅🚧✅🚧🚧
    Array / object shapes✅✅✅🚧🚧
    Advanced PHPDoc types6✅🚧🔒🚧🚧🚧
    Closure parameter inference✅🚧🚧🚧🚧
    Frameworks
    Laravel & Blade✅❌🚧❌🚧🧩
    Symfony & Twig✅❌❌🚧🧩
    WordPress✅🚧🚧❌✅
    Drupal✅❌❌❌✅🧩
    Yii 3✅❌❌❌🚧🧩🔒
    Tempest🚧❌❌❌🧩
    PHPUnit & Pest✅❌🚧🚧✅
    Other frameworks❌🚧🚧🚧🧩
    Refactoring
    Rename✅✅🔒✅✅✅
    Extract & generate8✅❌🚧🔒🚧✅
    Simplify & modernize9✅❌✅🔒❌✅
    Performance
    Time to ready (from cache)2 s11 s (1 s)10 s1 min 34 s (4 s)1 min 7 s (5 s)
    RAM578 MB766 MB594 MB467 MB2 GB
    CPU time17 s16 s36 s5 min 33 s17 min 55 s
    Disk cache-51 MB-2.3 GB379 MB

    [!TIP] Want to verify? Open 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. Each framework has a standalone project of its own, with a README listing what to try: examples/laravel/, examples/symfony/, examples/wordpress/, examples/yii/, and examples/tempest/.

    Context-Aware Intelligence

    • Smart PHPDoc completion. @throws detects uncaught exceptions in the method body, @param pre-fills from the signature, and tags are filtered to context and never suggested twice.
    • Array shape inference. Literal arrays offer key completion with no annotation. Nested shapes, spreads, and array functions like array_map preserve element types.
    • Closure parameter inference. $users->map(fn($u) => $u->name) infers $u as User from the collection's generic context.
    • 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.
    • The full modern language. PHP 8.4 and 8.5 features resolve end-to-end: property hooks, asymmetric visibility, and the pipe operator behave like any other code.
    • 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 completion on both relation and column names. Blade templates get completion, hover, go-to-definition, and diagnostics through virtual PHP preprocessing, and Livewire components and Folio pages are understood alongside them. No ide-helper or database access required.
    • Symfony. The names Symfony takes as strings complete, navigate, and list their references across PHP, YAML, XML, and Twig, and the ones nothing declares are reported. Form fields and validation mappings resolve to the properties they name. Doctrine entities and repositories link to each other, and renames reach the mapping files. Everything is read from the project's files, without booting the application.
    • Twig. Template names complete, navigate, and are checked in any project that uses Twig, Symfony or not. Blocks navigate between the templates that override them, filters and functions lead to the PHP that runs them, with its signature on hover, and variables lead to the renders and templates that pass them and hold what those pass, so post. completes and navigates the entity's attributes. The template directories are found from the templates themselves, with no configuration.
    • WordPress. Core's functions and classes come from php-stubs/wordpress-stubs once Composer installs it, and their return types follow the arguments. Hook names, core's included, complete and navigate between the places that fire a hook and the ones that listen to it, and the project's own rename. @global tags type the globals a function imports.
    • Yii 3. Route names, route arguments, and view names complete, navigate, and are checked, and a route's handler renames with the method it names. Everything is read from the application's configuration without booting it.
    • Tempest. View templates get completion, hover, go-to-definition, and diagnostics through virtual PHP, the same way Blade templates do. The data a view is rendered with is typed from the calls and View objects that render it, and component attributes and slots get the scope Tempest renders them in. Component tags and their attributes complete and navigate, and the paths passed to view() complete and lead to their templates.
    • Testing. PHPUnit's assertions narrow types, in Pest tests too. Test metadata (data providers, dependencies, coverage) navigates and renames, a class links to its tests and each test back to the class it tests, a class without a test gets one created in the right place, and mocks keep the mocked class, with the methods an expectation names completing, navigating, and renaming like calls. Inside a Pest test, $this is the test case your Pest.php binds.
    • Everything else you'd expect. Generics, type narrowing, named arguments, destructuring, first-class callables, anonymous classes, @deprecated detection, and namespace segment drilling.

    Project Awareness

    PHPantom understands Composer projects out of the box, but works without setup on non-Composer projects too:

    • Frameworks need no setup. Laravel, Symfony, WordPress, Drupal, Yii, Tempest, PHPUnit, and Pest are recognised from the project itself, and their code is read from where the project already has it. There is nothing to enable, download, or point at.
    • Autoloader-accurate results. Completions and go-to-definition only surface classes that Composer's autoloader can actually load, avoiding false positives from internal, inaccessible, or duplicate vendor classes. You see exactly what your application can use.
    • PSR-4 correctness. Quick fixes for when the namespace or class name does not match their PSR-4 path.
    • PSR-4 autoloading. Resolves classes across files on demand.
    • Classmap and file autoloading. autoload_classmap.php and autoload_files.php.
    • Embedded PHP stubs from phpstorm-stubs bundled in the binary, no runtime downloads needed.
    • Drupal project support. Detects Drupal projects, with or without a composer.json, resolves the web root, and indexes Drupal-specific directories and PHP extensions (.module, .install, .theme, etc.) with .gitignore bypassed so that Composer-managed core and contrib code is always available.
    • require_once discovery. Functions from required files are available for completion.
    • Go-to-implementation. Jump from an interface or abstract class to all concrete implementations. Scans open files, classmap, PSR-4 directories, and embedded stubs.
    • Go-to-declaration. Jump from a method call or a method to the interface or abstract method it implements.
    • Workspace-wide diagnostics. After startup, diagnostics run in the background across every file in the project, not just the ones you have open, so problems are already visible when you navigate to a file. Configured external tools (PHPStan, PHPCS, PHPMD, Mago) run project-wide too, once the background pass finishes. Both are deferred until after startup so they never slow down time to ready.

    Acknowledgements

    PHPantom stands on the shoulders of:

    • Mago: the PHP parser that powers all of PHPantom's AST analysis.
    • PHPStan and Psalm: for their pioneering work in PHP static analysis and shaping the type ecosystem. PHPantom builds on PHPStan-informed type experience, with full support for @phpstan-* annotations.
    • JetBrains phpstorm-stubs: type information for the entire PHP standard library, embedded directly into the binary.
    • Viktor Szépe (@szepeviktor): for wordpress-stubs, which supplies PHPantom's knowledge of WordPress core, and phpstan-wordpress, whose return-type rules and test fixtures shaped PHPantom's WordPress support.
    • Phpactor: the PHP language server whose comprehensive test suite and benchmark fixtures informed PHPantom's own test coverage. Many of PHPantom's type inference fixtures were adapted from Phpactor's reflection tests.

    License

    MIT. See LICENSE.