Skip to content

Latest commit

 

History

History
6109 lines (4413 loc) · 91.7 KB

rules_overview.md

File metadata and controls

6109 lines (4413 loc) · 91.7 KB

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

👍


BoolishClassMethodPrefixRule

Method "%s()" returns bool type, so the name should start with is/has/was...

class SomeClass
{
    public function old(): bool
    {
        return $this->age > 100;
    }
}


class SomeClass
{
    public function isOld(): bool
    {
        return $this->age > 100;
    }
}

👍


CheckAttributteArgumentClassExistsRule

Class was not found

#[SomeAttribute(firstName: 'MissingClass::class')]
class SomeClass
{
}


#[SomeAttribute(firstName: ExistingClass::class)]
class SomeClass
{
}

👍


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

👍


CheckConstantExpressionDefinedInConstructOrSetupRule

Move constant expression to __construct(), setUp() method or constant

class SomeClass
{
    public function someMethod()
    {
        $mainPath = getcwd() . '/absolute_path';
        return __DIR__ . $mainPath;
    }
}


class SomeClass
{
    private $mainPath;

    public function __construct()
    {
        $this->mainPath = getcwd() . '/absolute_path';
    }

    public function someMethod()
    {
        return $this->mainPath;
    }
}

👍


CheckNotTestsNamespaceOutsideTestsDirectoryRule

"*Test.php" file cannot be located outside "Tests" namespace

// file: "SomeTest.php
namespace App;

class SomeTest
{
}


// file: "SomeTest.php
namespace App\Tests;

class SomeTest
{
}

👍


CheckOptionArgumentCommandRule

Argument and options "%s" got confused

class SomeClass extends Command
{
    protected function configure(): void
    {
        $this->addOption('source');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $source = $input->getArgument('source');
    }
}


class SomeClass extends Command
{
    protected function configure(): void
    {
        $this->addArgument('source');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $source = $input->getArgument('source');
    }
}

👍


CheckParentChildMethodParameterTypeCompatibleRule

Method parameters must be compatible with its parent

class ParentClass
{
    public function run(string $someParameter)
    {
    }
}

class SomeClass extends ParentClass
{
    public function run($someParameter)
    {
    }
}


class ParentClass
{
    public function run(string $someParameter)
    {
    }
}

class SomeClass extends ParentClass
{
    public function run(string $someParameter)
    {
    }
}

👍


CheckReferencedClassInAnnotationRule

Class "%s" used in annotation is missing

/**
 * @SomeAnnotation(value=MissingClass::class)
 */
class SomeClass
{
}


/**
 * @SomeAnnotation(value=ExistingClass::class)
 */
class SomeClass
{
}

👍


CheckRequiredAutowireAutoconfigurePublicInConfigServiceRule

autowire(), autoconfigure(), and public() are required in config service

use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
    $services = $containerConfigurator->services();

    $services->defaults()
        ->public();
};


use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
    $services = $containerConfigurator->services();

    $services->defaults()
        ->public()
        ->autowire()
        ->autoconfigure();
};

👍


CheckRequiredInterfaceInContractNamespaceRule

Interface must be located in "Contract" namespace

namespace App\Repository;

interface ProductRepositoryInterface
{
}


namespace App\Contract\Repository;

interface ProductRepositoryInterface
{
}

👍


CheckRequiredMethodNamingRule

Autowired/inject method name must respect "autowire/inject" + class name

final class SomeClass
{
    /**
     * @required
     */
    public function autowireRandom(...)
    {
        // ...
    }
}


final class SomeClass
{
    /**
     * @required
     */
    public function autowireSomeClass(...)
    {
        // ...
    }
}

👍


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

👍


CheckUsedNamespacedNameOnClassNodeRule

Use $class->namespaceName instead of $class->name that only returns short class name

use PhpParser\Node\Stmt\Class_;

final class SomeClass
{
    public function run(Class_ $class)
    {
        $className = (string) $class->name;
    }
}


use PhpParser\Node\Stmt\Class_;

final class SomeClass
{
    public function run(Class_ $class)
    {
        $className = (string) $class->namespacedName;
    }
}

👍


ClassLikeCognitiveComplexityRule

Cognitive complexity of class/trait must be under specific limit

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\CognitiveComplexity\Rules\ClassLikeCognitiveComplexityRule
        tags: [phpstan.rules.rule]
        arguments:
            maxClassCognitiveComplexity: 10

class SomeClass
{
    public function simple($value)
    {
        if ($value !== 1) {
            if ($value !== 2) {
                return false;
            }
        }

        return true;
    }

    public function another($value)
    {
        if ($value !== 1 && $value !== 2) {
            return false;
        }

        return true;
    }
}


class SomeClass
{
    public function simple($value)
    {
        return $this->someOtherService->count($value);
    }

    public function another($value)
    {
        return $this->someOtherService->delete($value);
    }
}

👍


services:
    -
        class: Symplify\PHPStanRules\CognitiveComplexity\Rules\ClassLikeCognitiveComplexityRule
        tags: [phpstan.rules.rule]
        arguments:
            limitsByTypes:
                Symfony\Component\Console\Command\Command: 5

use Symfony\Component\Console\Command\Command;

class SomeCommand extends Command
{
    public function configure()
    {
        $this->setName('...');
    }

    public function execute()
    {
        if (...) {
            // ...
        } else {
            // ...
        }
    }
}


use Symfony\Component\Console\Command\Command;

class SomeCommand extends Command
{
    public function configure()
    {
        $this->setName('...');
    }

    public function execute()
    {
        return $this->externalService->resolve(...);
    }
}

👍


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

👍


ConstantMapRuleRule

Static constant map should be extracted from this method

class SomeClass
{
    public function run($value)
    {
        if ($value instanceof SomeType) {
            return 100;
        }

        if ($value instanceof AnotherType) {
            return 1000;
        }

        return 200;
    }
}


class SomeClass
{
    /**
     * @var array<string, int>
     */
    private const TYPE_TO_VALUE = [
        SomeType::class => 100,
        AnotherType::class => 1000,
    ];

    public function run($value)
    {
        foreach (self::TYPE_TO_VALUE as $type => $value) {
            if (is_a($value, $type, true)) {
                return $value;
            }
        }

        return 200;
    }
}

👍


DifferentMethodNameToParameterRule

Method name should be different to its parameter name, in a verb form

class SomeClass
{
    public function apple(string $apple)
    {
        // ...
    }
}


class SomeClass
{
    public function eatApple(string $apple)
    {
        // ...
    }
}

👍


ExcessiveParameterListRule

Method "%s()" is using too many parameters - %d. Make it under %d

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\ExcessiveParameterListRule
        tags: [phpstan.rules.rule]
        arguments:
            maxParameterCount: 2

