iwf-web / phpstan-rules
Custom rules for PHPStan for applications developed by IWF
Requires
- php: >=8.3
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.95
- iwf-web/php-coding-standard: ^1.0
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^12.0
- rector/rector: ^2.4
- symfony/messenger: ^7.0|^8.0
- symfony/routing: ^7.0|^8.0
Suggests
- phpstan/phpstan: Required to use the bundled PHPStan rules (^2.1)
This package is auto-updated.
Last update: 2026-04-20 07:59:48 UTC
README
Custom PHPStan rules used across IWF projects to enforce coding standards, security practices, and architectural conventions.
Requirements
- PHP 8.3 or higher
- PHPStan ^2.1
Installation
composer require --dev iwf-web/phpstan-rules
Usage
Include the rule set in your phpstan.neon or phpstan.neon.dist:
includes: - vendor/iwf-web/phpstan-rules/rules.neon
Configuration
Several rules require or accept configuration parameters under the iwf key.
Controller rules
parameters: iwfWeb: controller: controllerNamespace: 'App\Controller' excludedNamespaces: [] excludedControllers: - 'App\Controller\Api\Security\LoginController'
Required use aliases
Enforce that specific namespaces are always imported with a defined alias:
parameters: iwfWeb: requiredUseAlias: aliasDefinitions: - { namespace: 'Doctrine\ORM\Mapping', alias: 'ORM' } - { namespace: 'Symfony\Component\Validator\Constraints', alias: 'Assert' }
Attribute requirements
Enforce that certain attributes may only appear alongside required companion attributes:
parameters: iwfWeb: attributeRequirements: attributeDefinitions: - attribute: 'Symfony\Component\Routing\Attribute\Route' requires: - 'OpenApi\Attributes\Tag' - 'Symfony\Component\Security\Http\Attribute\IsGranted'
Force DateProvider (requires coala/date-provider-bundle)
parameters: iwfWeb: forceDateProvider: allowedFormats: - 'Y-m-d' - 'Y-m-d H:i:s' - 'Y-m-d\TH:i:s' - 'Y-m-d\TH:i:sP' - 'U'
Handle-bus traits (requires coala/messenger-bundle)
parameters: iwfWeb: handleBusTrait: handleBusTraitMappings: queryBus: 'Coala\MessengerBundle\Messenger\HandleQueryBusTrait' handleBusTraitNamespaces: - 'App\Controller'
Require invalid-data-test group (requires coala/testing-bundle)
parameters: iwfWeb: requireInvalidDataTestGroup: requireInvalidDataTestGroupNamespaces: - 'App\Tests'
Rules
Common
iwfWeb.mbFunctionUsageRule — Multibyte function usage
Flags calls to string functions that have a multibyte-safe counterpart and may produce incorrect results when the input contains multibyte characters (e.g. UTF-8).
Affected functions: chr, ord, parse_str, str_pad, str_split, stripos, stristr, strlen, strpos, strrchr, strripos, strrpos, strstr, strtolower, strtoupper, substr, substr_count.
// ❌ flagged $len = strlen($userInput); // ✅ correct $len = mb_strlen($userInput);
iwfWeb.noAnnotationAsAttribute — No legacy Symfony annotation namespaces
Prevents using classes from the legacy Symfony\...\Annotation\ namespace as PHP 8 attributes. Symfony has migrated all annotations to Symfony\...\Attribute\.
// ❌ flagged #[Symfony\Component\Routing\Annotation\Route('/foo')] // ✅ correct #[Symfony\Component\Routing\Attribute\Route('/foo')]
iwfWeb.requiredUseAlias — Required import aliases
Enforces that configured namespaces are always imported under a specific alias. Applies to both regular use statements and group use statements.
// ❌ flagged — missing alias use Doctrine\ORM\Mapping; // ✅ correct use Doctrine\ORM\Mapping as ORM;
iwfWeb.attributeRequirements — Attribute companion requirements
Ensures that when a trigger attribute is present on a method, all configured companion attributes are also present.
// ❌ flagged — #[Route] without #[IsGranted] #[Route('/admin/users')] public function list(): object { ... } // ✅ correct #[Route('/admin/users')] #[IsGranted('ROLE_ADMIN')] public function list(): object { ... }
Controller
iwfWeb.controllerHandleReturnType — Controller handle() return type
In controllers that use Symfony's HandleTrait, actions returning $this->handle(...) must declare their return type as object (or mixed). A more specific type causes a TypeError when the message bus returns an unexpected response such as an ErrorResponse.
// ❌ flagged — too specific, will TypeError on error responses public function __invoke(Request $request): RecordsResponse { return $this->handle(new GetRecordsQuery()); } // ✅ correct public function __invoke(Request $request): object { return $this->handle(new GetRecordsQuery()); }
iwfWeb.controllerMissingIsGranted — Controller missing #[IsGranted]
Every public controller method carrying a #[Route] attribute must also carry a #[IsGranted] attribute — either on the method itself or on the class. Excludes abstract classes and any configured namespaces or class names.
// ❌ flagged #[Route('/api/users')] public function index(): object { ... } // ✅ correct #[Route('/api/users')] #[IsGranted('ROLE_USER')] public function index(): object { ... }
Coala — DateProvider
These rules are only active when
Coala\DateProviderBundleis present in the project.
iwfWeb.forceDateProviderNew / iwfWeb.forceDateProviderFuncCall / iwfWeb.forceDateProviderStaticCall
Disallows creating DateTime/DateTimeImmutable without an absolute date string argument, calling time-sensitive functions (time(), date(), etc.), or using static factory methods that produce the current time. Enforces the use of DateProviderInterface instead, which enables deterministic time in tests.
// ❌ flagged $now = new DateTimeImmutable(); $ts = time(); // ✅ correct $now = $this->dateProvider->now();
Coala — Messenger
This rule is only active when
Coala\MessengerBundleis present in the project.
iwfWeb.useHandleBusTrait
In configured namespaces, if a class defines a setter method that corresponds to a configured handle-bus trait (e.g. setQueryBus()), it must use the matching trait instead of defining the setter manually.
Coala — Testing
This rule is only active when
Coala\TestingBundleis present in the project.
iwfWeb.requireInvalidDataTestGroup
Test methods that call assertFailingValidation() must carry #[Group('invalid-data-test')]. This allows the test suite to run invalid-data tests in isolation.
// ❌ flagged public function testInvalidEmail(): void { $this->assertFailingValidation(...); } // ✅ correct #[Group('invalid-data-test')] public function testInvalidEmail(): void { $this->assertFailingValidation(...); }
Development
Prerequisites
- Docker with Compose
Bootstrap
After cloning the repository, run the install script to set up the vendor directory and extract PHPStan source files for IDE indexing:
bin/install.sh
This is equivalent to running composer install — the PHPStan extraction is triggered automatically as a post-install hook and requires no additional IDE configuration.
Running tests
bin/test.sh
Runs PHPStan and PHPUnit against all configured Docker services sequentially.
To target a specific PHP version:
bin/test.sh 8.3
Linting
bin/lint.sh
Runs PHP CS Fixer and applies fixes in place. To check without modifying files (as CI does), run:
composer lint:check
Running Composer commands
bin/composer.sh <args>
Runs Composer inside the default Docker container. Examples:
bin/composer.sh install bin/composer.sh require --dev some/package
Debugging with Xdebug
Xdebug is included in the local Docker images and configured with start_with_request=trigger. To activate it, set the XDEBUG_TRIGGER environment variable:
XDEBUG_TRIGGER=1 bin/test.sh
Or configure your IDE to listen on port 9003 and set XDEBUG_TRIGGER=1 in the Docker run environment.
Contributing
Please read CONTRIBUTING.md for details on our code of conduct and the process for submitting pull requests.
This project uses Conventional Commits for automated releases and changelog generation.
Versioning
We use SemVer for versioning. For available versions, see the tags on this repository.
Authors
Special thanks for all the people who had helped this project so far
- Oliver - Oliver-Zieschang
See also the full list of contributors who participated in this project.
I would like to join this list. How can I help the project?
We're currently looking for contributions for the following:
- Bug fixes
- Translations
- etc...
For more information, please refer to our CONTRIBUTING.md guide.
License
This project is licensed under the MIT License - see the LICENSE.txt file for details.
Acknowledgments
This project currently uses no third-party libraries or copied code.