Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f2b2fdb

Browse files
committed
[DI] Add section about Service Closures
1 parent 08e6ad8 commit f2b2fdb

File tree

3 files changed

+122
-1
lines changed

3 files changed

+122
-1
lines changed

service_container/lazy_services.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ Lazy Services
66

77
.. seealso::
88

9-
Another way to inject services lazily is via a :doc:`service subscriber </service_container/service_subscribers_locators>`.
9+
Other ways to inject services lazily are via a :doc:`service closure </service_container/service_closures>` or
10+
:doc:`service subscriber </service_container/service_subscribers_locators>`.
1011

1112
Why Lazy Services?
1213
------------------
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
.. index::
2+
single: DependencyInjection; Service Closures
3+
4+
Service Closures
5+
================
6+
7+
.. versionadded:: 5.4
8+
9+
The ``service_closure()`` function was introduced in Symfony 5.4.
10+
11+
This feature wraps the injected service into a closure allowing it to be
12+
lazily loaded when and if needed.
13+
This is useful if the service being injected is a bit heavy to instantiate
14+
or is used only in certain cases.
15+
The service is instantiated the first time the closure is called, while
16+
all subsequent calls return the same instance, unless the service is
17+
:doc:`not shared </service_container/shared>`::
18+
19+
// src/Service/MyService.php
20+
namespace App\Service;
21+
22+
use Symfony\Component\Mailer\MailerInterface;
23+
24+
class MyService
25+
{
26+
/**
27+
* @var callable(): MailerInterface
28+
*/
29+
private \Closure $mailer;
30+
31+
public function __construct(\Closure $mailer)
32+
{
33+
$this->mailer = $mailer;
34+
}
35+
36+
public function doSomething(): void
37+
{
38+
// ...
39+
40+
$this->getMailer()->send($email);
41+
}
42+
43+
private function getMailer(): MailerInterface
44+
{
45+
return ($this->mailer)();
46+
}
47+
}
48+
49+
To define a service closure and inject it to another service, create an
50+
argument of type ``service_closure``:
51+
52+
.. configuration-block::
53+
54+
.. code-block:: yaml
55+
56+
# config/services.yaml
57+
services:
58+
App\Service\MyService:
59+
arguments: [!service_closure '@mailer']
60+
61+
.. code-block:: xml
62+
63+
<!-- config/services.xml -->
64+
<?xml version="1.0" encoding="UTF-8" ?>
65+
<container xmlns="http://symfony.com/schema/dic/services"
66+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
67+
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
68+
69+
<services>
70+
<service id="App\Service\MyService">
71+
<argument type="service_closure" id="mailer"/>
72+
</service>
73+
</services>
74+
</container>
75+
76+
.. code-block:: php
77+
78+
// config/services.php
79+
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
80+
81+
use App\Service\MyService;
82+
83+
return function (ContainerConfigurator $containerConfigurator) {
84+
$services = $containerConfigurator->services();
85+
86+
$services->set(MyService::class)
87+
->args([service_closure('mailer')]);
88+
89+
// In case the dependency is optional
90+
// $services->set(MyService::class)
91+
// ->args([service_closure('mailer')->ignoreOnInvalid()]);
92+
};
93+
94+
.. seealso::
95+
96+
Another way to inject services lazily is via a
97+
:doc:`service locator </service_container/service_subscribers_locators>`.
98+
99+
Using a Service Closure in a Compiler Pass
100+
------------------------------------------
101+
102+
In :doc:`compiler passes </service_container/compiler_passes>` you can create
103+
a service closure by wrapping the service reference into an instance of
104+
:class:`Symfony\\Component\\DependencyInjection\\Argument\\ServiceClosureArgument`::
105+
106+
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
107+
use Symfony\Component\DependencyInjection\ContainerBuilder;
108+
use Symfony\Component\DependencyInjection\Reference;
109+
110+
public function process(ContainerBuilder $containerBuilder): void
111+
{
112+
// ...
113+
114+
$myService->addArgument(new ServiceClosureArgument(new Reference('mailer')));
115+
}

service_container/service_subscribers_locators.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ instantiation of the services to be lazy. However, that's not possible using
1212
the explicit dependency injection since services are not all meant to
1313
be ``lazy`` (see :doc:`/service_container/lazy_services`).
1414

15+
.. seealso::
16+
17+
Another way to inject services lazily is via a
18+
:doc:`service closure </service_container/service_closures>`.
19+
1520
This can typically be the case in your controllers, where you may inject several
1621
services in the constructor, but the action called only uses some of them.
1722
Another example are applications that implement the `Command pattern`_

0 commit comments

Comments
 (0)