# 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.

---

## Avoid heavy construction in Twig extension

**Feature ID:** `ConstructorTwigExtensionHeavyConstructorInspection`  
**Date:** 2022-03-24

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([]);
    }
}
```

---

## 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

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);
```

---

## Avoid heavy construction in non lazy Command

**Feature ID:** `ConstructorCommandHeavyConstructorInspection`  
**Date:** 2022-05-31

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.

---

## Use ON instead of WITH for arbitrary joins

**Feature ID:** `DoctrineQueryBuilderJoinWithToOnInspection`  
**Date:** 2026-01-08

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');
```

---

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

**Feature ID:** `ConstructorNoRequestStackInspection`  
**Date:** 2022-03-19

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();
}
```

---

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

**Feature ID:** `DoctrineQueryBuilderSetParametersArrayDeprecationInspection`  
**Date:** 2025-12-02

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)
]));
```

---

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

**Feature ID:** `TwigWithExtendsWithRootHtmlInspection`  
**Date:** 2022-04-17

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

### Code Examples

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

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

---

## Controller action is deprecated

**Feature ID:** `RouteControllerDeprecatedInspection`  
**Date:** 2022-06-16

Reports routes (YAML/XML) that point to controller actions marked with `#[Deprecated]` attribute. This helps identify outdated routes that should be removed or updated. The route will be highlighted in your routing configuration with a deprecation warning.

---

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

**Feature ID:** `TwigForConditionDeprecatedInspection`  
**Date:** 2022-07-11

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

### Code Examples

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

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

---

## Missing Service

**Feature ID:** `MissingServiceInspection`  
**Date:** 2017-08-30

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.

---

## Invokable command must return an integer value

**Feature ID:** `CommandInvokableReturnValueInspection`  
**Date:** 2025-12-03

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
    }
}
```

---

## Reports deprecated dependency injection patterns in service constructors.

**Feature ID:** `ConstructorDeprecatedInjection`  
**Date:** 2021-09-29

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: `SessionInterface` - deprecated since Symfony 5.3 `FlashBagInterface` - deprecated since Symfony 5.1

### Code Examples

```php
# Example 1 - SessionInterface (deprecated since Symfony 5.3):
            Instead of:
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):
            Instead of:
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;

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

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

---

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

**Feature ID:** `DoctrineOneToOneOwingSideDeprecationInspection`  
**Date:** 2023-12-23

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;
```

---

## Detects invalid _self macro imports inside Twig Components

**Feature ID:** `TwigComponentSelfMacroImportInspection`  
**Date:** 2026-02-27

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

```php
# 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 %}
```

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

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

---

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

**Feature ID:** `CommandStaticDeprecationInspection`  
**Date:** 2023-12-17

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
{
}
```

---

## Entity class must have #[HasLifecycleCallbacks] attribute

**Feature ID:** `DoctrineLifecycleMissingCallbacksAttributeInspection`  
**Date:** 2026-01-06

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 User must not be used inside a constructor

**Feature ID:** `ConstructorNoUserInspection`  
**Date:** 2022-03-19

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();
}
```

---

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

**Feature ID:** `DoctrineQueryBuilderArrayDeprecationInspection`  
**Date:** 2025-12-09

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');
```

---

## Named argument does not exists

**Feature ID:** `ServiceNamedArgumentExistsInspection`  
**Date:** 2022-06-17

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

---

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

**Feature ID:** `FormTypeBuilderInstanceInspection`  
**Date:** 2022-06-04

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

---

## Doctrine lifecycle callback methods must be public

**Feature ID:** `DoctrineLifecycleMethodVisibilityInspection`  
**Date:** 2026-01-06

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
{
    // ...
}
```

---

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

**Feature ID:** `ProjectDirParameterInspection`  
**Date:** 2022-03-19

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');
```

---

## Deprecated Twig variables and methods

**Feature ID:** `TwigVariableDeprecatedInspection`  
**Date:** 2016-03-02

Reports deprecated methods and classes used in Twig variables. When accessing properties or calling methods on Twig variables, this inspection detects if the underlying PHP method or class is deprecated.

### Code Examples

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

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

---

## Consider using first class callable syntax

**Feature ID:** `FirstClassCallableInspection`  
**Date:** 2024-04-04

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);
```

---

## Empty Table attribute on entities is useless

**Feature ID:** `DoctrineEmptyTableAttributeInspection`  
**Date:** 2026-01-09

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
{
    // ...
}
```

---

## OneToMany mapping requires the 'mappedBy' attribute

**Feature ID:** `DoctrineOneToManyMappedByRequiredInspection`  
**Date:** 2024-03-21

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`.

---

## 'redirectToRoute' shortcut can be used

**Feature ID:** `UseControllerShortcutsInspection`  
**Date:** 2022-11-04

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');
```

---

## You probably dont want to inject a Controller here

**Feature ID:** `AutowireControllerAsServiceInspection`  
**Date:** 2022-03-19

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) {}
```

---

## A Session must not be used inside a constructor

**Feature ID:** `ConstructorNoSessionInspection`  
**Date:** 2022-03-19

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');
}
```

---

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

**Feature ID:** `AutowireWrongClassInspection`  
**Date:** 2022-03-19

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) {}
```

---

## Probably missing 'throw' for exception

**Feature ID:** `ControllerActionMissingExitInspection`  
**Date:** 2022-11-04

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');
```

---

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

**Feature ID:** `ConstructorNoTokenInspection`  
**Date:** 2022-03-19

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!
}
```

---

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

**Feature ID:** `DoctrineQueryBuilderAliasDeprecationInspection`  
**Date:** 2025-12-02

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');
```

---

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

**Feature ID:** `TwigExtensionDeprecatedInspection`  
**Date:** 2020-01-03

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

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

---

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

**Feature ID:** `CommandInvokableReturnTypeInspection`  
**Date:** 2025-12-03

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;
    }
}
```

---

## Constraint array argument deprecated, use named arguments

**Feature ID:** `ConstraintDeprecatedArrayInspection`  
**Date:** 2025-12-01

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');
```

---

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

**Feature ID:** `RequestStackInActionInspection`  
**Date:** 2022-10-20

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');
}
```

---

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

**Feature ID:** `TwigComponentMixedSyntaxInspection`  
**Date:** 2026-02-27

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

```php
# 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>
```

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

---

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

**Feature ID:** `RouteAttributeNamespaceDeprecatedInspection`  
**Date:** 2023-12-17

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 {}
```

---

## Route exists for using 'path'

**Feature ID:** `TwigPathReplacementInspection`  
**Date:** 2023-07-02

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

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

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

---

## Twig Enum Function Validation

**Feature ID:** `TwigEnumFunctionInspection`  
**Date:** 2026-01-01

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.

### Code Examples

```php
# 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 %}
```

```php
# Error - class doesn't exist:
{# Error: class doesn't exist #}
{{ enum('App\\Enum\\Missing<caret>Enum') }}
```

```php
# Warning - class exists but is not an enum:
{# Warning: class exists but is not an enum #}
{{ enum('App\\Entity\\Us<caret>er') }}
```

```php
# 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') %}
```

---

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

**Feature ID:** `DoctrineQueryBuilderAdderInspection`  
**Date:** 2022-06-01

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');
```

---

