Skip to content

Commit 07db233

Browse files
committed
Document automatic registration of extension compiler passes
1 parent 3ebf2d0 commit 07db233

File tree

3 files changed

+97
-51
lines changed

3 files changed

+97
-51
lines changed

Diff for: components/dependency_injection/compilation.rst

+69-34
Original file line numberDiff line numberDiff line change
@@ -306,46 +306,85 @@ For more details, see :doc:`/cookbook/bundles/prepend_extension`, which
306306
is specific to the Symfony Framework, but contains more details about this
307307
feature.
308308

309-
Creating a Compiler Pass
310-
------------------------
309+
.. _components-di-compiler-pass:
311310

312-
You can also create and register your own compiler passes with the container.
313-
To create a compiler pass it needs to implement the
314-
:class:`Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface`
315-
interface. The compiler pass gives you an opportunity to manipulate the
316-
service definitions that have been compiled. This can be very powerful,
317-
but is not something needed in everyday use.
311+
Execute Code During Compilation
312+
-------------------------------
318313

319-
The compiler pass must have the ``process`` method which is passed the container
320-
being compiled::
314+
You can also execute custom code during compilation by writing your own
315+
compiler pass. By implementing
316+
:class:`Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface`
317+
in your extension, the added ``process()`` method will be called during
318+
compilation::
321319

320+
// ...
322321
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
323-
use Symfony\Component\DependencyInjection\ContainerBuilder;
324322

325-
class CustomCompilerPass implements CompilerPassInterface
323+
class AcmeDemoExtension implements ExtensionInterface, CompilerPassInterface
326324
{
327325
public function process(ContainerBuilder $container)
328326
{
329-
// ...
327+
// ... do something during the compilation
330328
}
329+
330+
// ...
331331
}
332332

333+
.. versionadded:: 2.8
334+
Prior to Symfony 2.8, extensions implementing ``CompilerPassInterface``
335+
were not automatically registered. You need to register it as explained in
336+
:ref:`the next section <components-di-separate-compiler-passes>`.
337+
338+
As ``process()`` is called *after* all extensions are loaded, it allows you to
339+
edit service definitions of other extensions as well as retrieving information
340+
about service definitions.
341+
333342
The container's parameters and definitions can be manipulated using the
334-
methods described in the :doc:`/components/dependency_injection/definitions`.
335-
One common thing to do in a compiler pass is to search for all services
336-
that have a certain tag in order to process them in some way or dynamically
337-
plug each into some other service.
343+
methods described in :doc:`/components/dependency_injection/definitions`.
344+
345+
.. note::
346+
347+
As a rule, only work with services definition in a compiler pass and do not
348+
create service instances. Practically, this means using methods ``has()``,
349+
``findDefinition()``, ``getDefinition()``, ``setDefinition()``, etc.
350+
instead of ``get()``, ``set()``, etc.
351+
352+
.. tip::
353+
354+
Make sure your compiler pass does not require services to exist. Abort the
355+
method call if some required service is not available.
356+
357+
A common use-case of compiler passes is to search for all service definitions
358+
that have a certain tag in order to process dynamically plug each into some
359+
other service. See the section on :ref:`service tags <components-di-compiler-pass-tags>`
360+
for an example.
361+
362+
.. _components-di-separate-compiler-passes:
338363

339-
Registering a Compiler Pass
340-
---------------------------
364+
Creating Separate Compiler Passes
365+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
341366

342-
You need to register your custom pass with the container. Its process method
343-
will then be called when the container is compiled::
367+
Sometimes, you need to do more than one thing during compliation or want to use
368+
compiler passes without an extension. In this case, you can create a new class
369+
implementing the ``CompilerPassInterface``::
370+
371+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
372+
use Symfony\Component\DependencyInjection\ContainerBuilder;
373+
374+
class CustomPass implements CompilerPassInterface
375+
{
376+
public function process(ContainerBuilder $container)
377+
{
378+
// ... do something during the compilation
379+
}
380+
}
381+
382+
You then need to register your custom pass with the container::
344383

345384
use Symfony\Component\DependencyInjection\ContainerBuilder;
346385

347386
$container = new ContainerBuilder();
348-
$container->addCompilerPass(new CustomCompilerPass);
387+
$container->addCompilerPass(new CustomPass());
349388

350389
.. note::
351390

@@ -354,17 +393,16 @@ will then be called when the container is compiled::
354393
more details.
355394

356395
Controlling the Pass Ordering
357-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
396+
.............................
358397

359398
The default compiler passes are grouped into optimization passes and removal
360399
passes. The optimization passes run first and include tasks such as resolving
361400
references within the definitions. The removal passes perform tasks such
362-
as removing private aliases and unused services. You can choose where in
363-
the order any custom passes you add are run. By default they will be run
364-
before the optimization passes.
401+
as removing private aliases and unused services. When registering compiler
402+
passes using ``addCompilerPass()``, you can configure when your compiler pass
403+
is run. By default, they are run before the optimization passes.
365404