class SomeClass
{
    public function __construct($one, $two, $three)
    {
        // ...
    }
}


class SomeClass
{
    public function __construct($one, $two)
    {
        // ...
    }
}

👍


ExcessivePublicCountRule

Too many public elements on class - %d. Narrow it down under %d

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\ExcessivePublicCountRule
        tags: [phpstan.rules.rule]
        arguments:
            maxPublicClassElementCount: 2

class SomeClass
{
    public $one;

    public $two;

    public $three;
}


class SomeClass
{
    public $one;

    public $two;
}

👍


ExclusiveDependencyRule

Dependency of specific type can be used only in specific class types

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\ExclusiveDependencyRule
        tags: [phpstan.rules.rule]
        arguments:
            allowedExclusiveDependencyInTypes:
                Doctrine\ORM\EntityManager:
                    - *Repository

                Doctrine\ORM\EntityManagerInterface:
                    - *Repository

class CheckboxController
{
    /**
     * @var EntityManagerInterface
     */
    private $entityManager;

    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->entityManager = $entityManager;
    }
}


class CheckboxRepository
{
    /**
     * @var EntityManagerInterface
     */
    private $entityManager;

    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->entityManager = $entityManager;
    }
}

👍


ExclusiveNamespaceRule

Exclusive namespace can only contain classes of specific type, nothing else

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\ExclusiveNamespaceRule
        tags: [phpstan.rules.rule]
        arguments:
            namespaceParts:
                - Presenter

namespace App\Presenter;

class SomeRepository
{
}


namespace App\Presenter;

class SomePresenter
{
}

👍


ForbiddenAnonymousClassRule

Anonymous class is not allowed.

new class() {
};


class SomeClass
{
}

new SomeClass();

👍


ForbiddenArrayDestructRule

Array destruct is not allowed. Use value object to pass data instead

final class SomeClass
{
    public function run(): void
    {
        [$firstValue, $secondValue] = $this->getRandomData();
    }
}


final class SomeClass
{
    public function run(): void
    {
        $valueObject = $this->getValueObject();
        $firstValue = $valueObject->getFirstValue();
        $secondValue = $valueObject->getSecondValue();
    }
}

👍


ForbiddenArrayWithStringKeysRule

Array with keys is not allowed. Use value object to pass data instead

final class SomeClass
{
    public function run()
    {
        return [
            'name' => 'John',
            'surname' => 'Dope',
        ];
    }
}


final class SomeClass
{
    public function run()
    {
        return new Person('John', 'Dope');
    }
}

👍


ForbiddenAttributteArgumentRule

Attribute key "%s" cannot be used

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\ForbiddenAttributteArgumentRule
        tags: [phpstan.rules.rule]
        arguments:
            $argumentsByAttributes:
                Doctrine\ORM\Mapping\Entity:
                    - repositoryClass

use Doctrine\ORM\Mapping\Entity;

#[Entity(repositoryClass: SomeRepository::class)]
class SomeClass
{
}


use Doctrine\ORM\Mapping\Entity;

#[Entity]
class SomeClass
{
}

👍


ForbiddenCallOnTypeRule

Method call or Static Call on %s is not allowed

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\ForbiddenCallOnTypeRule
        tags: [phpstan.rules.rule]
        arguments:
            forbiddenTypes:
                - Symfony\Component\DependencyInjection\Container

use Symfony\Component\DependencyInjection\Container;

class SomeClass
{
    /**
     * @var Container
     */
    private $some;

    public function __construct(Container $some)
    {
        $this->some = $some;
    }

    public function call()
    {
        $this->some->call();
    }
}


use Other\SpecificService;

class SomeClass
{
    /**
     * @var SpecificService
     */
    private $specificService;

    public function __construct(SpecificService $specificService)
    {
        $this->specificService = $specificService;
    }

    public function call()
    {
        $this->specificService->call();
    }
}

👍


ForbiddenClassConstRule

Constants in this class are not allowed, move them to custom Enum class instead

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\Enum\ForbiddenClassConstRule
        tags: [phpstan.rules.rule]
        arguments:
            classTypes:
                - AbstractEntity

final class Product extends AbstractEntity
{
    public const TYPE_HIDDEN = 0;

    public const TYPE_VISIBLE = 1;
}


final class Product extends AbstractEntity
{
}

class ProductVisibility extends Enum
{
    public const HIDDEN = 0;

    public const VISIBLE = 1;
}

👍


ForbiddenComplexArrayConfigInSetRule

For complex configuration use value object over array

use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
    $services = $containerConfigurator->services();

    $services->set('...')
        ->call('...', [[
            'options' => ['Cake\Network\Response', ['withLocation', 'withHeader']],
        ]]);
};


use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
    $services = $containerConfigurator->services();

    $services->set('...')
        ->call('...', [[
            'options' => inline_value_objects([
                new SomeValueObject('Cake\Network\Response', ['withLocation', 'withHeader']),
            ]),
        ]]);
};

👍


ForbiddenComplexForeachIfExprRule

foreach(...), while(), for() or if(...) cannot contains a complex expression. Extract it to a new variable assign on line before

foreach ($this->getData($arg) as $key => $item) {
    // ...
}


$data = $this->getData($arg);
foreach ($arg as $key => $item) {
    // ...
}

👍


ForbiddenComplexFuncCallRule

Do not use "%s" function with complex content, make it more readable with extracted method or single-line statement

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\ForbiddenComplexFuncCallRule
        tags: [phpstan.rules.rule]
        arguments:
            forbiddenComplexFunctions:
                - array_filter

            maximumStmtCount: 2

