diff --git a/service_container/service_decoration.rst b/service_container/service_decoration.rst index 4c7f2ed0158..06f7a0df1ab 100644 --- a/service_container/service_decoration.rst +++ b/service_container/service_decoration.rst @@ -258,17 +258,18 @@ the ``decoration_priority`` option. Its value is an integer that defaults to .. code-block:: yaml # config/services.yaml - Foo: ~ + services: + Foo: ~ - Bar: - decorates: Foo - decoration_priority: 5 - arguments: ['@.inner'] + Bar: + decorates: Foo + decoration_priority: 5 + arguments: ['@.inner'] - Baz: - decorates: Foo - decoration_priority: 1 - arguments: ['@.inner'] + Baz: + decorates: Foo + decoration_priority: 1 + arguments: ['@.inner'] .. code-block:: xml @@ -300,14 +301,14 @@ the ``decoration_priority`` option. Its value is an integer that defaults to return function(ContainerConfigurator $configurator) { $services = $configurator->services(); - $services->set(Foo::class); + $services->set(\Foo::class); - $services->set(Bar::class) - ->decorate(Foo::class, null, 5) + $services->set(\Bar::class) + ->decorate(\Foo::class, null, 5) ->args([service('.inner')]); - $services->set(Baz::class) - ->decorate(Foo::class, null, 1) + $services->set(\Baz::class) + ->decorate(\Foo::class, null, 1) ->args([service('.inner')]); }; @@ -316,6 +317,226 @@ The generated code will be the following:: $this->services[Foo::class] = new Baz(new Bar(new Foo())); +Stacking Decorators +------------------- + +An alternative to using decoration priorities is to create a ``stack`` of +ordered services, each one decorating the next: + +.. configuration-block:: + + .. code-block:: yaml + + # config/services.yaml + services: + decorated_foo_stack: + stack: + - class: Baz + arguments: ['@.inner'] + - class: Bar + arguments: ['@.inner'] + - class: Foo + + # using the short syntax: + decorated_foo_stack: + stack: + - Baz: ['@.inner'] + - Bar: ['@.inner'] + - Foo: ~ + + # can be simplified when autowiring is enabled: + decorated_foo_stack: + stack: + - Baz: ~ + - Bar: ~ + - Foo: ~ + + .. code-block:: xml + + + + + + + + + + + + + + + + + + + + + + + + + .. code-block:: php + + // config/services.php + namespace Symfony\Component\DependencyInjection\Loader\Configurator; + + return function(ContainerConfigurator $container) { + $container->services() + ->stack('decorated_foo_stack', [ + inline_service(\Baz::class)->args([service('.inner')]), + inline_service(\Bar::class)->args([service('.inner')]), + inline_service(\Foo::class), + ]) + + // can be simplified when autowiring is enabled: + ->stack('decorated_foo_stack', [ + inline_service(\Baz::class), + inline_service(\Bar::class), + inline_service(\Foo::class), + ]) + ; + }; + +The result will be the same as in the previous section:: + + $this->services['decorated_foo_stack'] = new Baz(new Bar(new Foo())); + +Like aliases, a ``stack`` can only use ``public`` and ``deprecated`` attributes. + +Each frame of the ``stack`` can be either an inlined service, a reference or a +child definition. +The latter allows embedding ``stack`` definitions into each others, here's an +advanced example of composition: + +.. configuration-block:: + + .. code-block:: yaml + + # config/services.yaml + services: + some_decorator: + class: App\Decorator + + embedded_stack: + stack: + - alias: some_decorator + - App\Decorated: ~ + + decorated_foo_stack: + stack: + - parent: embedded_stack + - Baz: ~ + - Bar: ~ + - Foo: ~ + + .. code-block:: xml + + + + + + + + + + + + + + + + + + + + + + .. code-block:: php + + // config/services.php + namespace Symfony\Component\DependencyInjection\Loader\Configurator; + + use App\Decorated; + use App\Decorator; + + return function(ContainerConfigurator $container) { + $container->services() + ->set('some_decorator', Decorator::class) + + ->stack('embedded_stack', [ + service('some_decorator'), + inline_service(Decorated::class), + ]) + + ->stack('decorated_foo_stack', [ + inline_service()->parent('embedded_stack'), + inline_service(\Baz::class), + inline_service(\Bar::class), + inline_service(\Foo::class), + ]) + ; + }; + +The result will be:: + + $this->services['decorated_foo_stack'] = new App\Decorator(new App\Decorated(new Baz(new Bar(new Foo())))); + +.. note:: + + To change existing stacks (i.e. from a compiler pass), you can access each + frame by its generated id with the following structure: + ``.stack_id.frame_key``. + From the example above, ``.decorated_foo_stack.1`` would be a reference to + the inlined ``Baz`` service and ``.decorated_foo_stack.0`` to the embedded + stack. + To get more explicit ids, you can give a name to each frame: + + .. configuration-block:: + + .. code-block:: yaml + + # ... + decorated_foo_stack: + stack: + first: + parent: embedded_stack + second: + Baz: ~ + # ... + + .. code-block:: xml + + + + + + + + + .. code-block:: php + + // ... + ->stack('decorated_foo_stack', [ + 'first' => inline_service()->parent('embedded_stack'), + 'second' => inline_service(\Baz::class), + // ... + ]) + + The ``Baz`` frame id will now be ``.decorated_foo_stack.second``. + +.. versionadded:: 5.1 + + The ability to define ``stack`` was introduced in Symfony 5.1. + Control the Behavior When the Decorated Service Does Not Exist --------------------------------------------------------------