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#';
}
π
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
{
}
π
Interface must be located in "Contract" or "Contracts" namespace
namespace App\Repository;
interface ProductRepositoryInterface
{
}
β
namespace App\Contract\Repository;
interface ProductRepositoryInterface
{
}
namespace App\Contracts\Repository;
interface ProductRepositoryInterface
{
}
π
Parameter %d should use "%s" type as the only type passed to this method
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
class SomeClass
{
public function run(MethodCall $node)
{
$this->isCheck($node);
}
private function isCheck(Node $node)
{
}
}
β
use PhpParser\Node\Expr\MethodCall;
class SomeClass
{
public function run(MethodCall $node)
{
$this->isCheck($node);
}
private function isCheck(MethodCall $node)
{
}
}
π
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
{
}
π
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
{
}
π
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);
};
π
Only abstract classes can be extended
final class SomeClass extends ParentClass
{
}
class ParentClass
{
}
β
final class SomeClass extends ParentClass
{
}
abstract class ParentClass
{
}
π
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;
π
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
{
}
π
"%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('...');
π
Removing parent param type is forbidden
interface RectorInterface
{
public function refactor(Node $node);
}
final class SomeRector implements RectorInterface
{
public function refactor($node)
{
}
}
β
interface RectorInterface
{
public function refactor(Node $node);
}
final class SomeRector implements RectorInterface
{
public function refactor(Node $node)
{
}
}
π
New objects with "%s" name are overridden. This can lead to unwanted bugs, please pick a different name to avoid it.
$product = new Product();
$product = new Product();
$this->productRepository->save($product);
β
$firstProduct = new Product();
$secondProduct = new Product();
$this->productRepository->save($firstProduct);
π
Parameters should use "%s" types as the only types passed to this method
use PhpParser\Node\Expr\MethodCall;
final class SomeClass
{
public function run(SomeService $someService, MethodCall $methodCall)
{
$someService->isCheck($node);
}
}
final class SomeService
{
public function isCheck($methodCall)
{
}
}
β
use PhpParser\Node\Expr\MethodCall;
final class SomeClass
{
public function run(SomeService $someService, MethodCall $methodCall)
{
$someService->isCheck($node);
}
}
final class SomeService
{
public function isCheck(MethodCall $methodCall)
{
}
}
π
Use explicit methods over array access on object
class SomeClass
{
public function run(MagicArrayObject $magicArrayObject)
{
return $magicArrayObject['more_magic'];
}
}
β
class SomeClass
{
public function run(MagicArrayObject $magicArrayObject)
{
return $magicArrayObject->getExplicitValue();
}
}
π
Class with base "%s" name is already used in "%s". Use unique name to make classes easy to recognize
π§ configure it!
services:
-
class: Symplify\PHPStanRules\Rules\NoDuplicatedShortClassNameRule
tags: [phpstan.rules.rule]
arguments:
toleratedNestingLevel: 1
β
namespace App;
class SomeClass
{
}
namespace App\Nested;
class SomeClass
{
}
β
namespace App;
class SomeClass
{
}
namespace App\Nested;
class AnotherClass
{
}
π
Use explicit names over dynamic ones
class SomeClass
{
public function old(): bool
{
return $this->${variable};
}
}
β
class SomeClass
{
public function old(): bool
{
return $this->specificMethodName();
}
}
π
There should be no empty class
class SomeClass
{
}
β
class SomeClass
{
public function getSome()
{
}
}
π
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);
}
}
π
Use default null value and nullable compare instead of isset on object
class SomeClass
{
public function run()
{
if (random_int(0, 1)) {
$object = new SomeClass();
}
if (isset($object)) {
return $object;
}
}
}
β
class SomeClass
{
public function run()
{
$object = null;
if (random_int(0, 1)) {
$object = new SomeClass();
}
if ($object !== null) {
return $object;
}
}
}
π
The path "%s" was not found
$filePath = __DIR__ . '/missing_location.txt';
β
$filePath = __DIR__ . '/existing_location.txt';
π
Anonymous variable in a %s->...()
method call can lead to false dead methods. Make sure the variable type is known
function run($unknownType)
{
return $unknownType->call();
}
β
function run(KnownType $knownType)
{
return $knownType->call();
}
π
Anonymous variables in a "%s->..." property fetch can lead to false dead property. Make sure the variable type is known
function run($unknownType)
{
return $unknownType->name;
}
β
function run(KnownType $knownType)
{
return $knownType->name;
}
π
Use required typed property over of nullable array property
final class SomeClass
{
private ?array $property = null;
}
β
final class SomeClass
{
private array $property = [];
}
π
Use explicit return value over magic &reference
class SomeClass
{
public function run(&$value)
{
}
}
β
class SomeClass
{
public function run($value)
{
return $value;
}
}
π
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);
}
}
π
Returning false in non return bool class method. Use null instead
class SomeClass
{
/**
* @var Item[]
*/
private $items = [];
public function getItem($key)
{
if (isset($this->items[$key])) {
return $this->items[$key];
}
return false;
}
}
β
class SomeClass
{
/**
* @var Item[]
*/
private $items = [];
public function getItem($key): ?Item
{
if (isset($this->items[$key])) {
return $this->items[$key];
}
return null;
}
}
π
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;
}
}
π
Do not name "%s", shorter than %d chars
π§ configure it!
services:
-
class: Symplify\PHPStanRules\ObjectCalisthenics\Rules\NoShortNameRule
tags: [phpstan.rules.rule]
arguments:
minNameLength: 3
β
function is()
{
}
β
function isClass()
{
}
π
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 {};
}
}
π
Getter method must return something, not void
final class SomeClass
{
public function getData(): void
{
// ...
}
}
β
final class SomeClass
{
public function getData(): array
{
// ...
}
}
π
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('...');
}
}
π
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()
{
}
}
π
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);
}
}
π
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()
{
}
}
π
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
{
}
π
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
{
}
π
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()
{
}
}
π
Provide more specific return type "%s" over abstract one
final class IssueControlFactory
{
public function create(): Control
{
return new IssueControl();
}
}
final class IssueControl extends Control
{
}
β
final class IssueControlFactory
{
public function create(): IssueControl
{
return new IssueControl();
}
}
final class IssueControl extends Control
{
}
π
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';
}
π
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
{
}
π
Constant "%s" must be uppercase
final class SomeClass
{
public const some = 'value';
}
β
final class SomeClass
{
public const SOME = 'value';
}
π