# Symfony Plugin Inspections

**Inspections** analyze your code in real-time and highlight potential problems, bugs, or code style issues. They provide warnings and suggestions as you type, helping you catch errors early.

---

## You probably dont want to inject a Controller here

**Feature ID:** `AutowireControllerAsServiceInspection`  
**Date:** 2022-03-19  
**Feature Page:** [You probably dont want to inject a Controller here](https://espend.de/phpstorm/plugin/symfony#autowire-controller-as-service-inspection)  

Reports when a Symfony Controller is injected as a dependency into another service. Controllers are designed to be invoked by the framework via routing, not used as dependencies in other services.

This inspection helps identify incorrect service injection patterns that may lead to architectural issues.

### Code Examples

```php
# Avoid:
public function __construct(private ProductController $controller) {}
```

---

## You probably dont want to inject a "{class}" here

**Feature ID:** `AutowireWrongClassInspection`  
**Date:** 2022-03-19  
**Feature Page:** [You probably dont want to inject a "{class}" here](https://espend.de/phpstorm/plugin/symfony#autowire-wrong-class-inspection)  

Reports constructor parameters that reference classes which cannot be resolved to a valid service in the container. This typically occurs when:

### Code Examples

```php
# Instead of:
public function __construct(private EntityManager $em) {}
```

```php
# Use the interface:
public function __construct(private EntityManagerInterface $em) {}
```

---

## Invokable command __invoke() should have int return type

**Feature ID:** `CommandInvokableReturnTypeInspection`  
**Date:** 2025-12-03  
**Feature Page:** [Invokable command __invoke() should have int return type](https://espend.de/phpstorm/plugin/symfony#command-invokable-return-type-inspection)  

Reports invokable Symfony Commands using `#[AsCommand]` attribute whose `__invoke()` method lacks an `int` return type. Since Symfony 7.3, commands should declare `int` as return type for the exit code (e.g., `Command::SUCCESS`, `Command::FAILURE`).

### Code Examples

```php
# Instead of:
#[AsCommand(name: 'app:my-command')]
class MyCommand extends Command
{
    public function __invoke()
    {
        return Command::SUCCESS;
    }
}
```

```php
# Use:
#[AsCommand(name: 'app:my-command')]
class MyCommand extends Command
{
    public function __invoke(): int
    {
        return Command::SUCCESS;
    }
}
```

---

## Invokable command must return an integer value

**Feature ID:** `CommandInvokableReturnValueInspection`  
**Date:** 2025-12-03  
**Feature Page:** [Invokable command must return an integer value](https://espend.de/phpstorm/plugin/symfony#command-invokable-return-value-inspection)  

Reports invokable Symfony Commands using `#[AsCommand]` attribute whose `__invoke()` method returns non-integer values. Since Symfony 7.3, commands must return an integer exit code (e.g., `Command::SUCCESS`, `Command::FAILURE`).

### Code Examples

```php
# Instead of:
#[AsCommand(name: 'app:my-command')]
class MyCommand extends Command
{
    public function __invoke(): int
    {
        return null; // or return 'success';
    }
}
```

```php
# Use:
#[AsCommand(name: 'app:my-command')]
class MyCommand extends Command
{
    public function __invoke(): int
    {
        return Command::SUCCESS;
        // or: Command::FAILURE
        // or: Command::INVALID
    }
}
```

---

## A RequestStack request must no be used inside a constructor

**Feature ID:** `ConstructorNoRequestStackInspection`  
**Date:** 2022-03-19  
**Feature Page:** [A RequestStack request must no be used inside a constructor](https://espend.de/phpstorm/plugin/symfony#constructor-no-request-stack-inspection)  

Reports when `RequestStack::getCurrentRequest()` is called inside a service constructor. The current Request is not available during service construction, resulting in `null`.

### Code Examples

```php
# Avoid:
public function __construct(private RequestStack $requestStack)
{
    $request = $requestStack->getCurrentRequest(); // Returns null!
}
```

```php
# Instead, inject RequestStack and use it in your methods:
public function someMethod()
{
    $request = $this->requestStack->getCurrentRequest();
}
```

---

## Avoid heavy construction in non lazy Command

**Feature ID:** `ConstructorCommandHeavyConstructorInspection`  
**Date:** 2022-05-31  
**Feature Page:** [Avoid heavy construction in non lazy Command](https://espend.de/phpstorm/plugin/symfony#constructor-command-heavy-constructor-inspection)  

Reports Console Commands with heavy dependencies in their constructor. Commands with heavy services (like database connections) should be marked as lazy to improve container compilation performance.

See [Symfony Documentation](https://symfony.com/doc/current/console/commands_as_services.html#lazy-loading) for more details.

### Code Examples

```php
# Example
// Add 'lazy: true' to make the command lazy-loaded:
#[AsCommand(name: 'app:my-command', lazy: true)]
class MyCommand extends Command
{
    public function __construct(private HeavyService $service)
    {
        parent::__construct();
    }
}
```

---

## A Session must not be used inside a constructor

**Feature ID:** `ConstructorNoSessionInspection`  
**Date:** 2022-03-19  
**Feature Page:** [A Session must not be used inside a constructor](https://espend.de/phpstorm/plugin/symfony#constructor-no-session-inspection)  

Reports when a Session is started or accessed inside a service constructor. The Session may not be available during service construction, similar to the Request.

### Code Examples

```php
# Avoid:
public function __construct(private SessionInterface $session)
{
    $session->set('key', 'value'); // May not work!
}
```

```php
# Use session in your methods instead:
public function someMethod()
{
    $this->session->set('key', 'value');
}
```

---

## A token fetch must not be used inside a constructor

**Feature ID:** `ConstructorNoTokenInspection`  
**Date:** 2022-03-19  
**Feature Page:** [A token fetch must not be used inside a constructor](https://espend.de/phpstorm/plugin/symfony#constructor-no-token-inspection)  

Reports when the security token is fetched via `TokenStorageInterface::getToken()` inside a constructor. The security context is not yet initialized during service construction.

### Code Examples

```php
# Avoid:
public function __construct(private TokenStorageInterface $tokenStorage)
{
    $token = $tokenStorage->getToken(); // Returns null!
    $user = $token->getUser(); // Error!
}
```

---

## A User must not be used inside a constructor

**Feature ID:** `ConstructorNoUserInspection`  
**Date:** 2022-03-19  
**Feature Page:** [A User must not be used inside a constructor](https://espend.de/phpstorm/plugin/symfony#constructor-no-user-inspection)  

Reports when the current User is fetched from the TokenStorage in a constructor. The security context (including User) is not available during service construction.

### Code Examples

```php
# Avoid:
public function __construct(private TokenStorageInterface $tokenStorage)
{
    $user = $tokenStorage->getToken()->getUser(); // Error!
}
```

```php
# Use the security helper or fetch user in your methods:
#[Autowire(service: 'security.helper')]
private SecurityHelper $security;

public function someMethod()
{
    $user = $this->security->getUser();
}
```

---

## Avoid heavy construction in Twig extension

**Feature ID:** `ConstructorTwigExtensionHeavyConstructorInspection`  
**Date:** 2022-03-24  
**Feature Page:** [Avoid heavy construction in Twig extension](https://espend.de/phpstorm/plugin/symfony#constructor-twig-extension-heavy-constructor-inspection)  

Reports Twig extensions with heavy constructor dependencies (like database connections). Twig extensions are instantiated early during container compilation, which can significantly slow down the application if they have complex dependencies.

Consider making your Twig extension lazy-loaded or refactoring to avoid heavy dependencies in the constructor.

See [Symfony Documentation](https://symfony.com/doc/current/templating/twig_extension.html#creating-lazy-loaded-twig-extensions) for more details.

### Code Examples

```php
# Avoid:
class MyExtension extends AbstractExtension
{
    public function __construct(private ProductRepository $repository) {}
}
```

```php
# Instead, use a runtime loader for lazy loading:
class MyExtension extends AbstractExtension
{
    public function getFunctions(): array
    {
        return [
            new TwigFunction('product_count', [ProductRuntime::class, 'getCount']),
        ];
    }
}

class ProductRuntime
{
    public function __construct(private ProductRepository $repository) {}

    public function getCount(): int
    {
        return $this->repository->count([]);
    }
}
```

---

## Constraint array argument deprecated, use named arguments

**Feature ID:** `ConstraintDeprecatedArrayInspection`  
**Date:** 2025-12-01  
**Feature Page:** [Constraint array argument deprecated, use named arguments](https://espend.de/phpstorm/plugin/symfony#constraint-deprecated-array-inspection)  

Reports deprecated usage of arrays as first argument in Symfony Constraint constructors. Symfony deprecated passing constraint options as arrays in favor of named arguments.

### Code Examples

```php
# Instead of:
new Assert\Choice(['choices' => ['a', 'b']]);
new Assert\NotBlank(['message' => 'This value is required']);
```

```php
# Use:
new Assert\Choice(choices: ['a', 'b']);
new Assert\NotBlank(message: 'This value is required');
```

---

## You probably want to inject the parameter "kernel.project_dir"

**Feature ID:** `ProjectDirParameterInspection`  
**Date:** 2022-03-19  
**Feature Page:** [You probably want to inject the parameter "kernel.project_dir"](https://espend.de/phpstorm/plugin/symfony#project-dir-parameter-inspection)  

Reports usage of the deprecated `kernel.root_dir` parameter. Since Symfony 4.2, this parameter is deprecated in favor of `kernel.project_dir`.

### Code Examples

```php
# Instead of:
$dir = $this->getParameter('kernel.root_dir');
```

```php
# Use:
$dir = $this->getParameter('kernel.project_dir');
```

---

## Class does not implement 'Symfony\Component\Form\FormTypeInterface'

**Feature ID:** `FormTypeBuilderInstanceInspection`  
**Date:** 2022-06-04  
**Feature Page:** [Class does not implement 'Symfony\Component\Form\FormTypeInterface'](https://espend.de/phpstorm/plugin/symfony#form-type-builder-instance-inspection)  

Reports form type classes that do not properly implement Symfony's form type interface. Valid form types must extend `AbstractType` or implement `FormTypeInterface`.

### Code Examples

```php
# Example
// Make sure your form type extends AbstractType:
use Symfony\Component\Form\AbstractType;

class ProductType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder->add('name', TextType::class);
    }
}
```

---

## Previous QueryBuilder method call should be removed, because is overwritten here

**Feature ID:** `DoctrineQueryBuilderAdderInspection`  
**Date:** 2022-06-01  
**Feature Page:** [Previous QueryBuilder method call should be removed, because is overwritten here](https://espend.de/phpstorm/plugin/symfony#doctrine-query-builder-adder-inspection)  

Reports when Doctrine QueryBuilder method chaining accidentally overwrites previous conditions. Calling `where()` after `andWhere()` or `orWhere()` will reset the WHERE clause.

### Code Examples

```php
# Incorrect - second where() overwrites andWhere():
$qb->andWhere('x.status = :status')
   ->where('x.active = true'); // Overwrites previous condition!
```

```php
# Correct - use andWhere/orWhere consistently:
$qb->where('x.active = true')
   ->andWhere('x.status = :status');
```

---

## QueryBuilder update()/delete() require alias parameter (Doctrine ORM 3.0)

**Feature ID:** `DoctrineQueryBuilderAliasDeprecationInspection`  
**Date:** 2025-12-02  
**Feature Page:** [QueryBuilder update()/delete() require alias parameter (Doctrine ORM 3.0)](https://espend.de/phpstorm/plugin/symfony#doctrine-query-builder-alias-deprecation-inspection)  

Reports deprecated usage of Doctrine QueryBuilder `update()` and `delete()` methods without providing the alias parameter. In Doctrine ORM 3.0, these methods require the alias parameter to be provided explicitly.

### Code Examples

```php
# Instead of:
$qb->delete('User u')->where('u.id = :user_id');
$qb->update('User u')->set('u.status', ':status');
```

```php
# Use:
$qb->delete('User', 'u')->where('u.id = :user_id');
$qb->update('User', 'u')->set('u.status', ':status');
```

---

## QueryBuilder array arguments deprecated (Doctrine DBAL 2.11+)

**Feature ID:** `DoctrineQueryBuilderArrayDeprecationInspection`  
**Date:** 2025-12-09  
**Feature Page:** [QueryBuilder array arguments deprecated (Doctrine DBAL 2.11+)](https://espend.de/phpstorm/plugin/symfony#doctrine-query-builder-array-deprecation-inspection)  

Reports deprecated array usage in Doctrine DBAL QueryBuilder methods (`select()`, `addSelect()`, `groupBy()`, `addGroupBy()`). In Doctrine DBAL 2.11+, these methods no longer accept arrays and require individual arguments.

### Code Examples

```php
# Instead of:
$qb->select(['u.id', 'p.id']);
$qb->addSelect(['u.name', 'p.name']);
$qb->groupBy(['u.category']);
$qb->addGroupBy(['u.status']);
```

```php
# Use:
$qb->select('u.id', 'p.id');
$qb->addSelect('u.name', 'p.name');
$qb->groupBy('u.category');
$qb->addGroupBy('u.status');
```

---

## Use ON instead of WITH for arbitrary joins

**Feature ID:** `DoctrineQueryBuilderJoinWithToOnInspection`  
**Date:** 2026-01-08  
**Feature Page:** [Use ON instead of WITH for arbitrary joins](https://espend.de/phpstorm/plugin/symfony#doctrine-query-builder-join-with-to-on-inspection)  

Reports deprecated `WITH` usage in Doctrine ORM QueryBuilder join methods and DQL for arbitrary joins. Since Doctrine ORM 3.x, `WITH` should only be used for association joins (defined relations), while `ON` should be used for arbitrary joins (joins with entity class and join condition). The inspection only triggers when the Doctrine ORM version has this deprecation.

### Code Examples

```php
# QueryBuilder - Before (Deprecated):
$qb->join(Partner::class, 'p', Expr\Join::WITH, 'p.id = u.myId');
$qb->leftJoin('App\Entity\Geo', 'g', 'WITH', 'g.id = u.geoId');
```

```php
# QueryBuilder - After (Correct):
$qb->join(Partner::class, 'p', Expr\Join::ON, 'p.id = u.myId');
$qb->leftJoin('App\Entity\Geo', 'g', 'ON', 'g.id = u.geoId');
```

```php
# DQL - Before (Deprecated):
$em->createQuery('SELECT u FROM User u JOIN Banlist b WITH u.email = b.email');
$em->createQuery('SELECT u, p FROM User u JOIN ' . CmsPhonenumber::class . ' p WITH u = p.user');
```

```php
# DQL - After (Correct):
$em->createQuery('SELECT u FROM User u JOIN Banlist b ON u.email = b.email');
$em->createQuery('SELECT u, p FROM User u JOIN ' . CmsPhonenumber::class . ' p ON u = p.user');
```

```php
# Association joins (WITH is still valid):
// No warning - this is an association join (u.orders has a defined relation)
$qb->join('u.orders', 'o', 'WITH', 'o.status = :status');
```

---

## QueryBuilder setParameters() array deprecated (Doctrine ORM 3.0)

**Feature ID:** `DoctrineQueryBuilderSetParametersArrayDeprecationInspection`  
**Date:** 2025-12-02  
**Feature Page:** [QueryBuilder setParameters() array deprecated (Doctrine ORM 3.0)](https://espend.de/phpstorm/plugin/symfony#doctrine-query-builder-set-parameters-array-deprecation-inspection)  

Reports deprecated array usage in Doctrine QueryBuilder `setParameters()` method. In Doctrine ORM 3.0, `setParameters()` only accepts `ArrayCollection` of `Parameter` objects, not plain arrays.

### Code Examples

```php
# Instead of:
$qb->setParameters([
    'user_id1' => 1,
    'user_id2' => 2
]);
```

```php
# Use:
$qb->setParameters(new ArrayCollection([
    new Parameter('user_id1', 1),
    new Parameter('user_id2', 2)
]));
```

---

## Empty Table attribute on entities is useless

**Feature ID:** `DoctrineEmptyTableAttributeInspection`  
**Date:** 2026-01-09  
**Feature Page:** [Empty Table attribute on entities is useless](https://espend.de/phpstorm/plugin/symfony#doctrine-empty-table-attribute-inspection)  

Reports empty `#[ORM\Table]` attributes on Doctrine entities. An empty Table attribute (with no arguments) provides no additional configuration beyond the default table name and can be safely removed. The inspection only reports when an `Entity` attribute is also present on the class.

### Code Examples

```php
# Before:
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ORM\Table]  class Product
{
    // ...
}
```

```php
# After:
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class Product
{
    // ...
}
```

```php
# Table attribute is only useful when you need custom configuration:
#[ORM\Entity]
#[ORM\Table(name: 'products', schema: 'app')]
class Product
{
    // ...
}
```

---

## Request type-hint via Action should be used instead of RequestStack

**Feature ID:** `RequestStackInActionInspection`  
**Date:** 2022-10-20  
**Feature Page:** [Request type-hint via Action should be used instead of RequestStack](https://espend.de/phpstorm/plugin/symfony#request-stack-in-action-inspection)  

In controller actions, suggests injecting `Request` directly instead of `RequestStack` and then calling `getCurrentRequest()`. This is cleaner and more straightforward.

### Code Examples

```php
# Instead of:
public function index(RequestStack $requestStack): Response
{
    $request = $requestStack->getCurrentRequest();
    $id = $request->query->get('id');
}
```

```php
# Use:
public function index(Request $request): Response
{
    $id = $request->query->get('id');
}
```

---

## Reports deprecated dependency injection patterns in service constructors.

**Feature ID:** `ConstructorDeprecatedInjection`  
**Date:** 2021-09-29  
**Feature Page:** [Reports deprecated dependency injection patterns in service constructors.](https://espend.de/phpstorm/plugin/symfony#constructor-deprecated-injection)  

Reports constructor parameters using deprecated classes or interfaces that should no longer be injected directly. Symfony deprecates certain injection patterns in favor of accessing these services through other means for better architecture.

Detected deprecated injections:

### Code Examples

```php
# Example 1 - SessionInterface (deprecated since Symfony 5.3):
use Symfony\Component\HttpFoundation\Session\SessionInterface;

class MyService
{
    public function __construct(private SessionInterface $session)
    {
    }

    public function doSomething()
    {
        $this->session->set('key', 'value');
    }
}
```

```php
# Example 2 - FlashBagInterface (deprecated since Symfony 5.1):
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;

class MyService
{
    public function __construct(private FlashBagInterface $flashBag)
    {
    }

    public function doSomething()
    {
        $this->flashBag->add('notice', 'Success!');
    }
}
```

---

## Probably missing 'throw' for exception

**Feature ID:** `ControllerActionMissingExitInspection`  
**Date:** 2022-11-04  
**Feature Page:** [Probably missing 'throw' for exception](https://espend.de/phpstorm/plugin/symfony#controller-action-missing-exit-inspection)  

Reports when an exception is instantiated but not thrown. This typically happens when you create an exception object but forget the `throw` keyword.

### Code Examples

```php
# Wrong - exception created but not thrown:
new \Exception('Error');
```

```php
# Correct:
throw new \Exception('Error');
```

---

## 'redirectToRoute' shortcut can be used

**Feature ID:** `UseControllerShortcutsInspection`  
**Date:** 2022-11-04  
**Feature Page:** ['redirectToRoute' shortcut can be used](https://espend.de/phpstorm/plugin/symfony#use-controller-shortcuts-inspection)  

Suggests using built-in `AbstractController` shortcut methods instead of directly accessing services. Shortcuts like `render()`, `redirect()`, `json()` provide cleaner, more concise code.

### Code Examples

```php
# Instead of:
return new Response($this->twig->render('template.html.twig'));
```

```php
# Use:
return $this->render('template.html.twig');
```

---

## '$defaultName' deprecated since Symfony 6.1, use the AsCommand attribute instead

**Feature ID:** `CommandStaticDeprecationInspection`  
**Date:** 2023-12-17  
**Feature Page:** ['$defaultName' deprecated since Symfony 6.1, use the AsCommand attribute instead](https://espend.de/phpstorm/plugin/symfony#command-static-deprecation-inspection)  

Reports Commands using the deprecated `$defaultName` and `$defaultDescription` static properties. Since Symfony 6.1, use the `#[AsCommand]` attribute instead.

### Code Examples

```php
# Instead of:
class MyCommand extends Command
{
    protected static $defaultName = 'app:my-command';
    protected static $defaultDescription = 'My command description';
}
```

```php
# Use:
#[AsCommand(
    name: 'app:my-command',
    description: 'My command description'
)]
class MyCommand extends Command
{
}
```

---

## Doctrine lifecycle callback methods must be public

**Feature ID:** `DoctrineLifecycleMethodVisibilityInspection`  
**Date:** 2026-01-06  
**Feature Page:** [Doctrine lifecycle callback methods must be public](https://espend.de/phpstorm/plugin/symfony#doctrine-lifecycle-method-visibility-inspection)  

Reports methods with Doctrine lifecycle callback attributes that are not public. Doctrine lifecycle callback methods (like `#[PostLoad]`, `#[PrePersist]`, etc.) must be public to work correctly.

### Code Examples

```php
# Instead of:
#[PostLoad]
private function afterLoad(): void
{
    // ...
}
```

```php
# Use:
#[PostLoad]
public function afterLoad(): void
{
    // ...
}
```

---

## Entity class must have #[HasLifecycleCallbacks] attribute

**Feature ID:** `DoctrineLifecycleMissingCallbacksAttributeInspection`  
**Date:** 2026-01-06  
**Feature Page:** [Entity class must have #[HasLifecycleCallbacks] attribute](https://espend.de/phpstorm/plugin/symfony#doctrine-lifecycle-missing-callbacks-attribute-inspection)  

Reports entity classes using Doctrine lifecycle callback attributes (like `#[PostLoad]`, `#[PrePersist]`, etc.) without the `#[HasLifecycleCallbacks]` attribute. Doctrine requires this attribute on the entity class to enable lifecycle callbacks.

### Code Examples

```php
# Instead of:
class User
{
    #[PostLoad]
    public function afterLoad(): void
    {
        // ...
    }
}
```

```php
# Use:
#[HasLifecycleCallbacks]
class User
{
    #[PostLoad]
    public function afterLoad(): void
    {
        // ...
    }
}
```

---

## A template that extends another one cannot include content outside Twig blocks

**Feature ID:** `TwigWithExtendsWithRootHtmlInspection`  
**Date:** 2022-04-17  
**Feature Page:** [A template that extends another one cannot include content outside Twig blocks](https://espend.de/phpstorm/plugin/symfony#twig-with-extends-with-root-html-inspection)  

Reports when a template that extends another template has content outside of `{% block %}` tags. Content outside blocks is ignored and not rendered.

### Code Examples

```twig
# Incorrect - content outside blocks is ignored:
{% extends 'base.html.twig' %}
<div>This content is ignored!</div>
{% block content %}{% endblock %}
```

```twig
# Correct - all content inside blocks:
{% extends 'base.html.twig' %}
{% block content %}
<div>This content is rendered.</div>
{% endblock %}
```

---

## Twig conditional 'for' syntax is deprecated as of Twig 2.10

**Feature ID:** `TwigForConditionDeprecatedInspection`  
**Date:** 2022-07-11  
**Feature Page:** [Twig conditional 'for' syntax is deprecated as of Twig 2.10](https://espend.de/phpstorm/plugin/symfony#twig-for-condition-deprecated-inspection)  

Reports the deprecated `if` condition in `for` loops. Since Twig 2.10, use the `filter` tag instead.

### Code Examples

```twig
# Deprecated:
{% for item in items if item.active %}
    {{ item.name }}
{% endfor %}
```

```twig
# Use filter instead:
{% for item in items|filter(item => item.active) %}
    {{ item.name }}
{% endfor %}
```

---

## Deprecated Twig extensions (tags, filters, functions)

**Feature ID:** `TwigExtensionDeprecatedInspection`  
**Date:** 2020-01-03  
**Feature Page:** [Deprecated Twig extensions (tags, filters, functions)](https://espend.de/phpstorm/plugin/symfony#twig-extension-deprecated-inspection)  

Reports deprecated Twig extensions including tags, filters, and functions. The inspection detects usage of deprecated Twig features like `{% spaceless %}` tag and various deprecated filters/functions.

### Code Examples

```twig
# Deprecated:
{% spaceless %}{{ content }}{% endspaceless %}
{{ content }}
{{ value|deprecated_filter }}
{{ deprecated_function() }}
```

---

## Route exists for using 'path'

**Feature ID:** `TwigPathReplacementInspection`  
**Date:** 2023-07-02  
**Feature Page:** [Route exists for using 'path'](https://espend.de/phpstorm/plugin/symfony#twig-path-replacement-inspection)  

Suggests using `path()` instead of `url()` for internal URLs. The `path()` function generates relative URLs, which is typically what you want for internal links. Use `url()` only when you need absolute URLs.

### Code Examples

```twig
# For internal links, use path():
<a href="{{ path('product_show', {id: product.id}) }}">View Product</a>
```

```twig
# Use url() only when you need absolute URLs (e.g., for emails):
<a href="{{ url('product_show', {id: product.id}) }}">View Product</a>
```

---

## Deprecated Twig variables, methods, and properties

**Feature ID:** `TwigVariableDeprecatedInspection`  
**Date:** 2016-03-02  
**Feature Page:** [Deprecated Twig variables, methods, and properties](https://espend.de/phpstorm/plugin/symfony#twig-variable-deprecated-inspection)  

Reports deprecated methods, properties, fields, and classes used through Twig variables. When Twig accesses PHP members through property syntax or method calls, this inspection detects deprecations on the resolved PHP target.

### Code Examples

```twig
# Example:
{{ user.getDeprecatedMethod() }}
{{ user.deprecatedProperty }}
{{ deprecated_service.someMethod() }}
```

```php
# The inspection will warn when calling deprecated methods like:
Method 'User::getDeprecatedMethod' is deprecated
Field 'User::$deprecatedProperty' is deprecated
Method 'DeprecatedService::someMethod' is deprecated
```

---

## Detects mixing of Twig block syntax and HTML component syntax

**Feature ID:** `TwigComponentMixedSyntaxInspection`  
**Date:** 2026-02-27  
**Feature Page:** [Detects mixing of Twig block syntax and HTML component syntax](https://espend.de/phpstorm/plugin/symfony#twig-component-mixed-syntax-inspection)  

Reports when Twig block syntax `{% block name %}` is used inside HTML component syntax `<twig:Component>`. Inside component contexts, you should use the HTML-compatible `<twig:block>` tag instead.

### Code Examples

```twig
# Invalid - Twig block syntax inside HTML component:
<twig:Card>
    {% block footer %}
        Footer content
    {% endblock %}  <!-- ERROR: Cannot use Twig block syntax inside HTML component -->
</twig:Card>
```

```twig
# Valid - Use twig:block HTML syntax:
<twig:Card>
    <twig:block name="footer">
        Footer content
    </twig:block>
</twig:Card>
```

---

## Detects invalid _self macro imports inside Twig Components

**Feature ID:** `TwigComponentSelfMacroImportInspection`  
**Date:** 2026-02-27  
**Feature Page:** [Detects invalid _self macro imports inside Twig Components](https://espend.de/phpstorm/plugin/symfony#twig-component-self-macro-import-inspection)  

Reports when `{% from _self import ... %}` is used inside a Symfony UX Twig Component. Inside a component context, `_self` does not refer to the current template, so macro imports via `_self` will silently fail at runtime.

### Code Examples

```twig
# Invalid - _self does not work inside components:
<twig:Alert>
    {% from _self import message_formatter %}  <!-- ERROR: _self doesn't work here -->

    {{ message_formatter(message) }}
</twig:Alert>

{% macro message_formatter(message) %}
    {{ message }}
{% endmacro %}
```

```twig
# Valid - Use the full template path:
<twig:Alert>
    {% from 'components/alert.html.twig' import message_formatter %}

    {{ message_formatter(message) }}
</twig:Alert>
```

---

## Twig Enum Function Validation

**Feature ID:** `TwigEnumFunctionInspection`  
**Date:** 2026-01-01  
**Feature Page:** [Twig Enum Function Validation](https://espend.de/phpstorm/plugin/symfony#twig-enum-function-inspection)  

Validates class references in Twig `enum()` and `enum_cases()` functions. The inspection checks that the provided class names exist in the project and are actually PHP `enum` types, not regular classes.

**Detects:**

### Code Examples

```twig
# Valid usage:
{# Valid: enum class exists #}
{{ enum('App\\Enum\\Status') }}

{# Valid: returns all cases of the enum #}
{% for status in enum_cases('App\\Enum\\Status') %}
    {{ status.value }}
{% endfor %}
```

```twig
# Error - class doesn't exist:
{# Error: class doesn't exist #}
{{ enum('App\\Enum\\MissingEnum') }}
```

```twig
# Warning - class exists but is not an enum:
{# Warning: class exists but is not an enum #}
{{ enum('App\\Entity\\User') }}
```

```twig
# Various contexts supported:
{{ enum('App\\Enum\\Status') }}
{{ enum_cases('App\\Enum\\Status') }}
{% if enum('App\\Enum\\Status') == 'value' %}{% endif %}
{% set options = enum_cases('App\\Enum\\Status') %}
```

---

## OneToMany mapping requires the 'mappedBy' attribute

**Feature ID:** `DoctrineOneToManyMappedByRequiredInspection`  
**Date:** 2024-03-21  
**Feature Page:** [OneToMany mapping requires the 'mappedBy' attribute](https://espend.de/phpstorm/plugin/symfony#doctrine-one-to-many-mapped-by-required-inspection)  

Reports `OneToMany` relationships without the required `mappedBy` attribute. In Doctrine, the `OneToMany` side is always the inverse side and must specify which field on the target entity owns the relationship via `ManyToOne`.

### Code Examples

```php
# Example
class Category
{
    #[ORM\OneToMany(targetEntity: Product::class, mappedBy: 'category')]
    private array $products = [];
}

class Product
{
    #[ORM\ManyToOne(targetEntity: Category::class, inversedBy: 'products')]
    #[ORM\JoinColumn(name: 'category_id')]
    private ?Category $category = null;
}
```

---

## JoinColumn not allowed on inverse side of one-to-one associations

**Feature ID:** `DoctrineOneToOneOwingSideDeprecationInspection`  
**Date:** 2023-12-23  
**Feature Page:** [JoinColumn not allowed on inverse side of one-to-one associations](https://espend.de/phpstorm/plugin/symfony#doctrine-one-to-one-owing-side-deprecation-inspection)  

Reports `#[JoinColumn]` configuration on the inverse side of one-to-one associations. In Doctrine ORM 3.0+, `JoinColumn` is only allowed on the owning side of bidirectional one-to-one associations (the side without `mappedBy`).

### Code Examples

```php
# Instead of:
#[OneToOne(mappedBy: 'customer')]
#[JoinColumn]  // Error: JoinColumn not allowed here
private ?Customer $customer = null;
```

```php
# Use:
#[OneToOne(mappedBy: 'customer')]
private ?Customer $customer = null;
```

```php
# JoinColumn should be on the owning side:
#[OneToOne(inversedBy: 'customer')]
#[JoinColumn]
private ?Address $address = null;
```

---

## Consider using first class callable syntax

**Feature ID:** `FirstClassCallableInspection`  
**Date:** 2024-04-04  
**Feature Page:** [Consider using first class callable syntax](https://espend.de/phpstorm/plugin/symfony#first-class-callable-inspection)  

Suggests using PHP 8.1+ first-class callable syntax instead of callable array notation. First-class callables provide better type safety and are more concise.

### Code Examples

```php
# Instead of:
array_map([$this, 'processItem'], $items);
```

```php
# Use first-class callable syntax:
array_map($this->processItem(...), $items);
```

```php
# Works with static methods too:
array_map([MyClass::class, 'method'], $items);
// becomes:
array_map(MyClass::method(...), $items);
```

---

## Annotation namespace will be deprecated in > 6.4/7.0 replace with 'Symfony\Component\Routing\Attribute\Route'

**Feature ID:** `RouteAttributeNamespaceDeprecatedInspection`  
**Date:** 2023-12-17  
**Feature Page:** [Annotation namespace will be deprecated in > 6.4/7.0 replace with 'Symfony\Component\Routing\Attribute\Route'](https://espend.de/phpstorm/plugin/symfony#route-attribute-namespace-deprecated-inspection)  

Reports usage of the deprecated `Symfony\Component\Routing\Annotation\Route` namespace. Since Symfony 6.3, Route attributes should use the `Attribute` namespace instead of `Annotation`.

### Code Examples

```php
# Instead of:
use Symfony\Component\Routing\Annotation\Route;

#[Route('/product', name: 'app_product')]
public function index(): Response {}
```

```php
# Use:
use Symfony\Component\Routing\Attribute\Route;

#[Route('/product', name: 'app_product')]
public function index(): Response {}
```

---

## Calling clear() with any arguments to clear specific entities is deprecated and will not be supported in Doctrine ORM 3.0.

**Feature ID:** `DoctrineClearParameterDeprecationInspection`  
**Date:** 2024-04-18  
**Feature Page:** [Calling clear() with any arguments to clear specific entities is deprecated and will not be supported in Doctrine ORM 3.0.](https://espend.de/phpstorm/plugin/symfony#doctrine-clear-parameter-deprecation-inspection)  

Reports calls to `EntityManager::clear()` with arguments. Since Doctrine ORM 2.11, calling `clear()` with entity class names is deprecated and will be removed in Doctrine ORM 3.0.

### Code Examples

```php
# Instead of:
$entityManager->clear(Foo::class); // Deprecated!
```

```php
# Use:
$entityManager->clear(); // Clears all entities
```

```php
# Or manage entity state manually:
$entityManager->detach($entity);
```

---

## Missing Service

**Feature ID:** `MissingServiceInspection`  
**Date:** 2017-08-30  
**Feature Page:** [Missing Service](https://espend.de/phpstorm/plugin/symfony#missing-service-inspection)  

Reports when `#[Autowire]` attributes reference services that don't exist in the container. This helps catch typos or references to services that haven't been registered yet.

### Code Examples

```php
# Example
#[Autowire(service: 'app.product_repository')] // Service exists
private ProductRepository $productRepository;

#[Autowire(service: 'non_existent_service')] // Error: service not found
private ?SomeService $someService;
```

---

## Named argument does not exists

**Feature ID:** `ServiceNamedArgumentExistsInspection`  
**Date:** 2022-06-17  
**Feature Page:** [Named argument does not exists](https://espend.de/phpstorm/plugin/symfony#service-named-argument-exists-inspection)  

Reports YAML service configurations that reference non-existent container parameters via named arguments (using `$parameterName` syntax).

### Code Examples

```yaml
# Example
# config/services.yaml
parameters:
    app.admin_email: 'admin@example.com'

services:
    App\Service\NotificationService:
        arguments:
            $adminEmail: '%app.admin_email%' # Correct
            $unknownParam: '%app.unknown%' # Error: parameter doesn't exist
```

---

## Wrong service instance in PHP service config

**Feature ID:** `PhpServiceInstanceInspection`  
**Date:** 2026-03-31  
**Feature Page:** [Wrong service instance in PHP service config](https://espend.de/phpstorm/plugin/symfony#php-service-instance-inspection)  

Reports PHP service configuration arguments whose resolved service class does not match the expected constructor parameter type.

The inspection works for array-style definitions, fluent `->args([...])` calls, `service()`, `ref()`, raw `@service_id` strings, and class constant service IDs.

When a better matching service is available, the inspection offers a replacement suggestion directly from the PHP service file.

### Code Examples

```php
# PHP service configuration with a wrong service type:
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;

return [
    App\Service\Mailer::class => [
        'arguments' => [
            service('logger'),
        ],
    ],
];
```

```php
# Fluent configuration is checked as well:
$services->set(App\Service\Mailer::class)
    ->args([
        service('logger'),
    ]);
```

```php
# Suggested replacement:
// Constructor expects App\Contract\MailTransportInterface
// Inspection can suggest app.mail_transport instead of logger.
```

---

## Controller action is deprecated

**Feature ID:** `RouteControllerDeprecatedInspection`  
**Date:** 2026-05-26  
**Feature Page:** [Controller action is deprecated](https://espend.de/phpstorm/plugin/symfony#route-controller-deprecated-inspection)  

Reports routes and route usages that point to controller actions marked as deprecated. This helps identify outdated routes that should be removed or updated.

The inspection covers YAML/XML route definitions, PHP route references, and Twig `path()`/`url()` calls.

### Code Examples

```php
# Example
// Controller with deprecated action:
class ProductController extends AbstractController
{
    #[Route('/old-product', name: 'app_old_product')]
    #[Deprecated('Use app_product_show instead')]
    public function oldAction(): Response
    {
        // ...
    }
}
```

```twig
# Twig route usage:
{{ url('app_old_product') }}
{{ path('app_old_product') }}
```

---

