Skip to content

0.6.0

Choose a tag to compare

@AJenbo AJenbo released this 26 Mar 16:42
· 428 commits to main since this release

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/phpstan or $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.json require-dev automatically 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 count and strlen, 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/@implements tags. Uncaught exceptions get @throws with 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 = true in 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 @param tags, 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.
  • @throws code actions. Quick-fixes for adding missing and removing unnecessary @throws tags, triggered by PHPStan diagnostics. Adding inserts the tag and a use import 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/include paths are now Ctrl+Clickable. Path resolution supports string literals, __DIR__ concatenation, dirname(__DIR__), dirname(__FILE__), and nested dirname with levels.
  • Analyze command. phpantom_lsp analyze scans 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 --severity filtering and --no-colour for 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 ?Foo return type), null is stripped from the LHS and the result is the union of the non-null LHS with the RHS.
  • @mixin generic 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 @var completion. Inline @var above variable assignments sorts first and pre-fills the inferred type when available. Template parameters from @template enrich @param, @return, and @var type hints.
  • @see and @link improvements. @see references in docblocks now work with go-to-definition (class, member, and function forms). Hover popups show all @link and @see URLs as clickable links. Deprecation diagnostics include @see targets when the @deprecated docblock 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 .phar archives (e.g. PHPStan's phpstan.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.toml in 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.toml is 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|false in 7.x becoming int in 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 previous Class: ClassName detail 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 Order puts Order above OrderLine above CheckOrderFlowJob regardless 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 use statement 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 legacy deprecated boolean. 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 master branch 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 @param tag 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 use statement FQNs, preserves explicit aliases, and introduces an alias when the new name collides with an existing import.
  • False-positive diagnostics for $this inside traits. Accessing host-class members via $this->, self::, static::, or parent:: 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 $order resolve 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 @param annotations no longer produce a false "unknown class" diagnostic.
  • Removed PHP symbols in stubs. Functions, methods, and classes annotated with @removed X.Y in phpstorm-stubs are now filtered out when the target PHP version is at or above the removal version. Previously symbols like mysql_tablename (removed in PHP 7.0) and each (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 $ambiguous is Lamp|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. Previously User::find() would incorrectly show class User even though find() is declared on Model.
  • 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 foreach body are now visible after the loop.
  • Variable-to-variable type propagation. Assignments like $found = $pen now resolve $found to the type of $pen. This also eliminates false-positive diagnostics when the initial assignment was $found = null and a later reassignment provided the real type.
  • Variable type inside self-referencing assignment RHS. In $request = new Foo(arg: $request->uuid), the $request reference 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 use imports now resolve correctly in consuming files. Function parameter types and @throws types 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 refine string, but array<int> no longer incorrectly overrides string).
  • 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 ?ClassName or Collection<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\Provider vs Concrete\Provider).
  • Guard clause narrowing across instanceof branches. After if ($x instanceof Y) { return; }, subsequent instanceof checks on the same variable no longer incorrectly resolve to Y.
  • instanceof self/static/parent narrowing. Type narrowing with instanceof self, instanceof static, and instanceof parent now works correctly in all contexts (assert, if-blocks, guard clauses, compound conditions).
  • Type narrowing inside return statements. instanceof checks in && chains and ternary conditions now narrow the variable type when the expression is the operand of a return statement.
  • 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 like self::Active->value inside an enum method now resolve correctly. Previously, self, static, and parent were only recognized as bare subjects, not when followed by ::MemberName in 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\Re with use 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. @throws continues to use Throwable-filtered completion.
  • Trait alias go-to-definition. Clicking a trait alias (e.g. $this->__foo() from use 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 morphedByMany relationships. The inverse side of polymorphic many-to-many relationships is now recognised. Virtual properties and _count properties 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