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.
Installation:
composer require --dev phpstan/phpstan-symfony
Use phpstan/extension-installer for automatic inclusion, or manually add to phpstan.neon:
includes:
- vendor/phpstan/phpstan-symfony/extension.neon
- vendor/phpstan/phpstan-symfony/rules.neon
Configure Container XML Path:
Add this to your phpstan.neon (adjust path for Symfony 4/5/6):
parameters:
symfony:
containerXmlPath: var/cache/dev/App_KernelDevDebugContainer.xml
First Use Case: Run PHPStan on your Symfony project:
vendor/bin/phpstan analyse src --level=max
The extension will now provide accurate type hints for Symfony services, controllers, and dependencies.
Automatic Type Inference:
Replace get() calls with precise return types:
$mailer = $container->get(MailerInterface::class); // No `mixed` return
The extension resolves service IDs to their actual classes.
Service Existence Checks:
Use has() with confidence:
if ($container->has('app.mailer')) { // Type-safe condition
$mailer = $container->get('app.mailer');
}
Constructor Injection: PHPStan validates autowired services:
public function __construct(
private MailerInterface $mailer, // Validated as `MailerInterface`
private LoggerInterface $logger // Validated as `LoggerInterface`
) {}
Method Injection:
Use @required or #[Required] attributes for mandatory services:
#[Required] private EntityManagerInterface $em;
HandleTrait wrappers in phpstan.neon:
parameters:
symfony:
messenger:
handleTraitWrappers:
- App\Bus\QueryBus::dispatch
Now dispatch() returns the handler’s result type:
$product = $queryBus->dispatch(new GetProductQuery()); // Returns `Product`
parameters:
symfony:
consoleApplicationLoader: tests/ConsoleApplication.php
PHPStan validates argument/option types:
$name = $input->getArgument('name'); // Type-checked as `string`
FormInterface::getErrors() returns typed FormErrorInterface[]:
$errors = $form->getErrors(true, false); // Returns `FormErrorInterface[]`
deserialize() resolves return types from $type:
$user = $serializer->deserialize($data, User::class, 'json');
$builder->root('app', null)
->children()
->scalarNode('timeout')->defaultValue(30)->end()
->end();
CacheInterface::get() infers callback return types.Container XML Path:
containerXmlPath causes no service type resolution.App_KernelDevDebugContainer.xml for Symfony 5+).php bin/console debug:container --parameters to confirm the correct path.Constant Hassers:
has() methods may incorrectly resolve to true/false for optional dependencies.parameters:
symfony:
constantHassers: false
Console Application Loader:
container.dumper.inline_class_loader is true.config/packages/phpstan_env/parameters.yaml:
parameters:
container.dumper.inline_class_loader: false
Messenger HandleTrait:
mixed return types.handleTraitWrappers.Private Services:
private app.mailer) triggers warnings.allowPrivateServices: true (not recommended) or refactor to public services.Dynamic Stub Loading:
TraceableMessageBus) may lack stubs.stubs/.--verbose to see which extensions are loaded.--generate-report=html to identify untyped Symfony components.phpstan.neon to isolate configuration problems.container.xml only when needed (since v2.0.17), reducing startup time.parameters:
scanDirectories:
- var/cache/dev/Symfony/Config
SymfonyExtension to add project-specific checks (e.g., validating custom service aliases).stubs/ for unsupported Symfony versions or custom bundles.handleTraitWrappers for non-standard query buses.?ServiceInterface for optional services:
public function __construct(?MailerInterface $mailer = null) {}
@var:
/** @var mixed */
$service = $container->get('legacy_service');
--level=5 and incrementally raise to max to avoid overwhelming feedback.php bin/console cache:clear --env=test --no-warmup
```markdown
## Example Workflow
1. **Daily Use**:
```bash
vendor/bin/phpstan analyse src --level=7 --memory-limit=1G
Fix type errors in controllers/services, then commit.
Pre-Merge:
Add --level=max to catch edge cases before PRs.
Onboarding:
Run vendor/bin/phpstan analyse --generate-report=html to identify high-priority fixes for new devs.
How can I help you explore Laravel packages today?