symplify / phpstan-rules
Set of Symplify rules for PHPStan
Fund package maintenance!
tomasvotruba
www.paypal.me/rectorphp
Installs: 4 671 491
Dependents: 161
Suggesters: 2
Security: 0
Stars: 154
Watchers: 5
Forks: 23
Open Issues: 0
Type:phpstan-extension
Requires
- php: ^7.2|^8.0
- nette/utils: ^3.2.9 || ^4.0
- phpstan/phpstan: ^1.10.30
- webmozart/assert: ^1.11
- dev-main
- 13.0.1
- 13.0.0
- 12.7.0
- 12.6.4
- 12.6.3
- 12.6.2
- 12.6.1
- 12.6.0
- 12.5.2
- 12.5.1
- 12.5.0
- 12.4.9
- 12.4.8
- 12.4.7
- 12.4.6
- 12.4.5
- 12.4.4
- 12.4.3
- 12.4.2
- 12.4.1
- 12.4.0
- 12.3.1
- 12.3.0.72
- 12.3.0
- 12.2.7.72
- 12.2.7
- 12.2.6.72
- 12.2.6
- 12.2.5.72
- 12.2.5
- 12.2.4.72
- 12.2.4
- 12.2.3.72
- 12.2.3
- 12.2.2.72
- 12.2.2
- 12.2.1.72
- 12.2.1
- 12.2.0.72
- 12.2.0
- 12.1.4.72
- 12.1.4
- 12.1.3.72
- 12.1.3
- 12.1.2.72
- 12.1.2
- 12.1.1.72
- 12.1.1
- 12.1.0.72
- 12.1.0
- 12.0.4.72
- 12.0.4
- 12.0.3.72
- 12.0.3
- 12.0.2.72
- 12.0.2
- 12.0.1.72
- 12.0.1
- 12.0.0.72
- 12.0.0
- 11.4.1.72
- 11.4.1
- 11.4.0.72
- 11.4.0
- 11.3.5.72
- 11.3.5
- 11.3.4.72
- 11.3.4
- 11.3.3.72
- 11.3.3
- 11.3.2.72
- 11.3.2
- 11.3.1.72
- 11.3.1
- 11.3.0.72
- 11.3.0
- 11.2.5.72
- 11.2.5
- 11.2.4.72
- 11.2.4
- 11.2.3.72
- 11.2.3
- 11.2.2.72
- 11.2.2
- 11.2.1
- 11.2.0.72
- 11.2.0
- 11.1.28.72
- 11.1.28
- 11.1.27.72
- 11.1.27
- 11.1.26.72
- 11.1.26
- 11.1.25.72
- 11.1.25
- 11.1.24
- 11.1.23
- 11.1.22
- 11.1.21
- 11.1.20
- 11.1.19
- 11.1.18
- 11.1.17
- 11.1.16
- 11.1.15
- 11.1.14
- 11.1.13
- 11.1.12
- 11.1.11
- 11.1.10
- 11.1.9
- 11.1.8
- 11.1.7
- 11.1.6
- 11.1.5
- 11.1.4
- 11.1.1
- 11.1.0
- 11.0.9
- 11.0.8
- 11.0.7
- 11.0.6
- 11.0.5
- 11.0.4
- 11.0.3
- 11.0.2
- 11.0.1
- 11.0.0
- 10.3.3
- 10.3.2
- 10.3.1
- 10.3.0
- 10.2.11
- 10.2.10
- 10.2.9
- 10.2.8
- 10.2.7
- 10.2.6
- 10.2.5
- 10.2.4
- 10.2.3
- 10.2.2
- 10.2.1
- 10.2.0
- 10.1.4
- 10.1.3
- 10.1.2
- 10.1.1
- 10.1.0
- 10.0.25
- 10.0.24
- 10.0.23
- 10.0.22
- 10.0.21
- 10.0.20
- 10.0.19
- 10.0.18
- 10.0.17
- 10.0.16
- 10.0.15
- 10.0.14
- 10.0.13
- 10.0.12
- 10.0.11
- 10.0.10
- 10.0.9
- 10.0.8
- 10.0.7
- 10.0.6
- 10.0.5
- 10.0.4
- 10.0.3
- 10.0.2
- 10.0.1
- 10.0.0
- 10.0.0-beta17
- 10.0.0-beta16
- 10.0.0-beta15
- 10.0.0-beta14
- 10.0.0-beta13
- 10.0.0-beta12
- 10.0.0-beta11
- 10.0.0-beta10
- 10.0.0-beta9
- 10.0.0-beta8
- 10.0.0-beta7
- 10.0.0-beta6
- 10.0.0-beta5
- 10.0.0-beta4
- 10.0.0-beta3
- 10.0.0-beta2
- 10.0.0-beta1
- 9.4.70
- 9.4.69
- 9.4.68
- 9.4.67
- 9.4.66
- 9.4.65
- 9.4.64
- 9.4.63
- 9.4.62
- 9.4.61
- 9.4.60
- 9.4.59
- 9.4.58
- 9.4.57
- 9.4.56
- 9.4.55
- 9.4.54
- 9.4.53
- 9.4.52
- 9.4.51
- 9.4.50
- 9.4.49
- 9.4.48
- 9.4.47
- 9.4.46
- 9.4.45
- 9.4.44
- 9.4.43
- 9.4.42
- 9.4.41
- 9.4.40
- 9.4.39
- 9.4.38
- 9.4.37
- 9.4.36
- 9.4.35
- 9.4.34
- 9.4.33
- 9.4.32
- 9.4.31
- 9.4.30
- 9.4.29
- 9.4.28
- 9.4.27
- 9.4.26
- 9.4.25
- 9.4.24
- 9.4.23
- 9.4.22
- 9.4.21
- 9.4.20
- 9.4.19
- 9.4.18
- 9.4.17
- 9.4.16
- 9.4.15
- 9.4.14
- 9.4.13
- 9.4.12
- 9.4.11
- 9.4.10
- 9.4.9
- 9.4.8
- 9.4.7
- 9.4.6
- 9.4.5
- 9.4.4
- 9.4.3
- 9.4.2
- v9.4.1
- v9.4.0
- v9.3.27
- v9.3.26
- v9.3.25
- v9.3.24
- v9.3.23
- v9.3.22
- v9.3.21
- v9.3.20
- v9.3.19
- v9.3.18
- v9.3.17
- v9.3.16
- v9.3.15
- v9.3.14
- v9.3.13
- v9.3.12
- v9.3.11
- v9.3.10
- v9.3.8
- v9.3.6
- v9.3.5
- v9.3.4
- v9.3.3
- v9.3.1
- v9.3.0
- v9.2.24
- v9.2.23
- v9.2.22
- v9.2.21
- v9.2.20
- v9.2.19
- v9.2.18
- v9.2.17
- v9.2.16
- v9.2.15
- v9.2.14
- v9.2.13
- v9.2.12
- v9.2.11
- v9.2.10
- v9.2.9
- v9.2.8
- v9.2.7
- v9.2.6
- v9.2.5
- v9.2.4
- v9.2.3
- v9.2.2
- 9.2.1
- 9.2.0
- 9.1.9
- 9.1.8
- 9.1.7
- 9.1.6
- 9.1.5
- 9.1.4
- 9.1.3
- 9.1.1
- 9.1.0
- 9.0.50
- 9.0.49
- 9.0.48
- 9.0.47
- 9.0.46
- 9.0.45
- 9.0.44
- 9.0.43
- 9.0.42
- 9.0.41
- 9.0.40
- 9.0.39
- 9.0.38
- 9.0.37
- 9.0.36
- 9.0.35
- 9.0.34
- 9.0.33
- 9.0.32
- 9.0.31
- 9.0.30
- 9.0.29
- 9.0.28
- 9.0.27
- 9.0.26
- 9.0.25
- 9.0.24
- 9.0.23
- 9.0.22
- 9.0.21
- 9.0.20
- 9.0.19
- 9.0.18
- 9.0.17
- 9.0.16
- 9.0.15
- 9.0.14
- 9.0.13
- 9.0.12
- 9.0.11
- 9.0.10
- 9.0.9
- 9.0.8
- 9.0.7
- 9.0.6
- 9.0.5
- 9.0.4
- 9.0.3
- 9.0.2
- 9.0.1
- 9.0.0
- 9.0.0-rc1
- 9.0.0-BETA9
- 9.0.0-BETA8
- 9.0.0-BETA7
- 9.0.0-BETA6
- 9.0.0-BETA5
- 9.0.0-BETA4
- 9.0.0-BETA3
- 9.0.0-BETA2
- 9.0.0-BETA1
- 8.3.48
This package is auto-updated.
Last update: 2024-11-03 08:26:03 UTC
README
Set of rules for PHPStan used by Symplify projects
- See Rules Overview
Install
composer require symplify/phpstan-rules --dev
Note: Make sure you use phpstan/extension-installer
to load necessary service configs.
1. Add Prepared Sets
Sets are bunch of rules grouped by a common area, e.g. improve naming. You can pick from 5 sets:
includes: - vendor/symplify/phpstan-rules/config/code-complexity-rules.neon - vendor/symplify/phpstan-rules/config/naming-rules.neon - vendor/symplify/phpstan-rules/config/regex-rules.neon - vendor/symplify/phpstan-rules/config/static-rules.neon
Add sets one by one, fix what you find useful and ignore the rest.
Do you write custom Rector rules? Add rules for them too:
includes: - vendor/symplify/phpstan-rules/config/rector-rules.neon
2. Cherry-pick Configurable Rules
There is one set with pre-configured configurable rules. Include it and see what is errors are found:
# phpstan.neon includes: - vendor/symplify/phpstan-rules/config/configurable-rules.neon
Would you like to tailor it to fit your taste? Pick one PHPStan rule and configure it manually ↓
services: - class: Symplify\PHPStanRules\Rules\ForbiddenNodeRule tags: [phpstan.rules.rule] arguments: forbiddenNodes: - PhpParser\Node\Expr\Empty_ - PhpParser\Node\Stmt\Switch_
3. Register Particular Rules
30 Rules Overview
AnnotateRegexClassConstWithRegexLinkRule
Add regex101.com link to that shows the regex in practise, so it will be easier to maintain in case of bug/extension in the future
class SomeClass { private const COMPLICATED_REGEX = '#some_complicated_stu|ff#'; }
❌
class SomeClass { /** * @see https://regex101.com/r/SZr0X5/12 */ private const COMPLICATED_REGEX = '#some_complicated_stu|ff#'; }
👍
CheckClassNamespaceFollowPsr4Rule
Class like namespace "%s" does not follow PSR-4 configuration in composer.json
// defined "Foo\Bar" namespace in composer.json > autoload > psr-4 namespace Foo; class Baz { }
❌
// defined "Foo\Bar" namespace in composer.json > autoload > psr-4 namespace Foo\Bar; class Baz { }
👍
CheckRequiredInterfaceInContractNamespaceRule
Interface must be located in "Contract" or "Contracts" namespace
namespace App\Repository; interface ProductRepositoryInterface { }
❌
namespace App\Contract\Repository; interface ProductRepositoryInterface { }
👍
ClassNameRespectsParentSuffixRule
Class should have suffix "%s" to respect parent type
🔧 configure it!
services: - class: Symplify\PHPStanRules\Rules\ClassNameRespectsParentSuffixRule tags: [phpstan.rules.rule] arguments: parentClasses: - Symfony\Component\Console\Command\Command
↓
class Some extends Command { }
❌
class SomeCommand extends Command { }
👍
ExplicitClassPrefixSuffixRule
Interface have suffix of "Interface", trait have "Trait" suffix exclusively
<?php interface NotSuffixed { } trait NotSuffixed { } abstract class NotPrefixedClass { }
❌
<?php interface SuffixedInterface { } trait SuffixedTrait { } abstract class AbstractClass { }
👍
ForbiddenArrayMethodCallRule
Array method calls [$this, "method"] are not allowed. Use explicit method instead to help PhpStorm, PHPStan and Rector understand your code
usort($items, [$this, "method"]);
❌
usort($items, function (array $apples) { return $this->method($apples); };
👍
ForbiddenExtendOfNonAbstractClassRule
Only abstract classes can be extended
final class SomeClass extends ParentClass { } class ParentClass { }
❌
final class SomeClass extends ParentClass { } abstract class ParentClass { }
👍
ForbiddenFuncCallRule
Function "%s()"
cannot be used/left in the code
🔧 configure it!
services: - class: Symplify\PHPStanRules\Rules\ForbiddenFuncCallRule tags: [phpstan.rules.rule] arguments: forbiddenFunctions: - eval
↓
echo eval('...');
❌
echo '...';
👍
services: - class: Symplify\PHPStanRules\Rules\ForbiddenFuncCallRule tags: [phpstan.rules.rule] arguments: forbiddenFunctions: dump: 'seems you missed some debugging function'
↓
dump($value); echo $value;
❌
echo $value;
👍
ForbiddenMultipleClassLikeInOneFileRule
Multiple class/interface/trait is not allowed in single file
// src/SomeClass.php class SomeClass { } interface SomeInterface { }
❌
// src/SomeClass.php class SomeClass { } // src/SomeInterface.php interface SomeInterface { }
👍
ForbiddenNodeRule
"%s" is forbidden to use
🔧 configure it!
services: - class: Symplify\PHPStanRules\Rules\ForbiddenNodeRule tags: [phpstan.rules.rule] arguments: forbiddenNodes: - PhpParser\Node\Expr\ErrorSuppress
↓
return @strlen('...');
❌
return strlen('...');
👍
ForbiddenStaticClassConstFetchRule
Avoid static access of constants, as they can change value. Use interface and contract method instead
class SomeClass { public function run() { return static::SOME_CONST; } }
❌
class SomeClass { public function run() { return self::SOME_CONST; } }
👍
NoDynamicNameRule
Use explicit names over dynamic ones
class SomeClass { public function old(): bool { return $this->${variable}; } }
❌
class SomeClass { public function old(): bool { return $this->specificMethodName(); } }
👍
NoEntityOutsideEntityNamespaceRule
Class with #[Entity] attribute must be located in "Entity" namespace to be loaded by Doctrine
namespace App\ValueObject; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity] class Product { }
❌
namespace App\Entity; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity] class Product { }
👍
NoGlobalConstRule
Global constants are forbidden. Use enum-like class list instead
const SOME_GLOBAL_CONST = 'value';
❌
class SomeClass { public function run() { return self::SOME_CONST; } }
👍
NoInlineStringRegexRule
Use local named constant instead of inline string for regex to explain meaning by constant name
class SomeClass { public function run($value) { return preg_match('#some_stu|ff#', $value); } }
❌
class SomeClass { /** * @var string */ public const SOME_STUFF_REGEX = '#some_stu|ff#'; public function run($value) { return preg_match(self::SOME_STUFF_REGEX, $value); } }
👍
NoReferenceRule
Use explicit return value over magic &reference
class SomeClass { public function run(&$value) { } }
❌
class SomeClass { public function run($value) { return $value; } }
👍
NoReturnArrayVariableListRule
Use value object over return of values
class ReturnVariables { public function run($value, $value2): array { return [$value, $value2]; } }
❌
final class ReturnVariables { public function run($value, $value2): ValueObject { return new ValueObject($value, $value2); } }
👍
NoReturnSetterMethodRule
Setter method cannot return anything, only set value
final class SomeClass { private $name; public function setName(string $name): int { return 1000; } }
❌
final class SomeClass { private $name; public function setName(string $name): void { $this->name = $name; } }
👍
NoSingleInterfaceImplementerRule
Interface "%s" has only single implementer. Consider using the class directly as there is no point in using the interface.
class SomeClass implements SomeInterface { } interface SomeInterface { }
❌
class SomeClass implements SomeInterface { } class AnotherClass implements SomeInterface { } interface SomeInterface { }
👍
NoTestMocksRule
Mocking "%s" class is forbidden. Use direct/anonymous class instead for better static analysis
use PHPUnit\Framework\TestCase; final class SkipApiMock extends TestCase { public function test() { $someTypeMock = $this->createMock(SomeType::class); } }
❌
use PHPUnit\Framework\TestCase; final class SkipApiMock extends TestCase { public function test() { $someTypeMock = new class() implements SomeType {}; } }
👍
PreferredClassRule
Instead of "%s" class/interface use "%s"
🔧 configure it!
services: - class: Symplify\PHPStanRules\Rules\PreferredClassRule tags: [phpstan.rules.rule] arguments: oldToPreferredClasses: SplFileInfo: CustomFileInfo
↓
class SomeClass { public function run() { return new SplFileInfo('...'); } }
❌
class SomeClass { public function run() { return new CustomFileInfo('...'); } }
👍
PreventParentMethodVisibilityOverrideRule
Change "%s()"
method visibility to "%s" to respect parent method visibility.
class SomeParentClass { public function run() { } } class SomeClass extends SomeParentClass { protected function run() { } }
❌
class SomeParentClass { public function run() { } } class SomeClass extends SomeParentClass { public function run() { } }
👍
RegexSuffixInRegexConstantRule
Name your constant with "_REGEX" suffix, instead of "%s"
class SomeClass { public const SOME_NAME = '#some\s+name#'; public function run($value) { $somePath = preg_match(self::SOME_NAME, $value); } }
❌
class SomeClass { public const SOME_NAME_REGEX = '#some\s+name#'; public function run($value) { $somePath = preg_match(self::SOME_NAME_REGEX, $value); } }
👍
RequireAttributeNameRule
Attribute must have all names explicitly defined
use Symfony\Component\Routing\Annotation\Route; class SomeController { #[Route("/path")] public function someAction() { } }
❌
use Symfony\Component\Routing\Annotation\Route; class SomeController { #[Route(path: "/path")] public function someAction() { } }
👍
RequireAttributeNamespaceRule
Attribute must be located in "Attribute" namespace
// app/Entity/SomeAttribute.php namespace App\Controller; #[\Attribute] final class SomeAttribute { }
❌
// app/Attribute/SomeAttribute.php namespace App\Attribute; #[\Attribute] final class SomeAttribute { }
👍
RequireExceptionNamespaceRule
Exception
must be located in "Exception" namespace
// app/Controller/SomeException.php namespace App\Controller; final class SomeException extends Exception { }
❌
// app/Exception/SomeException.php namespace App\Exception; final class SomeException extends Exception { }
👍
RequireInvokableControllerRule
Use invokable controller with __invoke()
method instead of named action method
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Routing\Annotation\Route; final class SomeController extends AbstractController { #[Route()] public function someMethod() { } }
❌
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Routing\Annotation\Route; final class SomeController extends AbstractController { #[Route()] public function __invoke() { } }
👍
RequireUniqueEnumConstantRule
Enum constants "%s" are duplicated. Make them unique instead
use MyCLabs\Enum\Enum; class SomeClass extends Enum { private const YES = 'yes'; private const NO = 'yes'; }
❌
use MyCLabs\Enum\Enum; class SomeClass extends Enum { private const YES = 'yes'; private const NO = 'no'; }
👍
SeeAnnotationToTestRule
Class "%s" is missing @see
annotation with test case class reference
🔧 configure it!
services: - class: Symplify\PHPStanRules\Rules\SeeAnnotationToTestRule tags: [phpstan.rules.rule] arguments: requiredSeeTypes: - Rule
↓
class SomeClass extends Rule { }
❌
/** * @see SomeClassTest */ class SomeClass extends Rule { }
👍
UppercaseConstantRule
Constant "%s" must be uppercase
final class SomeClass { public const some = 'value'; }
❌
final class SomeClass { public const SOME = 'value'; }
👍
Happy coding!