PHPStan: Advanced Static Analysis for PHP
PHPStan is a powerful static analysis tool for PHP that finds bugs in your code without running it. It performs deep analysis of your codebase to catch errors, enforce type safety, and improve code quality before your code reaches production.
Table of Contents
- What is PHPStan?
- Why Use PHPStan?
- Installation
- Configuration Explained
- Analysis Levels
- Common Use Cases
- Running PHPStan
- Best Practices
- Advanced Configuration
What is PHPStan?
PHPStan is a static analysis tool that analyzes your PHP code without executing it. It:
- Detects bugs before they reach production
- Enforces type safety through comprehensive type checking
- Validates code logic and identifies unreachable code
- Analyzes method calls and property access
- Checks PHPDoc annotations for accuracy
PHPStan understands your code structure and can identify issues that traditional testing might miss.
Why Use PHPStan?
1. Early Bug Detection
Catches errors before they cause runtime failures:
- Undefined variables and methods
- Type mismatches and incompatibilities
- Logic errors and unreachable code
- Missing return statements
2. Type Safety Enforcement
Ensures your code respects type contracts:
- Validates method signatures
- Checks parameter and return types
- Identifies nullable type violations
- Enforces strict type declarations
3. Code Quality Improvement
Promotes better coding practices:
- Identifies unused code and variables
- Validates PHPDoc annotations
- Ensures consistent API usage
- Detects potential security issues
4. Refactoring Confidence
Provides safety net during code changes:
- Validates that changes don't break existing functionality
- Identifies impact of API changes
- Ensures backward compatibility
- Catches regression bugs
Installation
Via Composer (Recommended)
# Install as dev dependency
composer require --dev phpstan/phpstan
# Install with extensions
composer require --dev phpstan/phpstan
composer require --dev phpstan/extension-installer
Verify Installation
# Check PHPStan version
./vendor/bin/phpstan --version
# Run basic analysis
./vendor/bin/phpstan analyse src
Configuration Explained
Here's the configuration file (phpstan.neon.dist
) with detailed explanations:
parameters:
level: max
paths:
- src
reportUnmatchedIgnoredErrors: true
Configuration Breakdown
1. Analysis Level
level: max
- Purpose: Sets the strictness level of analysis
- Range: 0-9 (0 = basic, 9 = maximum strictness)
max
: Always uses the highest available level- Effect: Enables all available checks and rules
2. Analysis Paths
paths:
- src
- Purpose: Defines which directories to analyze
- Common paths:
src/
,app/
,tests/
- Effect: Focuses analysis on relevant code directories
3. Error Reporting
reportUnmatchedIgnoredErrors: true
- Purpose: Reports when ignore patterns don't match any errors
- Benefit: Prevents outdated ignore rules from hiding new issues
- Effect: Ensures ignore patterns remain relevant
Analysis Levels
PHPStan uses levels 0-9 to gradually increase analysis strictness:
Level 0 (Basic)
- Basic syntax checking
- Undefined functions and classes
- Wrong number of arguments passed to methods
Level 1-3 (Intermediate)
- Unknown methods called on objects
- Unknown properties accessed on objects
- Unknown variables in certain contexts
Level 4-6 (Advanced)
- Return types declared in PHPDoc
- Basic dead code detection
- Unreachable statements after return/throw
Level 7-9 (Maximum)
- Report partially wrong union types
- Report calling methods on nullable types
- Strict comparisons of incompatible types
Level Max (Bleeding Edge)
- Uses the highest available level
- Includes experimental rules
- Future-proof configuration
Common Use Cases
1. Type Safety Validation
PHPStan detects:
// Type mismatch
function processUser(User $user): void
{
echo $user->name; // Error if User::$name is private
}
// Nullable type violation
function getName(?string $name): string
{
return $name; // Error: might return null
}
// Correct version
function getName(?string $name): string
{
return $name ?? 'Unknown';
}
2. Method and Property Validation
PHPStan catches:
class User
{
private string $name;
public function getName(): string
{
return $this->name;
}
}
$user = new User();
echo $user->email; // Error: Property User::$email does not exist
$user->invalidMethod(); // Error: Method does not exist
3. Array and Collection Analysis
PHPStan validates:
// Array key existence
$config = ['database' => ['host' => 'localhost']];
echo $config['cache']['driver']; // Error: Offset 'cache' does not exist
// Collection type checking
/** @var User[] $users */
$users = getUsers();
foreach ($users as $user) {
echo $user->name; // PHPStan knows $user is User instance
}
4. Return Type Validation
PHPStan enforces:
function findUser(int $id): User
{
$user = User::find($id);
if (!$user) {
return null; // Error: Cannot return null, expects User
}
return $user;
}
// Correct version
function findUser(int $id): ?User
{
return User::find($id);
}
Running PHPStan
1. Basic Analysis
# Analyze src directory
./vendor/bin/phpstan analyse src
# Analyze multiple directories
./vendor/bin/phpstan analyse src tests
# Analyze specific file
./vendor/bin/phpstan analyse src/User.php
2. Configuration Options
# Use custom configuration
./vendor/bin/phpstan analyse -c phpstan.neon
# Set analysis level
./vendor/bin/phpstan analyse --level=5 src
# Memory limit for large projects
./vendor/bin/phpstan analyse --memory-limit=1G src
3. Output Formats
# Default output
./vendor/bin/phpstan analyse src
# JSON output for CI/CD
./vendor/bin/phpstan analyse --error-format=json src
# GitHub Actions format
./vendor/bin/phpstan analyse --error-format=github src
4. Advanced Options
# Generate baseline (ignore existing errors)
./vendor/bin/phpstan analyse --generate-baseline
# Clear cache
./vendor/bin/phpstan clear-cache
# Debug mode
./vendor/bin/phpstan analyse --debug src
Best Practices
1. Incremental Adoption
Start with lower levels and gradually increase:
# Start with level 0
./vendor/bin/phpstan analyse --level=0 src
# Gradually increase
./vendor/bin/phpstan analyse --level=5 src
# Eventually reach max
./vendor/bin/phpstan analyse --level=max src
2. Use Baseline for Legacy Code
Generate baseline to ignore existing errors:
# Generate baseline
./vendor/bin/phpstan analyse --generate-baseline
# Run analysis ignoring baseline errors
./vendor/bin/phpstan analyse
3. CI/CD Integration
Add PHPStan to your CI pipeline:
# .github/workflows/phpstan.yml
name: PHPStan
on: [push, pull_request]
jobs:
phpstan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: shivammathur/setup-php@v2
with:
php-version: "8.2"
- run: composer install --no-dev --optimize-autoloader
- run: ./vendor/bin/phpstan analyse --error-format=github
4. IDE Integration
VS Code with PHPStan extension:
{
"phpstan.enabled": true,
"phpstan.path": "./vendor/bin/phpstan",
"phpstan.configFile": "phpstan.neon"
}
Advanced Configuration
Complete Configuration Example
parameters:
level: max
paths:
- src
- tests
# Exclude specific directories
excludePaths:
- src/Legacy/*
- tests/fixtures/*
# Custom error reporting
reportUnmatchedIgnoredErrors: true
checkMissingIterableValueType: true
checkGenericClassInNonGenericObjectType: true
# Ignore specific errors
ignoreErrors:
- '#Call to an undefined method App\\User::invalidMethod\(\)#'
-
message: '#Access to an undefined property#'
path: src/Legacy/OldClass.php
# Additional rules
checkAlwaysTrueCheckTypeFunctionCall: true
checkAlwaysTrueInstanceof: true
checkAlwaysTrueStrictComparison: true
checkExplicitMixedMissingReturn: true
checkFunctionNameCase: true
checkInternalClassCaseSensitivity: true
# Bootstrap files
bootstrapFiles:
- tests/bootstrap.php
# Autoload directories
scanDirectories:
- src/helpers
# Stub files for missing extensions
stubFiles:
- stubs/custom.stub
Framework-Specific Configuration
Laravel Configuration:
includes:
- ./vendor/nunomaduro/larastan/extension.neon
parameters:
level: max
paths:
- app
- database
- routes
- tests
# Laravel-specific ignores
ignoreErrors:
- '#Unsafe usage of new static\(\)#'
- '#Call to an undefined method Illuminate\\Database\\Eloquent\\Builder#'
# Laravel features
checkMissingIterableValueType: false
checkGenericClassInNonGenericObjectType: false
Symfony Configuration:
includes:
- ./vendor/phpstan/phpstan-symfony/extension.neon
parameters:
level: max
paths:
- src
- tests
symfony:
container_xml_path: var/cache/dev/App_KernelDevDebugContainer.xml
Custom Rules and Extensions
parameters:
level: max
paths:
- src
# Custom rules
rules:
- App\PHPStan\Rules\NoEntityManagerInControllerRule
# Service extensions
services:
-
class: App\PHPStan\Extension\CustomExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
# Type extensions
typeAliases:
UserId: 'int<1, max>'
EmailAddress: 'string'
Performance Optimization
parameters:
level: max
paths:
- src
# Memory optimization
memoryLimitFile: .phpstan-memory-limit
# Parallel processing
parallel:
jobSize: 20
maximumNumberOfProcesses: 4
# Cache optimization
tmpDir: var/cache/phpstan
# Exclude large files
excludePaths:
- src/Generated/*
- '*/large-file.php'
Extensions and Integrations
Popular Extensions
# Doctrine extension
composer require --dev phpstan/phpstan-doctrine
# Symfony extension
composer require --dev phpstan/phpstan-symfony
# PHPUnit extension
composer require --dev phpstan/phpstan-phpunit
# Laravel extension (Larastan)
composer require --dev nunomaduro/larastan
Integration with Other Tools
With Rector:
#!/bin/bash
# Quality check script
./vendor/bin/rector process --dry-run
./vendor/bin/phpstan analyse
./vendor/bin/pint --test
With PHPUnit:
#!/bin/bash
# Test and analyze
./vendor/bin/phpunit
./vendor/bin/phpstan analyse
Troubleshooting
Common Issues
- Memory Limit Exceeded
php -d memory_limit=1G ./vendor/bin/phpstan analyse src
- Autoloading Issues
parameters:
bootstrapFiles:
- vendor/autoload.php
- bootstrap/app.php
- False Positives
parameters:
ignoreErrors:
- '#Specific error pattern#'
- Performance Issues
# Clear cache
./vendor/bin/phpstan clear-cache
# Reduce parallel processes
./vendor/bin/phpstan analyse --no-progress src
Debugging Tips
# Verbose output
./vendor/bin/phpstan analyse --debug src
# Analyse specific error
./vendor/bin/phpstan analyse --debug src/User.php
# Generate baseline for debugging
./vendor/bin/phpstan analyse --generate-baseline phpstan-baseline.neon
Conclusion
PHPStan is an essential tool for maintaining high-quality PHP codebases. It provides comprehensive static analysis that catches bugs early, enforces type safety, and improves overall code quality.
Key Benefits:
- Early bug detection prevents runtime errors
- Type safety enforcement improves code reliability
- Code quality insights guide better architecture decisions
- Refactoring confidence through comprehensive analysis
- CI/CD integration maintains quality standards
Getting Started:
- Install PHPStan via Composer
- Create basic configuration (
phpstan.neon
) - Start with lower analysis levels
- Gradually increase strictness
- Integrate with development workflow
Best Practices:
- Use baseline for legacy code
- Integrate with CI/CD pipeline
- Configure IDE for real-time feedback
- Combine with other quality tools
- Regular analysis during development
PHPStan becomes more valuable as your codebase grows and team size increases. It's particularly beneficial for teams working on critical applications where code quality and reliability are paramount.
Related Resources
Want to see more PHP development tools? Check out our comprehensive guides on building robust PHP applications.