Skip to content

Latest commit

Β 

History

History
1589 lines (1118 loc) Β· 22.3 KB

rules_overview.md

File metadata and controls

1589 lines (1118 loc) Β· 22.3 KB

42 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
{
}

namespace App\Contracts\Repository;

interface ProductRepositoryInterface
{
}

πŸ‘


CheckTypehintCallerTypeRule

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)
    {
    }
}

πŸ‘


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('...');

πŸ‘


ForbiddenParamTypeRemovalRule

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)
    {
    }
}

πŸ‘


ForbiddenSameNamedNewInstanceRule

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);

πŸ‘


NarrowPublicClassMethodParamTypeByCallerTypeRule

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)
    {
    }
}

πŸ‘


NoArrayAccessOnObjectRule

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();
    }
}

πŸ‘


NoDuplicatedShortClassNameRule

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
{
}

πŸ‘


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();
    }
}

πŸ‘


NoEmptyClassRule

There should be no empty class

class SomeClass
{
}

❌


class SomeClass
{
    public function getSome()
    {
    }
}

πŸ‘


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);
    }
}

πŸ‘


NoIssetOnObjectRule

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;
        }
    }
}

πŸ‘


NoMissingDirPathRule

The path "%s" was not found

$filePath = __DIR__ . '/missing_location.txt';

❌


$filePath = __DIR__ . '/existing_location.txt';

πŸ‘


NoMixedMethodCallerRule

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();
}

πŸ‘


NoMixedPropertyFetcherRule

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;
}

πŸ‘


NoNullableArrayPropertyRule

Use required typed property over of nullable array property

final class SomeClass
{
    private ?array $property = null;
}

❌


final class SomeClass
{
    private array $property = [];
}

πŸ‘


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);
    }
}

πŸ‘


NoReturnFalseInNonBoolClassMethodRule

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;
    }
}

πŸ‘


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;
    }
}

πŸ‘


NoShortNameRule

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()
{
}

πŸ‘


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 {};
    }
}

πŸ‘


NoVoidGetterMethodRule

Getter method must return something, not void

final class SomeClass
{
    public function getData(): void
    {
        // ...
    }
}

❌


final class SomeClass
{
    public function getData(): array
    {
        // ...
    }
}

πŸ‘


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()
    {
    }
}

πŸ‘


RequireSpecificReturnTypeOverAbstractRule

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
{
}

πŸ‘


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';
}

πŸ‘