Skip to main content
The PHPStan analyzer integrates PHPStan - a powerful static analysis tool - into Retina’s analysis pipeline, providing deep type checking and error detection.

What it does

This analyzer:
  • Runs PHPStan on your plugin’s src/ directory
  • Loads PocketMine-MP stubs for accurate type information
  • Categorizes errors into specific issue types
  • Determines severity based on error characteristics
  • Reports results alongside other Retina analyzers
PHPStan is optional. If PHPStan is not installed, Retina will show an informational message and continue with other analyzers.

Installation

Install PHPStan in your plugin as a dev dependency:
composer require --dev phpstan/phpstan
Retina will automatically detect and use it.

How it works

1. PHPStan detection

Retina searches for the PHPStan binary in:
  • {plugin}/vendor/bin/phpstan (plugin’s local installation)
  • {retina}/vendor/bin/phpstan (Retina’s installation)
  • /usr/local/bin/phpstan (global installation)
  • /usr/bin/phpstan (system installation)
  • System PATH via which phpstan

2. Configuration generation

Retina automatically creates a temporary PHPStan configuration with:
parameters:
    level: 6  # Configurable via --level flag
    paths:
        - /path/to/plugin/src
    scanDirectories:
        - /path/to/retina/stubs  # PocketMine-MP type stubs
    stubFiles:
        - /path/to/retina/stubs/**/*.php
    reportUnmatchedIgnoredErrors: false
    treatPhpDocTypesAsCertain: false

3. Analysis execution

Retina runs:
phpstan analyse --error-format=json --no-progress -c {config} -l {level} src/
With a 300-second timeout to handle large projects.

4. Result parsing

PHPStan’s JSON output is parsed and categorized into Retina issue types:
  • Undefined variables, methods, classes, functions, properties
  • Type mismatches and violations
  • Return type errors
  • Unused code
  • Dead code
  • Interface and inheritance violations
  • Null safety issues
  • And more…

Error categorization

The analyzer maps PHPStan errors to specific categories:
  • Undefined variable: $varUNDEFINED_VARIABLE
  • Call to undefined methodUNDEFINED_METHOD
  • Class X not foundUNDEFINED_CLASS
  • Access to undefined constantUNDEFINED_CONSTANT
  • Function X not foundUNDEFINED_FUNCTION
  • Access to an undefined propertyUNDEFINED_PROPERTY
  • expects X, Y givenTYPE_MISMATCH
  • Parameter #1 expects X, Y givenPARAMETER_TYPE
  • should return X but returns YRETURN_TYPE
  • return statement is missingMISSING_RETURN
  • Unused variableUNUSED_VARIABLE
  • is never usedUNUSED_VARIABLE
  • Dead codeDEAD_CODE
  • Unreachable statementDEAD_CODE
  • does not implement interfaceINTERFACE_VIOLATION
  • cannot extend final classINVALID_INHERITANCE
  • abstract class contains abstract methodABSTRACT_VIOLATION
  • Cannot instantiate abstract classINSTANTIATION_ERROR
  • Access to private propertyVISIBILITY_VIOLATION
  • Cannot access protected methodVISIBILITY_VIOLATION
  • Static call to instance methodSTATIC_CALL_ERROR
  • on nullNULL_SAFETY
  • might be nullNULL_SAFETY
  • possibly nullNULL_SAFETY

Severity determination

PHPStan errors are assigned severity levels:
  • ERROR - Non-ignorable issues that will cause runtime failures
  • WARNING - Issues containing “deprecated”, “unused”, “might”, or “possibly”
  • INFO - Suggestions and hints

Analysis levels

