Extension for PHPStan is providing several services and rules to help find bugs in your applications.
To use this extension, require it in Composer:
composer require --dev efabrica/phpstan-rules
This extension adds several rules. You can use them all by including these files in your phpstan.neon
:
includes:
- vendor/efabrica/phpstan-rules/extension.neon
- vendor/efabrica/phpstan-rules/rules.neon
Or include just:
includes:
- vendor/efabrica/phpstan-rules/extension.neon
and pick rules you want to use
Finds all calls of GuzzleHttp\Client methods without some option e.g. timeout, connect_timeout
services:
-
factory: Efabrica\PHPStanRules\Rule\Guzzle\ClientCallWithoutOptionRule(['timeout', 'connect_timeout'])
tags:
- phpstan.rules.rule
use GuzzleHttp\Client;
$guzzleClient = new Client();
$guzzleClient->request('GET', 'https://example.com/api/url');
β
use GuzzleHttp\Client;
$guzzleClient = new Client();
$guzzleClient->request('GET', 'https://example.com/api/url', ['timeout' => 3, 'connect_timeout' => 1]);
π
Checks if names of all input parameters. Every name has to contain only alphanumeric characters and _
services:
-
factory: Efabrica\PHPStanRules\Rule\Tomaj\NetteApi\InputParamNameRule
tags:
- phpstan.rules.rule
use Tomaj\NetteApi\Handlers\BaseHandler;
final class SomeHandler extends BaseHandler
{
public function params(): array
{
return [
new GetInputParam('my-name')
];
}
}
β
use Tomaj\NetteApi\Handlers\BaseHandler;
final class SomeHandler extends BaseHandler
{
public function params(): array
{
return [
new GetInputParam('my_name')
];
}
}
π
Checks if traits are used only in context of classes specified in them via comment @context {Type}
services:
-
factory: Efabrica\PHPStanRules\Rule\General\TraitContextRule
tags:
- phpstan.rules.rule
/**
* @context MyInterface
*/
trait MyTrait
{
}
final class SomeClass
{
use MyTrait;
}
β
/**
* @context MyInterface
*/
trait MyTrait
{
}
final class SomeClass implements MyInterface
{
use MyTrait;
}
π
Checks if some method is not used in disabled context - specific method of object.
parameters:
disabledMethodCalls:
-
context: 'WithCallInterface::checkedMethod'
disabled: 'ClassWithDisabledMethod::disabledMethod'
services:
-
factory: Efabrica\PHPStanRules\Rule\General\DisableMethodCallInContextRule(%disabledMethodCalls%)
tags:
- phpstan.rules.rule
class ClassWithDisabledMethod implements WithDisabledMethodInterface
{
public function disabledMethod() {} // this method shouldn't be called in WithCallInterface::checkedMethod
}
final class SomeClass implements WithCallInterface
{
public function checkedMethod(): array
{
return [(new ClassWithDisabledMethod)->disabledMethod()]
}
}
β
final class SomeClass implements WithCallInterface
{
public function checkedMethod(): array
{
return [(new ClassWithDisabledMethod)]
}
}
π
Checks if some method is called with all required parameters with corresponding types.
parameters:
requiredParametersInMethodCalls:
-
context: 'SomeClass::someMethod'
parameters:
-
name: someParameter
type: string
tip: 'Always use parameter someParameter as string because...'
services:
-
factory: Efabrica\PHPStanRules\Rule\General\RequiredParametersInMethodCallRule(%requiredParametersInMethodCalls%)
tags:
- phpstan.rules.rule
class SomeClass
{
public function someMethod(?string $someParameter = null): void
{
// this method should be called with string value of $someParameter
}
}
class Foo
{
public function bar(SomeClass $someClass)
{
$someClass->someMethod();
}
}
β
class Foo
{
public function bar(SomeClass $someClass)
{
$someClass->someMethod('baz');
}
}
π
Every language has its own word order in sentences, we can't use e.g. variables at the same place for all languages. There are mechanisms in translate libraries e.g. symfony/translator - we can use placeholders like %name% etc. This rule checks if you use translated messages and then concat them with some other strings.
parameters:
translateCalls:
- iAmTranslateFunction
- Efabrica\PHPStanRules\Tests\Rule\General\DisabledConcatenationWithTranslatedStringsRule\Source\TranslatorInterface::iAmTranslateMethod
- Efabrica\PHPStanRules\Tests\Rule\General\DisabledConcatenationWithTranslatedStringsRule\Source\TranslatorInterface::iAmTranslateStaticMethod
allowedTranslateConcatenationPatterns:
- '[\s]*<.*?>[\s]*<\/.*?>[\s]*'
- '[\s]*This is allowed text[\s]*'
- '[\s]*\#[0-9]+[\s]*'
services:
-
factory: Efabrica\PHPStanRules\Rule\General\DisabledConcatenationWithTranslatedStringsRule(%translateCalls%)
tags:
- phpstan.rules.rule
$message = 'Hello';
$name = 'Mark';
echo $translator->iAmTranslateMethod($message) . ' ' . $name;
β
$message = 'Hello %name%';
$name = 'Mark';
echo $translator->iAmTranslateMethod($message, ['name' => $name];
π
This rule checks if constructor contains forbidden parameter types.
parameters:
forbiddenConstructorParametersTypes:
-
context: 'SomeClass'
forbiddenTypes:
-
type: ForbiddenType
tip: 'ForbiddenType is not allowed, use NotForbiddenType instead'
services:
-
factory: Efabrica\PHPStanRules\Rule\General\ForbiddenConstructorParametersTypesRule(%forbiddenConstructorParametersTypes%)
tags:
- phpstan.rules.rule
class SomeClass
{
}
class ForbiddenType
{
}
class NotForbiddenType
{
}
class Foo extends SomeClass
{
public function __construct(ForbiddenType $type)
{
}
}
β
class Foo extends SomeClass
{
public function __construct(NotForbiddenType $type)
{
}
}
π
Some functions are not recommended to be called in loops. For example array_merge.
services:
-
factory: Efabrica\PHPStanRules\Rule\Performance\DisabledCallsInLoopsRule
tags:
- phpstan.rules.rule
$result = [];
for ($i = 0; $i < 100; $i++) {
$result = array_merge($result, $data[$i]);
}
β
$result = array_merge([], ...$data);
π