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