phpstan/phpstan-symfony
PHPStan extension for Symfony that improves static analysis with precise return types and framework-specific rules. Understands container/services, parameters, controllers, request/headers, serializer, forms, messenger handlers, cache callbacks, config tree builders, and more.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require --dev phpstan/phpstan-symfony
Use phpstan/extension-installer for automatic configuration or manually include:
includes:
- vendor/phpstan/phpstan-symfony/extension.neon
- vendor/phpstan/phpstan-symfony/rules.neon
Configure Container Path:
Add your Symfony container XML path to phpstan.neon:
parameters:
symfony:
containerXmlPath: var/cache/dev/App_KernelDevDebugContainer.xml
First Use Case: Run PHPStan on a Symfony controller:
vendor/bin/phpstan analyse src/Controller/
The extension will now provide accurate return types for ContainerInterface::get(), Request::getContent(), etc.
Pattern: Use ContainerInterface::get() with precise return types.
$userRepository = $this->container->get(UserRepositoryInterface::class);
// PHPStan now knows $userRepository is UserRepositoryInterface
Workflow:
services.yaml with proper types.Pattern: Configure HandleTrait wrappers for Query Buses.
parameters:
symfony:
messenger:
handleTraitWrappers:
- App\Bus\QueryBus::dispatch
class QueryBus {
use HandleTrait;
public function dispatch(object $query): mixed { /* ... */ }
}
// PHPStan infers return types for $queryBus->dispatch(new GetProductQuery())
Workflow:
#[AsMessageHandler].handleTraitWrappers in phpstan.neon.dispatch() methods with type-safe return values.Pattern: Enable console command argument/option type checking.
parameters:
symfony:
consoleApplicationLoader: tests/ConsoleApplication.php
// tests/ConsoleApplication.php
use App\Kernel;
use Symfony\Bundle\FrameworkBundle\Console\Application;
return new Application(new Kernel('test', true));
Workflow:
console-application.php file.consoleApplicationLoader in phpstan.neon.$input->getArgument() and $input->getOption().Pattern: Leverage type hints for FormInterface::getErrors() and SerializerInterface::deserialize().
$form = $this->createForm(UserType::class);
$errors = $form->getErrors(true, false); // PHPStan knows return type is FormErrorIterator
Workflow:
Pattern: Detect unregistered or private services.
$this->container->get('unregistered_service'); // Error: Service not found
$this->container->get('private_service'); // Error: Service is private
Workflow:
containerXmlPath leads to no type resolution.
var/cache/dev/App_KernelDevDebugContainer.xmlvar/cache/dev/srcApp_KernelDevDebugContainer.xmlbin/console debug:container --dump to regenerate the file.::has() methods may cause false positives for optional dependencies.
if ($this->container->has('optional_service')) { // Always true/false
// ...
}
constantHassers in phpstan.neon:
parameters:
symfony:
constantHassers: false
try-catch with get() instead of has() for optional services.mixed return types.
HandleTrait wrappers:
parameters:
symfony:
messenger:
handleTraitWrappers:
- App\Bus\CommandBus::handle
- App\Bus\QueryBus::dispatch
consoleApplicationLoader.
# config/packages/phpstan_env/parameters.yaml
parameters:
container.dumper.inline_class_loader: false
phpstan_env kernel for console analysis.vendor/bin/phpstan analyse src/ --exclude tests/
scanDirectories includes cache paths for PHP config files (Symfony 5.3+):
parameters:
scanDirectories:
- var/cache/dev/Symfony/Config
phpstan diagnose to identify missing stubs.InputBag::get()).
level in phpstan.neon for specific rules:
rules:
Symfony\Container\ContainerInterface:
level: 3
// @phpstan-ignore-next-line sparingly for known edge cases.phpstan/extension-installer for automatic updates:
composer require --dev phpstan/extension-installer
composer.json for stability:
"extra": {
"phpstan-extensions": ["phpstan/phpstan-symfony:^2.0"]
}
--strict flag:
vendor/bin/phpstan analyse --strict
phpstan extension with phpstan.neon path.
---
How can I help you explore Laravel packages today?