@@ -313,4 +313,115 @@ The generated code will be the following::
313313
314314 $this->services[Foo::class] = new Baz(new Bar(new Foo()));
315315
316+ Control the Behavior When the Decorated Service Does Not Exist
317+ --------------------------------------------------------------
318+
319+ .. versionadded :: 4.4
320+
321+ The ``decoration_on_invalid `` option has been introduced in Symfony 4.4.
322+ In previous versions, a ``ServiceNotFoundException `` was always thrown.
323+
324+ When you decorate a service that doesn't exist, the ``decoration_on_invalid ``
325+ option allows you to choose the behavior to adopt.
326+
327+ Three different behaviors are available:
328+
329+ * ``exception ``: A ``ServiceNotFoundException `` will be thrown telling that decorator's dependency is missing. (default)
330+ * ``ignore ``: The container will remove the decorator.
331+ * ``null ``: The container will keep the decorator service and will set the decorated one to ``null ``.
332+
333+ .. configuration-block ::
334+
335+ .. code-block :: yaml
336+
337+ # config/services.yaml
338+ Foo : ~
339+
340+ Bar :
341+ decorates : Foo
342+ decoration_on_invalid : ignore
343+ arguments : ['@Bar.inner']
344+
345+ .. code-block :: xml
346+
347+ <!-- config/services.xml -->
348+ <?xml version =" 1.0" encoding =" UTF-8" ?>
349+
350+ <container xmlns =" http://symfony.com/schema/dic/services"
351+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
352+ xsi : schemaLocation =" http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd" >
353+
354+ <services >
355+ <service id =" Foo" />
356+
357+ <service id =" Bar" decorates =" Foo" decoration-on-invalid =" ignore" >
358+ <argument type =" service" id =" Bar.inner" />
359+ </service >
360+ </services >
361+ </container >
362+
363+ .. code-block :: php
364+
365+ // config/services.php
366+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
367+
368+ use Symfony\Component\DependencyInjection\ContainerInterface;
369+
370+ return function(ContainerConfigurator $configurator) {
371+ $services = $configurator->services();
372+
373+ $services->set(Foo::class);
374+
375+ $services->set(Bar::class)
376+ ->decorate(Foo::class, null, 0, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)
377+ ->args([ref(Bar::class.'.inner')])
378+ ;
379+ };
380+
381+ .. caution ::
382+
383+ When using ``null ``, you may have to update the decorator constructor in
384+ order to make decorated dependency nullable.
385+
386+ .. configuration-block ::
387+
388+ .. code-block :: yaml
389+
390+ App\Service\DecoratorService :
391+ decorates : Acme\OptionalBundle\Service\OptionalService
392+ decoration_on_invalid : null
393+ arguments : ['@App\Service\DecoratorService.inner']
394+
395+ .. code-block :: php
396+
397+ namespace App\Service;
398+
399+ use Acme\OptionalBundle\Service\OptionalService;
400+
401+ class DecoratorService
402+ {
403+ private $decorated;
404+
405+ public function __construct(?OptionalService $decorated)
406+ {
407+ $this->decorated = $decorated;
408+ }
409+
410+ public function tellInterestingStuff(): string
411+ {
412+ if (!$this->decorated) {
413+ return 'Just one interesting thing';
414+ }
415+
416+ return $this->decorated->tellInterestingStuff().' + one more interesting thing';
417+ }
418+ }
419+
420+ .. note ::
421+
422+ Sometimes, you may want to add a compiler pass that creates service
423+ definitions on the fly. If you want to decorate such a service,
424+ be sure that your compiler pass is registered with ``PassConfig::TYPE_BEFORE_OPTIMIZATION ``
425+ type so that the decoration pass will be able to find the created services.
426+
316427.. _`Decorator pattern` : https://en.wikipedia.org/wiki/Decorator_pattern
0 commit comments