Skip to content

[DependencyInjection] Update the article about compiler passes #20365

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 6, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 54 additions & 26 deletions service_container/compiler_passes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,85 @@ How to Work with Compiler Passes

Compiler passes give you an opportunity to manipulate other
:doc:`service definitions </service_container/definitions>` that have been
registered with the service container. You can read about how to create them in
the components section ":ref:`components-di-separate-compiler-passes`".
registered with the service container.

Compiler passes are registered in the ``build()`` method of the application kernel::
.. _kernel-as-compiler-pass:

If your compiler pass is relatively small, you can define it inside the
application's ``Kernel`` class instead of creating a
:ref:`separate compiler pass class <components-di-separate-compiler-passes>`.

To do so, make your kernel implement :class:`Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface`
and add the compiler pass code inside the ``process()`` method::

// src/Kernel.php
namespace App;

use App\DependencyInjection\Compiler\CustomPass;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;

class Kernel extends BaseKernel
class Kernel extends BaseKernel implements CompilerPassInterface
{
use MicroKernelTrait;

// ...

protected function build(ContainerBuilder $container): void
public function process(ContainerBuilder $container): void
{
$container->addCompilerPass(new CustomPass());
// in this method you can manipulate the service container:
// for example, changing some container service:
$container->getDefinition('app.some_private_service')->setPublic(true);

// or processing tagged services:
foreach ($container->findTaggedServiceIds('some_tag') as $id => $tags) {
// ...
}
}
}

.. _kernel-as-compiler-pass:

One of the most common use-cases of compiler passes is to work with :doc:`tagged
services </service_container/tags>`. In those cases, instead of creating a
compiler pass, you can make the kernel implement
:class:`Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface`
and process the services inside the ``process()`` method::
If you create separate compiler pass classes, enable them in the ``build()``
method of the application kernel::

// src/Kernel.php
namespace App;

use App\DependencyInjection\Compiler\CustomPass;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;

class Kernel extends BaseKernel implements CompilerPassInterface
class Kernel extends BaseKernel
{
use MicroKernelTrait;

// ...

protected function build(ContainerBuilder $container): void
{
$container->addCompilerPass(new CustomPass());
}
}

Working with Compiler Passes in Bundles
---------------------------------------

If your compiler pass is relatively small, you can add it directly in the main
bundle class. To do so, make your bundle implement the
:class:`Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface`
and place the compiler pass code inside the ``process()`` method of the main
bundle class::

// src/MyBundle/MyBundle.php
namespace App\MyBundle;

use App\DependencyInjection\Compiler\CustomPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;

class MyBundle extends AbstractBundle
{
public function process(ContainerBuilder $container): void
{
// in this method you can manipulate the service container:
Expand All @@ -63,12 +95,8 @@ and process the services inside the ``process()`` method::
}
}

Working with Compiler Passes in Bundles
---------------------------------------

:doc:`Bundles </bundles>` can define compiler passes in the ``build()`` method of
the main bundle class (this is not needed when implementing the ``process()``
method in the extension)::
Alternatively, when using :ref:`separate compiler pass classes <components-di-separate-compiler-passes>`,
bundles can enable them in the ``build()`` method of their main bundle class::

// src/MyBundle/MyBundle.php
namespace App\MyBundle;
Expand All @@ -88,7 +116,7 @@ method in the extension)::
}

If you are using custom :doc:`service tags </service_container/tags>` in a
bundle then by convention, tag names consist of the name of the bundle
(lowercase, underscores as separators), followed by a dot, and finally the
"real" name. For example, if you want to introduce some sort of "transport" tag
in your AcmeMailerBundle, you should call it ``acme_mailer.transport``.
bundle, the convention is to format tag names by starting with the bundle's name
in lowercase (using underscores as separators), followed by a dot, and finally
the specific tag name. For example, to introduce a "transport" tag in your
AcmeMailerBundle, you would name it ``acme_mailer.transport``.
Loading