$filteredElements = array_filter($elemnets, function ($item) {
    if ($item) {
        return true;
    }

    if ($item === null) {
        return true;
    }

    return false;
};


$filteredElements = array_filter($elemnets, function ($item) {
    return $item instanceof KeepItSimple;
};

👍


ForbiddenDependencyByTypeRule

Object instance of "%s" is forbidden to be passed to constructor

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\ForbiddenDependencyByTypeRule
        tags: [phpstan.rules.rule]
        arguments:
            forbiddenTypes:
                - EntityManager

class SomeClass
{
    public function __construct(EntityManager $entityManager)
    {
        // ...
    }
}


class SomeClass
{
    public function __construct(ProductRepository $productRepository)
    {
        // ...
    }
}

👍


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

class SomeClass
{
    return eval('...');
}


class SomeClass
{
    return echo '...';
}

👍


ForbiddenMethodCallOnNewRule

Method call on new expression is not allowed.

(new SomeClass())->run();


$someClass = new SomeClass();
$someClass->run();

👍


ForbiddenMethodCallOnTypeRule

Prevent using certain method calls on certains types

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\ForbiddenMethodCallOnTypeRule
        tags: [phpstan.rules.rule]
        arguments:
            forbiddenMethodNamesByTypes:
                SpecificType:
                    - nope

class SomeClass
{
    public function process(SpecificType $specificType)
    {
        $specificType->nope();
    }
}


class SomeClass
{
    public function process(SpecificType $specificType)
    {
        $specificType->yes();
    }
}

👍


ForbiddenMultipleClassLikeInOneFileRule

Multiple class/interface/trait is not allowed in single file

class SomeClass
{
}

interface SomeInterface
{
}


// SomeClass.php
class SomeClass
{
}

// SomeInterface.php
interface SomeInterface
{
}

👍


ForbiddenNestedCallInAssertMethodCallRule

Decouple method call in assert to standalone line to make test core more readable

use PHPUnit\Framework\TestCase;

final class SomeClass extends TestCase
{
    public function test()
    {
        $this->assertSame('oooo', $this->someMethodCall());
    }
}


use PHPUnit\Framework\TestCase;

final class SomeClass extends TestCase
{
    public function test()
    {
        $result = $this->someMethodCall();
        $this->assertSame('oooo', $result);
    }
}

👍


ForbiddenNestedForeachWithEmptyStatementRule

Nested foreach with empty statement is not allowed

$collectedFileErrors = [];

foreach ($errors as $fileErrors) {
    foreach ($fileErrors as $fileError) {
        $collectedFileErrors[] = $fileError;
    }
}


$collectedFileErrors = [];

foreach ($fileErrors as $fileError) {
    $collectedFileErrors[] = $fileError;
}

👍


ForbiddenNetteInjectOverrideRule

Assign to already injected property is not allowed

abstract class AbstractParent
{
    /**
     * @inject
     * @var SomeType
     */
    protected $someType;
}

final class SomeChild extends AbstractParent
{
    public function __construct(AnotherType $anotherType)
    {
        $this->someType = $anotherType;
    }
}


abstract class AbstractParent
{
    /**
     * @inject
     * @var SomeType
     */
    protected $someType;
}

final class SomeChild extends AbstractParent
{
}

👍


ForbiddenNewOutsideFactoryServiceRule

"new" outside factory is not allowed for object type "%s"

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\ForbiddenNewOutsideFactoryServiceRule
        tags: [phpstan.rules.rule]
        arguments:
            types:
                - AnotherObject

class SomeClass
{
    public function process()
    {
        $anotherObject = new AnotherObject();
        // ...
    }
}


class SomeClass
{
    public function __construt(AnotherObjectFactory $anotherObjectFactory)
    {
        $this->anotherObjectFactory = $anotherObjectFactory;
    }

    public function process()
    {
        $anotherObject = $this->anotherObjectFactory = $anotherObjectFactory->create();
        // ...
    }
}

👍


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

👍


ForbiddenNullableParameterRule

Parameter "%s" cannot be nullable

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\ForbiddenNullableParameterRule
        tags: [phpstan.rules.rule]
        arguments:
            forbiddenTypes:
                - PhpParser\Node

            allowedTypes:
                - PhpParser\Node\Scalar\String_

use PhpParser\Node;

class SomeClass
{
    public function run(?Node $node = null): void
    {
    }
}


use PhpParser\Node;

class SomeClass
{
    public function run(Node $node): void
    {
    }
}

👍


ForbiddenNullableReturnRule

Return type "%s" cannot be nullable

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\ForbiddenNullableReturnRule
        tags: [phpstan.rules.rule]
        arguments:
            forbiddenTypes:
                - PhpParser\Node

            allowedTypes:
                - PhpParser\Node\Scalar\String_

use PhpParser\Node;

class SomeClass
{
    public function run(): ?Node
    {
    }
}


use PhpParser\Node;

class SomeClass
{
    public function run(): Node
    {
    }
}

👍


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

👍


ForbiddenPrivateMethodByTypeRule

Private method in is not allowed here - it should only delegate to others. Decouple the private method to a new service class

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\ForbiddenPrivateMethodByTypeRule
        tags: [phpstan.rules.rule]
        arguments:
            forbiddenTypes:
                - Command

class SomeCommand extends Command
{
    public function run()
    {
        $this->somePrivateMethod();
    }

    private function somePrivateMethod()
    {
        // ...
    }
}


class SomeCommand extends Command
{
    /**
     * @var ExternalService
     */
    private $externalService;

    public function __construct(ExternalService $externalService)
    {
        $this->externalService = $externalService;
    }

    public function run()
    {
        $this->externalService->someMethod();
    }
}

👍


ForbiddenProtectedPropertyRule

Property with protected modifier is not allowed. Use interface contract method instead

class SomeClass
{
    protected $repository;
}


class SomeClass implements RepositoryAwareInterface
{
    public function getRepository()
    {
        // ....
    }
}

👍


ForbiddenReturnValueOfIncludeOnceRule

Cannot return include_once/require_once

class SomeClass
{
    public function run()
    {
        return require_once 'Test.php';
    }
}


class SomeClass
{
    public function run()
    {
        require_once 'Test.php';
    }
}

👍


ForbiddenSpreadOperatorRule

Spread operator is not allowed.

$args = [$firstValue, $secondValue];
$message = sprintf('%s', ...$args);


$message = sprintf('%s', $firstValue, $secondValue);

👍


ForbiddenTestsNamespaceOutsideTestsDirectoryRule

"Tests" namespace can be only in "/tests" directory

// file path: "src/SomeClass.php

namespace App\Tests;

class SomeClass
{
}


// file path: "tests/SomeClass.php

namespace App\Tests;

class SomeClass
{
}

👍


ForbiddenThisArgumentRule

$this as argument is not allowed. Refactor method to service composition

$this->someService->process($this, ...);


$this->someService->process($value, ...);

👍


FunctionLikeCognitiveComplexityRule

Cognitive complexity of function/method must be under specific limit

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\CognitiveComplexity\Rules\FunctionLikeCognitiveComplexityRule
        tags: [phpstan.rules.rule]
        arguments:
            maxMethodCognitiveComplexity: 5

class SomeClass
{
    public function simple($value)
    {
        if ($value !== 1) {
            if ($value !== 2) {
                return false;
            }
        }

        return true;
    }
}


class SomeClass
{
    public function simple($value)
    {
        if ($value === 1) {
            return true;
        }

        return $value === 2;
    }
}

👍


IfImplementsInterfaceThenNewTypeRule

Class that implements specific interface, must use related class in new SomeClass

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\IfImplementsInterfaceThenNewTypeRule
        tags: [phpstan.rules.rule]
        arguments:
            newTypesByInterface:
                ConfigurableRuleInterface: ConfiguredCodeSample

class SomeRule implements ConfigurableRuleInterface
{
    public function some()
    {
        $codeSample = new CodeSample();
    }
}


class SomeRule implements ConfigurableRuleInterface
{
    public function some()
    {
        $configuredCodeSample = new ConfiguredCodeSample();
    }
}

👍


IfNewTypeThenImplementInterfaceRule

Class must implement "%s" interface

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\IfNewTypeThenImplementInterfaceRule
        tags: [phpstan.rules.rule]
        arguments:
            interfacesByNewTypes:
                ConfiguredCodeSample: ConfiguredRuleInterface

class SomeRule
{
    public function run()
    {
        return new ConfiguredCodeSample('...');
    }
}


class SomeRule implements ConfiguredRuleInterface
{
    public function run()
    {
        return new ConfiguredCodeSample('...');
    }
}

👍


InvokableControllerByRouteNamingRule

Use controller class name based on route name instead

use Symfony\Component\Routing\Annotation\Route;

final class SecurityController extends AbstractController
{
    #[Route(path: '/logout', name: 'logout')]
    public function __invoke(): Response
    {
    }
}


use Symfony\Component\Routing\Annotation\Route;

final class LogoutController extends AbstractController
{
    #[Route(path: '/logout', name: 'logout')]
    public function __invoke(): Response
    {
    }
}

👍


MatchingTypeConstantRule

Constant type should be "%s", but is "%s"

class SomeClass
{
    /**
     * @var int
     */
    private const LIMIT = 'max';
}


class SomeClass
{
    /**
     * @var string
     */
    private const LIMIT = 'max';
}

👍


NoAbstractMethodRule

Use explicit interface contract or a service over unclear abstract methods

abstract class SomeClass
{
    abstract public function run();
}


abstract class SomeClass implements RunnableInterface
{
}

interface RunnableInterface
{
    public function run();
}

👍


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

👍


NoArrayStringObjectReturnRule

Use another value object over array with string-keys and objects, array<string, ValueObject>

final class SomeClass
{
    public getItems()
    {
        return $this->getValues();
    }

    /**
     * @return array<string, Value>
     */
    private function getValues()
    {
    }
}


final class SomeClass
{
    public getItems()
    {
        return $this->getValues();
    }

    /**
     * @return WrappingValue[]
     */
    private function getValues()
    {
        // ...
    }
}

👍


NoBinaryOpCallCompareRule

No magic closure function call is allowed, use explicit class with method instead

return array_filter($items, function ($item) {
}) !== [];


$values = array_filter($items, function ($item) {
});
return $values !== [];

👍


NoChainMethodCallRule

Do not use chained method calls. Put each on separated lines.

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\ObjectCalisthenics\Rules\NoChainMethodCallRule
        tags: [phpstan.rules.rule]
        arguments:
            allowedChainTypes:
                - AllowedFluent

$this->runThis()
    ->runThat();

$fluentClass = new AllowedFluent();
$fluentClass->one()
    ->two();


$this->runThis();
$this->runThat();

$fluentClass = new AllowedFluent();
$fluentClass->one()
    ->two();

👍


NoClassWithStaticMethodWithoutStaticNameRule

Class "%s" with static method must have "Static" in its name it explicit

class SomeClass
{
    public static function getSome()
    {
    }
}


class SomeStaticClass
{
    public static function getSome()
    {
    }
}

👍


NoConstantInterfaceRule

Reserve interface for contract only. Move constant holder to a class soon-to-be Enum

interface SomeContract
{
    public const YES = 'yes';

    public const NO = 'ne';
}


class SomeValues
{
    public const YES = 'yes';

    public const NO = 'ne';
}

👍


NoConstructorInTestRule

Do not use constructor in tests. Move to setUp() method

final class SomeTest
{
    public function __construct()
    {
        // ...
    }
}


final class SomeTest
{
    protected function setUp()
    {
        // ...
    }
}

👍


NoContainerInjectionInConstructorRule

Instead of container injection, use specific service

class SomeClass
{
    public function __construct(ContainerInterface $container)
    {
        $this->someDependency = $container->get('...');
    }
}


class SomeClass
{
    public function __construct(SomeDependency $someDependency)
    {
        $this->someDependency = $someDependency;
    }
}

👍


NoDefaultExceptionRule

Use custom exceptions instead of native "%s"

throw new RuntimeException('...');


use App\Exception\FileNotFoundException;

throw new FileNotFoundException('...');

👍


NoDefaultParameterValueRule

Parameter "%s" cannot have default value

class SomeClass
{
    public function run($value = true): void
    {
    }
}


class SomeClass
{
    public function run($value): void
    {
    }
}

👍


NoDependencyJugglingRule

Use dependency injection instead of dependency juggling

public function __construct($service)
{
    $this->service = $service;
}

public function run($someObject)
{
    return $someObject->someMethod($this->service);
}


public function run($someObject)
{
    return $someObject->someMethod();
}

👍


NoDuplicatedShortClassNameRule

Class with base "%s" name is already used in "%s". Use unique name to make classes easy to recognize

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

👍


NoDynamicPropertyOnStaticCallRule

Use non-dynamic property on static calls or class const fetches

class SomeClass
{
    public function run()
    {
        return $this->connection::literal();
    }
}


class SomeClass
{
    public function run()
    {
        return Connection::literal();
    }
}

👍


NoElseAndElseIfRule

Do not use "else/elseif". Refactor to early return

if (...) {
    return 1;
} else {
    return 2;
}


if (...) {
    return 1;
}

return 2;

👍


NoEmptyClassRule

There should be no empty class

class SomeClass
{
}


class SomeClass
{
    public function getSome()
    {
    }
}

👍


NoFactoryInConstructorRule

Do not use factory/method call in constructor. Put factory in config and get service with dependency injection

class SomeClass
{
    private $someDependency;

    public function __construct(SomeFactory $factory)
    {
        $this->someDependency = $factory->build();
    }
}


class SomeClass
{
    private $someDependency;

    public function __construct(SomeDependency $someDependency)
    {
        $this->someDependency = $someDependency;
    }
}

👍


NoFuncCallInMethodCallRule

Separate function "%s()" in method call to standalone row to improve readability

final class SomeClass
{
    public function run($value): void
    {
        $this->someMethod(strlen('fooo'));
    }

    // ...
}


final class SomeClass
{
    public function run($value): void
    {
        $fooLength = strlen('fooo');
        $this->someMethod($fooLength);
    }

    // ...
}

👍


NoGetRepositoryOutsideConstructorRule

Do not use "$entityManager->getRepository()" outside of the constructor of repository service or setUp() method in test case

final class SomeController
{
    public function someAction(EntityManager $entityManager): void
    {
        $someEntityRepository = $entityManager->getRepository(SomeEntity::class);
    }
}


final class SomeRepository
{
    public function __construct(EntityManager $entityManager): void
    {
        $someEntityRepository = $entityManager->getRepository(SomeEntity::class);
    }
}

👍


NoInheritanceRule

Do not inherit from abstract class, better use composition

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\NoInheritanceRule
        tags: [phpstan.rules.rule]
        arguments:
            allowedParentTypes:
                - AnotherParent

class SomeClass extends AbstratcClass
{
    public function run()
    {
        $this->parentMethod();
    }
}


class SomeClass
{
    private function __construct(
        private $dependency Dependency
    ) {
    }

    public function run()
    {
        $this->dependency->otherMethod();
    }
}

👍


NoInjectOnFinalRule

Use constructor on final classes, instead of property injection

final class SomePresenter
{
    /**
     * @inject
     */
    public $property;
}


abstract class SomePresenter
{
    /**
     * @inject
     */
    public $property;
}

👍


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

        if (isset($object)) {
            return $object;
        }
    }
}


