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#';
}
👍
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;
}
}
👍
Class was not found
#[SomeAttribute(firstName: 'MissingClass::class')]
class SomeClass
{
}
❌
#[SomeAttribute(firstName: ExistingClass::class)]
class SomeClass
{
}
👍
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
{
}
👍
"*Test.php" file cannot be located outside "Tests" namespace
// file: "SomeTest.php
namespace App;
class SomeTest
{
}
❌
// file: "SomeTest.php
namespace App\Tests;
class SomeTest
{
}
// file: "AnotherTest.php
namespace App\Tests\Features;
class AnotherTest
{
}
// file: "SomeOtherTest.php
namespace Tests\Features;
class SomeOtherTest
{
}
👍
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
{
}
👍
sprintf()
call mask type at index [%d] expects type "%s", but "%s" given
echo sprintf('My name is %s and I have %d children', 10, 'Tomas');
❌
echo sprintf('My name is %s and I have %d children', 'Tomas', 10);
👍
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
{
}
👍
Constants "%s" should be extract to standalone enum class
🔧 configure it!
services:
-
class: Symplify\PHPStanRules\Rules\Enum\EmbeddedEnumClassConstSpotterRule
tags: [phpstan.rules.rule]
arguments:
parentTypes:
- AbstractObject
↓
class SomeProduct extends AbstractObject
{
public const STATUS_ENABLED = 1;
public const STATUS_DISABLED = 0;
}
❌
class SomeProduct extends AbstractObject
{
}
class SomeStatus
{
public const ENABLED = 1;
public const DISABLED = 0;
}
👍
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
{
}
👍
The "%s" class always calls "%s()"
setters, better move it to constructor
$firstPerson = new Person();
$firstPerson->setName('John');
$secondPerson = new Person();
$secondPerson->setName('Van');
❌
$firstPerson = new Person('John');
$secondPerson = new Person('Van');
👍
Anonymous class is not allowed.
new class {};
❌
class SomeClass
{
}
new SomeClass;
👍
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();
}
}
👍
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);
};
👍
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');
}
}
👍
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
{
}
👍
Named arguments do not add any value here. Use normal arguments in the same order
return strlen(string: 'name');
❌
return strlen('name');
👍
"%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);
👍
Spread operator is not allowed.
$args = [$firstValue, $secondValue];
$message = sprintf('%s', ...$args);
❌
$message = sprintf('%s', $firstValue, $secondValue);
👍
"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
{
}
👍
$this
as argument is not allowed. Refactor method to service composition
$this->someService->process($this, ...);
❌
$this->someService->process($value, ...);
👍
If/else construction can be replace with more robust match()
class SomeClass
{
public function spot($value)
{
if ($value === 100) {
$items = ['yes'];
} else {
$items = ['no'];
}
return $items;
}
}
❌
class SomeClass
{
public function spot($value)
{
return match($value) {
100 => ['yes'],
default => ['no'],
};
}
}
👍
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 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();
}
👍
Instead of abstract class, use specific service with composition
final class NormalHelper extends AbstractHelper
{
}
abstract class AbstractHelper
{
}
❌
final class NormalHelper
{
public function __construct(
private SpecificHelper $specificHelper
) {
}
}
final class SpecificHelper
{
}
👍
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();
}
}
👍
Do not use constructor in tests. Move to setUp()
method
final class SomeTest
{
public function __construct()
{
// ...
}
}
❌
final class SomeTest
{
public function setUp()
{
// ...
}
}
👍
This object is used in a Symfony form, that uses magic setters/getters, so it cannot have required constructor
final class Ticket
{
public function __construct(private int $price)
{
}
}
---
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use App\Entity\Ticket;
final class TicketFormType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Ticket::class,
]);
}
}
❌
final class Ticket
{
private ?int $price = null;
public function setPrice(int $price): void
{
$this->price = $price;
}
public function getPrice(): ?int
{
return $this->price;
}
}
---
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use App\Entity\Ticket;
final class TicketFormType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Ticket::class,
]);
}
}
👍
Use custom exceptions instead of native "%s"
throw new RuntimeException('...');
❌
use App\Exception\FileNotFoundException;
throw new FileNotFoundException('...');
👍
The "%s" constant contains duplicated regex "%s". Instead of duplicated regexes, extract domain regexes together to save maintenance
class SomeClass
{
private const CLASS_NAME_REGEX = '#[\w\\]+#';
}
class AnotherClass
{
private const DIFFERENT_NAME_REGEX = '#[\w\\]+#';
}
❌
class ClassRegexRecipies
{
private const NAME_REGEX = '#[\w\\]+#';
}
👍
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
{
}
👍
Method name "%s()"
is used in multiple traits. Make it unique to avoid conflicts
trait FirstTrait
{
public function run()
{
}
}
trait SecondTrait
{
public function run()
{
}
}
❌
trait FirstTrait
{
public function run()
{
}
}
trait SecondTrait
{
public function fly()
{
}
}
👍
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()
{
}
}
👍
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);
}
// ...
}
👍
There are 2 way to get "%s" value: public property and getter now - pick one to avoid variant behavior.
final class SomeProduct
{
public $name;
public function getName(): string
{
return $this->name;
}
}
❌
final class SomeProduct
{
private $name;
public function getName(): string
{
return $this->name;
}
}
👍
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';
👍
Make callable type explicit. Here is how: https://phpstan.org/writing-php-code/phpdoc-types#callables
function run(callable $callable)
{
return $callable(100);
}
❌
/**
* @param callable(): int $callable
*/
function run(callable $callable): int
{
return $callable(100);
}
👍
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 separate function calls with readable variable names
$filteredValues = array_filter(array_map($callback, $items));
❌
$mappedItems = array_map($callback, $items);
$filteredValues = array_filter($mappedItems);
👍
Use required typed property over of nullable array property
final class SomeClass
{
private ?array $property = null;
}
❌
final class SomeClass
{
private array $property = [];
}
👍
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
{
}
👍
Instead of protected element, use private element or contract method
final class SomeClass
{
protected function run()
{
}
}
❌
final class SomeClass
{
private function run()
{
}
}
👍
Class cannot have public properties. Use getter/setters instead
🔧 configure it!
services:
-
class: Symplify\PHPStanRules\Rules\Privatization\NoPublicPropertyByTypeRule
tags: [phpstan.rules.rule]
arguments:
classTypes:
- Entity
↓
final class Person extends Entity
{
public $name;
}
❌
final class Person extends Entity
{
private $name;
public function getName()
{
return $this->name;
}
}
👍
Use explicit return value over magic &reference
class SomeClass
{
public function run(&$value)
{
}
}
❌
class SomeClass
{
public function run($value)
{
return $value;
}
}
👍
Relative file path "%s" is not allowed, use absolute one with DIR
$filePath = 'some_file.txt';
❌
$filePath = __DIR__ . '/some_file.txt';
👍
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;
}
}
👍
The compare assert arguments are switched. Move the expected value to the 1st left
use PHPUnit\Framework\TestCase;
final class SomeFlippedAssert extends TestCase
{
public function test()
{
$value = 1000;
$this->assertSame($value, 10);
}
}
❌
use PHPUnit\Framework\TestCase;
final class SomeFlippedAssert extends TestCase
{
public function test()
{
$value = 1000;
$this->assertSame(10, $value);
}
}
👍
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()
{
}
👍
Do not use static property
final class SomeClass
{
private static $customFileNames = [];
}
❌
final class SomeClass
{
private $customFileNames = [];
}
👍
Getter method must return something, not void
final class SomeClass
{
public function getData(): void
{
// ...
}
}
❌
final class SomeClass
{
public function getData(): array
{
// ...
}
}
👍
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()
{
}
}
👍
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('...');
}
}
👍
Abstract class name "%s" must be prefixed with "Abstract"
abstract class SomeClass
{
}
❌
abstract class AbstractSomeClass
{
}
👍
Content of method "%s()"
is duplicated. Use unique content or service instead
🔧 configure it!
services:
-
class: Symplify\PHPStanRules\Rules\PreventDuplicateClassMethodRule
tags: [phpstan.rules.rule]
arguments:
minimumLineCount: 3
↓
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();
}
}
}
👍
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
{
}
👍
Property "$%s" is missing @Valid
annotation
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Valid;
final class NullablePropertyFormType extends AbstractType
{
public function configureOptions(OptionsResolver $optionsResolver): void
{
$optionsResolver->setDefaults([
'data_class' => SomeEntity::class,
'constraints' => new Valid(),
]);
}
}
class SomeEntity
{
/**
* @var NestedEntity
*/
private $nestedEntity;
}
❌
use Symfony\Component\Validator\Constraints as Assert;
class SomeEntity
{
/**
* @Assert\Valid
* @var NestedEntity
*/
private $nestedEntity;
}
👍
Parameter argument on position %d must use constant
🔧 configure it!
services:
-
class: Symplify\PHPStanRules\Rules\Enum\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);
}
}
👍
On passing a constant, the method should have an enum type. See https://phpstan.org/writing-php-code/phpdoc-types#literals-and-constants
final class Direction
{
public const LEFT = 'left';
public const RIGHT = 'right';
}
final class Driver
{
public function goToWork()
{
$this->turn(Direction::LEFT);
}
private function turn(string $direction)
{
// ...
}
}
❌
final class Direction
{
public const LEFT = 'left';
public const RIGHT = 'right';
}
final class Driver
{
public function goToWork()
{
$this->turn(Direction::LEFT);
}
/**
* @param Direction::*
*/
private function turn(string $direction)
{
// ...
}
}
👍
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()
{
}
}
👍
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'
]);
}
}
👍
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 name starting with "Abstract" must have an abstract
keyword
class AbstractClass
{
}
❌
abstract class AbstractClass
{
}
👍
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
{
}
👍
Interface must be suffixed with "Interface" exclusively
interface SomeClass
{
}
❌
interface SomeInterface
{
}
👍
Trait must be suffixed by "Trait" exclusively
trait SomeClass
{
}
❌
trait SomeTrait
{
}
👍
Switch construction can be replace with more robust match()
switch ($key) {
case 1:
return 100;
case 2:
return 200;
default:
return 300;
};
❌
return match($key) {
1 => 100,
2 => 200,
default => 300,
};
👍
The callable method [$this, "%s"] was not found
use Twig\Extension\AbstractExtension;
use Twig_SimpleFunction;
final class TwigExtensionWithMissingCallable extends AbstractExtension
{
public function getFunctions()
{
return [
new Twig_SimpleFunction('someFunctionName', [$this, 'someMethod']),
];
}
}
❌
use Twig\Extension\AbstractExtension;
use Twig_SimpleFunction;
final class TwigExtensionWithMissingCallable extends AbstractExtension
{
public function getFunctions()
{
return [
new Twig_SimpleFunction('someFunctionName', [$this, 'someMethod']),
];
}
public function someMethod()
{
// ...
}
}
👍
Constant "%s" must be uppercase
final class SomeClass
{
public const some = 'value';
}
❌
final class SomeClass
{
public const SOME = 'value';
}
👍