PhpArrayContainer::createService()   F
last analyzed

Complexity

Conditions 34
Paths 18007

Size

Total Lines 132
Code Lines 80

Duplication

Lines 110
Ratio 83.33 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 110
loc 132
rs 2
c 1
b 0
f 0
cc 34
eloc 80
nc 18007
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
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'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
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'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
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) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
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'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
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'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
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'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
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