ShipMonk PHPStan rules
Various rules we found useful in ShipMonk. You may found some of them opinionated, so we recommend picking only those fitting your needs. If you would like to use all of them anyway, use:
includes:
- vendor/shipmonk/phpstan-rules/rules.neon
Installation:
composer require shipmonk/phpstan-rules
Rules:
All you need to enable most of the rules is to register them as documented in phpstan/phpstan. Some of them need some specific rich parser node visitor to be registered as well. Rarely, some rules are reliable only when some other rule is enabled.
AllowNamedArgumentOnlyInAttributesRule
- Allows usage of named arguments only in native attributes
- Before native attributes, we used DisallowNamedArguments. But we used Doctrine annotations, which almost "require" named arguments when converted to native attributes.
- Requires NamedArgumentSourceVisitor to work
rules:
- ShipMonk\PHPStan\Rule\AllowNamedArgumentOnlyInAttributesRule
services:
-
class: ShipMonk\PHPStan\Visitor\NamedArgumentSourceVisitor
tags:
- phpstan.parser.richParserNodeVisitor
class User {
#[Column(type: Types::STRING, nullable: false)] // allowed
private string $email;
public function __construct(string $email) {
$this->setEmail(email: $email); // forbidden
}
}
ForbidFetchOnMixedRule
- Denies property fetch on unknown type.
- Any property fetch assumes the caller is an object with such property and therefore, the typehint/phpdoc should be fixed.
- Similar to
ForbidMethodCallOnMixedRule
rules:
- ShipMonk\PHPStan\Rule\ForbidFetchOnMixedRule
function example($unknown) {
$unknown->property; // cannot fetch property on mixed
}
ForbidMatchDefaultArmForEnumsRule
- Denies using default arm in
match()construct when native enum is passed as subject - This rules makes sense only as a complement of native phpstan rule that guards that all enum cases are handled in match arms
- As a result, you are forced to add new arm when new enum case is added
rules:
- ShipMonk\PHPStan\Rule\ForbidFetchOnMixedRule
match ($enum) {
MyEnum::Case: 1;
default: 2; // default arm forbidden
}
ForbidMethodCallOnMixedRule
- Denies calling methods on unknown type.
- Any method call assumes the caller is an object with such method and therefore, the typehint/phpdoc should be fixed.
- Similar to
ForbidFetchOnMixedRule
rules:
- ShipMonk\PHPStan\Rule\ForbidMethodCallOnMixedRule
function example($unknown) {
$unknown->call(); // cannot call method on mixed
}
ForbidUnsetClassFieldRule
- Denies calling
unsetover class field as it causes un-initialization, see https://3v4l.org/V8uuP - Null assignment should be used instead
rules:
- ShipMonk\PHPStan\Rule\ForbidUnsetClassFieldRule
function example(MyClass $class) {
unset($class->field); // denied
}
ForbidUselessNullableReturnRule
- Denies marking method return type as nullable when null is never returned
- Recommended to be used together with
UselessPrivatePropertyDefaultValueRuleandUselessPrivatePropertyNullabilityRule
rules:
- ShipMonk\PHPStan\Rule\ForbidUselessNullableReturnRule
class Example {
public function example(int $foo): ?int { // null never returned
if ($foo < 0) {
return 0;
}
return $foo;
}
}
ForbidUnusedExceptionRule
- Reports forgotten exception throw (created or returned from function, but not used in any way)
- Requires
UnusedExceptionVisitorto work
rules:
- ShipMonk\PHPStan\Rule\ForbidUnusedExceptionRule
services:
-
class: ShipMonk\PHPStan\Visitor\UnusedExceptionVisitor
tags:
- phpstan.parser.richParserNodeVisitor
function validate(): void {
new Exception(); // forgotten throw
}
RequirePreviousExceptionPassRule
- Detects forgotten exception pass-as-previous when re-throwing
- Checks if caught exception can be passed as argument to the call (including constructor call) in
thrownode inside the catch block - You may encounter false-positives in some edge-cases, where you do not want to pass exception as previous, feel free to ignore those
rules:
- ShipMonk\PHPStan\Rule\RequirePreviousExceptionPassRule
try {
// some code
} catch (RuntimeException $e) {
throw new LogicException('Cannot happen'); // $e not passed as previous
}
UselessPrivatePropertyDefaultValueRule:
- Detects useless default value of a private property that is always initialized in constructor.
- Cannot handle conditions or private method calls within constructor.
- Requires
TopLevelConstructorPropertyFetchMarkingVisitorto work - Should be used together with
ForbidReturnInConstructorRuleto avoid false positives when return statement is used in constructor - Recommended to be used with
UselessPrivatePropertyNullabilityRuleandForbidUselessNullableReturnRule
rules:
- ShipMonk\PHPStan\Rule\UselessPrivatePropertyDefaultValueRule
- ShipMonk\PHPStan\Rule\ForbidReturnInConstructorRule
services:
-
class: ShipMonk\PHPStan\Visitor\TopLevelConstructorPropertyFetchMarkingVisitor
tags:
- phpstan.parser.richParserNodeVisitor
class Example
{
private ?int $field = null; // useless default value
public function __construct()
{
$this->field = 1;
}
}
UselessPrivatePropertyNullabilityRule:
- Detects useless nullability of a private property by checking type of all assignments.
- Requires
ClassPropertyAssignmentVisitorto work - Recommended to be used with
UselessPrivatePropertyNullabilityRuleandForbidUselessNullableReturnRuleas removing useless default value may cause useless nullability to be detected
rules:
- ShipMonk\PHPStan\Rule\UselessPrivatePropertyNullabilityRule
services:
-
class: ShipMonk\PHPStan\Visitor\ClassPropertyAssignmentVisitor
tags:
- phpstan.parser.richParserNodeVisitor
class Example
{
private ?int $field; // useless nullability
public function __construct()
{
$this->field = 1;
}
public function setField(int $value)
{
$this->field = $value;
}
}