Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a84c8b2

Browse filesBrowse 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
Copy full SHA for a84c8b2

File tree

3 files changed

+188
-0
lines changed
Filter options

3 files changed

+188
-0
lines changed

‎service_container/calls.rst

Copy file name to clipboardExpand all lines: service_container/calls.rst
+76Lines 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

Copy file name to clipboardExpand all lines: service_container/definitions.rst
+3Lines 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

Copy file name to clipboardExpand all lines: service_container/injection_types.rst
+109Lines 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)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.