class SomeClass
{
    public function run()
    {
        $object = null;
        if (random_int(0, 1)) {
            $object = new self();
        }

        if ($object !== null) {
            return $object;
        }
    }
}

👍


NoMagicClosureRule

No magic closure function call is allowed, use explicit class with method instead

(static function () {
    // ...
})


final class HelpfulName
{
    public function clearName()
    {
        // ...
    }
}

👍


NoMaskWithoutSprintfRule

Missing sprintf() function for a mask

return 'Hey %s';


return sprintf('Hey %s', 'Matthias');

👍


NoMethodTagInClassDocblockRule

Do not use @method tag in class docblock

/**
 * @method getMagic() string
 */
class SomeClass
{
    public function __call()
    {
        // more magic
    }
}


class SomeClass
{
    public function getExplicitValue()
    {
        return 'explicit';
    }
}

👍


NoMissingDirPathRule

The path "%s" was not found

class SomeClass
{
    public function run()
    {
        return __DIR__ . '/missing_location.txt';
    }
}


class SomeClass
{
    public function run()
    {
        return __DIR__ . '/existing_location.txt';
    }
}

👍


NoModifyAndReturnSelfObjectRule

Use void instead of modify and return self object

final class SomeClass
{
    public function modify(ComposerJson $composerJson): ComposerJson
    {
        $composerJson->addPackage('some-package');
        return $composerJson;
    }
}


