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
unset
over 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
UselessPrivatePropertyDefaultValueRule
andUselessPrivatePropertyNullabilityRule
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
UnusedExceptionVisitor
to 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
throw
node 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
TopLevelConstructorPropertyFetchMarkingVisitor
to work - Should be used together with
ForbidReturnInConstructorRule
to avoid false positives when return statement is used in constructor - Recommended to be used with
UselessPrivatePropertyNullabilityRule
andForbidUselessNullableReturnRule
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
ClassPropertyAssignmentVisitor
to work - Recommended to be used with
UselessPrivatePropertyNullabilityRule
andForbidUselessNullableReturnRule
as 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;
}
}