8000 feature #10991 [DIC] Static injection (codedmonkey) · symfony/symfony-docs@a84c8b2 · GitHub
[go: up one dir, main page]

Skip to content

Commit a84c8b2

Browse files
committed
feature #10991 [DIC] Static injection (codedmonkey)
This PR was merged into the 4.3 branch. Discussion ---------- [DIC] Static injection Hi everyone, As discussed with @nicolas-grekas, the DIC is planned to be capable of ensuring services immutability (using "wither" calls), the implementation can be found here: - symfony/symfony#30212 Commits ------- a598bc0 feat(DI): static injection
2 parents 9bfd35c + a598bc0 commit a84c8b2

File tree

3 files changed

+188
-0
lines changed

3 files changed

+188
-0
lines changed

service_container/calls.rst

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,79 @@ To configure the container to call the ``setLogger`` method, use the ``calls`` k
7777
->call('setLogger', [ref('logger')]);
7878
};
7979
80+
81+
.. versionadded:: 4.3
82+
83+
The ``immutable-setter`` injection was introduced in Symfony 4.3.
84+
85+
In order to provide immutable services, some classes implement immutable setters.
86+
Such setters return a new instance of the configured class
87+
instead of mutating the object they were called on::
88+
89+
namespace App\Service;
90+
91+
use Psr\Log\LoggerInterface;
92+
93+
class MessageGenerator
94+
{
95+
private $logger;
96+
97+
/**
98+
* @return static
99+
*/
100+
public function withLogger(LoggerInterface $logger)
101+
{
102+
$new = clone $this;
103+
$new->logger = $logger;
104+
105+
return $new;
106+
}
107+
108+
// ...
109+
}
110+
111+
Because the method returns a separate cloned instance, configuring such a service means using
112+
the return value of the wither method (``$service = $service->withLogger($logger);``).
113+
The configuration to tell the container it should do so would be like:
114+
115+
.. configuration-block::
116+
117+
.. code-block:: yaml
118+
119+
# config/services.yaml
120+
services:
121+
App\Service\MessageGenerator:
122+
# ...
123+
calls:
124+
- method: withLogger
125+
arguments:
126+
- '@logger'
127+
returns_clone: true
128+
129+
.. code-block:: xml
130+
131+
<!-- config/services.xml -->
132+
<?xml version="1.0" encoding="UTF-8" ?>
133+
<container xmlns="http://symfony.com/schema/dic/services"
134+
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
135+
xsi:schemaLocation="http://symfony.com/schema/dic/services
136+
https://symfony.com/schema/dic/services/services-1.0.xsd">
137+
138+
<services>
139+
<service id="App\Service\MessageGenerator">
140+
<!-- ... -->
141+
<call method="withLogger" returns-clone="true">
142+
<argument type="service" id="logger"/>
143+
</call>
144+
</service>
145+
</services>
146+
</container>
147+
148+
.. code-block:: php
149+
150+
// config/services.php
151+
use App\Service\MessageGenerator;
152+
use Symfony\Component\DependencyInjection\Reference;
153+
154+
$container->register(MessageGenerator::class)
155+
->addMethodCall('withLogger', [new Reference('logger')], true);

service_container/definitions.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ any method calls in the definitions as well::
117117
// configures a new method call
118118
$definition->addMethodCall('setLogger', [new Reference('logger')]);
119119

120+
// configures an immutable-setter
121+
$definition->addMethodCall('withLogger', [new Reference('logger')], true);
122+
120123
// replaces all previously configured method calls with the passed array
121124
$definition->setMethodCalls($methodCalls);
122125

service_container/injection_types.rst

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,112 @@ working with optional dependencies. It is also more difficult to use in
105105
combination with class hierarchies: if a class uses constructor injection
106106
then extending it and overriding the constructor becomes problematic.
107107

108+
Immutable-setter Injection
109+
--------------------------
110+
111+
.. versionadded:: 4.3
112+
113+
The ``immutable-setter`` injection was introduced in Symfony 4.3.
114+
115+
Another possible injection is to use a method which returns a separate instance
116+
by cloning the original service, this approach allows you to make a service immutable::
117+
118+
// ...
119+
use Symfony\Component\Mailer\MailerInterface;
120+
121+
class NewsletterManager
122+
{
123+
private $mailer;
124+
125+
/**
126+
* @required
127+
* @return static
128+
*/
129+
public function withMailer(MailerInterface $mailer)
130+
{
131+
$new = clone $this;
132+
$new->mailer = $mailer;
133+
134+
return $new;
135+
}
136+
137+
// ...
138+
}
139+
140+
In order to use this type of injection, don't forget to configure it:
141+
142+
.. configuration-block::
143+
144+
.. code-block:: yaml
145+
146+
# config/services.yaml
147+
services:
148+
# ...
149+
150+
app.newsletter_manager:
151+
class: App\Mail\NewsletterManager
152+
calls:
153+
- [withMailer, ['@mailer'], true]
154+
155+
.. code-block:: xml
156+
157+
<!-- config/services.xml -->
158+
<?xml version="1.0" encoding="UTF-8" ?>
159+
<container xmlns="http://symfony.com/schema/dic/services"
160+
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
161+
xsi:schemaLocation="http://symfony.com/schema/dic/services
162+
https://symfony.com/schema/dic/services/services-1.0.xsd">
163+
164+
<services>
165+
<!-- ... -->
166+
167+
<service id="app.newsletter_manager" class="App\Mail\NewsletterManager">
168+
<call method="withMailer" returns-clone="true">
169+
<argument type="service" id="mailer"/>
170+
</call>
171+
</service>
172+
</services>
173+
</container>
174+
175+
.. code-block:: php
176+
177+
// config/services.php
178+
use App\Mail\NewsletterManager;
179+
use Symfony\Component\DependencyInjection\Reference;
180+
181+
// ...
182+
$container->register('app.newsletter_manager', NewsletterManager::class)
183+
->addMethodCall('withMailer', [new Reference('mailer')], true);
184+
185+
.. note::
186+
187+
If you decide to use autowiring, this type of injection requires
188+
that you add a ``@return static`` docblock in order for the container
189+
to be capable of registering the method.
190+
191+
This approach is useful if you need to configure your service according to your needs,
192+
so, here's the advantages of immutable-setters:
193+
194+
* Immutable setters works with optional dependencies, this way, if you don't need
195+
a dependency, the setter don't need to be called.
196+
197+
* Like the constructor injection, using immutable setters force the dependency to stay
198+
the same during the lifetime of a service.
199+
200+
* This type of injection works well with traits as the service can be composed,
201+
this way, adapting the service to your application requirements is easier.
202+
203+
* The setter can be called multiple times, this way, adding a dependency to a collection
204+
becomes easier and allows you to add a variable number of dependencies.
205+
206+
The disadvantages are:
207+
208+
* As the setter call is optional, a dependency can be null during execution,
209+
you must check that the dependency is available before calling it.
210+
211+
* Unless the service is declared lazy, it is incompatible with services
212+
that reference each other in what are called circular loops.
213+
108214
Setter Injection
109215
----------------
110216

@@ -180,6 +286,9 @@ This time the advantages are:
180286
the method adds the dependency to a collection. You can then have a variable
181287
number of dependencies.
182288

289+
* Like the immutable-setter one, this type of injection works well with
290+
traits and allows you to compose your service.
291+
183292
The disadvantages of setter injection are:
184293

185294
* The setter can be called more than just at the time of construction so

0 commit comments

Comments
 (0)
0