@@ -323,17 +323,18 @@ the ``decoration_priority`` option. Its value is an integer that defaults to
323323 .. code-block :: yaml
324324
325325 # config/services.yaml
326- Foo : ~
326+ services :
327+ Foo : ~
327328
328- Bar :
329- decorates : Foo
330- decoration_priority : 5
331- arguments : ['@.inner']
329+ Bar :
330+ decorates : Foo
331+ decoration_priority : 5
332+ arguments : ['@.inner']
332333
333- Baz :
334- decorates : Foo
335- decoration_priority : 1
336- arguments : ['@.inner']
334+ Baz :
335+ decorates : Foo
336+ decoration_priority : 1
337+ arguments : ['@.inner']
337338
338339 .. code-block :: xml
339340
@@ -365,14 +366,14 @@ the ``decoration_priority`` option. Its value is an integer that defaults to
365366 return function(ContainerConfigurator $configurator) {
366367 $services = $configurator->services();
367368
368- $services->set(Foo::class);
369+ $services->set(\ Foo::class);
369370
370- $services->set(Bar::class)
371- ->decorate(Foo::class, null, 5)
371+ $services->set(\ Bar::class)
372+ ->decorate(\ Foo::class, null, 5)
372373 ->args([service('.inner')]);
373374
374- $services->set(Baz::class)
375- ->decorate(Foo::class, null, 1)
375+ $services->set(\ Baz::class)
376+ ->decorate(\ Foo::class, null, 1)
376377 ->args([service('.inner')]);
377378 };
378379
@@ -381,6 +382,226 @@ The generated code will be the following::
381382
382383 $this->services[Foo::class] = new Baz(new Bar(new Foo()));
383384
385+ Stacking Decorators
386+ -------------------
387+
388+ An alternative to using decoration priorities is to create a ``stack `` of
389+ ordered services, each one decorating the next:
390+
391+ .. configuration-block ::
392+
393+ .. code-block :: yaml
394+
395+ # config/services.yaml
396+ services :
397+ decorated_foo_stack :
398+ stack :
399+ - class : Baz
400+ arguments : ['@.inner']
401+ - class : Bar
402+ arguments : ['@.inner']
403+ - class : Foo
404+
405+ # using the short syntax:
406+ decorated_foo_stack :
407+ stack :
408+ - Baz : ['@.inner']
409+ - Bar : ['@.inner']
410+ - Foo : ~
411+
412+ # can be simplified when autowiring is enabled:
413+ decorated_foo_stack :
414+ stack :
415+ - Baz : ~
416+ - Bar : ~
417+ - Foo : ~
418+
419+ .. code-block :: xml
420+
421+ <!-- config/services.xml -->
422+ <?xml version =" 1.0" encoding =" UTF-8" ?>
423+ <container xmlns =" http://symfony.com/schema/dic/services"
424+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
425+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
426+ https://symfony.com/schema/dic/services/services-1.0.xsd"
427+ >
428+ <services >
429+ <stack id =" decorated_foo_stack" >
430+ <service class =" Baz" >
431+ <argument type =" service" id =" .inner" />
432+ </service >
433+ <service class =" Bar" >
434+ <argument type =" service" id =" .inner" />
435+ </service >
436+ <service class =" Foo" />
437+ </stack >
438+
439+ <!-- can be simplified when autowiring is enabled: -->
440+ <stack id =" decorated_foo_stack" >
441+ <service class =" Baz" />
442+ <service class =" Bar" />
443+ <service class =" Foo" />
444+ </stack >
445+ </services >
446+ </container >
447+
448+ .. code-block :: php
449+
450+ // config/services.php
451+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
452+
453+ return function(ContainerConfigurator $container) {
454+ $container->services()
455+ ->stack('decorated_foo_stack', [
456+ inline_service(\Baz::class)->args([service('.inner')]),
457+ inline_service(\Bar::class)->args([service('.inner')]),
458+ inline_service(\Foo::class),
459+ ])
460+
461+ // can be simplified when autowiring is enabled:
462+ ->stack('decorated_foo_stack', [
463+ inline_service(\Baz::class),
464+ inline_service(\Bar::class),
465+ inline_service(\Foo::class),
466+ ])
467+ ;
468+ };
469+
470+ The result will be the same as in the previous section::
471+
472+ $this->services['decorated_foo_stack'] = new Baz(new Bar(new Foo()));
473+
474+ Like aliases, a ``stack `` can only use ``public `` and ``deprecated `` attributes.
475+
476+ Each frame of the ``stack `` can be either an inlined service, a reference or a
477+ child definition.
478+ The latter allows embedding ``stack `` definitions into each others, here's an
479+ advanced example of composition:
480+
481+ .. configuration-block ::
482+
483+ .. code-block :: yaml
484+
485+ # config/services.yaml
486+ services :
487+ some_decorator :
488+ class : App\Decorator
489+
490+ embedded_stack :
491+ stack :
492+ - alias : some_decorator
493+ - App\Decorated : ~
494+
495+ decorated_foo_stack :
496+ stack :
497+ - parent : embedded_stack
498+ - Baz : ~
499+ - Bar : ~
500+ - Foo : ~
501+
502+ .. code-block :: xml
503+
504+ <!-- config/services.xml -->
505+ <?xml version =" 1.0" encoding =" UTF-8" ?>
506+ <container xmlns =" http://symfony.com/schema/dic/services"
507+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
508+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
509+ https://symfony.com/schema/dic/services/services-1.0.xsd"
510+ >
511+ <services >
512+ <service id =" some_decorator" class =" App\Decorator" />
513+
514+ <stack id =" embedded_stack" >
515+ <service alias =" some_decorator" />
516+ <service class =" App\Decorated" />
517+ </stack >
518+
519+ <stack id =" decorated_foo_stack" >
520+ <service parent =" embedded_stack" />
521+ <service class =" Baz" />
522+ <service class =" Bar" />
523+ <service class =" Foo" />
524+ </stack >
525+ </services >
526+ </container >
527+
528+ .. code-block :: php
529+
530+ // config/services.php
531+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
532+
533+ use App\Decorated;
534+ use App\Decorator;
535+
536+ return function(ContainerConfigurator $container) {
537+ $container->services()
538+ ->set('some_decorator', Decorator::class)
539+
540+ ->stack('embedded_stack', [
541+ service('some_decorator'),
542+ inline_service(Decorated::class),
543+ ])
544+
545+ ->stack('decorated_foo_stack', [
546+ inline_service()->parent('embedded_stack'),
547+ inline_service(\Baz::class),
548+ inline_service(\Bar::class),
549+ inline_service(\Foo::class),
550+ ])
551+ ;
552+ };
553+
554+ The result will be::
555+
556+ $this->services['decorated_foo_stack'] = new App\Decorator(new App\Decorated(new Baz(new Bar(new Foo()))));
557+
558+ .. note ::
559+
560+ To change existing stacks (i.e. from a compiler pass), you can access each
561+ frame by its generated id with the following structure:
562+ ``.stack_id.frame_key ``.
563+ From the example above, ``.decorated_foo_stack.1 `` would be a reference to
564+ the inlined ``Baz `` service and ``.decorated_foo_stack.0 `` to the embedded
565+ stack.
566+ To get more explicit ids, you can give a name to each frame:
567+
568+ .. configuration-block ::
569+
570+ .. code-block :: yaml
571+
572+ # ...
573+ decorated_foo_stack :
574+ stack :
575+ first :
576+ parent : embedded_stack
577+ second :
578+ Baz : ~
579+ # ...
580+
581+ .. code-block :: xml
582+
583+ <!-- ... -->
584+ <stack id =" decorated_foo_stack" >
585+ <service id =" first" parent =" embedded_stack" />
586+ <service id =" second" class =" Baz" />
587+ <!-- ... -->
588+ </stack >
589+
590+ .. code-block :: php
591+
592+ // ...
593+ ->stack('decorated_foo_stack', [
594+ 'first' => inline_service()->parent('embedded_stack'),
595+ 'second' => inline_service(\Baz::class),
596+ // ...
597+ ])
598+
599+ The ``Baz `` frame id will now be ``.decorated_foo_stack.second ``.
600+
601+ .. versionadded :: 5.1
602+
603+ The ability to define ``stack `` was introduced in Symfony 5.1.
604+
384605Control the Behavior When the Decorated Service Does Not Exist
385606--------------------------------------------------------------
386607
0 commit comments