PHPStan supports 10 analysis levels (0-9). Control strictness with the --level flag:
retina run --level 9  # Maximum strictness
retina run -l 3       # Relaxed analysis
Or in retina.yml:
level: 6  # Default
  • Level 0 - Basic checks, minimal false positives
  • Level 1 - Unknown classes, unknown methods called on $this
  • Level 2 - Unknown methods on all expressions, validating PHPDocs
  • Level 3 - Return types, types assigned to properties
  • Level 4 - Dead code, unreachable code
  • Level 5 - Checking types of arguments passed to methods
  • Level 6 - Report missing typehints (default)
  • Level 7 - Report partially wrong union types
  • Level 8 - Report calling methods and accessing properties on nullable types
  • Level 9 - Be strict about mixed type
Higher levels produce more accurate results but may report more issues. Start at level 6 and adjust based on your needs.

PocketMine-MP stubs

Retina includes PocketMine-MP type stubs that provide:
  • Accurate type information for PM API classes
  • Method signatures and return types
  • Property types
  • Constant definitions
This prevents false positives when using PocketMine-MP APIs.

Example output

Parameter #1 $world of method Player::teleport() expects pocketmine\world\World, pocketmine\level\Level given.
  File: src/Main.php:45
  Category: PARAMETER_TYPE
  Severity: ERROR
  Fix: Update to use pocketmine\world\World instead of deprecated pocketmine\level\Level

Method Main::handleData() has no return type specified.
  File: src/Main.php:78
  Category: OTHER
  Severity: WARNING
  Tip: Add return type to improve type safety

Handling PHPStan not found

If PHPStan is not installed, Retina reports:
PHPStan binary not found. Install PHPStan for enhanced static analysis: composer require --dev phpstan/phpstan
  File: composer.json:1
  Category: OTHER
  Severity: INFO
  Code: phpstan_not_found
  Fix: Run: composer require --dev phpstan/phpstan
This is informational - Retina continues with other analyzers.

Handling analysis failures

If PHPStan crashes or times out:
PHPStan analysis failed: Process timed out after 300 seconds
  File: src:1
  Category: OTHER
  Severity: WARNING
  Code: phpstan_analysis_failed
  Fix: Check PHPStan configuration and ensure it is properly installed

Configuration

Disable PHPStan analyzer

In retina.yml:
excludeAnalyzers:
  - PHPStan

Set analysis level

level: 8

Create custom PHPStan config

If your plugin already has a phpstan.neon or phpstan.neon.dist, Retina will still generate its own temporary config to ensure PocketMine-MP stubs are loaded. To use your own config:
  1. Include Retina’s stubs in your config:
parameters:
    scanDirectories:
        - vendor/retina/stubs
  1. Retina will respect your configuration when found.

Performance

  • PHPStan analysis can be slow on large plugins (30-300 seconds)
  • Results are worth the wait - PHPStan catches bugs other analyzers miss
  • Consider excluding PHPStan in CI for faster feedback, running it separately

When to use PHPStan analyzer

Use when

  • You want deep type checking
  • You’re refactoring complex code
  • You’re preparing for production
  • You want to catch edge cases

Skip when

  • You need fast analysis
  • You’re doing rapid prototyping
  • You only want PM-specific checks
  • CI pipeline is time-sensitive

Advanced usage

Ignore specific PHPStan errors

Use PHPStan’s inline ignore comments:
// @phpstan-ignore-next-line
$result = $this->riskyOperation();
Or in PHPStan config:
parameters:
    ignoreErrors:
        - '#Call to an undefined method#'

Custom PHPStan rules

Install PHPStan extensions for additional checks:
composer require --dev phpstan/phpstan-strict-rules
composer require --dev phpstan/phpstan-deprecation-rules
Then reference them in your phpstan.neon:
includes:
    - vendor/phpstan/phpstan-strict-rules/rules.neon
    - vendor/phpstan/phpstan-deprecation-rules/rules.neon

See also

Deprecated API analyzer

Detect PocketMine-MP API deprecations

Thread safety analyzer

Find async task thread safety issues

PHPStan documentation

Learn more about PHPStan

Build docs developers (and LLMs) love