4
4
How to Work with Scopes
5
5
=======================
6
6
7
- This entry is all about scopes, a somewhat advanced topic related to the
7
+ This article is all about scopes, a somewhat advanced topic related to the
8
8
:doc: `/book/service_container `. If you've ever gotten an error mentioning
9
- "scopes" when creating services, then this entry is for you.
9
+ "scopes" when creating services, then this article is for you.
10
10
11
11
.. note ::
12
12
@@ -25,10 +25,11 @@ The scope of a service controls how long an instance of a service is used
25
25
by the container. The DependencyInjection component provides two generic
26
26
scopes:
27
27
28
- - ``container `` (the default one): The same instance is used each time you
29
- request it from this container.
28
+ ``container `` (the default one):
29
+ The same instance is used each time you ask for it from this container.
30
30
31
- - ``prototype ``: A new instance is created each time you request the service.
31
+ ``prototype ``:
32
+ A new instance is created each time you ask for the service.
32
33
33
34
The
34
35
:class: `Symfony\\ Component\\ HttpKernel\\ DependencyInjection\\ ContainerAwareHttpKernel `
@@ -40,9 +41,9 @@ An Example: Client Scope
40
41
~~~~~~~~~~~~~~~~~~~~~~~~
41
42
42
43
Other than the ``request `` service (which has a simple solution, see the
43
- above note), no services in the default Symfony2 container belong to any
44
+ above note), no services in the default Symfony container belong to any
44
45
scope other than ``container `` and ``prototype ``. But for the purposes of
45
- this entry , imagine there is another scope ``client `` and a service ``client_configuration ``
46
+ this article , imagine there is another scope ``client `` and a service ``client_configuration ``
46
47
that belongs to it. This is not a common situation, but the idea is that
47
48
you may enter and exit multiple ``client `` scopes during a request, and each
48
49
has its own ``client_configuration `` service.
@@ -71,7 +72,7 @@ when compiling the container. Read the sidebar below for more details.
71
72
called *ConfigurationA * here) is passed to it. Life is good!
72
73
73
74
* Your application now needs to do something with another client, and
74
- you've architected your application in such a way that you handle this
75
+ you've designed your application in such a way that you handle this
75
76
by entering a new ``client_configuration `` scope and setting a new
76
77
``client_configuration `` service into the container. Call this
77
78
*ConfigurationB *.
@@ -96,169 +97,28 @@ when compiling the container. Read the sidebar below for more details.
96
97
Using a Service from a Narrower Scope
97
98
-------------------------------------
98
99
99
- There are several solutions to the scope problem:
100
+ There are two solutions to the scope problem:
100
101
101
- * A) Use setter injection if the dependency is ``synchronized `` (see
102
- :ref: `using-synchronized-service `);
103
-
104
- * B) Put your service in the same scope as the dependency (or a narrower one). If
102
+ * A) Put your service in the same scope as the dependency (or a narrower one). If
105
103
you depend on the ``client_configuration `` service, this means putting your
106
104
new service in the ``client `` scope (see :ref: `changing-service-scope `);
107
105
108
- * C ) Pass the entire container to your service and retrieve your dependency from
106
+ * B ) Pass the entire container to your service and retrieve your dependency from
109
107
the container each time you need it to be sure you have the right instance
110
108
-- your service can live in the default ``container `` scope (see
111
109
:ref: `passing-container `).
112
110
113
111
Each scenario is detailed in the following sections.
114
112
115
- .. _using-synchronized-service :
116
-
117
- A) Using a Synchronized Service
118
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
119
-
120
- .. versionadded :: 2.3
121
- Synchronized services were introduced in Symfony 2.3.
122
-
123
- Both injecting the container and setting your service to a narrower scope have
124
- drawbacks. Assume first that the ``client_configuration `` service has been
125
- marked as ``synchronized ``:
126
-
127
- .. configuration-block ::
128
-
129
- .. code-block :: yaml
130
-
131
- # app/config/config.yml
132
- services :
133
- client_configuration :
134
- class : AppBundle\Client\ClientConfiguration
135
- scope : client
136
- synchronized : true
137
- synthetic : true
138
- # ...
139
-
140
- .. code-block :: xml
141
-
142
- <!-- app/config/config.xml -->
143
- <?xml version =" 1.0" encoding =" UTF-8" ?>
144
- <container xmlns =" http://symfony.com/schema/dic/services"
145
- xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
146
- xsi : schemaLocation =" http://symfony.com/schema/dic/services
147
- http://symfony.com/schema/dic/services/services-1.0.xsd"
148
- >
149
-
150
- <services >
151
- <service
152
- id =" client_configuration"
153
- scope =" client"
154
- synchronized =" true"
155
- synthetic =" true"
156
- class =" AppBundle\Client\ClientConfiguration"
157
- />
158
- </services >
159
- </container >
160
-
161
- .. code-block :: php
162
-
163
- // app/config/config.php
164
- use Symfony\Component\DependencyInjection\Definition;
165
-
166
- $definition = new Definition(
167
- 'AppBundle\Client\ClientConfiguration',
168
- array()
169
- );
170
- $definition->setScope('client');
171
- $definition->setSynchronized(true);
172
- $definition->setSynthetic(true);
173
- $container->setDefinition('client_configuration', $definition);
174
-
175
- Now, if you inject this service using setter injection, there are no drawbacks
176
- and everything works without any special code in your service or in your definition::
177
-
178
- // src/AppBundle/Mail/Mailer.php
179
- namespace AppBundle\Mail;
180
-
181
- use AppBundle\Client\ClientConfiguration;
182
-
183
- class Mailer
184
- {
185
- protected $clientConfiguration;
186
-
187
- public function setClientConfiguration(ClientConfiguration $clientConfiguration = null)
188
- {
189
- $this->clientConfiguration = $clientConfiguration;
190
- }
191
-
192
- public function sendEmail()
193
- {
194
- if (null === $this->clientConfiguration) {
195
- // throw an error?
196
- }
197
-
198
- // ... do something using the client configuration here
199
- }
200
- }
201
-
202
- Whenever the ``client `` scope is active, the service container will
203
- automatically call the ``setClientConfiguration() `` method when the
204
- ``client_configuration `` service is set in the container.
205
-
206
- You might have noticed that the ``setClientConfiguration() `` method accepts
207
- ``null `` as a valid value for the ``client_configuration `` argument. That's
208
- because when leaving the ``client `` scope, the ``client_configuration `` instance
209
- can be ``null ``. Of course, you should take care of this possibility in
210
- your code. This should also be taken into account when declaring your service:
211
-
212
- .. configuration-block ::
213
-
214
- .. code-block :: yaml
215
-
216
- # app/config/services.yml
217
- services :
218
- my_mailer :
219
- class : AppBundle\Mail\Mailer
220
- calls :
221
- - [setClientConfiguration, ["@?client_configuration="]]
222
-
223
- .. code-block :: xml
224
-
225
- <!-- app/config/services.xml -->
226
- <services >
227
- <service id =" my_mailer"
228
- class =" AppBundle\Mail\Mailer"
229
- >
230
- <call method =" setClientConfiguration" >
231
- <argument
232
- type =" service"
233
- id =" client_configuration"
234
- on-invalid =" null"
235
- strict =" false"
236
- />
237
- </call >
238
- </service >
239
- </services >
240
-
241
- .. code-block :: php
242
-
243
- // app/config/services.php
244
- use Symfony\Component\DependencyInjection\Definition;
245
- use Symfony\Component\DependencyInjection\ContainerInterface;
113
+ .. note ::
246
114
247
- $definition = $container->setDefinition(
248
- 'my_mailer',
249
- new Definition('AppBundle\Mail\Mailer')
250
- )
251
- ->addMethodCall('setClientConfiguration', array(
252
- new Reference(
253
- 'client_configuration',
254
- ContainerInterface::NULL_ON_INVALID_REFERENCE,
255
- false
256
- )
257
- ));
115
+ In Symfony 2.6 and previous versions, there was another alternative based
116
+ on ``synchronized `` services. However, these kind of services have been
117
+ removed starting from Symfony 2.7.
258
118
259
119
.. _changing-service-scope :
260
120
261
- B ) Changing the Scope of your Service
121
+ A ) Changing the Scope of your Service
262
122
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
263
123
264
124
Changing the scope of a service should be done in its definition. This example
@@ -302,7 +162,7 @@ argument is the ``ClientConfiguration`` object:
302
162
303
163
.. _passing-container :
304
164
305
- C ) Passing the Container as a Dependency of your Service
165
+ B ) Passing the Container as a Dependency of your Service
306
166
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
307
167
308
168
Setting the scope to a narrower one is not always possible (for instance, a
@@ -338,7 +198,7 @@ into your service::
338
198
in the first section (except that Symfony cannot detect that you are
339
199
wrong).
340
200
341
- The service config for this class would look something like this:
201
+ The service configuration for this class would look something like this:
342
202
343
203
.. configuration-block ::
344
204
0 commit comments