@@ -258,17 +258,18 @@ the ``decoration_priority`` option. Its value is an integer that defaults to
258258 .. code-block :: yaml
259259
260260 # config/services.yaml
261- Foo : ~
261+ services :
262+ Foo : ~
262263
263- Bar :
264- decorates : Foo
265- decoration_priority : 5
266- arguments : ['@.inner']
264+ Bar :
265+ decorates : Foo
266+ decoration_priority : 5
267+ arguments : ['@.inner']
267268
268- Baz :
269- decorates : Foo
270- decoration_priority : 1
271- arguments : ['@.inner']
269+ Baz :
270+ decorates : Foo
271+ decoration_priority : 1
272+ arguments : ['@.inner']
272273
273274 .. code-block :: xml
274275
@@ -300,14 +301,14 @@ the ``decoration_priority`` option. Its value is an integer that defaults to
300301 return function(ContainerConfigurator $configurator) {
301302 $services = $configurator->services();
302303
303- $services->set(Foo::class);
304+ $services->set(\ Foo::class);
304305
305- $services->set(Bar::class)
306- ->decorate(Foo::class, null, 5)
306+ $services->set(\ Bar::class)
307+ ->decorate(\ Foo::class, null, 5)
307308 ->args([service('.inner')]);
308309
309- $services->set(Baz::class)
310- ->decorate(Foo::class, null, 1)
310+ $services->set(\ Baz::class)
311+ ->decorate(\ Foo::class, null, 1)
311312 ->args([service('.inner')]);
312313 };
313314
@@ -316,6 +317,226 @@ The generated code will be the following::
316317
317318 $this->services[Foo::class] = new Baz(new Bar(new Foo()));
318319
320+ Stacking Decorators
321+ -------------------
322+
323+ An alternative to using decoration priorities is to create a ``stack `` of
324+ ordered services, each one decorating the next:
325+
326+ .. configuration-block ::
327+
328+ .. code-block :: yaml
329+
330+ # config/services.yaml
331+ services :
332+ decorated_foo_stack :
333+ stack :
334+ - class : Baz
335+ arguments : ['@.inner']
336+ - class : Bar
337+ arguments : ['@.inner']
338+ - class : Foo
339+
340+ # using the short syntax:
341+ decorated_foo_stack :
342+ stack :
343+ - Baz : ['@.inner']
344+ - Bar : ['@.inner']
345+ - Foo : ~
346+
347+ # can be simplified when autowiring is enabled:
348+ decorated_foo_stack :
349+ stack :
350+ - Baz : ~
351+ - Bar : ~
352+ - Foo : ~
353+
354+ .. code-block :: xml
355+
356+ <!-- config/services.xml -->
357+ <?xml version =" 1.0" encoding =" UTF-8" ?>
358+ <container xmlns =" http://symfony.com/schema/dic/services"
359+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
360+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
361+ https://symfony.com/schema/dic/services/services-1.0.xsd"
362+ >
363+ <services >
364+ <stack id =" decorated_foo_stack" >
365+ <service class =" Baz" >
366+ <argument type =" service" id =" .inner" />
367+ </service >
368+ <service class =" Bar" >
369+ <argument type =" service" id =" .inner" />
370+ </service >
371+ <service class =" Foo" />
372+ </stack >
373+
374+ <!-- can be simplified when autowiring is enabled: -->
375+ <stack id =" decorated_foo_stack" >
376+ <service class =" Baz" />
377+ <service class =" Bar" />
378+ <service class =" Foo" />
379+ </stack >
380+ </services >
381+ </container >
382+
383+ .. code-block :: php
384+
385+ // config/services.php
386+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
387+
388+ return function(ContainerConfigurator $container) {
389+ $container->services()
390+ ->stack('decorated_foo_stack', [
391+ inline_service(\Baz::class)->args([service('.inner')]),
392+ inline_service(\Bar::class)->args([service('.inner')]),
393+ inline_service(\Foo::class),
394+ ])
395+
396+ // can be simplified when autowiring is enabled:
397+ ->stack('decorated_foo_stack', [
398+ inline_service(\Baz::class),
399+ inline_service(\Bar::class),
400+ inline_service(\Foo::class),
401+ ])
402+ ;
403+ };
404+
405+ The result will be the same as in the previous section::
406+
407+ $this->services['decorated_foo_stack'] = new Baz(new Bar(new Foo()));
408+
409+ Like aliases, a ``stack `` can only use ``public `` and ``deprecated `` attributes.
410+
411+ Each frame of the ``stack `` can be either an inlined service, a reference or a
412+ child definition.
413+ The latter allows embedding ``stack `` definitions into each others, here's an
414+ advanced example of composition:
415+
416+ .. configuration-block ::
417+
418+ .. code-block :: yaml
419+
420+ # config/services.yaml
421+ services :
422+ some_decorator :
423+ class : App\Decorator
424+
425+ embedded_stack :
426+ stack :
427+ - alias : some_decorator
428+ - App\Decorated : ~
429+
430+ decorated_foo_stack :
431+ stack :
432+ - parent : embedded_stack
433+ - Baz : ~
434+ - Bar : ~
435+ - Foo : ~
436+
437+ .. code-block :: xml
438+
439+ <!-- config/services.xml -->
440+ <?xml version =" 1.0" encoding =" UTF-8" ?>
441+ <container xmlns =" http://symfony.com/schema/dic/services"
442+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
443+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
444+ https://symfony.com/schema/dic/services/services-1.0.xsd"
445+ >
446+ <services >
447+ <service id =" some_decorator" class =" App\Decorator" />
448+
449+ <stack id =" embedded_stack" >
450+ <service alias =" some_decorator" />
451+ <service class =" App\Decorated" />
452+ </stack >
453+
454+ <stack id =" decorated_foo_stack" >
455+ <service parent =" embedded_stack" />
456+ <service class =" Baz" />
457+ <service class =" Bar" />
458+ <service class =" Foo" />
459+ </stack >
460+ </services >
461+ </container >
462+
463+ .. code-block :: php
464+
465+ // config/services.php
466+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
467+
468+ use App\Decorated;
469+ use App\Decorator;
470+
471+ return function(ContainerConfigurator $container) {
472+ $container->services()
473+ ->set('some_decorator', Decorator::class)
474+
475+ ->stack('embedded_stack', [
476+ service('some_decorator'),
477+ inline_service(Decorated::class),
478+ ])
479+
480+ ->stack('decorated_foo_stack', [
481+ inline_service()->parent('embedded_stack'),
482+ inline_service(\Baz::class),
483+ inline_service(\Bar::class),
484+ inline_service(\Foo::class),
485+ ])
486+ ;
487+ };
488+
489+ The result will be::
490+
491+ $this->services['decorated_foo_stack'] = new App\Decorator(new App\Decorated(new Baz(new Bar(new Foo()))));
492+
493+ .. note ::
494+
495+ To change existing stacks (i.e. from a compiler pass), you can access each
496+ frame by its generated id with the following structure:
497+ ``.stack_id.frame_key ``.
498+ From the example above, ``.decorated_foo_stack.1 `` would be a reference to
499+ the inlined ``Baz `` service and ``.decorated_foo_stack.0 `` to the embedded
500+ stack.
501+ To get more explicit ids, you can give a name to each frame:
502+
503+ .. configuration-block ::
504+
505+ .. code-block :: yaml
506+
507+ # ...
508+ decorated_foo_stack :
509+ stack :
510+ first :
511+ parent : embedded_stack
512+ second :
513+ Baz : ~
514+ # ...
515+
516+ .. code-block :: xml
517+
518+ <!-- ... -->
519+ <stack id =" decorated_foo_stack" >
520+ <service id =" first" parent =" embedded_stack" />
521+ <service id =" second" class =" Baz" />
522+ <!-- ... -->
523+ </stack >
524+
525+ .. code-block :: php
526+
527+ // ...
528+ ->stack('decorated_foo_stack', [
529+ 'first' => inline_service()->parent('embedded_stack'),
530+ 'second' => inline_service(\Baz::class),
531+ // ...
532+ ])
533+
534+ The ``Baz `` frame id will now be ``.decorated_foo_stack.second ``.
535+
536+ .. versionadded :: 5.1
537+
538+ The ability to define ``stack `` was introduced in Symfony 5.1.
539+
319540Control the Behavior When the Decorated Service Does Not Exist
320541--------------------------------------------------------------
321542
0 commit comments