You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Strip run-time type checks from PHPStan's own code with a turbo optimizer pass
PHPStan's code is verified by PHPStan at its strictest level, so the
engine's argument and return type checks on it re-check what analysis
already proved — at a measurable price: a class-typed parameter costs a
class-entry lookup plus an instanceof on every call (ZEND_RECV), a typed
return the same on the way out (ZEND_VERIFY_RETURN_TYPE). A php-src build
that skipped both for PHPStan's files put the ceiling at -8.3% user CPU on
the self-analysis.
The extension now registers an OPcache optimizer pass
(zend_optimizer_register_pass, resolved by name so the extension keeps
loading without OPcache). Registered passes run at the end of
zend_optimize_script(), before the script is persisted, so what shared
memory holds is the stripped code. For every function of a script compiled
from the running phar (the prefix TurboExtensionEnabler hands over):
ZEND_ACC_HAS_TYPE_HINTS is cleared — the engine then skips the RECV opcodes
of the passed arguments altogether and RECV_INIT stops verifying, while
reflection and inheritance checks keep reading arg_info — and
VERIFY_RETURN_TYPE on a variable or temporary becomes a NOP. Signatures that
can coerce (float without int), typed variadics (RECV_VARIADIC reads
arg_info directly), by-reference returns, the implicit missing-return check
and typed property writes are left as they are.
Nothing outside the phar is touched, and since a check sits in the callee,
extensions, bootstrap files and the analysed project keep checking what
PHPStan hands them. What goes is the TypeError at the boundary when such
code passes PHPStan a wrong value; it surfaces later instead. A --debug run
therefore keeps the checks — the "run with --debug" advice on internal
errors then yields the original error — and PHPUnit never reaches the
switch, so test suites always run fully checked. The restarted process pins
opcache.optimization_level to PHP's default because the pass lives inside
the optimizer.
Slevomat, full project, distributed phar (restart -> OPcache -> turbo ->
forked workers), pass on vs off, interleaved: 612.8/634.7s vs 645.4/670.3s
user CPU (-5.2%), identical output. The phar run gains less than the
ceiling because turbo already runs the hottest classes natively and the
variadic checks stay.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MJ5dqQJ8BgjZjK28Zj9WBL
0 commit comments