PHP Typing Conformance Results
Each row links to a per-test detail page with the source, expectations, and every analyzer’s raw output.
Results last updated: 2026-08-15T00:10:02+09:00
Legend — what the cell values mean
Verdict. Most rows ask whether the analyzer catches unsafe code.
Pass The analyzer’s diagnostics match what the test expects.
Fail They do not: an expected diagnostic is missing, or something unexpected is reported.
Not measured The test is newer than the analyzer’s last run, so nothing has been measured for it yet. Only ever appears for Qodana, which is run by hand from PhpStorm rather than by this suite; re-run Inspect Code and the cell fills in.
Not reported (by design) The analyzer declines to report the diagnostic and has said so upstream; the issue is linked in the cell notes. Hand-curated — the harness cannot tell this apart from a plain miss.
PHPDoc type handling. Tests that mark a declaration with // T ask a different question: not “is the code unsafe?” but “how is this type spelling treated?” That splits into two independent facets, and a single word cannot carry both:
Recognition — does the analyzer resolve the spelling? Read off the // T declaration lines. Level-independent.Enforcement — does it then reject the values the spelling excludes? Read off the // E call sites. For PHPStan this is gated by the level; recognition never is.
The combinations:
Unrecognized The spelling is not resolved at all: the analyzer reports an unresolvable type, an undeclared type, or a docblock parse error on the declaration. Expected whenever a test uses another analyzer’s dialect — not a defect.
Enforced The spelling is resolved and every value the type excludes is rejected, without also rejecting values the type admits.
Incidental (n /m ) The violating lines fired, but so did valid controls: the analyzer also rejects values the type admits (class-name fallback, sealed where the test asked for unsealed, over-strict purity, …). Hits on the // E lines are then the wrong reason, not enforcement.
Partly enforced (n /m ) Resolved, and some but not all of the excluded values are rejected. Common in files that probe several related spellings at once.
Widened to X Resolved, but widened to X , so nothing is rejected. Not an error — just a coarser type. The base type is hand-curated.
Not enforced Resolved, nothing rejected, and the base type it widened to has not been pinned down yet.
Recognized (no probes) The spelling resolves; this test has no rejection probes, so enforcement is not measured.
Not enforced (by design) Resolved and reportable, but the analyzer deliberately declines; the upstream issue is in the cell notes.
Tags.
reported Lv.5+
PHPStan only. The lowest level whose rules report the diagnostic this test expects. PHPStan levels switch rule sets on and off — type inference is identical at every level — so this is a configuration threshold, not a type-support tier. Level 5 turns on argument-type checks, level 6 missing typehints, level 8 nullables, level 9 explicit mixed. Each diagnostic on the detail page carries its own [reported-from-level=N]; this tag shows the lowest one on an expected line.
⚠ 3 false positives
Diagnostics on lines the test neither expects nor marks with // T. On a type-handling test these are the analyzer’s own false positives — Phan resolving number as a class name and then rejecting 1 for it, say. Hover for the line numbers.
(strict)
PHPStan only. Nothing is reported with the standard config; only phpstan-strict-rules catches it.
(pzoom≠)
Psalm only. pzoom, the Rust port of Psalm, flags a different set of lines than Psalm itself.
Soundness — potential runtime type errors
Positives here flag code that can actually fail at runtime — type mismatches, null access, invalid arguments, uninitialized reads. Pass means the analyzer agrees with the expected diagnostic; rows whose test marks a declaration with // T instead report recognition and enforcement of the type spelling. See the legend above.
Test
phan6.0.7
phpstan2.2.8
psalm6.16.1 next: 7.0.0-beta19
mago1.46.0
mir0.70.1
phpantom0.9.0
intelephense1.18.5
phpy0.2.0
qodana262.8665.325
noverify0.5.5
steins0.1.5
Native types
Basic scalar parameter compatibility checks
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Fail
Pass
resource is not a valid native type declaration
Pass
Pass reported Lv.0+
Pass
Pass
Fail
Fail
Pass
Pass
Pass
Fail
Fail Notes
Union types
Basic union member compatibility checks
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Fail
Pass
Union member rejection for object types
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Fail
Pass
Intersection types
Basic intersection compatibility
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Pass
Pass
Fail
Pass
Fail
Pass
Callables
Callable return type compatibility checks
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Fail
Fail
Fail
Fail
Fail
Pass
Callable signature compatibility in PHPDoc
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Fail
Fail
Fail
Fail
Fail
Pass
PHPDoc generics
Basic PHPDoc template propagation checks
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Fail
Fail
Fail
Fail
Fail
Pass
Generic inheritance via @extends / @implements
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Fail
Fail
Fail
Fail
Fail
Pass
@template T of array — a bound that is not a class
Pass
Pass reported Lv.4+
Pass
Pass
Pass
Fail
Fail
Fail
Fail
Fail
Pass
Template upper bounds
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Fail
Fail
Fail
Fail
Fail
Pass
Template variance: contravariant type parameters
Fail
Pass
Fail (pzoom≠)
Pass
Pass
Pass
Fail
Pass
Pass
Fail
Pass
Template variance: covariant type parameters
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Arrays and shapes
array{foo: int, ...}
Unrecognized Notes
Enforced reported Lv.5+
Enforced
Enforced
Enforced Notes
Not enforced
Not enforced Notes
Not enforced Notes
Incidental (3/3) Notes
Unrecognized
Enforced
array{foo: int, bar?: bool, ...}
Unrecognized Notes
Enforced reported Lv.5+
Enforced
Enforced
Enforced Notes
Not enforced
Not enforced Notes
Not enforced Notes
Incidental (3/3) Notes
Unrecognized
Enforced
Basic array shape required-key checks
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Fail
Fail
Fail
Pass
Fail
Pass
Discriminated union of array shapes, narrowed through match
Pass
Pass reported Lv.3+
Fail (pzoom≠)
Fail
Fail
Pass
Fail
Pass
Fail
Fail
Pass
Is array{foo: int} sealed?
Fail Notes
Pass reported Lv.5+
Pass
Pass
Fail Notes
Fail
Fail Notes
Fail Notes
Fail Notes
Fail Notes
Pass Notes
List element type compatibility checks
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Pass
Fail
Fail
Fail
Fail
Pass
List shape support for positional destructuring and indexed access
Not enforced
Partly enforced (4/8) reported Lv.5+
Partly enforced (4/8)
Partly enforced (4/8)
Partly enforced (4/8)
Partly enforced (3/8)
Partly enforced (3/8)
Partly enforced (1/8)
Unrecognized
Unrecognized
Partly enforced (1/8)
Non-empty list constraints
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Fail
Fail
Fail
Fail
Fail
Pass
Open array and list shape support
Unrecognized
Partly enforced (2/5) reported Lv.5+
Partly enforced (2/5)
Partly enforced (2/5)
Partly enforced (2/5)
Not enforced
Not enforced
Not enforced
Unrecognized
Unrecognized
Partly enforced (2/5)
Object compatibility
Basic interface compatibility checks
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Fail
Pass
Static return type compatibility
Pass
Pass reported Lv.3+
Pass (pzoom≠)
Pass
Pass
Pass
Pass
Pass
Pass
Fail
Pass
Properties
Basic readonly property write checks
Pass
Pass reported Lv.2+
Pass
Pass
Pass
Fail
Pass
Pass
Pass
Fail
Pass
PHP 8.4+ asymmetric property visibility
Pass
Pass reported Lv.2+
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
PHP 8.4+ property hooks: typed set side
Pass
Pass reported Lv.3+
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Promoted property constructor parameter compatibility checks
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Fail
Pass
Typed property reads before initialization
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Fail
Pass
Pass
Constants
Basic class constant value compatibility checks
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Fail
Pass
Enums
Basic backed enum compatibility checks
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Fail
Pass
Enum cases are objects, not backing scalar values
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
PHPDoc basics
Basic PHPDoc-only parameter type checks
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Pass
Fail
Pass
Pass
Fail
Pass
Advanced PHPDoc types
array-key
Partly enforced (3/5)
Partly enforced (3/5) reported Lv.5+
Enforced
Enforced
Partly enforced (3/5)
Enforced
Partly enforced (2/5)
Partly enforced (3/5)
Partly enforced (2/5)
Partly enforced (2/5)
Partly enforced (3/5) Notes
arraylike-object<string, int> is a Psalm-only object refinement
Unrecognized ⚠ 1 false positive
Unrecognized reported Lv.max
Enforced (pzoom≠)
Unrecognized ⚠ 1 false positive
Partly enforced (1/3)
Unrecognized
Partly enforced (2/3)
Unrecognized
Unrecognized ⚠ 1 false positive
Partly enforced (2/3)
Partly enforced (1/3) Notes
A same-named class takes precedence over the integer pseudo-type keyword
Incidental (0/1) Notes
Enforced reported Lv.5+
Enforced
Enforced
Incidental (0/1) Notes
Incidental (0/1)
Not enforced
Enforced
Enforced
Not enforced
Enforced
associative-array
Enforced
Widened to array
Widened to array (pzoom≠)
Widened to array
Incidental (1/1)
Incidental (1/1)
Not enforced
Unrecognized
Unrecognized
Unrecognized
Enforced
boolean PHPDoc synonym is compatible with native bool
Enforced
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Enforced
Not enforced
Enforced
Enforced
Not enforced ⚠ 1 false positive Notes
Enforced
By-reference parameters should match even if PHPDoc omits the reference marker
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Fail
Pass
callable-array
Not enforced
Enforced reported Lv.5+ Notes
Partly enforced (1/2)
Unrecognized
Incidental (2/2)
Not enforced
Not enforced
Unrecognized
Unrecognized
Unrecognized ⚠ 1 false positive
Not enforced
callable-object
Enforced
Enforced reported Lv.5+
Not enforced
Unrecognized
Incidental (0/1)
Not enforced
Not enforced
Unrecognized
Unrecognized
Unrecognized
Not enforced
callable-string
Enforced
Enforced reported Lv.5+
Enforced
Partly enforced (1/2)
Enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Widened to string Notes
Callable PHPDoc signatures should be compatible with callable typehints
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
class-string
Enforced
Enforced reported Lv.5+
Enforced
Widened to string
Widened to string
Not enforced
Widened to string
Not enforced
Not enforced
Widened to string
Widened to string Notes
class-string-map<T of X, T> is a Psalm-only dependent generic
Unrecognized
Unrecognized
Incidental (1/1)
Unrecognized
Incidental (1/1)
Unrecognized
Unrecognized
Unrecognized
Unrecognized
Unrecognized ⚠ 1 false positive
Unrecognized Notes
closed-resource
Unrecognized
Partly enforced (1/2) reported Lv.5+
Enforced
Incidental (2/2)
Partly enforced (1/2)
Not enforced
Not enforced
Unrecognized
Partly enforced (1/2)
Partly enforced (1/2) ⚠ 2 false positives
Widened to a nonexistent-class reference Notes
CONSTANT[T]
Unrecognized Notes
Enforced reported Lv.5+
Unrecognized (pzoom≠) Notes
Unrecognized Notes
Incidental (2/2) Notes
Unrecognized
Unrecognized Notes
Unrecognized Notes
Unrecognized Notes
Unrecognized Notes
Not enforced Notes
decimal-int-string
Unrecognized
Enforced reported Lv.5+
Unrecognized (pzoom≠)
Unrecognized
Incidental (3/3)
Unrecognized
Incidental (0/3)
Not enforced
Unrecognized
Unrecognized ⚠ 1 false positive
Enforced
double PHPDoc synonym is compatible with native float
Enforced
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Enforced
Not enforced
Enforced
Enforced
Not enforced ⚠ 1 false positive Notes
Enforced
enum-string
Unrecognized
Enforced reported Lv.5+
Not enforced
Widened to class-string
Incidental (1/1)
Not enforced
Widened to class-string
Not enforced
Unrecognized
Unrecognized
Widened to string Notes
false
Partly enforced (1/2)
Enforced reported Lv.5+
Enforced
Enforced
Partly enforced (1/2)
Not enforced
Widened to bool
Not enforced
Partly enforced (1/2)
Enforced
Partly enforced (1/2) Notes
Imported aliases should remain compatible with canonical PHPDoc names
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Fail
Pass
Pass
Pass
int-mask-of<Class::*>
Unrecognized Notes
Enforced reported Lv.5+
Enforced (pzoom≠)
Enforced
Widened to int Notes
Not enforced
Widened to int
Not enforced
Not enforced
Widened to int Notes
Widened to a nonexistent-class reference Notes
int-mask<1, 2, 4>
Unrecognized Notes
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Not enforced
Widened to int
Not enforced
Not enforced
Unrecognized ⚠ 1 false positive Notes
Widened to a nonexistent-class reference Notes
int-range<0, 255> (Phan spelling)
Enforced
Unrecognized Notes
Unrecognized (pzoom≠) Notes
Unrecognized Notes
Incidental (1/1) Notes
Unrecognized
Widened to int
Unrecognized
Unrecognized
Widened to int
Enforced
int<0, 255>
Widened to int Notes
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Enforced
Widened to int
Not enforced
Enforced
Widened to int Notes
Enforced
integer PHPDoc synonym is compatible with native int
Enforced
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Enforced
Not enforced
Enforced
Enforced
Not enforced ⚠ 1 false positive Notes
Enforced
interface-string
Unrecognized
Not enforced
Not enforced
Widened to class-string
Enforced
Incidental (1/1)
Incidental (0/1)
Not enforced
Unrecognized
Not enforced
Widened to string Notes
key-of<T>
Enforced
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Incidental (2/2)
Not enforced
Not enforced
Unrecognized
Not enforced
Enforced
key-of<T> over a template parameter
Enforced
Enforced reported Lv.5+
Unrecognized (pzoom≠)
Enforced
Not enforced
Incidental (1/1)
Enforced
Not enforced
Not enforced
Unrecognized ⚠ 1 false positive
Widened to mixed Notes
key-of<T> with a class-constant operand
Unrecognized
Enforced reported Lv.5+
Enforced
Enforced
Not enforced
Incidental (1/1)
Not enforced
Not enforced
Unrecognized
Not enforced
Enforced
literal-int is a Psalm-only literal-ness marker
Unrecognized
Unrecognized
Enforced
Enforced
Not enforced Notes
Incidental (1/1)
Not enforced
Not enforced
Unrecognized
Enforced ⚠ 1 false positive
Not enforced
literal-string
Unrecognized
Enforced reported Lv.7+
Enforced
Enforced
Not enforced Notes
Not enforced
Widened to string
Not enforced
Not enforced
Not enforced
Widened to string Notes
lowercase-string
Not enforced
Enforced reported Lv.5+
Enforced
Enforced
Widened to string
Not enforced
Widened to string
Not enforced
Not enforced
Not enforced
Enforced
negative-int
Enforced
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Enforced
Widened to int
Not enforced
Not enforced
Unrecognized ⚠ 1 false positive
Enforced
never-return as a bottom-type spelling
Partly enforced (1/2)
Enforced reported Lv.3+
Unrecognized (pzoom≠)
Enforced
Not enforced
Partly enforced (1/2)
Not enforced
Partly enforced (1/2)
Unrecognized
Unrecognized ⚠ 1 false positive
Partly enforced (1/2)
never-returns as a bottom-type spelling
Partly enforced (1/2)
Enforced reported Lv.3+
Unrecognized (pzoom≠)
Enforced
Not enforced
Partly enforced (1/2)
Not enforced
Unrecognized
Unrecognized
Unrecognized ⚠ 1 false positive
Partly enforced (1/2)
new<CONSTANT[T]>
Unrecognized Notes
Enforced reported Lv.5+
Unrecognized (pzoom≠) Notes
Unrecognized Notes
Not enforced Notes
Unrecognized
Unrecognized Notes
Unrecognized Notes
Unrecognized Notes
Unrecognized Notes
Not enforced Notes
new<TClass>
Unrecognized Notes
Enforced reported Lv.5+
Unrecognized (pzoom≠) Notes
Unrecognized Notes
Not enforced Notes
Not enforced
Unrecognized Notes
Unrecognized Notes
Unrecognized Notes
Unrecognized Notes
Not enforced Notes
non-decimal-int-string
Unrecognized
Enforced reported Lv.5+
Unrecognized (pzoom≠)
Unrecognized
Incidental (2/2)
Unrecognized
Incidental (0/2)
Not enforced
Unrecognized
Unrecognized ⚠ 1 false positive
Enforced
non-empty-array
Enforced
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Not enforced
Not enforced
Not enforced
Not enforced
Unrecognized ⚠ 1 false positive
Enforced
non-empty-associative-array is a Phan-only array refinement
Partly enforced (1/2)
Unrecognized
Unrecognized (pzoom≠)
Unrecognized
Incidental (2/2)
Unrecognized
Not enforced
Unrecognized
Unrecognized
Unrecognized
Enforced
non-empty-literal-string
Unrecognized
Enforced reported Lv.5+
Enforced (pzoom≠)
Enforced
Incidental (2/2)
Not enforced
Incidental (0/2)
Not enforced
Unrecognized
Unrecognized ⚠ 1 false positive
Widened to a nonexistent-class reference Notes
non-empty-lowercase-string
Partly enforced (1/2)
Enforced reported Lv.5+
Enforced
Enforced
Widened to string
Not enforced
Widened to string
Not enforced
Not enforced
Not enforced
Enforced
non-empty-mixed
Enforced
Not enforced
Unrecognized (pzoom≠)
Partly enforced (1/4)
Incidental (4/4)
Not enforced
Not enforced
Not enforced
Unrecognized
Unrecognized ⚠ 1 false positive
Enforced
non-empty-scalar
Unrecognized
Partly enforced (3/5) reported Lv.5+
Unrecognized (pzoom≠)
Unrecognized
Incidental (5/5)
Not enforced
Incidental (0/5)
Unrecognized
Unrecognized
Unrecognized ⚠ 1 false positive
Enforced
non-empty-string
Enforced
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Enforced
Not enforced
Not enforced
Not enforced
Not enforced
Enforced
non-empty-uppercase-string
Unrecognized
Enforced reported Lv.5+
Unrecognized (pzoom≠)
Enforced
Widened to string
Not enforced
Incidental (0/2)
Not enforced
Unrecognized
Unrecognized ⚠ 1 false positive
Enforced
non-falsy-string
Enforced
Enforced reported Lv.5+
Enforced
Enforced
Partly enforced (1/2)
Enforced
Not enforced
Not enforced
Not enforced
Unrecognized ⚠ 1 false positive
Enforced
non-negative-int
Unrecognized
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Enforced
Widened to int
Not enforced
Not enforced
Unrecognized ⚠ 1 false positive
Enforced
non-null-mixed is a Phan-only refinement of mixed
Enforced
Unrecognized
Unrecognized (pzoom≠)
Unrecognized
Incidental (1/1)
Unrecognized
Not enforced
Unrecognized
Unrecognized
Not enforced ⚠ 1 false positive
Enforced
non-positive-int
Unrecognized
Enforced reported Lv.5+
Enforced
Enforced
Incidental (1/1)
Enforced
Widened to int
Unrecognized
Not enforced
Unrecognized ⚠ 1 false positive
Enforced
non-zero-int
Enforced
Enforced reported Lv.5+
Unrecognized (pzoom≠)
Enforced
Incidental (1/1)
Enforced
Widened to int
Unrecognized
Unrecognized
Unrecognized ⚠ 1 false positive
Enforced
noreturn is a non-standard alias for never
Unrecognized Notes
Enforced reported Lv.3+
Unrecognized Notes
Unrecognized Notes
Not enforced
Not enforced
Unrecognized
Unrecognized
Unrecognized
Unrecognized ⚠ 1 false positive Notes
Partly enforced (1/2)
number
Unrecognized
Enforced reported Lv.5+
Unrecognized (pzoom≠)
Unrecognized
Incidental (2/2)
Enforced
Unrecognized
Enforced
Unrecognized
Unrecognized ⚠ 1 false positive
Enforced
numeric
Unrecognized
Enforced reported Lv.5+
Enforced
Enforced
Incidental (2/2)
Incidental (2/2)
Incidental (0/2)
Enforced
Partly enforced (1/2)
Partly enforced (1/2)
Enforced
numeric-string
Unrecognized Notes
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Enforced
Widened to string
Not enforced
Not enforced
Widened to string
Enforced
object{foo: int}
Widened to object Notes
Enforced reported Lv.5+
Enforced
Enforced
Widened to object Notes
Incidental (2/2)
Not enforced Notes
Not enforced Notes
Widened to object Notes
Unrecognized Notes
Widened to object Notes
open-resource
Unrecognized
Partly enforced (1/2) reported Lv.5+
Unrecognized (pzoom≠)
Partly enforced (1/2)
Partly enforced (1/2)
Not enforced
Not enforced
Unrecognized
Unrecognized
Partly enforced (1/2) ⚠ 2 false positives
Widened to a nonexistent-class reference Notes
PHPDoc/native array mismatch for nullable array parameters
Pass
Not reported (by design) Notes
Not reported (by design) Notes
Not reported (by design) Notes
Fail
Fail
Fail
Fail
Fail
Pass
Not reported (by design) Notes
PHPDoc/native type mismatch for nullable parameters
Pass
Not reported (by design) Notes
Not reported (by design) Notes
Not reported (by design) Notes
Fail
Fail
Fail
Fail
Pass
Pass
Not reported (by design) Notes
positive-int
Enforced
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Enforced
Widened to int
Not enforced
Not enforced
Widened to int Notes
Enforced
properties-of<T> is a Psalm-only utility type
Unrecognized
Unrecognized
Enforced
Enforced
Incidental (1/1)
Not enforced
Not enforced
Unrecognized
Unrecognized
Unrecognized
Widened to a nonexistent-class reference Notes
public-properties-of<T> is a Psalm visibility-filtered utility type
Unrecognized
Unrecognized
Enforced
Enforced
Incidental (2/2)
Not enforced
Not enforced
Unrecognized
Unrecognized
Unrecognized
Not enforced
pure-callable
Unrecognized
Enforced reported Lv.5+
Enforced (pzoom≠)
Incidental (3/3)
Partly enforced (1/3)
Unrecognized
Not enforced
Partly enforced (1/3)
Unrecognized
Unrecognized
Enforced
pure-closure
Unrecognized
Incidental (2/2) reported Lv.5+
Unrecognized (pzoom≠)
Incidental (2/2)
Partly enforced (1/2)
Unrecognized
Widened to Closure
Unrecognized
Unrecognized
Unrecognized ⚠ 1 false positive
Enforced
resource
Enforced
Enforced reported Lv.5+
Enforced
Enforced
Not enforced
Enforced
Not enforced
Enforced
Enforced
Not enforced
Widened to a nonexistent-class reference Notes
scalar
Enforced
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Enforced
Incidental (0/3)
Enforced
Partly enforced (2/3)
Unrecognized ⚠ 1 false positive
Enforced
static-closure
Unrecognized
Enforced reported Lv.5+
Unrecognized (pzoom≠)
Unrecognized
Incidental (2/2)
Unrecognized
Incidental (0/2)
Unrecognized
Unrecognized
Unrecognized ⚠ 1 false positive
Enforced
static-pure-closure
Unrecognized
Incidental (2/2) reported Lv.5+
Unrecognized (pzoom≠)
Unrecognized
Incidental (2/2)
Unrecognized
Incidental (0/2)
Unrecognized
Unrecognized
Unrecognized ⚠ 1 false positive
Enforced
stdClass{field: T} object shapes are a Phan-only notation
Enforced
Unrecognized reported Lv.max
Unrecognized (pzoom≠)
Incidental (1/1)
Not enforced
Unrecognized
Unrecognized
Not enforced
Not measured Notes
Unrecognized ⚠ 1 false positive
Widened to mixed Notes
stringable-object is a Psalm-only object refinement
Unrecognized
Unrecognized
Enforced
Unrecognized
Not enforced
Incidental (1/1)
Not enforced
Unrecognized
Unrecognized
Unrecognized
Widened to a nonexistent-class reference Notes
The template-type utility
Unrecognized
Enforced reported Lv.5+
Unrecognized (pzoom≠)
Enforced
Incidental (1/1)
Not enforced
Incidental (1/1)
Enforced
Unrecognized
Unrecognized ⚠ 1 false positive
Unrecognized
trait-string
Unrecognized
Not enforced
Unrecognized (pzoom≠)
Widened to class-string
Incidental (1/1)
Not enforced
Widened to class-string
Not enforced
Not enforced
Not enforced
Widened to string Notes
true
Partly enforced (1/2)
Enforced reported Lv.5+
Enforced
Enforced
Partly enforced (1/2)
Not enforced
Widened to bool
Not enforced
Partly enforced (1/2)
Enforced
Partly enforced (1/2) Notes
truthy-string
Unrecognized
Enforced reported Lv.5+
Enforced
Enforced
Partly enforced (1/2)
Enforced
Not enforced
Not enforced
Not enforced
Unrecognized ⚠ 1 false positive
Enforced
uppercase-string
Unrecognized
Enforced reported Lv.5+
Unrecognized (pzoom≠)
Enforced
Widened to string
Not enforced
Incidental (0/2)
Not enforced
Unrecognized
Unrecognized ⚠ 1 false positive
Enforced
value-of<T>
Enforced
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Incidental (2/2)
Not enforced
Not enforced
Unrecognized
Not enforced
Enforced
value-of<T> over a template parameter
Enforced
Enforced reported Lv.5+
Enforced ⚠ 1 false positive
Enforced ⚠ 1 false positive
Not enforced
Incidental (1/1)
Enforced
Not enforced
Incidental (0/1) ⚠ 1 false positive
Unrecognized ⚠ 1 false positive
Widened to mixed Notes
value-of<T> over a template parameter bound to a backed enum
Incidental (1/1)
Enforced reported Lv.5+
Unrecognized (pzoom≠)
Enforced
Not enforced
Incidental (1/1)
Enforced
Enforced
Enforced
Unrecognized ⚠ 1 false positive
Widened to mixed Notes
value-of<T> with a backed-enum operand
Not enforced
Enforced reported Lv.5+
Enforced
Enforced
Not enforced
Incidental (2/2)
Not enforced
Not enforced
Partly enforced (1/2)
Unrecognized
Widened to mixed Notes
value-of<T> with a class-constant operand
Unrecognized
Enforced reported Lv.5+
Enforced
Enforced
Not enforced
Incidental (2/2)
Not enforced
Not enforced
Unrecognized
Not enforced
Enforced
PHPDoc tags
Conditional assertion @phpstan-assert-if-false
Not enforced
Not enforced reported Lv.4+ ⚠ 1 false positive
Not enforced (pzoom≠) ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Conditional assertion @psalm-assert-if-false
Not enforced
Not enforced reported Lv.4+ ⚠ 1 false positive
Not enforced (pzoom≠) ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Equality assertion form @phpstan-assert-if-true =Type
Not enforced
Not enforced reported Lv.4+ ⚠ 1 false positive
Not enforced (pzoom≠) ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Equality assertion form @psalm-assert-if-true =Type
Not enforced
Not enforced reported Lv.4+ ⚠ 1 false positive
Not enforced (pzoom≠) ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
@mixin
Recognized (no probes)
Enforced reported Lv.2+
Enforced
Enforced
Recognized (no probes)
Recognized (no probes)
Not enforced
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Negated assertion form @phpstan-assert !Type
Not enforced
Not enforced reported Lv.4+
Not enforced (pzoom≠)
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Negated assertion form @psalm-assert !Type
Not enforced
Not enforced reported Lv.4+
Not enforced (pzoom≠)
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
@param-closure-this
Not enforced
Enforced reported Lv.0+
Not enforced
Enforced
Recognized (no probes)
Recognized (no probes)
Enforced
Enforced
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
@param-immediately-invoked-callable
Recognized (no probes)
Recognized (no probes) reported Lv.8+
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
@param-later-invoked-callable
Recognized (no probes)
Recognized (no probes) reported Lv.8+
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
@phan-assert
Not enforced
Not enforced reported Lv.4+
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
@phan-assert-false-condition
Not enforced
Not enforced
Not enforced
Not enforced ⚠ 1 false positive
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
@phan-assert-true-condition
Not enforced
Not enforced
Not enforced
Not enforced ⚠ 1 false positive
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
@phan-closure-scope
Not enforced
Recognized (no probes) reported Lv.0+
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
@phan-output-reference
Recognized (no probes)
Recognized (no probes) reported Lv.8+
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
@phan-param
Enforced
Enforced reported Lv.5+
Unrecognized
Enforced
Enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Unrecognized Notes
@phan-read-only and @phan-write-only
Enforced
Unrecognized reported Lv.3+
Not enforced
Partly enforced (1/2)
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
@phan-side-effect-free
Not enforced
Enforced reported Lv.2+
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
@phan-type
Enforced
Enforced reported Lv.5+
Unrecognized (pzoom≠)
Enforced
Incidental (1/1)
Unrecognized
Unrecognized
Unrecognized
Unrecognized
Unrecognized
Not enforced
@phpstan-all-methods-impure
Recognized (no probes)
Enforced reported Lv.8+
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
@phpstan-all-methods-pure
Not enforced
Enforced reported Lv.2+
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Enforced
@phpstan-assert
Not enforced
Not enforced reported Lv.4+ ⚠ 1 false positive
Not enforced (pzoom≠) ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
@phpstan-assert-if-true
Not enforced
Not enforced reported Lv.4+ ⚠ 1 false positive
Not enforced (pzoom≠) ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
@phpstan-consistent-constructor
Not enforced
Not enforced reported Lv.0+ ⚠ 1 false positive
Not enforced
Not enforced ⚠ 1 false positive
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced ⚠ 1 false positive
Not enforced
@phpstan-import-type
Unrecognized
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Not enforced
Not enforced
Not enforced
Enforced
Unrecognized
Not enforced
@phpstan-impure
Recognized (no probes)
Enforced reported Lv.8+
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
@phpstan-param
Not enforced
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Enforced
Not enforced
Enforced
Enforced
Not enforced
Enforced
@phpstan-pure
Not enforced
Enforced reported Lv.2+
Enforced
Enforced
Enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Enforced
@phpstan-readonly-allow-private-mutation
Not enforced
Enforced reported Lv.3+
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
@phpstan-require-extends
Not enforced
Enforced reported Lv.2+
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Enforced
Not enforced
Not enforced
Not enforced
@phpstan-require-implements
Not enforced
Enforced reported Lv.2+
Not enforced
Enforced
Enforced
Not enforced
Not enforced
Enforced
Not enforced
Not enforced
Not enforced
@phpstan-return
Incidental (0/1) ⚠ 1 false positive
Incidental (0/1) reported Lv.5+ ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Not enforced
Not enforced ⚠ 1 false positive
@phpstan-sealed
Not enforced
Enforced reported Lv.0+
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
@phpstan-type
Unrecognized
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Not enforced
Not enforced
Not enforced
Enforced
Unrecognized
Not enforced
@phpstan-var
Incidental (0/1) ⚠ 1 false positive
Incidental (0/1) reported Lv.5+ ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Not enforced
@psalm-allow-private-mutation with @readonly
Enforced
Enforced reported Lv.3+
Enforced
Enforced
Enforced
Not enforced
Not enforced
Enforced
Not enforced
Not enforced
Not enforced
@psalm-assert
Not enforced
Not enforced reported Lv.4+ ⚠ 1 false positive
Not enforced (pzoom≠) ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
@psalm-assert-if-true
Not enforced
Not enforced reported Lv.4+ ⚠ 1 false positive
Not enforced (pzoom≠) ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
@psalm-consistent-constructor
Not enforced
Not enforced reported Lv.0+ ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced ⚠ 1 false positive
Not enforced
@psalm-external-mutation-free
Not enforced
Not enforced
Enforced
Not enforced
Enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
@psalm-ignore-falsable-return
Enforced
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Enforced
Enforced
Enforced
Enforced
Enforced
Enforced
@psalm-ignore-nullable-return
Enforced
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Enforced
Enforced
Enforced
Enforced
Enforced
Not enforced
@psalm-immutable
Not enforced
Partly enforced (1/2) reported Lv.3+
Partly enforced (1/2)
Not enforced
Partly enforced (1/2)
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
@psalm-import-type
Unrecognized
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Not enforced
Not enforced
Not enforced
Enforced
Unrecognized
Not enforced
@psalm-internal
Not enforced
Not enforced
Enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
@psalm-mutation-free
Not enforced
Not enforced
Not enforced (pzoom≠)
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
@psalm-param
Enforced
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Enforced
Not enforced
Enforced
Enforced
Not enforced
Enforced
@psalm-pure
Not enforced
Enforced reported Lv.2+
Enforced
Enforced
Enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Enforced
@psalm-readonly / @readonly on properties
Not enforced
Not enforced reported Lv.3+
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
@psalm-return
Incidental (0/1) ⚠ 1 false positive
Incidental (0/1) reported Lv.5+ ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Not enforced
Not enforced ⚠ 1 false positive
@psalm-seal-methods / @no-seal-methods
Recognized (no probes)
Recognized (no probes) reported Lv.2+
Enforced
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
@psalm-seal-properties / @no-seal-properties
Recognized (no probes)
Recognized (no probes) reported Lv.2+
Enforced
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
Recognized (no probes)
@psalm-type
Unrecognized
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Not enforced
Not enforced
Not enforced
Enforced
Unrecognized
Not enforced
@psalm-var
Incidental (0/1) ⚠ 1 false positive
Incidental (0/1) reported Lv.5+ ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Not enforced
@pure-unless-callable-is-impure
Not enforced
Enforced reported Lv.2+
Enforced
Not enforced
Enforced
Not enforced
Not enforced
Not enforced
Not enforced
Unrecognized ⚠ 1 false positive
Enforced
Separate @readonly + @phpstan-allow-private-mutation
Enforced
Enforced reported Lv.3+
Enforced (pzoom≠)
Enforced
Enforced
Not enforced
Not enforced
Enforced
Not enforced
Not enforced
Not enforced
Conditional types
Conditional return type combined with a template parameter
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Conditional return type driven by a parameter type check
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Type aliases
Importing a local type alias into another class
Unrecognized ⚠ 2 false positives
Incidental (0/1) reported Lv.5+ ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Not enforced ⚠ 1 false positive
Incidental (0/1) ⚠ 1 false positive
Not enforced
Not enforced
Not enforced
Incidental (0/1) ⚠ 1 false positive
Unrecognized
Not enforced
Local type aliases via vendor-prefixed annotations
Unrecognized
Enforced reported Lv.5+
Enforced
Enforced
Enforced
Not enforced
Not enforced
Not enforced
Enforced
Unrecognized
Not enforced
Assertions and narrowing
Assertion-driven narrowing to non-empty list
Pass
Pass reported Lv.3+
Pass
Pass
Pass
Fail
Fail
Fail
Fail
Fail
Pass
Basic instanceof-based narrowing checks
Pass
Pass reported Lv.2+
Pass
Pass
Pass
Pass
Pass
Pass
Fail
Fail
Pass
By-reference output parameters via @param-out
Enforced
Enforced
Enforced
Enforced
Enforced
Not enforced
Enforced
Enforced
Not enforced
Not enforced
Enforced
!== false narrowing with non-empty-string|false
Partly enforced (2/3)
Enforced reported Lv.5+
Enforced
Enforced
Partly enforced (2/3)
Partly enforced (2/3)
Partly enforced (2/3)
Partly enforced (2/3)
Partly enforced (2/3)
Unrecognized
Partly enforced (2/3)
!== false narrowing with non-falsy-string|false
Partly enforced (2/3)
Partly enforced (2/3) reported Lv.5+
Partly enforced (2/3)
Partly enforced (2/3)
Partly enforced (2/3)
Incidental (1/3)
Partly enforced (2/3)
Partly enforced (2/3)
Partly enforced (2/3)
Unrecognized
Partly enforced (2/3)
Null-check narrowing
Pass
Pass reported Lv.2+
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Fail
Pass
Post-call narrowing via self-out annotations
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Fail
Fail
Fail
Fail
Fail
Pass
Precondition on the receiver via @psalm-if-this-is
Partly enforced (1/2)
Not enforced
Enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Not enforced
Partly enforced (3/4)
Not enforced
Stubs and external signatures
External signature support through companion helper files
Pass
Pass reported Lv.5+
Pass
Pass
Pass
Fail
Pass
Pass
Pass
Fail
Pass
FIG and PSR compatibility
FIG proposed PHPDoc: @param / @return as the baseline type contract
Pass
Pass reported Lv.4+
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Historical and legacy behavior
Historical compatibility for legacy nullable-via-default patterns
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Fail
Pass
Cross-analyzer regressions
A reversed-key array literal passed to a list{...} parameter
Pass
Fail
Pass
Fail
Fail
Fail
Fail
Fail
Pass
Fail
Pass
array_is_list() on a keyless tuple shape
Pass
Pass reported Lv.4+
Pass (pzoom≠)
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Conflicting property types composed from two traits
Pass
Pass
Pass (pzoom≠)
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Custom @assert-if-true predicate narrowing a string to a string subtype
Pass
Pass reported Lv.7+
Pass (pzoom≠)
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Element-type narrowing by subtracting the null case
Pass
Pass reported Lv.8+
Pass
Pass
Pass
Pass
Pass
Pass
Fail
Pass
Pass
Explicit-integer-key sealed shape as a list
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
list<T>|array<K, V> rejects a hybrid list-and-map value
Fail
Fail
Fail (pzoom≠)
Pass
Fail
Fail
Fail
Fail
Fail
Pass
Pass
Narrowing a backed enum by its ->value comparison
Pass
Pass reported Lv.4+
Pass
Pass
Pass
Pass
Pass
Pass
Fail
Pass
Pass
Narrowing an object union by a discriminating property
Pass
Pass reported Lv.7+
Pass (pzoom≠)
Pass
Pass
Pass
Fail
Pass
Pass
Pass
Pass
Negative-branch narrowing via a class-string identity comparison
Pass
Pass reported Lv.7+
Pass (pzoom≠)
Pass
Pass
Pass
Fail
Pass
Pass
Pass
Pass
Optional-key shapes stay list-compatible
Pass
Pass reported Lv.4+
Pass
Fail
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Positional list destructuring of a string-keyed array
Pass
Pass reported Lv.3+
Fail
Pass
Fail
Fail
Fail
Fail
Fail
Fail
Pass
Union of homogeneous arrays is not an array of unions
Fail
Fail
Fail (pzoom≠)
Pass
Pass
Fail
Fail
Fail
Fail
Fail
Pass
Style & opinionated rules — no runtime-safety impact
Lint-style opinions and advisories (PHPStan strict-rules, deprecations, doc conventions) that do not change whether the code runs. Cells show whether each analyzer opts into reporting the rule — not a pass/fail verdict.
Debug features — type-inspection helpers
Dump/trace APIs used while debugging inference (PHPStan\dumpType(), @psalm-trace, @phan-debug-var, Mago\inspect(), …). Cells use the same recognition/enforcement vocabulary as type-spelling rows: does the analyzer accept this helper, and does it emit a diagnostic that reveals the inferred type?
Analyzers
Reference metadata for each analyzer compared above; versions are those pinned by this suite. Analysis is the one column whose values are coined here rather than read off the project: a type checker aims at type correctness, a type-aware linter at a rule catalogue that inference sharpens, and code intelligence infers types mainly to drive completion and navigation, with diagnostics one feature among many.
Language servers
Every cell in this table records what the project claims about itself — from its README, docs, changelog, release notes, package metadata, or its maintainer’s own blog — and no claim here has been verified by executing the tool. What has been measured lives elsewhere: Intelephense’s and Psalm’s diagnostics scores are in the matrix above, and the servers this machine can launch headless have their protocol behaviour recorded in the measured capability matrix below . Cells read Not stated where the project simply does not say.
Four rows also appear in the analyzer table above. Intelephense and Psalm are in both as the same tool — Psalm’s server runs the analyzer its CLI does, reached over the protocol instead of the command line. The other two are pairs: php-lsp drives mir , and phpy is the CLI frontend of devsense-php-ls . Each pair is versioned separately, so each artifact keeps its own row and its own dates. Phan ships a language server too and belongs here; it has simply not been researched yet.
Language server capabilities — measured
Everything in this section comes from launching each server headless against a small fixture workspace: no cell repeats a claim from a README. Advertised is what the server’s own initialize response declared; the probe verdict says what happened when the suite then called the capability at a position prepared for it. A dash means the handshake did not advertise the capability. The two freemium servers gate paid features in opposite ways, and the matrix shows both faithfully: a free Intelephense omits them from its handshake (they render as dashes), while DEVSENSE advertises them and refuses the call by licence (they render as Gated ). Hover any cell for what it does and does not claim.
Hover type conformance
The typing question, asked over the protocol: hovering a variable whose type the annotation or the narrowing has already established, does the server show that type? Precise means the shown type matches the expected spelling; Widened / other means hover answered with something else — typically the declared type where the narrowed one was expected, or a widened fallback — and the cell’s hover note quotes it verbatim.
Navigating a real project’s dependency graph
The corpus is a real project — psy/psysh@0e8ec1af5990 from the steins-survey checkout — and each row is one symbol in it, chosen per kind. Go to definition jumps from a use site and must land on the declaration; find references asks from the declaration and is scored against the complete, grep-verified set of code references, with only the two files involved in each probe opened — everything else must come out of the server’s own index. A +n after the score counts locations beyond the expected set: the corpus contains same-name decoys (another info(), other getAll()s), so extras expose reference implementations that match by name rather than by symbol. The corpus defines no namespace-level constant that code references — true of much modern PHP — so that kind awaits a define()-era corpus.