PHPStan Source Code

repository·2.2.x·Indexed 18 days ago

https://github.com/phpstan/phpstan-src

The core engine source code for PHPStan, a static analysis tool for PHP. This repository includes documentation for local development, PHAR compilation, and the phpstan_turbo native C++ acceleration extension, as well as infrastructure stacks for apiref.phpstan.org using AWS CDK.

Tokens
11.8K
Snippets
43
Records
68
Agent score
63%

What's inside phpstan-src

  1. How the native parser engine works

    2.2.x

    The native parser engine reimplements the php-parser 5.8.0 LALR engine and node building in C++. It is integrated via the PHPStan\Parser\ParserRunner seam.

    Key characteristics:

    • Performance: Tokenization uses PHP's C tokenizer. The shift/reduce loop, semantic actions, node construction, and error recovery are all implemented natively.
    • Fallback: Non-Php8 parsers or non-string inputs automatically fall back to the standard $parser->parse() method.
    • Parity: The engine is designed for byte-identical parity with the PHP implementation, ensuring identical ASTs, error collections, and token streams.
  2. Infrastructure stacks for apiref.phpstan.org

    2.2.x

    The infrastructure is defined using AWS CDK in the us-east-1 region (required for CloudFront and ACM). It consists of two primary stacks:

    • PhpstanApirefOidcRoles: Manages the phpstan-apiref-infra-deploy role used by apiref-infra.yml. It reuses the existing account-wide OIDC provider.
    • PhpstanApirefWebsite: Manages the core website resources, including a private, versioned S3 bucket (with OAC), a CloudFront distribution, CloudFront Function 2.0 for per-version landing-page redirects, a Response Headers Policy, an ACM certificate, and the phpstan-apiref-deploy role used by apiref.yml.
  3. How the stub-shadowing pattern works

    2.2.x

    The extension uses a "stub-shadowing" pattern to transparently replace PHP implementations with native C++ ones. This allows existing PHP code to call the original class names while actually executing native code.

    The Process

    1. PHP Implementation: A class is written in plain PHP (e.g., PHPStan\Analyser\ScopeOps).
    2. Native Implementation: The extension implements the same class in the PHPStanTurbo namespace.
    3. Shadowing: The PHP class is marked with the #[ShadowedByTurboExtension] attribute, naming its native counterpart.
    4. Stub Generation: During composer dump-autoload, a stub shell is generated in vendor/turbo-stubs.php. This shell looks like: final class Foo extends \PHPStanTurbo\Foo {}.
    5. Activation: When enabled, PHPStan\Turbo\TurboExtensionEnabler requires the stub file before the Composer autoloader registers, allowing the native implementation to be inherited transparently.

    Constraints

    • Single Inheritance: A shadowed class cannot have a parent class because the stub must inherit from the native class to achieve shadowing.
    • Interfaces: The stub shell repeats the original class's implements clause to ensure instanceof and DI type lookups continue to work correctly.
    #[ShadowedByTurboExtension(PHPStanTurbo\Foo::class)]
    class Foo implements SomeInterface {}
    
    // Generated stub in vendor/turbo-stubs.php:
    final class Foo extends \PHPStanTurbo\Foo implements SomeInterface {}
  4. Understand the performance characteristics of phpstan_turbo

    2.2.x

    The phpstan_turbo extension provides native acceleration for PHPStan. As of July 2026, it provides a ~23% performance gain on PHPStan's self-analysis of src/.

    Performance gains are categorized by the level of native implementation:

    • 30% gain: Achieved via targeted ports like native ExpressionResultStorage, CachedParser fixes, and memoization in better-reflection.
    • 50% gain: Requires a native expression engine (moving NodeScopeResolver expression walks and ExprHandler dispatch loops into C++).
    • 70% gain: Requires making "everything PHPStan-owned" native, including a native Type kernel and statement-level walks.
    • 90% gain: Requires a ground-up native analyzer that abandons the hybrid approach (PHP rules/extensions), which is not the goal of this extension.

    The realistic ceiling for the current hybrid approach (keeping PHP rules and third-party extensions) is approximately 60–75%.

  5. How the shared-memory arena works

    2.2.x

    The shared-memory arena allows parallel worker processes to share lazily-computed, read-mostly data (like function signature maps or symbol indexes) without duplicating memory usage.

    Lifecycle and Mechanics:

    • Creation: The master process creates a named shared-memory object (POSIX shm_open or Windows pagefile-backed section) and passes the name to workers via the --arena option.
    • Publication: Publication is lock-free using a bump-allocate and CAS (Compare-And-Swap) strategy. If multiple processes race to publish the same key, they converge on the first writer.
    • Lifetime: The arena exists for exactly one run. The master unlinks the name once workers arrive and destroys the mapping when analysis ends.
    • Data Format: Records are flat, offset-based, position-independent blobs of data-only PHP values. Because they are not seen by the PHP GC, shared pages are never dirtied by refcounting.
  6. Upgrade from PHPStan 1.x to 2.0

    2.2.x

    To upgrade to PHPStan 2.0, follow these steps to ensure a smooth transition:

    1. Prepare with PHPStan 1.12: Update to the latest PHPStan 1.12 release and enable Bleeding Edge. This enables the rules and behaviors that become default in 2.0.
    2. Enable Deprecation Rules: Ensure phpstan/phpstan-deprecation-rules is installed and enabled.
    3. Verify Green Build: Achieve a clean build (no errors or deprecations) on PHPStan 1.12 with Bleeding Edge enabled.
    4. Update Dependencies: Update phpstan/phpstan and all related 1st party extensions (e.g., phpstan-doctrine, phpstan-phpunit, phpstan-symfony) to ^2.0 in your composer.json.
    5. Run Composer Update: Execute composer update 'phpstan/*' -W to update the packages and their dependencies.
    6. Handle New Errors: Address new reported errors or add them to your baseline.

    PHP Version Requirement: PHPStan 2.0 requires PHP 7.4 or newer.

    "require-dev": {
        "phpstan/phpstan": "^2.0",
        "phpstan/phpstan-deprecation-rules": "^2.0",
        "phpstan/phpstan-doctrine": "^2.0",
        "phpstan/phpstan-nette": "^2.0",
        "phpstan/phpstan-phpunit": "^2.0",
        "phpstan/phpstan-strict-rules": "^2.0",
        "phpstan/phpstan-symfony": "^2.0",
        "phpstan/phpstan-webmozart-assert": "^2.0"
    }
  7. Install the development environment

    2.2.x

    To set up the development environment for phpstan-src, use Composer to install dependencies.

    Note for macOS users: If you encounter patch application failures during installation due to an older version of patch, install a newer version using brew install gpatch.

    composer install
  8. Local development for apiref.phpstan.org infrastructure

    2.2.x

    To develop locally on the CDK application for the apiref.phpstan.org infrastructure, use the following npm commands to install dependencies, check types, run tests, synthesize templates, or compare changes against your AWS account.

    Note: npm run diff requires valid AWS credentials for the target account.

    npm ci
    npm run check     # tsc --noEmit
    npm test          # vitest: 25 redirect-fn tests + 11 stack assertions
    npm run synth     # cdk synth --all
    npm run diff      # cdk diff --all
  9. Compile the PHPStan PHAR

    2.2.x

    To compile the PHPStan PHAR from source, follow these steps. Note that running the compiler will modify your composer.json file and the vendor directory; you should revert these changes once the compilation is complete.

    1. Install dependencies using Composer.
    2. Run the preparation script.
    3. Navigate to the build directory.
    4. Use the box tool to compile the PHAR without parallel execution.

    The resulting PHAR file will be located at tmp/phpstan.phar.

    composer install
    php bin/prepare
    cd build
    php ../box/vendor/bin/box compile --no-parallel
  10. Enable the phpstan_turbo extension

    2.2.x

    To enable the extension, add it to your php.ini configuration. It is recommended to do this in the main configuration so that parallel worker processes inherit the setting.

    Use the absolute path to the compiled .so (or .dll on Windows) file.

    extension=/absolute/path/to/phpstan-src/turbo-ext/phpstan_turbo.so
  11. Test the phpstan_turbo extension

    2.2.x

    You can verify the extension using differential testing, running PHPStan's native test suite, or comparing output identity between runs with and without the extension.

    Important: When comparing output identity, ensure you clear the result cache between runs to get accurate results.

    # differential test of the native classes vs the PHP implementations
    php -d extension=$(pwd)/phpstan_turbo.so tests/smoke.php
    
    # PHPStan's own test suite with the extension loaded
    php vendor/bin/phpunit ...
    
    # output identity (clear the result cache between runs!)
    bin/phpstan analyse ... --error-format=raw   # with extension
    PHPSTAN_TURBO=0 bin/phpstan analyse ...      # without
  12. Install phpstan_turbo native acceleration extension

    2.2.x

    The phpstan_turbo extension is a native C++ extension that accelerates PHPStan's hottest code paths by approximately 25%. It is optional and does not change analysis results.

    Standard Installation

    Most users do not need to install anything manually. The phpstan/phpstan Composer package includes prebuilt binaries for common platforms (Linux, macOS, Windows) for PHP 8.3+. PHPStan automatically loads the correct binary into its worker processes.

    Manual Installation via PIE

    If you run phpstan.phar manually outside of a Composer environment, you must install the extension using PIE:

    pie install phpstan/turbo