366-
You can use the following constants as the second argument when registering
367-
a pass with the container to control where it goes in the order:
405+
You can use the following constants to determine when your pass is executed:
368406

369407
* ``PassConfig::TYPE_BEFORE_OPTIMIZATION``
370408
* ``PassConfig::TYPE_OPTIMIZE``
@@ -373,14 +411,11 @@ a pass with the container to control where it goes in the order:
373411
* ``PassConfig::TYPE_AFTER_REMOVING``
374412

375413
For example, to run your custom pass after the default removal passes have
376-
been run::
414+
been run, use::
377415

378-
use Symfony\Component\DependencyInjection\ContainerBuilder;
379-
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
380-
381-
$container = new ContainerBuilder();
416+
// ...
382417
$container->addCompilerPass(
383-
new CustomCompilerPass,
418+
new CustomPass(),
384419
PassConfig::TYPE_AFTER_REMOVING
385420
);
386421

Diff for: components/dependency_injection/tags.rst

+14-5
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,13 @@ Notice that each was given a tag named ``acme_mailer.transport``. This is
117117
the custom tag that you'll use in your compiler pass. The compiler pass
118118
is what makes this tag "mean" something.
119119

120-
Create a ``CompilerPass``
121-
-------------------------
120+
.. _components-di-compiler-pass-tags:
122121

123-
Your compiler pass can now ask the container for any services with the
124-
custom tag::
122+
Create a CompilerPass
123+
---------------------
124+
125+
You can now use a :ref:`compiler pass <components-di-separate-compiler-pass>` to ask the
126+
container for any services with the ``acme_mailer.transport`` tag::
125127

126128
use Symfony\Component\DependencyInjection\ContainerBuilder;
127129
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
@@ -154,7 +156,7 @@ custom tag::
154156
The ``process()`` method checks for the existence of the ``acme_mailer.transport_chain``
155157
service, then looks for all services tagged ``acme_mailer.transport``. It
156158
adds to the definition of the ``acme_mailer.transport_chain`` service a
157-
call to ``addTransport()`` for each "acme_mailer.transport" service it has
159+
call to ``addTransport()`` for each ``acme_mailer.transport`` service it has
158160
found. The first argument of each of these calls will be the mailer transport
159161
service itself.
160162

@@ -175,6 +177,13 @@ run when the container is compiled::
175177
framework. See :doc:`/cookbook/service_container/compiler_passes` for
176178
more details.
177179

180+
.. tip::
181+
182+
When implementing the ``CompilerPassInterface`` in a service extension, you
183+
do not need to register it. See the
184+
:ref:`components documentation <components-di-compiler-pass>` for more
185+
information.
186+
178187
Adding Additional Attributes on Tags
179188
------------------------------------
180189

Diff for: cookbook/service_container/compiler_passes.rst

+14-12
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,34 @@ How to Work with Compiler Passes in Bundles
77

88
Compiler passes give you an opportunity to manipulate other service
99
definitions that have been registered with the service container. You
10-
can read about how to create them in the components section ":doc:`/components/dependency_injection/compilation`".
11-
To register a compiler pass from a bundle you need to add it to the build
12-
method of the bundle definition class::
10+
can read about how to create them in the components section
11+
":ref:`components-di-compiler-pass`".
1312

14-
// src/Acme/MailerBundle/AcmeMailerBundle.php
15-
namespace Acme\MailerBundle;
13+
When using :ref:`separate compiler passes <components-di-separate-compiler-passes>`,
14+
you need to register them in the ``build()`` method of the bundle class (this
15+
is not needed when implementing the ``process()`` method in the extension)::
16+
17+
// src/AppBundle/AppBundle.php
18+
namespace AppBundle;
1619

1720
use Symfony\Component\HttpKernel\Bundle\Bundle;
1821
use Symfony\Component\DependencyInjection\ContainerBuilder;
22+
use AppBundle\DependencyInjection\Compiler\CustomPass;
1923

20-
use Acme\MailerBundle\DependencyInjection\Compiler\CustomCompilerPass;
21-
22-
class AcmeMailerBundle extends Bundle
24+
class AppBundle extends Bundle
2325
{
2426
public function build(ContainerBuilder $container)
2527
{
2628
parent::build($container);
2729

28-
$container->addCompilerPass(new CustomCompilerPass());
30+
$container->addCompilerPass(new CustomPass());
2931
}
3032
}
3133

3234
One of the most common use-cases of compiler passes is to work with tagged services
3335
(read more about tags in the components section ":doc:`/components/dependency_injection/tags`").
3436
If you are using custom tags in a bundle then by convention, tag names consist
3537
of the name of the bundle (lowercase, underscores as separators), followed
36-
by a dot, and finally the "real" name. For example, if you want to introduce
37-
some sort of "transport" tag in your AcmeMailerBundle, you should call it
38-
``acme_mailer.transport``.
38+
by a dot and finally the "real" name. For example, if you want to introduce
39+
some sort of "mail_transport" tag in your AppBundle, you should call it
40+
``app.mail_transport``.

0 commit comments

Comments
 (0)