final class SomeClass
{
    public function modify(ComposerJson $composerJson): void
    {
        $composerJson->addPackage('some-package');
    }
}

👍


NoMultiArrayAssignRule

Use value object over multi array assign

final class SomeClass
{
    public function run()
    {
        $values = [];
        $values['person']['name'] = 'Tom';
        $values['person']['surname'] = 'Dev';
    }
}


final class SomeClass
{
    public function run()
    {
        $values = [];
        $values[] = new Person('Tom', 'Dev');
    }
}

👍


NoNestedFuncCallRule

Use separate function calls with readable variable names

class SomeClass
{
    public function run()
    {
        return array_filter(array_map($callback, $items));
    }
}


class SomeClass
{
    public function run()
    {
        $mappedItems = array_map($callback, $items);
        return array_filter($mappedItems);
    }
}

👍


NoNetteArrayAccessInControlRule

Avoid using magical unclear array access and use explicit "$this->getComponent()" instead

use Nette\Application\UI\Presenter;

class SomeClass extends Presenter
{
    public function render()
    {
        return $this['someControl'];
    }
}


use Nette\Application\UI\Presenter;

class SomeClass extends Presenter
{
    public function render()
    {
        return $this->getComponent('someControl');
    }
}

👍


NoNetteDoubleTemplateAssignRule

Avoid double template variable override of "%s"

use Nette\Application\UI\Presenter;

class SomeClass extends Presenter
{
    public function render()
    {
        $this->template->key = '1';
        $this->template->key = '2';
    }
}


use Nette\Application\UI\Presenter;

class SomeClass extends Presenter
{
    public function render()
    {
        $this->template->key = '2';
    }
}

👍


NoNetteInjectAndConstructorRule

Use either __construct() or @inject, not both together

class SomeClass
{
    private $someType;

    public function __construct()
    {
        // ...
    }

    public function injectSomeType($someType)
    {
        $this->someType = $someType;
    }
}


class SomeClass
{
    private $someType;

    public function __construct($someType)
    {
        $this->someType = $someType;
    }
}

👍


NoNetteRenderMissingVariableRule

Passed "%s" variable that are not used in the template

use Nette\Application\UI\Control;

final class SomeControl extends Control
{
    public function render()
    {
        $this->template->render(__DIR__ . '/some_file.latte', [
            'non_existing_variable' => 'value',
        ]);
    }
}


use Nette\Application\UI\Control;

final class SomeControl extends Control
{
    public function render()
    {
        $this->template->render(__DIR__ . '/some_file.latte', [
            'existing_variable' => 'value',
        ]);
    }
}

👍


NoNetteRenderUnusedVariableRule

Missing "%s" variable that are not passed to the template

use Nette\Application\UI\Control;

final class SomeControl extends Control
{
    public function render()
    {
        $this->template->render(__DIR__ . '/some_file.latte');
    }
}


use Nette\Application\UI\Control;

final class SomeControl extends Control
{
    public function render()
    {
        $this->template->render(__DIR__ . '/some_file.latte', [
            'existing_variable' => 'value',
        ]);
    }
}

👍


NoNetteTemplateVariableReadRule

Avoid $this->template->variable for read access, as it can be defined anywhere. Use local $variable instead

use Nette\Application\UI\Presenter;

class SomeClass extends Presenter
{
    public function render()
    {
        if ($this->template->key === 'value') {
            return;
        }
    }
}


use Nette\Application\UI\Presenter;

class SomeClass extends Presenter
{
    public function render()
    {
        $this->template->key = 'value';
    }
}

👍


NoNullableArrayPropertyRule

Use required typed property over of nullable array property

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


final class SomeClass
{
    private array $property;
}

👍


NoNullablePropertyRule

Use required typed property over of nullable property

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


final class SomeClass
{
    private DateTime $property;
}

👍


NoParentMethodCallOnEmptyStatementInParentMethodRule

Do not call parent method if parent method is empty

class ParentClass
{
    public function someMethod()
    {
    }
}

class SomeClass extends ParentClass
{
    public function someMethod()
    {
        parent::someMethod();
    }
}


class ParentClass
{
    public function someMethod()
    {
    }
}

class SomeClass extends ParentClass
{
    public function someMethod()
    {
    }
}

👍


NoParentMethodCallOnNoOverrideProcessRule

Do not call parent method if no override process

