kcs/phpstan-strict-rules
Fork of thecodingmachine/phpstan-strict-rules to support PHPStan v2. Adds stricter best-practice rules beyond core PHPStan, especially around exception handling (avoid throwing base Exception, empty catches, proper rethrowing).
Install the package via Composer:
composer require --dev kcs/phpstan-strict-rules
Enable via phpstan/extension-installer (recommended):
composer require --dev phpstan/extension-installer
This auto-configures the package in your phpstan.neon.
First use case: Run PHPStan on a Laravel controller to catch superglobal usage or exception anti-patterns:
vendor/bin/phpstan analyse app/Http/Controllers
vendor/kcs/phpstan-strict-rules/phpstan-strict-rules.neon for rule details.Route:: superglobals).Exception or empty catch blocks.
try {
$user = User::findOrFail($id);
} catch (Exception $e) {} // Empty catch → violation
try {
$user = User::findOrFail($id);
} catch (ModelNotFoundException $e) {
throw new ValidationException("User not found.", 0, $e);
}
$_GET usage in Laravel.
$id = $_GET['id']; // Forbidden
Request object.
use Illuminate\Http\Request;
$id = request()->input('id'); // Allowed
index.php usage is tolerated (e.g., PSR-7 initialization).default case.
switch ($status) {
case 'active': return true;
case 'inactive': return false;
// No default → violation
}
default with exception.
switch ($status) {
case 'active': return true;
case 'inactive': return false;
default: throw new InvalidArgumentException("Unknown status: $status");
}
# .github/workflows/phpstan.yml
jobs:
phpstan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: composer install
- run: vendor/bin/phpstan analyse --level=max
--error-format=github for PR feedback.phpstan.neon:
paths:
exclude:
- app/OldCode/**
rules:
TheCodingMachine\StrictRules\Rules\NoPublicPropertiesRule:
excludeClasses:
- App\Models\User
phpstan/laravel:
composer require --dev phpstan/laravel
Route::, Cache::, etc. Exclude them:
rules:
TheCodingMachine\StrictRules\Rules\ForbiddenSuperglobalsRule:
allowedFunctions:
- Route::*
- Cache::*
False Positives in Laravel:
public properties (e.g., $fillable) may violate NoPublicPropertiesRule. Exclude them:
rules:
TheCodingMachine\StrictRules\Rules\NoPublicPropertiesRule:
excludeClasses:
- App\Models\*
Route::, Auth::, etc., are technically superglobals. Whitelist them (see above).Performance Overhead:
--level=max with all rules may slow down CI. Start with:
vendor/bin/phpstan analyse --level=5
Root-Level Superglobals:
$_GET/$_POST in index.php (e.g., for PSR-7 initialization). Avoid using them elsewhere.Exception Chaining:
previous exceptions in catch blocks may break legacy code:
catch (Exception $e) {
throw new RuntimeException("Failed.", 0, $e); // Required
}
vendor/bin/phpstan analyse app/Http/Controllers/UserController.php
--error-format=json to identify rule names:
vendor/bin/phpstan analyse --error-format=json | jq '.files[].messages[] | {rule,message}'
Example output:
{
"rule": "TheCodingMachine\\StrictRules\\Rules\\ForbiddenSuperglobalsRule",
"message": "Superglobal $_GET is forbidden."
}
Customize Rules:
phpstan-strict-rules.neon in your project:
includes:
- vendor/kcs/phpstan-strict-rules/phpstan-strict-rules.neon
- phpstan-custom-rules.neon
NoPublicPropertiesRule for specific classes.Add New Rules:
Rule class and include in your config:
services:
- TheCodingMachine\StrictRules\Rules\CustomRule
Gradual Adoption:
level to enable rules incrementally:
level: 3 # Start with basic rules
max or enable specific rules:
rules:
TheCodingMachine\StrictRules\Rules\ExceptionSubtypingRule: true
global variables (e.g., $app['config']). Use dependency injection:
// Before (violation)
$config = $app['config'];
// After
public function __construct(protected Config $config) {}
$_ENV may be flagged. Use Laravel’s env() helper instead.Pair with pint:
composer require --dev laravel/pint
vendor/bin/pint --test
Enforce consistent code style alongside static analysis.
CI Feedback:
phpstan/phpstan-github-action for GitHub PR annotations:
- uses: phpstan/phpstan-github-action@v1
with:
level: max
Document Exceptions:
// phpcs:ignore TheCodingMachine.StrictRules.NoPublicPropertiesRule
public $fillable = ['name', 'email'];
How can I help you explore Laravel packages today?