PHPStan 2.3.0 was published by phpstan-bot on 6 October. The release fixes 75 issues. The maintainers report a large performance gain: analysing WordPress core took 60 seconds with PHPStan 2.1.33, released in December 2025, and takes under 8 seconds with 2.3.0.
includes:
- vendor/phpstan/phpstan/conf/bleedingEdge.neonPHPStan Turbo now covers most of the analyser engine. The extension delivers a 60% speedup over the no-Turbo baseline. The release also reduces Turbo binary size, hardens its traversal and arena handling, reuses binaries built from identical inputs, and improves warm result-cache runs.
Bleeding-edge analysis extends generic inference. PHPStan removes scalar generalization and can infer new Foo() from usages through multi-pass, bidirectional type narrowing. It detects unused variables and writes whose values never reach a use, including array offsets. Closure signatures can be inferred from destinations, by-reference effects are applied at invocation sites, and static variable types come from function bodies.
Further analysis work covers a single-pass inside-out internals rewrite, @pure-unless-parameter-passed, preg_quote delimiters in array patterns, and static::FOO_* or T::FOO_* PHPDoc types after class resolution.
Bug fixes improve scope and control-flow reasoning. Property fetches remain attached to the receiver after static methods or static closures invalidate it. Conditional guard types now use branch differences, and loop analysis builds back edges from continue statements when a loop body cannot reach its end. Loop widening also tracks the variables a loop can write. Closure parameter types in array arguments and generic parameters from array literal skeletons are inferred earlier. Conditional expression holders survive more scope merges, and isset() narrows optional array offsets.
The analyser no longer reads PHP_SBINDIR or PHP_BUILD_PROVIDER from the machine under analysis. Scope::getPhpVersion() now honours the composer.json require.php constraint and drives checks for typed class constants, supported features, properties, trait constants, arrow functions, printf formats, deprecated casts, and extension types. Nullsafe short-circuiting skips @phpstan-assert, conditional return types, and call effects when the chain may stop early. Other fixes cover float casts beyond the int range or NAN, callable-like checks, ?? narrowing, never preservation, array unions, template bounds, impure calls, variadic by-ref out types, private trait methods, and stale phpredis signatures.
Performance work optimizes constant-array offsets, enum-case removal, finite-type counting, large unions, loop widening of shapes, reflection name lookups, TypeCombinatorCache, file cache, type memo bookkeeping, expression keys, namespace uses, unchanged-file content reuse, closure and static-variable inference, and dependency results. The release adds TypeTraverser::mapMemoized(), avoids exporting functions declared inside functions, and reduces duplicate work in ConstantArrayType->checkOurKeys().
Signature updates declare mutually exclusive constant groups for json_encode, json_decode, filter_var, filter_input, openlog, and ZipArchive::open. Oversized array types receive more precise key and value generalization. Internal changes move result-cache dependencies onto scopes, deprecate ResultCacheMetaExtension, improve OpenSslCipherMethodsProvider, split NodeScopeResolver, and let rules listen for several node types. Contributors include staabm, zonuexe, rubas, arpitjain099, calebdw, Jean-Beru, VincentLanglet, and SanderMuller.




Comments
No comments yet — be the first.
Open the discussion
No account or password needed — just enter your e-mail and we’ll send you a one-time sign-in link. First time here? You’re set up automatically.
Your rating will be applied automatically after you sign in.
Check your inbox
We’ve sent a sign-in link to …. Open it on this device — this tab will sign you in automatically.
Nothing arrived? Check your spam folder — and mark the mail as "Not spam" so it lands in your inbox next time.