class SomeClass extends Printer
{
    public function print($nodes)
    {
        return parent::print($nodes);
    }
}


class SomeClass extends Printer
{
}

👍


NoPostIncPostDecRule

Post operation are forbidden, as they make 2 values at the same line. Use pre instead

class SomeClass
{
    public function run($value = 1)
    {
        // 1 ... 0
        if ($value--) {
        }
    }
}


class SomeClass
{
    public function run($value = 1)
    {
        // 0
        if (--$value) {
        }
    }
}

👍


NoProtectedElementInFinalClassRule

Instead of protected element in final class use private element or contract method

final class SomeClass
{
    private function run()
    {
    }
}


final class SomeClass
{
    private function run()
    {
    }
}

👍


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)
    {
        return 1000;
    }
}


final class SomeClass
{
    private $name;

    public function setName(string $name): void
    {
        $this->name = $name;
    }
}

👍


NoSetterOnServiceRule

Do not use setter on a service

class SomeService
{
    public function setSomeValue($value)
    {
    }
}


class SomeEntity
{
    public function setSomeValue($value)
    {
    }
}

👍


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

👍


NoStaticPropertyRule

Do not use static property

final class SomeClass
{
    private static $customFileNames = [];
}


final class SomeClass
{
    private $customFileNames = [];
}

👍


NoSuffixValueObjectClassRule

Value Object class name "%s" must be without "ValueObject" suffix. The correct class name is "%s".

class SomeValueObject
{
    public function __construct(string $name)
    {
        $this->name = $name;
    }
}


class Some
{
    public function __construct(string $name)
    {
        $this->name = $name;
    }
}

👍


NoTraitRule

Do not use trait, extract to a service and dependency injection instead

trait SomeTrait
{
    public function run()
    {
    }
}


class SomeService
{
    public function run(...)
    {
    }
}

👍


NoVoidGetterMethodRule

Getter method must return something, not void

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


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

👍


PreferConstantValueRule

Use defined constant %s::%s over string %s

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\PreferConstantValueRule
        tags: [phpstan.rules.rule]
        arguments:
            constantHoldingObjects:
                Symplify\ComposerJsonManipulator\ValueObject\ComposerJsonSection:
                    - "REQUIRE(_.*)?"
                    - "AUTOLOAD(_.*)?"

class SomeClass
{
    public function run()
    {
        return 'require';
    }
}


use Symplify\ComposerJsonManipulator\ValueObject\ComposerJsonSection;

class SomeClass
{
    public function run()
    {
        return ComposerJsonSection::REQUIRE;
    }
}

👍


PreferredAttributeOverAnnotationRule

Use attribute instead of "%s" annotation

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\PreferredAttributeOverAnnotationRule
        tags: [phpstan.rules.rule]
        arguments:
            annotations:
                - Symfony\Component\Routing\Annotation\Route

use Symfony\Component\Routing\Annotation\Route;

class SomeController
{
    /**
     * @Route()
     */
    public function action()
    {
    }
}


use Symfony\Component\Routing\Annotation\Route;

class SomeController
{
    #Route()
    public function action()
    {
    }
}

👍


PreferredClassRule

Instead of "%s" class/interface use "%s"

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\PreferredClassRule
        tags: [phpstan.rules.rule]
        arguments:
            oldToPreferredClasses:
                SplFileInfo: Symplify\SmartFileSystem\SmartFileInfo

class SomeClass
{
    public function run()
    {
        return new SplFileInfo('...');
    }
}


use Symplify\SmartFileSystem\SmartFileInfo;

class SomeClass
{
    public function run()
    {
        return new SmartFileInfo('...');
    }
}

👍


PreferredMethodCallOverFuncCallRule

Use "%s->%s()" method call over "%s()" func call

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\PreferredMethodCallOverFuncCallRule
        tags: [phpstan.rules.rule]
        arguments:
            funcCallToPreferredMethodCalls:
                strlen:
                    - Nette\Utils\Strings
                    - length

class SomeClass
{
    public function run($value)
    {
        return strlen($value);
    }
}


use Nette\Utils\Strings;

class SomeClass
{
    public function __construct(Strings $strings)
    {
        $this->strings = $strings;
    }

    public function run($value)
    {
        return $this->strings->length($value);
    }
}

👍


PreferredRawDataInTestDataProviderRule

Code configured at setUp() cannot be used in data provider. Move it to test() method

final class UseDataFromSetupInTestDataProviderTest extends TestCase
{
    private $data;

    protected function setUp()
    {
        $this->data = true;
    }

    public function provideFoo()
    {
        yield [$this->data];
    }

    /**
     * @dataProvider provideFoo
     */
    public function testFoo($value)
    {
        $this->assertTrue($value);
    }
}


use stdClass;

final class UseRawDataForTestDataProviderTest
{
    private $obj;

    private function setUp()
    {
        $this->obj = new stdClass();
    }

    public function provideFoo()
    {
        yield [true];
    }

    /**
     * @dataProvider provideFoo
     */
    public function testFoo($value)
    {
        $this->obj->x = $value;
        $this->assertTrue($this->obj->x);
    }
}

👍


PreferredStaticCallOverFuncCallRule

Use "%s::%s()" static call over "%s()" func call

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\PreferredStaticCallOverFuncCallRule
        tags: [phpstan.rules.rule]
        arguments:
            funcCallToPreferredStaticCalls:
                strlen:
                    - Nette\Utils\Strings
                    - length

class SomeClass
{
    public function run($value)
    {
        return strlen($value);
    }
}


use Nette\Utils\Strings;

class SomeClass
{
    public function run($value)
    {
        return Strings::length($value);
    }
}

👍


PrefixAbstractClassRule

Abstract class name "%s" must be prefixed with "Abstract"

abstract class SomeClass
{
}


abstract class AbstractSomeClass
{
}

👍


PreventDoubleSetParameterRule

Set param value is overriden. Merge it to previous set above

use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
    $parameters = $containerConfigurator->parameters();

    $parameters->set('some_param', [1]);
    $parameters->set('some_param', [2]);
};


use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
    $parameters = $containerConfigurator->parameters();

    $parameters->set('some_param', [1, 2]);
};

👍


PreventDuplicateClassMethodRule

Content of method "%s()" is duplicated with method "%s()" in "%s" class. Use unique content or service instead

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\PreventDuplicateClassMethodRule
        tags: [phpstan.rules.rule]
        arguments:
            minimumLineCount: 1

class SomeClass
{
    public function someMethod()
    {
        echo 'statement';
        $value = new SmartFinder();
    }
}

class AnotherClass
{
    public function someMethod()
    {
        echo 'statement';
        $differentValue = new SmartFinder();
    }
}


class SomeClass
{
    public function someMethod()
    {
        echo 'statement';
        $value = new SmartFinder();
    }
}
}

