0.6.0
What's Changed
Added
- Semantic Tokens. Type-aware syntax highlighting that goes beyond what a TextMate grammar can achieve. Classes, interfaces, enums, traits, methods, properties, parameters, variables, functions, constants, and template parameters all get distinct token types. Modifiers convey declaration sites, static access, readonly, deprecated, and abstract status.
- PHPStan diagnostics. PHPStan errors appear inline as you edit. Auto-detects
vendor/bin/phpstanor$PATH. Runs in the background without blocking native diagnostics. Configurable via[phpstan]in.phpantom.toml(command,memory-limit,timeout). "Ignore PHPStan error" and "Remove unnecessary @phpstan-ignore" code actions manage inline ignore comments. - Formatting. Built-in PHP formatting (PER-CS 2.0 style). Formatting works out of the box without any external tools. Projects that depend on php-cs-fixer or PHP_CodeSniffer in their
composer.jsonrequire-devautomatically use those tools instead (both can run in sequence). Per-tool command overrides and disable switches in[formatting]in.phpantom.toml. - Inlay hints. Parameter name and by-reference indicators appear at call sites. Hints are suppressed when the argument already makes the parameter obvious: variable names matching the parameter, property accesses with a matching trailing identifier, string literals whose content matches, well-known single-parameter functions like
countandstrlen, and spread arguments. Named arguments never receive a redundant hint. - PHPDoc block generation. Typing
/**above any declaration generates a docblock skeleton. Tags are only emitted when the native type hint needs enrichment. Properties and constants always get@var. Class-likes with templated parents or interfaces get@extends/@implementstags. Uncaught exceptions get@throwswith auto-import. Works both via completion and on-type formatting. - Syntax error diagnostic. Parse errors from the Mago parser now appear as Error-severity diagnostics instantly as you type.
- Implementation error diagnostic. Concrete classes that fail to implement all required methods from their interfaces or abstract parents are now flagged with an Error-severity diagnostic on the class name. The existing "Implement missing methods" quick-fix appears inline alongside the error.
- Argument count diagnostic. Flags function and method calls that pass too few arguments. The "too many arguments" check is off by default (PHP silently ignores extra arguments) and can be enabled with
extra-arguments = truein the[diagnostics]section of.phpantom.toml. - Completion item documentation. Selecting a completion item in the popup now shows rich documentation including the full typed signature, description, deprecation notice, and parameter details. Previously only the class name was shown.
- Method commit characters. Typing
(while a method completion is highlighted auto-accepts it and begins the argument list. - Document Symbols. The outline sidebar and breadcrumbs now show classes, interfaces, traits, enums, methods, properties, constants, and standalone functions with correct nesting, icons, visibility detail, and deprecation tags.
- Workspace Symbols. "Go to Symbol in Workspace" (Ctrl+T / Cmd+T) searches across all indexed files including vendor classes. Results include namespace context and deprecation markers, sorted by relevance.
- Type Hierarchy. "Show Type Hierarchy" on any class, interface, trait, or enum reveals its supertypes and subtypes with full up-and-down navigation through the inheritance tree, including cross-file resolution and transitive relationships.
- Code Lens. Clickable annotations above methods that override a parent class method or implement an interface method. Clicking navigates to the prototype declaration.
- Update docblock. Code action on a function or method whose existing docblock is out of sync with its signature. Adds missing
@paramtags, removes stale ones, reorders to match the signature, fixes contradicted types, and removes redundant@return void. Refinement types and unrelated tags are preserved. Only triggers on the signature or the preceding docblock, not inside the function body. - Change visibility. Code action on any method, property, constant, or promoted constructor parameter offers to change its visibility (
public,protected,private). Only triggers on the declaration signature, not inside the body. @throwscode actions. Quick-fixes for adding missing and removing unnecessary@throwstags, triggered by PHPStan diagnostics. Adding inserts the tag and auseimport when needed. Removing cleans up orphaned blank lines and deletes the entire docblock when it would be empty. The diagnostic disappears on the next keystroke without waiting for the next PHPStan run.- File rename on class rename. Renaming a class whose file follows PSR-4 naming now also renames the file to match. The file is only renamed when it contains a single class-like declaration and the editor supports file rename operations.
- Folding Ranges. AST-aware code folding for class bodies, method/function bodies, closures, arrays, argument/parameter lists, control flow blocks, doc comments, and consecutive single-line comment groups.
- Selection Ranges. Smart select / expand selection returns AST-aware nested ranges from innermost to outermost.
- Document Links.
require/includepaths are now Ctrl+Clickable. Path resolution supports string literals,__DIR__concatenation,dirname(__DIR__),dirname(__FILE__), and nesteddirnamewith levels. - Analyze command.
phpantom_lsp analyzescans a Composer project and reports PHPantom's own diagnostics in a PHPStan-like table format. Useful for measuring type coverage across an entire codebase without opening files one by one. Accepts an optional path argument to limit the scan to a single file or directory. Output includes diagnostic identifiers and supports--severityfiltering and--no-colourfor CI. - Null-coalesce (
??) type refinement. When the left-hand side of??is provably non-nullable (e.g.new Foo(),clone $x, a literal), the right-hand side is recognized as dead code and the result resolves to the LHS type only. When the LHS is nullable (e.g. a?Fooreturn type),nullis stripped from the LHS and the result is the union of the non-null LHS with the RHS. @mixingeneric substitution. When a class declares@mixin Foo<T>, the generic arguments are now preserved and substituted into the mixin's members, including through multi-level inheritance chains.- PHPDoc
@varcompletion. Inline@varabove variable assignments sorts first and pre-fills the inferred type when available. Template parameters from@templateenrich@param,@return, and@vartype hints. @seeand@linkimprovements.@seereferences in docblocks now work with go-to-definition (class, member, and function forms). Hover popups show all@linkand@seeURLs as clickable links. Deprecation diagnostics include@seetargets when the@deprecateddocblock references them.- Progress indicators. Go to Implementation and Find References now show a progress indicator in the editor while scanning.
- Phar archive class resolution. Classes inside
.phararchives (e.g. PHPStan'sphpstan.phar) are now discovered and indexed automatically. No PHP runtime needed. Only uncompressed phars are supported (the format used by PHPStan and most other phar-distributed tools). - PSR-0 autoload support. Packages that use the legacy PSR-0 autoloading standard are now discovered automatically.
- Global config. Settings from a global
.phpantom.tomlin the user's config directory (typically~/.config/phpantom_lsp/.phpantom.toml) are now loaded as defaults. Project-level configs take precedence. By @calebdw in #39 - Config schema. A JSON schema for
.phpantom.tomlis now bundled, enabling autocompletion and validation in editors that support TOML schemas. By @calebdw in #38
Changed
- Pull diagnostics. Diagnostics are now delivered via the LSP 3.17 pull model when the editor supports it. The editor requests diagnostics only for visible files, and cross-file invalidation no longer recomputes every open tab. Clients without pull support fall back to the previous push model automatically.
- Hover type accuracy. Hover now resolves variable types through the same pipeline as completion, so all narrowing features (instanceof, assert, custom type guards, in_array) apply. When the cursor is inside a specific if/else branch, hover shows only the type visible in that branch. Complex expressions like null-coalesce chains, array shapes, empty arrays, and unresolved symbols all display correctly.
- Version-aware stub types. Built-in function signatures that changed across PHP versions (e.g.
int|falsein 7.x becomingintin 8.0) now show the correct type for your project's PHP version. This eliminates false-positive diagnostics and incorrect completions from stale type annotations. - Completion labels. Method and function completion items now show only parameter names in the label (e.g.
setName($name)) with the return type displayed inline (e.g.: User). Properties and constants show just the type hint. The previousClass: ClassNamedetail line has been removed; class context is available in the documentation panel when the item is highlighted. - Completion sort order. Member completion items are now sorted by kind (constants, then properties, then methods) before alphabetical order within each group. Union-type completions apply the same kind-based ordering within both the intersection and branch-only tiers.
- Class name completion ranking. Completions now rank by match quality first (exact match, then starts-with, then substring), so typing
OrderputsOrderaboveOrderLineaboveCheckOrderFlowJobregardless of where the class comes from. Within each match quality group, use-imported and same-namespace classes appear first, followed by everything else sorted by namespace affinity (classes from heavily-imported namespaces rank higher). - Use-import completion. Same-namespace classes no longer appear in
usestatement completions (PHP auto-resolves them without an import). Classes that are already imported are filtered out. Namespace affinity still ranks the remaining candidates. - Deprecation tags. Completion items use the modern
tags: [DEPRECATED]field instead of the legacydeprecatedboolean. Both convey the same strikethrough rendering in editors. - Import class code action ordering. The "Import Class" code action now sorts candidates by namespace affinity (derived from existing imports) instead of alphabetically, so the most likely namespace appears first.
- Cross-file resolution. Completion, hover, and go-to-definition no longer fail when one reference uses a leading backslash and another does not.
- Embedded stubs track upstream master. The bundled phpstorm-stubs are now pulled from the
masterbranch instead of the latest GitHub release, matching what PHPStan does. This brings in upstream fixes and new PHP version annotations weeks or months before a formal release.
Fixed
- CLI analyze performance. Single-file analysis is up to 5.8× faster. Full-project analysis of ~2 500 files is up to 10× faster.
- Diagnostic performance on large files. Unknown-member diagnostics on files with many member accesses are up to 7× faster.
- Position encoding. All LSP position conversions now correctly count UTF-16 code units, matching the LSP specification. Files containing emoji or supplementary Unicode characters no longer produce incorrect positions.
- Rename and find references for parameters. Renaming a parameter in a function, method, or closure now correctly updates all usages in the body and the
@paramtag in the docblock. Previously, parameters were scoped incorrectly because they sit physically before the opening{of the body, causing rename and find references to miss body usages when triggered from the parameter (and vice versa). Document highlight is also fixed. - Rename updates imports. Renaming a class now updates
usestatement FQNs, preserves explicit aliases, and introduces an alias when the new name collides with an existing import. - False-positive diagnostics for
$thisinside traits. Accessing host-class members via$this->,self::,static::, orparent::inside a trait method no longer produces "not found" warnings, including chain expressions and accesses inside closures or arrow functions nested within trait methods. - False-positive diagnostics for same-named variables in different methods. Diagnostic resolution is now scoped to the enclosing function/method/closure body, so two methods using a variable like
$orderresolve it independently. - False positive on namespaced constants. Standalone namespaced constant references (e.g.
\PHPStan\PHP_VERSION_ID) no longer produce a spurious "Class not found" diagnostic. Previously the symbol map classified them as class references instead of constant references. - Diagnostic deduplication. Multiple diagnostics on the same span or line are no longer collapsed into one. If PHPStan reports five issues on a line, all five are shown. When PHPantom and PHPStan both flag the same issue, the more precise native diagnostic wins.
- Diagnostics. Enums that implement interfaces are now checked for missing methods. Scalar member access errors detect method-return chains where an intermediate call returns a scalar type. By-reference
@paramannotations no longer produce a false "unknown class" diagnostic. - Removed PHP symbols in stubs. Functions, methods, and classes annotated with
@removed X.Yin phpstorm-stubs are now filtered out when the target PHP version is at or above the removal version. Previously symbols likemysql_tablename(removed in PHP 7.0) andeach(removed in PHP 8.0) appeared in completions and resolved without warnings. - Hover on union member access. Hovering over a method, property, or constant on a union type (e.g.
$ambiguous->turnOff()where$ambiguousisLamp|Faucet) now shows hover information from all branches that declare the member, separated by a horizontal rule. Previously only the first matching branch was shown. When both branches inherit the member from the same declaring class, the hover is deduplicated to a single entry. - Hover on inherited members. Hovering over an inherited method, property, or constant now shows the declaring class in the code block (e.g.
class Model { public static function find(...) }) instead of the class it was accessed on. PreviouslyUser::find()would incorrectly showclass Usereven thoughfind()is declared onModel. - Constant type inference. Variables assigned from global constants (
$a = MY_CONST) or class constants without type hints ($b = Config::TIMEOUT) now resolve to the type implied by the constant's initializer value. Integer, float, string, bool, null, and array literals are all recognised. Typed class constants (public const string NAME = '...') continue to use their declared type hint. - Variable type after reassignment. When a method parameter is reassigned mid-body (e.g.
$file = $result->getFile()), subsequent member accesses now resolve against the new type instead of the original parameter type. - Variable assignments inside foreach loops. Variables conditionally reassigned inside a
foreachbody are now visible after the loop. - Variable-to-variable type propagation. Assignments like
$found = $pennow resolve$foundto the type of$pen. This also eliminates false-positive diagnostics when the initial assignment was$found = nulland a later reassignment provided the real type. - Variable type inside self-referencing assignment RHS. In
$request = new Foo(arg: $request->uuid), the$requestreference inside the constructor arguments now correctly resolves to the original type instead of the type being assigned. - Variable resolution inside anonymous classes. Variables inside anonymous class methods (e.g. closure parameters in
return new class extends Migration { ... }) now resolve correctly. Previously, anonymous class bodies were invisible to the variable resolution pipeline because they appear as expressions inside statements rather than top-level class declarations. - Closure and arrow function variable scope. Variable name completion now correctly respects PHP scoping rules for anonymous functions and arrow functions. Parameters and
use-captured variables are visible inside closures. Arrow function parameters are visible inside the arrow body while the enclosing scope's variables remain accessible. - Function return type resolution across files. Standalone functions that declare return types using short names from their own
useimports now resolve correctly in consuming files. Function parameter types and@throwstypes are also resolved. - Native type override compatibility. A docblock type only overrides a native type hint when it is a compatible refinement (e.g.
class-string<Foo>can refinestring, butarray<int>no longer incorrectly overridesstring). - PHPStan pseudo-type recognition. Types like
non-positive-int,non-negative-int,non-zero-int,lowercase-string,truthy-string,callable-object, and many other PHPStan pseudo-types are now recognized across the entire pipeline. - Nullable and generic types in class lookup. Variables typed as
?ClassNameorCollection<Item>now resolve correctly across all code paths. - Generic substitution through transitive interface chains. When a class implements an interface that itself extends another generic interface, template parameters are now substituted at each level instead of propagating raw template parameter names.
- Generic shape substitution. Template parameters inside array shapes (
array{data: T}) and object shapes (object{name: T}) are now correctly substituted when inherited through@extends. - Type narrowing with same-named classes from different namespaces. instanceof narrowing now correctly distinguishes classes that share a short name but live in different namespaces (e.g.
Contracts\ProvidervsConcrete\Provider). - Guard clause narrowing across instanceof branches. After
if ($x instanceof Y) { return; }, subsequentinstanceofchecks on the same variable no longer incorrectly resolve toY. instanceof self/static/parentnarrowing. Type narrowing withinstanceof self,instanceof static, andinstanceof parentnow works correctly in all contexts (assert, if-blocks, guard clauses, compound conditions).- Type narrowing inside
returnstatements.instanceofchecks in&&chains and ternary conditions now narrow the variable type when the expression is the operand of areturnstatement. - Inline array access on method returns. Expressions like
$c->items()[0]->getLabel()now resolve the element type correctly for both completion and diagnostics. - Array shape bracket access. Variables assigned from string-key bracket access on array shapes (
$name = $data['name']) now resolve to the correct value type. Chained access ($first = $result['items'][0]) walks through shape keys and generic element types in sequence. - Ternary and null-coalesce member access. Accessing a member on a ternary or null-coalesce expression (e.g.
($a ?: $b)->property,($x ?? $y)->method()) now resolves correctly for hover, go-to-definition, and diagnostics. - Null-safe method chain resolution. Null-safe method calls (
$obj?->method()) now resolve the return type correctly for variable type inference, including cross-file chains. - Clone expressions.
(clone $var)->now resolves to the same type as$var, providing correct completion, hover, and diagnostics. self::/static::/parent::in member access chains. Expressions likeself::Active->valueinside an enum method now resolve correctly. Previously,self,static, andparentwere only recognized as bare subjects, not when followed by::MemberNamein a chain.- Inherited methods missing through deep stub chains. Methods are now found on classes that inherit through multi-level chains where intermediate classes live in stubs.
- Interface constants through multi-extends chains. Constants defined on parent interfaces are now found when an interface extends multiple other interfaces.
- Double parentheses when completing calls. Completing a function, constructor, or static method name when parentheses already follow the cursor (e.g.
array_m|(),new Gadge|(),throw new Excepti|()) no longer inserts a second pair of parentheses. Previously only->and::method calls were handled. - Namespace alias completion. Typing a class name through a namespace alias (e.g.
OA\Rewithuse OpenApi\Attributes as OA) now correctly suggests classes under the aliased namespace. - Catch clause completion. Throwable interfaces and abstract exception classes now appear in catch clause completions.
- Type-hint and PHPDoc completion. Traits are now excluded from completions in parameter types, return types, property types, and PHPDoc type tags.
@throwscontinues to use Throwable-filtered completion. - Trait alias go-to-definition. Clicking a trait alias (e.g.
$this->__foo()fromuse Foo { foo as __foo; }) now jumps to the trait method instead of the class's own same-named method. - Self-referential array key assignments no longer crash. Patterns like
$numbers['price'] = $numbers['price']->add(...)no longer cause a stack overflow during hover or completion. - Eloquent
morphedByManyrelationships. The inverse side of polymorphic many-to-many relationships is now recognised. Virtual properties and_countproperties are synthesized for models using this relationship type. - Virtual property merging. Native type hints are now considered when determining virtual property specificity, preventing properties with native PHP type declarations from being incorrectly overridden by less specific virtual properties.
Full Changelog: 0.5.0...0.6.0