1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @file |
4
|
|
|
* Contains \Drupal\Component\DependencyInjection\PhpArrayContainer. |
5
|
|
|
*/ |
6
|
|
|
|
7
|
|
|
namespace Drupal\Component\DependencyInjection; |
8
|
|
|
|
9
|
|
|
use Symfony\Component\DependencyInjection\ContainerInterface; |
10
|
|
|
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; |
11
|
|
|
use Symfony\Component\DependencyInjection\Exception\RuntimeException; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* Provides a container optimized for Drupal's needs. |
15
|
|
|
* |
16
|
|
|
* This container implementation is compatible with the default Symfony |
17
|
|
|
* dependency injection container and similar to the Symfony ContainerBuilder |
18
|
|
|
* class, but optimized for speed. |
19
|
|
|
* |
20
|
|
|
* It is based on a human-readable PHP array container definition with a |
21
|
|
|
* structure very similar to the YAML container definition. |
22
|
|
|
* |
23
|
|
|
* @see \Drupal\Component\DependencyInjection\Container |
24
|
|
|
* @see \Drupal\Component\DependencyInjection\Dumper\PhpArrayDumper |
25
|
|
|
* @see \Drupal\Component\DependencyInjection\DependencySerializationTrait |
26
|
|
|
* |
27
|
|
|
* @ingroup container |
28
|
|
|
*/ |
29
|
|
|
class PhpArrayContainer extends Container { |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* {@inheritdoc} |
33
|
|
|
*/ |
34
|
|
View Code Duplication |
public function __construct(array $container_definition = array()) { |
35
|
|
|
if (isset($container_definition['machine_format']) && $container_definition['machine_format'] === TRUE) { |
36
|
|
|
throw new InvalidArgumentException('The machine-optimized format is not supported by this class. Use a human-readable format instead, e.g. as produced by \Drupal\Component\DependencyInjection\Dumper\PhpArrayDumper.'); |
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
// Do not call the parent's constructor as it would bail on the |
40
|
|
|
// machine-optimized format. |
41
|
|
|
$this->aliases = isset($container_definition['aliases']) ? $container_definition['aliases'] : array(); |
42
|
|
|
$this->parameters = isset($container_definition['parameters']) ? $container_definition['parameters'] : array(); |
43
|
|
|
$this->serviceDefinitions = isset($container_definition['services']) ? $container_definition['services'] : array(); |
44
|
|
|
$this->frozen = isset($container_definition['frozen']) ? $container_definition['frozen'] : FALSE; |
45
|
|
|
|
46
|
|
|
// Register the service_container with itself. |
47
|
|
|
$this->services['service_container'] = $this; |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* {@inheritdoc} |
52
|
|
|
*/ |
53
|
|
|
protected function createService(array $definition, $id) { |
54
|
|
|
// This method is a verbatim copy of |
55
|
|
|
// \Drupal\Component\DependencyInjection\Container::createService |
56
|
|
|
// except for the following difference: |
57
|
|
|
// - There are no instanceof checks on \stdClass, which are used in the |
58
|
|
|
// parent class to avoid resolving services and parameters when it is |
59
|
|
|
// known from dumping that there is nothing to resolve. |
60
|
|
View Code Duplication |
if (isset($definition['synthetic']) && $definition['synthetic'] === TRUE) { |
|
|
|
|
61
|
|
|
throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The service container does not know how to construct this service. The service will need to be set before it is first used.', $id)); |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
$arguments = array(); |
65
|
|
|
if (isset($definition['arguments'])) { |
66
|
|
|
$arguments = $this->resolveServicesAndParameters($definition['arguments']); |
67
|
|
|
} |
68
|
|
|
|
69
|
|
View Code Duplication |
if (isset($definition['file'])) { |
|
|
|
|
70
|
|
|
$file = $this->frozen ? $definition['file'] : current($this->resolveServicesAndParameters(array($definition['file']))); |
71
|
|
|
require_once $file; |
72
|
|
|
} |
73
|
|
|
|
74
|
|
View Code Duplication |
if (isset($definition['factory'])) { |
|
|
|
|
75
|
|
|
$factory = $definition['factory']; |
76
|
|
|
if (is_array($factory)) { |
77
|
|
|
$factory = $this->resolveServicesAndParameters(array($factory[0], $factory[1])); |
78
|
|
|
} |
79
|
|
|
elseif (!is_string($factory)) { |
80
|
|
|
throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory', $id)); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
$service = call_user_func_array($factory, $arguments); |
84
|
|
|
} |
85
|
|
|
else { |
86
|
|
|
$class = $this->frozen ? $definition['class'] : current($this->resolveServicesAndParameters(array($definition['class']))); |
87
|
|
|
$length = isset($definition['arguments_count']) ? $definition['arguments_count'] : count($arguments); |
88
|
|
|
|
89
|
|
|
// Optimize class instantiation for services with up to 10 parameters as |
90
|
|
|
// reflection is noticeably slow. |
91
|
|
|
switch ($length) { |
92
|
|
|
case 0: |
93
|
|
|
$service = new $class(); |
94
|
|
|
break; |
95
|
|
|
|
96
|
|
|
case 1: |
97
|
|
|
$service = new $class($arguments[0]); |
98
|
|
|
break; |
99
|
|
|
|
100
|
|
|
case 2: |
101
|
|
|
$service = new $class($arguments[0], $arguments[1]); |
102
|
|
|
break; |
103
|
|
|
|
104
|
|
|
case 3: |
105
|
|
|
$service = new $class($arguments[0], $arguments[1], $arguments[2]); |
106
|
|
|
break; |
107
|
|
|
|
108
|
|
|
case 4: |
109
|
|
|
$service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3]); |
110
|
|
|
break; |
111
|
|
|
|
112
|
|
|
case 5: |
113
|
|
|
$service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4]); |
114
|
|
|
break; |
115
|
|
|
|
116
|
|
|
case 6: |
117
|
|
|
$service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5]); |
118
|
|
|
break; |
119
|
|
|
|
120
|
|
|
case 7: |
121
|
|
|
$service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6]); |
122
|
|
|
break; |
123
|
|
|
|
124
|
|
|
case 8: |
125
|
|
|
$service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6], $arguments[7]); |
126
|
|
|
break; |
127
|
|
|
|
128
|
|
|
case 9: |
129
|
|
|
$service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6], $arguments[7], $arguments[8]); |
130
|
|
|
break; |
131
|
|
|
|
132
|
|
|
case 10: |
133
|
|
|
$service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6], $arguments[7], $arguments[8], $arguments[9]); |
134
|
|
|
break; |
135
|
|
|
|
136
|
|
|
default: |
137
|
|
|
$r = new \ReflectionClass($class); |
138
|
|
|
$service = $r->newInstanceArgs($arguments); |
139
|
|
|
break; |
140
|
|
|
} |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
// Share the service if it is public. |
144
|
|
View Code Duplication |
if (!isset($definition['public']) || $definition['public'] !== FALSE) { |
|
|
|
|
145
|
|
|
// Forward compatibility fix for Symfony 2.8 update. |
146
|
|
|
if (!isset($definition['shared']) || $definition['shared'] !== FALSE) { |
147
|
|
|
$this->services[$id] = $service; |
148
|
|
|
} |
149
|
|
|
} |
150
|
|
|
|
151
|
|
View Code Duplication |
if (isset($definition['calls'])) { |
|
|
|
|
152
|
|
|
foreach ($definition['calls'] as $call) { |
153
|
|
|
$method = $call[0]; |
154
|
|
|
$arguments = array(); |
155
|
|
|
if (!empty($call[1])) { |
156
|
|
|
$arguments = $call[1]; |
157
|
|
|
$arguments = $this->resolveServicesAndParameters($arguments); |
158
|
|
|
} |
159
|
|
|
call_user_func_array(array($service, $method), $arguments); |
160
|
|
|
} |
161
|
|
|
} |
162
|
|
|
|
163
|
|
View Code Duplication |
if (isset($definition['properties'])) { |
|
|
|
|
164
|
|
|
$definition['properties'] = $this->resolveServicesAndParameters($definition['properties']); |
165
|
|
|
foreach ($definition['properties'] as $key => $value) { |
166
|
|
|
$service->{$key} = $value; |
167
|
|
|
} |
168
|
|
|
} |
169
|
|
|
|
170
|
|
View Code Duplication |
if (isset($definition['configurator'])) { |
|
|
|
|
171
|
|
|
$callable = $definition['configurator']; |
172
|
|
|
if (is_array($callable)) { |
173
|
|
|
$callable = $this->resolveServicesAndParameters($callable); |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
if (!is_callable($callable)) { |
177
|
|
|
throw new InvalidArgumentException(sprintf('The configurator for class "%s" is not a callable.', get_class($service))); |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
call_user_func($callable, $service); |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
return $service; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* {@inheritdoc} |
188
|
|
|
*/ |
189
|
|
|
protected function resolveServicesAndParameters($arguments) { |
190
|
|
|
// This method is different from the parent method only for the following |
191
|
|
|
// cases: |
192
|
|
|
// - A service is denoted by '@service' and not by a \stdClass object. |
193
|
|
|
// - A parameter is denoted by '%parameter%' and not by a \stdClass object. |
194
|
|
|
// - The depth of the tree representing the arguments is not known in |
195
|
|
|
// advance, so it needs to be fully traversed recursively. |
196
|
|
|
foreach ($arguments as $key => $argument) { |
197
|
|
|
if ($argument instanceof \stdClass) { |
198
|
|
|
$type = $argument->type; |
199
|
|
|
|
200
|
|
|
// Private services are a special flavor: In case a private service is |
201
|
|
|
// only used by one other service, the ContainerBuilder uses a |
202
|
|
|
// Definition object as an argument, which does not have an ID set. |
203
|
|
|
// Therefore the format uses a \stdClass object to store the definition |
204
|
|
|
// and to be able to create the service on the fly. |
205
|
|
|
// |
206
|
|
|
// Note: When constructing a private service by hand, 'id' must be set. |
207
|
|
|
// |
208
|
|
|
// The PhpArrayDumper just uses the hash of the private service |
209
|
|
|
// definition to generate a unique ID. |
210
|
|
|
// |
211
|
|
|
// @see \Drupal\Component\DependecyInjection\Dumper\OptimizedPhpArrayDumper::getPrivateServiceCall |
212
|
|
|
if ($type == 'private_service') { |
213
|
|
|
$id = $argument->id; |
214
|
|
|
|
215
|
|
|
// Check if the private service already exists - in case it is shared. |
216
|
|
|
if (!empty($argument->shared) && isset($this->privateServices[$id])) { |
217
|
|
|
$arguments[$key] = $this->privateServices[$id]; |
218
|
|
|
continue; |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
// Create a private service from a service definition. |
222
|
|
|
$arguments[$key] = $this->createService($argument->value, $id); |
223
|
|
|
if (!empty($argument->shared)) { |
224
|
|
|
$this->privateServices[$id] = $arguments[$key]; |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
continue; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
if ($type !== NULL) { |
231
|
|
|
throw new InvalidArgumentException("Undefined type '$type' while resolving parameters and services."); |
232
|
|
|
} |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
if (is_array($argument)) { |
236
|
|
|
$arguments[$key] = $this->resolveServicesAndParameters($argument); |
237
|
|
|
continue; |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
if (!is_string($argument)) { |
241
|
|
|
continue; |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
// Resolve parameters. |
245
|
|
|
if (isset($argument[0]) && $argument[0] === '%') { |
246
|
|
|
$name = substr($argument, 1, -1); |
247
|
|
|
if (!isset($this->parameters[$name])) { |
248
|
|
|
$arguments[$key] = $this->getParameter($name); |
249
|
|
|
// This can never be reached as getParameter() throws an Exception, |
250
|
|
|
// because we already checked that the parameter is not set above. |
251
|
|
|
} |
252
|
|
|
$argument = $this->parameters[$name]; |
253
|
|
|
$arguments[$key] = $argument; |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
// Resolve services. |
257
|
|
|
if (isset($argument[0]) && $argument[0] === '@') { |
258
|
|
|
$id = substr($argument, 1); |
259
|
|
|
$invalid_behavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; |
260
|
|
|
if ($id[0] === '?') { |
261
|
|
|
$id = substr($id, 1); |
262
|
|
|
$invalid_behavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; |
263
|
|
|
} |
264
|
|
|
if (isset($this->services[$id])) { |
265
|
|
|
$arguments[$key] = $this->services[$id]; |
266
|
|
|
} |
267
|
|
|
else { |
268
|
|
|
$arguments[$key] = $this->get($id, $invalid_behavior); |
269
|
|
|
} |
270
|
|
|
} |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
return $arguments; |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
} |
277
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.