phpstan/phpstan-strict-rules is a PHPStan extension that provides extra strict and opinionated rules for PHP static analysis. While PHPStan focuses on finding bugs, this package enforces strictly and strongly typed code with no loose casting, targeting developers who want additional safety through defensive programming.
The package is installed via Composer (phpstan/phpstan-strict-rules) and integrates with PHPStan through the rules.neon configuration file.
This repository supports PHP 7.4+ (unlike phpstan-src which is PHP 8.1+ with PHAR downgrade). The composer.json sets "php": "^7.4 || ^8.0" and the platform is pinned to PHP 7.4.6 for dependency resolution.
Do not use PHP 8.0+ syntax (named arguments, match expressions, union type hints in signatures, etc.) in the source code. Property declarations with types (e.g., private Type $prop;) are allowed as they are PHP 7.4 syntax.
├── src/Rules/ # Rule implementations (PSR-4: PHPStan\)
│ ├── BooleansInConditions/ # Rules requiring booleans in conditions
│ ├── Cast/ # Useless cast detection
│ ├── Classes/ # Parent constructor call requirements
│ ├── DisallowedConstructs/ # Bans on empty(), loose comparison, backtick, etc.
│ ├── ForLoop/ # Variable overwrite detection in for loops
│ ├── ForeachLoop/ # Variable overwrite detection in foreach loops
│ ├── Functions/ # Strict array_filter, closure $this usage
│ ├── Methods/ # Illegal constructor calls, method name casing
│ ├── Operators/ # Numeric operand enforcement in arithmetic
│ ├── StrictCalls/ # Strict function calls, static method enforcement
│ ├── SwitchConditions/ # Type matching in switch/case
│ └── VariableVariables/ # Disallow variable variables
├── tests/Rules/ # Tests mirroring src/ structure
│ └── */data/ # PHP test fixture files
├── tests/Levels/ # Integration tests for PHPStan levels
├── rules.neon # Main extension config (parameters, services, conditional tags)
├── phpstan.neon # Self-analysis config (used for analysing this repo)
├── phpunit.xml # PHPUnit configuration
├── Makefile # Build commands
└── composer.json # Package definition
Each rule implements PHPStan\Rules\Rule<TNodeType>:
getNodeType(): string— Returns the PHP-Parser AST node class this rule inspects.processNode(Node $node, Scope $scope): array— Analyses the node and returns an array of errors (empty array = no errors).
Errors are built with RuleErrorBuilder::message('...')->identifier('error.id')->build().
Helper classes (BooleanRuleHelper, OperatorRuleHelper) contain shared validation logic and are injected via constructor.
Rules are registered as services in rules.neon and toggled via conditionalTags tied to strictRules.* parameters.
rules.neon serves two purposes:
- Sets stricter PHPStan defaults (lines 1-16): Parameters like
checkDynamicProperties,reportMaybesInMethodSignatures,checkFunctionNameCase, etc. These are always active when the extension is installed. - Registers toggleable custom rules (lines 17+): Each rule can be individually enabled/disabled through
strictRules.*parameters usingconditionalTags.
The allRules parameter (default: true) controls all rules at once. Individual parameters override it.
All commands are in the Makefile:
make check # Run all checks (lint + cs + tests + phpstan)
make lint # PHP syntax check via parallel-lint
make cs # Coding standards check (requires build-cs, see cs-install)
make cs-install # Clone and install phpstan/build-cs
make cs-fix # Auto-fix coding standard violations
make tests # Run PHPUnit tests
make phpstan # Run PHPStan self-analysis at level 8To set up the project:
composer installFor coding standards (separate tooling):
make cs-install
make csTests use PHPUnit 9.6 and PHPStan's RuleTestCase base class.
- Each rule has a corresponding
*Test.phpintests/Rules/mirroring thesrc/Rules/directory structure. - Test data files (PHP fixtures) live in
tests/Rules/*/data/. - Integration-level tests are in
tests/Levels/.
/**
* @extends RuleTestCase<YourRule>
*/
class YourRuleTest extends RuleTestCase
{
protected function getRule(): Rule
{
return new YourRule(/* dependencies */);
}
public function testRule(): void
{
$this->analyse([__DIR__ . '/data/fixture.php'], [
[
'Expected error message text.',
42, // line number
],
]);
}
}For rules with dependencies, use self::getContainer()->getByType(ClassName::class) to get services from PHPStan's DI container.
Some tests conditionally check based on PHP_VERSION_ID for version-specific behavior.
make tests # Run all tests
php vendor/bin/phpunit # Run all tests directly
php vendor/bin/phpunit tests/Rules/Cast/ # Run tests in a directory
php vendor/bin/phpunit --filter UselessCastRuleTest # Run a specific testGitHub Actions (.github/workflows/build.yml) runs on PRs and pushes to 2.0.x:
- Lint: PHP syntax check on PHP 7.4–8.4
- Coding Standard: phpcs via phpstan/build-cs (2.x branch)
- Tests: PHPUnit on PHP 7.4–8.4, both lowest and highest dependencies
- Static Analysis: PHPStan at level 8 on PHP 7.4–8.4, both lowest and highest dependencies
The default branch is 2.0.x.
- Uses phpstan/build-cs (2.x branch) with phpcs for coding standards.
- Tabs for indentation in PHP, XML, and Neon files (see
.editorconfig). - Spaces for YAML files.
declare(strict_types = 1)at the top of every PHP file.- PSR-4 autoloading:
PHPStan\namespace maps tosrc/. - Import functions explicitly (
use function sprintf;), not via\sprintf().