phpstan/phpstan-strict-rules
Opinionated extra rules for PHPStan to enforce strict, strongly typed PHP. Catches loose booleans in conditions, unsafe strict parameters, useless casts, non-numeric arithmetic, variable overwrites in loops, and switch/case type mismatches for safer defensive code.
Installation:
composer require --dev phpstan/phpstan-strict-rules
Use phpstan/extension-installer for automatic inclusion in your phpstan.neon config.
First Run: Execute PHPStan with the default strict rules:
vendor/bin/phpstan analyse src --level=max
The package enforces stricter defaults (e.g., checkStrictPrintfPlaceholderTypes=true).
First Use Case: Run PHPStan on a Laravel controller to catch common pitfalls like:
==/!=).$var[] =).Gradual Adoption:
Disable all rules initially (strictRules.allRules: false), then enable them incrementally:
parameters:
strictRules:
disallowedLooseComparison: true
booleansInConditions: true
Team Onboarding:
phpstan/extension-installer to auto-include rules in CI/CD pipelines.phpstan.neon:
ignoreErrors:
- '#DisallowedLooseComparison# in app/Helpers/legacy.php'
Laravel-Specific Patterns:
strictFunctionCalls for array_key_exists() in dependency injection.matchingInheritedMethodNames to catch case-sensitive method overrides.closureUsesThis to ensure closures reference $this directly.Testing:
phpunit/phpstan to fail builds on strict rule violations.- name: PHPStan Strict Rules
run: vendor/bin/phpstan analyse --level=max
PhpStan\StrictRules\Rules\* and override processNode().dynamicCallOnStaticMethod for facades (e.g., Cache::remember()) via:
ignoreErrors:
- '#DynamicCallOnStaticMethod# in app/Providers/AppServiceProvider.php'
@phpstan-ignore-next-line sparingly for legacy helpers.False Positives:
UselessCast: May flag (void) casts on #[\NoDiscard] methods (fixed in v2.0.11).disallowedEmpty: Conflicts with Laravel’s Arr::exists() helper. Exclude:
ignoreErrors:
- '#DisallowedEmpty# in vendor/laravel/framework/src/Illuminate/Support/Arr.php'
Performance:
checkAlwaysTrueStrictComparison add overhead. Disable if CI timeouts occur:
parameters:
strictRules:
checkAlwaysTrueStrictComparison: false
Laravel-Specific Issues:
requireParentConstructorCall may conflict with Laravel’s BootServiceProvider. Exclude:
ignoreErrors:
- '#RequireParentConstructorCall# in app/Providers/BootServiceProvider.php'
disallowedShortTernary may trigger in @php directives. Use:
@php($value ?? null) // Instead of $value ?: null
--verbose to see rule-specific messages:
vendor/bin/phpstan analyse --verbose
numericOperandsInArithmeticOperators).Custom Rules:
Create a Rules directory in your project and extend PhpStan\StrictRules\Rules\BaseRule:
namespace App\Rules;
use PhpStan\StrictRules\Rules\BaseRule;
class CustomRule extends BaseRule {
public function processNode(Node $node) { ... }
}
Include in phpstan.neon:
includes:
- app/Rules/*.php
Overriding Defaults:
Override PHPStan’s core parameters in phpstan.neon:
parameters:
checkStrictPrintfPlaceholderTypes: true
reportNonIntStringArrayKey: true
Selective Enforcement:
Use @phpstan-ignore-line for specific lines or @phpstan-ignore-file for entire files (e.g., third-party libraries).
phpstan/phpstan-doctrine: Enforce strict typing in Laravel’s Doctrine entities.phpstan/phpstan-phpunit: Catch type mismatches in test assertions.vendor/bin/phpstan analyse --generate-baseline
Commit the baseline and re-run with --baseline to only show new issues.
```markdown
### Laravel-Specific Quirks
1. **Facade Calls**:
Disable `dynamicCallOnStaticMethod` for Laravel facades (e.g., `Auth::user()`):
```neon
ignoreErrors:
- '#DynamicCallOnStaticMethod# in app/Http/Controllers/AuthController.php'
Artisan Commands:
Rules like disallowedBacktick may conflict with Artisan::call(). Exclude:
ignoreErrors:
- '#DisallowedBacktick# in app/Console/Commands/DeployCommand.php'
Service Container:
Use strictFunctionCalls to enforce $strict=true in array_key_exists() calls within service bindings:
$this->app->bind('key', function () {
return array_key_exists('key', $array, true); // Enforced by strictRules
});
Migration Files:
Disable disallowedLooseComparison for legacy migrations:
ignoreErrors:
- '#DisallowedLooseComparison# in database/migrations/2020_*.php'
Event Listeners:
Apply closureUsesThis to ensure closures in listeners reference $this directly:
Event::listen('event', function () {
$this->someMethod(); // Direct $this usage (enforced)
});
How can I help you explore Laravel packages today?