efabrica / phpstan-rules
Requires
- php: >=7.4 <8.5
- ext-json: *
- phpstan/phpstan: ^1.8
Requires (Dev)
- efabrica/php-extensions-finder: ^0.5
- guzzlehttp/guzzle: ^7.3
- nette/di: ^2.3.0 || ^3.0.0
- nikic/php-parser: ^4.14
- phpunit/phpunit: ^9.5
- tomaj/nette-api: ^3.0.0
- tracy/tracy: *
- dev-main
- 0.8.1
- 0.8.0
- 0.7.6
- 0.7.5
- 0.7.4
- 0.7.3
- 0.7.2
- 0.7.1
- 0.7.0
- 0.6.0
- 0.5.0
- 0.4.2
- 0.4.1
- 0.4.0
- 0.3.0
- 0.2.0
- 0.1.0
- dev-add-expression-to-calls-in-conditions
- dev-do-not-use-count-in-condition
- dev-popelis/arrow
- dev-slow_calls_in_conditions
- dev-nette-database-selection-fetch-with-limit
- dev-vrana/composer-update
This package is auto-updated.
Last update: 2026-04-08 21:40:22 UTC
README
Extension for PHPStan is providing several services and rules to help find bugs in your applications.
Installation
To use this extension, require it in Composer:
composer require --dev efabrica/phpstan-rules
Setup
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
Guzzle - ClientCallWithoutOptionRule
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]);
👍
Tomaj/Nette API - InputParamNameRule
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') ]; } }
👍
Check trait context - TraitContextRule
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; }
👍
Check calling method in object method
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)] } }
👍
Check calling method with required parameters
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'); } }
👍
Do not concatenate translated strings
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];
👍
Forbidden constructor parameters types
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) { } }
👍
Enforce arrow function
Checks closures and reports those that only contain a single return expression, because they can be replaced with fn.
This rule is optional and is not enabled by default in rules.neon.
services: - factory: Efabrica\PHPStanRules\Rule\General\EnforceArrowFunctionRule tags: - phpstan.rules.rule
$doubled = array_map(function (int $number): int { return $number * 2; }, $numbers);
❌
$doubled = array_map(fn (int $number): int => $number * 2, $numbers);
👍
Performance - DisabledCallsInLoopsRule
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);
👍
Performance - UseNetteDatabaseSelectionFetchTogetherWithLimitRule
Nette\Database\Table\Selection::fetch() should be used with limit(1) to avoid loading more rows than needed.
services: - factory: Efabrica\PHPStanRules\Rule\Performance\UseNetteDatabaseSelectionFetchTogetherWithLimitRule tags: - phpstan.rules.rule
return $selection->where(['category_id' => 1])->fetch();
❌
return $selection->where(['category_id' => 1])->limit(1)->fetch();
👍
Check calls in conditions
This rule checks if there are some (slow) calls (function call, method call, static method call) in if conditions before other expressions. There can be set list of slow calls in parameters. If not set, all calls are considered slower than other expressions.
parameters: conditionSlowCalls: - 'file_*' # all functions starting with file_ - 'Foo\Bar\Baz->foo' # method foo called on object of type Foo\Bar\Baz - 'Foo\Bar\Baz::bar' # static method bar from Foo\Bar\Baz
class SomeClass { public function doSomething(bool $someOption): void { if (file_exists($someFile) && $someOption) { // do something } } }
❌
class SomeClass { public function doSomething(bool $someOption): void { if ($someOption && file_exists($someFile)) { // do something } } }
👍
Performance - UseArrayComparisonInsteadOfCountInConditionRule
This rule checks if count($array) is used in if / elseif conditions for empty/non-empty checks and recommends array comparison with [].
services: - factory: Efabrica\PHPStanRules\Rule\Performance\UseArrayComparisonInsteadOfCountInConditionRule tags: - phpstan.rules.rule
if (count($items) > 0) { // ... }
❌
if ($items !== []) { // ... }
👍
Nette DI - PresenterInjectedPropertiesExtension
Will not report uninitialized properties with @Inject or #[Inject] attribute.
services: - class: Efabrica\PHPStanRules\Rule\Nette\PresenterInjectedPropertiesExtension tags: - phpstan.properties.readWriteExtension
class InjectPresenter { /** @var SomeInjectedClass @inject */ public SomeInjectedClass $someService; ... #[Inject] public OtherInjectedClass $otherService; ...