👍


PreventParentMethodVisibilityOverrideRule

Change "%s()" method visibility to "%s" to respect parent method visibility.

class SomeParentClass
{
    public function run()
    {
    }
}

class SomeClass
{
    protected function run()
    {
    }
}


class SomeParentClass
{
    public function run()
    {
    }
}

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

👍


RequireConstantInAttributeArgumentRule

Argument "%s" must be a constant

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\RequireConstantInAttributeArgumentRule
        tags: [phpstan.rules.rule]
        arguments:
            attributeWithNames:
                Symfony\Component\Routing\Annotation\Route:
                    - name

use Symfony\Component\Routing\Annotation\Route;

final class SomeClass
{
    #[Route(path: '/archive', name: 'blog_archive')]
    public function __invoke()
    {
    }
}


use Symfony\Component\Routing\Annotation\Route;

final class SomeClass
{
    #[Route(path: '/archive', name: RouteName::BLOG_ARCHIVE)]
    public function __invoke()
    {
    }
}

👍


RequireConstantInMethodCallPositionRule

Parameter argument on position %d must use %s constant

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\RequireConstantInMethodCallPositionRule
        tags: [phpstan.rules.rule]
        arguments:
            requiredLocalConstantInMethodCall:
                SomeType:
                    someMethod:
                        - 0

class SomeClass
{
    public function someMethod(SomeType $someType)
    {
        $someType->someMethod('hey');
    }
}


class SomeClass
{
    private const HEY = 'hey'

    public function someMethod(SomeType $someType)
    {
        $someType->someMethod(self::HEY);
    }
}

👍


RequireDataProviderTestMethodRule

The "%s()" method must use data provider

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\RequireDataProviderTestMethodRule
        tags: [phpstan.rules.rule]
        arguments:
            classesRequiringDataProvider:
                - *RectorTestCase

class SomeRectorTestCase extends RectorTestCase
{
    public function test()
    {
    }
}


class SomeRectorTestCase extends RectorTestCase
{
    /**
     * @dataProvider provideData()
     */
    public function test($value)
    {
    }

    public function provideData()
    {
        // ...
    }
}

👍


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

👍


RequireMethodCallArgumentConstantRule

Method call argument on position %d must use constant (e.g. "Option::NAME") over value

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\RequireMethodCallArgumentConstantRule
        tags: [phpstan.rules.rule]
        arguments:
            constantArgByMethodByType:
                SomeClass:
                    call:
                        - 0

class AnotherClass
{
    public function run(SomeClass $someClass)
    {
        $someClass->call('name');
    }
}


class AnotherClass
{
    private OPTION_NAME = 'name';

    public function run(SomeClass $someClass)
    {
        $someClass->call(self::OPTION_NAME);
    }
}

👍


RequireNativeArraySymfonyRenderCallRule

Second argument of $this->render("template.twig", [...]) method should be explicit array, to avoid accidental variable override, see https://tomasvotruba.com/blog/2021/02/15/how-dangerous-is-your-nette-template-assign/

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class SomeController extends AbstractController
{
    public function default()
    {
        $parameters['name'] = 'John';
        $parameters['name'] = 'Doe';
        return $this->render('...', $parameters);
    }
}


use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class SomeController extends AbstractController
{
    public function default()
    {
        return $this->render('...', [
            'name' => 'John',
        ]);
    }
}

👍


RequireNewArgumentConstantRule

New expression argument on position %d must use constant over value

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\RequireNewArgumentConstantRule
        tags: [phpstan.rules.rule]
        arguments:
            constantArgByNewByType:
                Symfony\Component\Console\Input\InputOption:
                    - 2

use Symfony\Component\Console\Input\InputOption;

$inputOption = new InputOption('name', null, 2);


use Symfony\Component\Console\Input\InputOption;

$inputOption = new InputOption('name', null, InputOption::VALUE_REQUIRED);

👍


RequireQuoteStringValueSprintfRule

"%s" in sprintf() format must be quoted

class SomeClass
{
    public function run()
    {
        echo sprintf('%s value', $variable);
    }
}


class SomeClass
{
    public function run()
    {
        echo sprintf('"%s" value', $variable);
    }
}

👍


RequireSkipPrefixForRuleSkippedFixtureRule

Skipped tested file must start with "Skip" prefix

use PHPStan\Testing\RuleTestCase;

final class SomeRuleTest extends RuleTestCase
{
    /**
     * @dataProvider provideData()
     * @param array<string|int> $expectedErrorMessagesWithLines
     */
    public function testRule(string $filePath, array $expectedErrorMessagesWithLines): void
    {
        $this->analyse([$filePath], $expectedErrorMessagesWithLines);
    }

    public function provideData(): Iterator
    {
        yield [__DIR__ . '/Fixture/NewWithInterface.php', []];
    }

    protected function getRule(): Rule
    {
        return new SomeRule());
    }
}


use PHPStan\Testing\RuleTestCase;

final class SomeRuleTest extends RuleTestCase
{
    /**
     * @dataProvider provideData()
     * @param array<string|int> $expectedErrorMessagesWithLines
     */
    public function testRule(string $filePath, array $expectedErrorMessagesWithLines): void
    {
        $this->analyse([$filePath], $expectedErrorMessagesWithLines);
    }

    public function provideData(): Iterator
    {
        yield [__DIR__ . '/Fixture/SkipNewWithInterface.php', []];
    }

    protected function getRule(): Rule
    {
        return new SomeRule());
    }
}

👍


RequireStringArgumentInConstructorRule

Use quoted string in constructor "new %s()" argument on position %d instead of "::class. It prevent scoping of the class in building prefixed package.

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\RequireStringArgumentInConstructorRule
        tags: [phpstan.rules.rule]
        arguments:
            stringArgPositionsByType:
                SomeClass:
                    - 0

class AnotherClass
{
    public function run()
    {
        new SomeClass(YetAnotherClass:class);
    }
}


class AnotherClass
{
    public function run()
    {
        new SomeClass('YetAnotherClass');
    }
}

👍


RequireStringArgumentInMethodCallRule

Use quoted string in method call "%s()" argument on position %d instead of "::class. It prevent scoping of the class in building prefixed package.

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\RequireStringArgumentInMethodCallRule
        tags: [phpstan.rules.rule]
        arguments:
            stringArgPositionByMethodByType:
                SomeClass:
                    someMethod:
                        - 0

class AnotherClass
{
    public function run(SomeClass $someClass)
    {
        $someClass->someMethod(YetAnotherClass:class);
    }
}


