| TurboExtensionEnabler |
Activates the phpstan_turbo extension's shadowing classes. The extension registers nothing under PHPStan's class names at module
startup. Once the Composer autoloader is registered, activateIfCompatible()
checks the loaded extension's version against EXPECTED_EXTENSION_VERSION
and, when it matches, asks Runtime::activateShadowing() to declare the
native classes under the PHP twins' real names — PHPStan\TrinaryLogic then
is the native class, linked like a PHP declaration (parents and interfaces
resolved through the autoloader). A mismatched or absent extension never
gets the call, and the PHP implementations load as usual. The version is the short SHA of the last commit touching turbo-ext/src/,
enforced by the phar.yml turbo-version job; the native classes must
behave exactly like the PHP implementations, hence the gate.
|
| TurboProcessRestarter |
Restarts the main PHPStan process via pcntl_exec() when the process it
was started as is not the one PHPStan wants to run — for either of two
reasons, checked independently. The distributed turbo extension is available but not loaded. Parallel
analysis prefers pcntl_fork()ed workers over spawned ones (see
ForkParallelChecker) — a forked worker inherits the booted process instead
of paying a full re-boot. But a forked worker also inherits the parent's
loaded extensions: the spawn-time -d extension= injection
(ProcessHelper) cannot reach it, and dl() cannot load a binary from next
to the phar. So the whole process is re-executed with the extension
before anything else runs — forked workers then inherit the extension,
its shadowing classes, and (when running from a phar) the phar-fork-guard
that keeps phar:// reads safe across fork. The OPcache configuration in effect is not the one PHPStan wants (see
resolveOpcacheArgs()) — usually because OPcache is dormant on CLI, or has
JIT on. The restarted process runs with it activated, JIT pinned off, and
forked workers share the parent's warm opcode cache. This reason stands
on its own so that a php.ini which already loads turbo (a source checkout
with a locally built extension, say) does not leave OPcache dormant just
because there is no extension left to restart for. The restart carries two marker ini entries: RESTARTED_INI is the retry
stop (a binary that failed to load, or an OPcache that could not start,
would otherwise restart forever), and EXTENSION_PATH_INI tells
TurboExtensionSelector that spawned workers still need the -d flag
(command-line -d flags, unlike php.ini, are not inherited). ProcessHelper
sets both on the workers it spawns, along with the OPcache entries: a
worker's configuration is decided by the process spawning it, so it never
restarts itself — before that, every spawned worker on a pcntl host
re-executed itself once to activate OPcache, rebuilding its command line
without the sys_temp_dir and extension entries of the spawn.
|