class AnotherClass
{
    public function run(SomeClass $someClass)
    {
        $someClass->someMethod('YetAnotherClass'');
    }
}

👍


RequireStringRegexMatchKeyRule

Regex must use string named capture groups instead of numeric

use Nette\Utils\Strings;

class SomeClass
{
    private const REGEX = '#(a content)#';

    public function run()
    {
        $matches = Strings::match('a content', self::REGEX);
        if ($matches) {
            echo $matches[1];
        }
    }
}


use Nette\Utils\Strings;

class SomeClass
{
    private const REGEX = '#(?<content>a content)#';

    public function run()
    {
        $matches = Strings::match('a content', self::REGEX);
        if ($matches) {
            echo $matches['content'];
        }
    }
}

👍


RequireTemplateInNetteControlRule

Set control template explicitly in $this->template->setFile(...) or $this->template->render(...)

use Nette\Application\UI\Control;

final class SomeControl extends Control
{
    public function render()
    {
    }
}


use Nette\Application\UI\Control;

final class SomeControl extends Control
{
    public function render()
    {
        $this->template->render('some_file.latte');
    }
}

👍


RequireThisCallOnLocalMethodRule

Use "$this->()" instead of "self::()" to call local method

class SomeClass
{
    public function run()
    {
        self::execute();
    }

    private function execute()
    {
    }
}


class SomeClass
{
    public function run()
    {
        $this->execute();
    }

    private function execute()
    {
    }
}

👍


RequireThisOnParentMethodCallRule

Use "$this->()" instead of "parent::()" unless in the same named method

class SomeParentClass
{
    public function run()
    {
    }
}

class SomeClass extends SomeParentClass
{
    public function go()
    {
        parent::run();
    }
}


class SomeParentClass
{
    public function run()
    {
    }
}

class SomeClass extends SomeParentClass
{
    public function go()
    {
        $tihs->run();
    }
}

👍


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

👍


RequiredAbstractClassKeywordRule

Class name starting with "Abstract" must have an abstract keyword

class AbstractClass
{
}


abstract class AbstractClass
{
}

👍


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

👍


ServiceAndValueObjectHaveSameStartsRule

Make specific service suffix to use similar value object names for configuring in Symfony configs

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\ServiceAndValueObjectHaveSameStartsRule
        tags: [phpstan.rules.rule]
        arguments:
            classSuffixes:
                - Rector

use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
    $services = $containerConfigurator->services();

    $services->set(SomeRector::class)
        ->call('configure', [[new Another()]]);
};


use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
    $services = $containerConfigurator->services();

    $services->set(SomeRector::class)
        ->call('configure', [[new Some()]]);
};

👍


SingleIndentationInMethodRule

Do not indent more than %dx in class methods

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\ObjectCalisthenics\Rules\SingleIndentationInMethodRule
        tags: [phpstan.rules.rule]
        arguments:
            maxNestingLevel:
                - 2

function someFunction()
{
    if (...) {
        if (...) {
        }
    }
}


function someFunction()
{
    if (! ...) {
    }

    if (!...) {
    }
}

👍


SingleNetteInjectMethodRule

Use single inject*() class method per class

class SomeClass
{
    private $type;

    private $anotherType;

    public function injectOne(Type $type)
    {
        $this->type = $type;
    }

    public function injectTwo(AnotherType $anotherType)
    {
        $this->anotherType = $anotherType;
    }
}


class SomeClass
{
    private $type;

    private $anotherType;

    public function injectSomeClass(Type $type, AnotherType $anotherType) {
        $this->type = $type;
        $this->anotherType = $anotherType;
    }
}

👍


SuffixInterfaceRule

Interface must be suffixed with "Interface" exclusively

interface SomeClass
{
}


interface SomeInterface
{
}

👍


SuffixTraitRule

Trait must be suffixed by "Trait" exclusively

trait SomeClass
{
}


trait SomeTrait
{
}

👍


TooDeepNewClassNestingRule

new is limited to %d "new (new ))" nesting to each other. You have %d nesting.

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\TooDeepNewClassNestingRule
        tags: [phpstan.rules.rule]
        arguments:
            maxNewClassNesting: 2

$someObject = new A(new B(new C()));


$firstObject = new B(new C());
$someObject = new A($firstObject);

👍


TooLongClassLikeRule

%s has %d lines, it is too long. Shorted it under %d lines

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\ObjectCalisthenics\Rules\TooLongClassLikeRule
        tags: [phpstan.rules.rule]
        arguments:
            maxClassLikeLength: 3

class SomeClass
{
    public function someMethod()
    {
        if (...) {
            return 1;
        } else {
            return 2;
        }
    }
}


class SomeClass
{
    public function someMethod()
    {
        return (...) ? 1 : 2;
    }
}

👍


TooLongFunctionLikeRule

%s has %d lines, it is too long. Shorted it under %d lines

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\ObjectCalisthenics\Rules\TooLongFunctionLikeRule
        tags: [phpstan.rules.rule]
        arguments:
            maxFunctionLikeLength: 3

function some()
{
    if (...) {
        return 1;
    } else {
        return 2;
    }
}


function some()
{
    return (...) ? 1 : 2;
}

👍


TooLongVariableRule

Variable "$%s" is too long with %d chars. Narrow it under %d chars

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\Rules\TooLongVariableRule
        tags: [phpstan.rules.rule]
        arguments:
            maxVariableLength: 10

class SomeClass
{
    public function run()
    {
        return $superLongVariableName;
    }
}


class SomeClass
{
    public function run()
    {
        return $shortName;
    }
}

👍


TooManyMethodsRule

Method has too many methods %d. Try narrowing it down under %d

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\ObjectCalisthenics\Rules\TooManyMethodsRule
        tags: [phpstan.rules.rule]
        arguments:
            maxMethodCount: 1

class SomeClass
{
    public function firstMethod()
    {
    }

    public function secondMethod()
    {
    }
}


class SomeClass
{
    public function firstMethod()
    {
    }
}

👍


TooManyPropertiesRule

Class has too many properties %d. Try narrowing it down under %d

🔧 configure it!

services:
    -
        class: Symplify\PHPStanRules\ObjectCalisthenics\Rules\TooManyPropertiesRule
        tags: [phpstan.rules.rule]
        arguments:
            maxPropertyCount: 2

class SomeClass
{
    private $some;

    private $another;

    private $third;
}


class SomeClass
{
    private $some;

    private $another;
}

👍


UppercaseConstantRule

Constant "%s" must be uppercase

final class SomeClass
{
    public const some = 'value';
}


final class SomeClass
{
    public const SOME = 'value';
}

👍


ValidNetteInjectRule

Nette @inject annotation/#[Inject] must be valid

class SomeClass
{
    /**
     * @inject
     */
    private $someDependency;
}


class SomeClass
{
    /**
     * @inject
     */
    public $someDependency;
}

👍