ContainerBuilder::createService()   F
last analyzed

Complexity

Conditions 20
Paths 678

Size

Total Lines 83
Code Lines 49

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 83
rs 2.4096
cc 20
eloc 49
nc 678
nop 3

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
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Symfony\Component\DependencyInjection;
13
14
use Symfony\Component\DependencyInjection\Compiler\Compiler;
15
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
16
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
17
use Symfony\Component\DependencyInjection\Exception\BadMethodCallException;
18
use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
19
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
20
use Symfony\Component\DependencyInjection\Exception\LogicException;
21
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
22
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
23
use Symfony\Component\Config\Resource\FileResource;
24
use Symfony\Component\Config\Resource\ResourceInterface;
25
use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface;
26
use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator;
27
use Symfony\Component\ExpressionLanguage\Expression;
28
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
29
30
/**
31
 * ContainerBuilder is a DI container that provides an API to easily describe services.
32
 *
33
 * @author Fabien Potencier <[email protected]>
34
 *
35
 * @api
36
 */
37
class ContainerBuilder extends Container implements TaggedContainerInterface
38
{
39
    /**
40
     * @var ExtensionInterface[]
41
     */
42
    private $extensions = array();
43
44
    /**
45
     * @var ExtensionInterface[]
46
     */
47
    private $extensionsByNs = array();
48
49
    /**
50
     * @var Definition[]
51
     */
52
    private $definitions = array();
53
54
    /**
55
     * @var Definition[]
56
     */
57
    private $obsoleteDefinitions = array();
58
59
    /**
60
     * @var Alias[]
61
     */
62
    private $aliasDefinitions = array();
63
64
    /**
65
     * @var ResourceInterface[]
66
     */
67
    private $resources = array();
68
69
    private $extensionConfigs = array();
70
71
    /**
72
     * @var Compiler
73
     */
74
    private $compiler;
75
76
    private $trackResources = true;
77
78
    /**
79
     * @var InstantiatorInterface|null
80
     */
81
    private $proxyInstantiator;
82
83
    /**
84
     * @var ExpressionLanguage|null
85
     */
86
    private $expressionLanguage;
87
88
    /**
89
     * @var ExpressionFunctionProviderInterface[]
90
     */
91
    private $expressionLanguageProviders = array();
92
93
    /**
94
     * Sets the track resources flag.
95
     *
96
     * If you are not using the loaders and therefore don't want
97
     * to depend on the Config component, set this flag to false.
98
     *
99
     * @param bool $track true if you want to track resources, false otherwise
100
     */
101
    public function setResourceTracking($track)
102
    {
103
        $this->trackResources = (bool) $track;
104
    }
105
106
    /**
107
     * Checks if resources are tracked.
108
     *
109
     * @return bool true if resources are tracked, false otherwise
110
     */
111
    public function isTrackingResources()
112
    {
113
        return $this->trackResources;
114
    }
115
116
    /**
117
     * Sets the instantiator to be used when fetching proxies.
118
     *
119
     * @param InstantiatorInterface $proxyInstantiator
120
     */
121
    public function setProxyInstantiator(InstantiatorInterface $proxyInstantiator)
122
    {
123
        $this->proxyInstantiator = $proxyInstantiator;
124
    }
125
126
    /**
127
     * Registers an extension.
128
     *
129
     * @param ExtensionInterface $extension An extension instance
130
     *
131
     * @api
132
     */
133
    public function registerExtension(ExtensionInterface $extension)
134
    {
135
        $this->extensions[$extension->getAlias()] = $extension;
136
137
        if (false !== $extension->getNamespace()) {
138
            $this->extensionsByNs[$extension->getNamespace()] = $extension;
139
        }
140
    }
141
142
    /**
143
     * Returns an extension by alias or namespace.
144
     *
145
     * @param string $name An alias or a namespace
146
     *
147
     * @return ExtensionInterface An extension instance
148
     *
149
     * @throws LogicException if the extension is not registered
150
     *
151
     * @api
152
     */
153
    public function getExtension($name)
154
    {
155
        if (isset($this->extensions[$name])) {
156
            return $this->extensions[$name];
157
        }
158
159
        if (isset($this->extensionsByNs[$name])) {
160
            return $this->extensionsByNs[$name];
161
        }
162
163
        throw new LogicException(sprintf('Container extension "%s" is not registered', $name));
164
    }
165
166
    /**
167
     * Returns all registered extensions.
168
     *
169
     * @return ExtensionInterface[] An array of ExtensionInterface
170
     *
171
     * @api
172
     */
173
    public function getExtensions()
174
    {
175
        return $this->extensions;
176
    }
177
178
    /**
179
     * Checks if we have an extension.
180
     *
181
     * @param string $name The name of the extension
182
     *
183
     * @return bool If the extension exists
184
     *
185
     * @api
186
     */
187
    public function hasExtension($name)
188
    {
189
        return isset($this->extensions[$name]) || isset($this->extensionsByNs[$name]);
190
    }
191
192
    /**
193
     * Returns an array of resources loaded to build this configuration.
194
     *
195
     * @return ResourceInterface[] An array of resources
196
     *
197
     * @api
198
     */
199
    public function getResources()
200
    {
201
        return array_unique($this->resources);
202
    }
203
204
    /**
205
     * Adds a resource for this configuration.
206
     *
207
     * @param ResourceInterface $resource A resource instance
208
     *
209
     * @return ContainerBuilder The current instance
210
     *
211
     * @api
212
     */
213
    public function addResource(ResourceInterface $resource)
214
    {
215
        if (!$this->trackResources) {
216
            return $this;
217
        }
218
219
        $this->resources[] = $resource;
220
221
        return $this;
222
    }
223
224
    /**
225
     * Sets the resources for this configuration.
226
     *
227
     * @param ResourceInterface[] $resources An array of resources
228
     *
229
     * @return ContainerBuilder The current instance
230
     *
231
     * @api
232
     */
233
    public function setResources(array $resources)
234
    {
235
        if (!$this->trackResources) {
236
            return $this;
237
        }
238
239
        $this->resources = $resources;
240
241
        return $this;
242
    }
243
244
    /**
245
     * Adds the object class hierarchy as resources.
246
     *
247
     * @param object $object An object instance
248
     *
249
     * @return ContainerBuilder The current instance
250
     *
251
     * @api
252
     */
253
    public function addObjectResource($object)
254
    {
255
        if ($this->trackResources) {
256
            $this->addClassResource(new \ReflectionClass($object));
257
        }
258
259
        return $this;
260
    }
261
262
    /**
263
     * Adds the given class hierarchy as resources.
264
     *
265
     * @param \ReflectionClass $class
266
     *
267
     * @return ContainerBuilder The current instance
268
     */
269
    public function addClassResource(\ReflectionClass $class)
270
    {
271
        if (!$this->trackResources) {
272
            return $this;
273
        }
274
275
        do {
276
            $this->addResource(new FileResource($class->getFileName()));
277
        } while ($class = $class->getParentClass());
278
279
        return $this;
280
    }
281
282
    /**
283
     * Loads the configuration for an extension.
284
     *
285
     * @param string $extension The extension alias or namespace
286
     * @param array  $values    An array of values that customizes the extension
287
     *
288
     * @return ContainerBuilder The current instance
289
     *
290
     * @throws BadMethodCallException When this ContainerBuilder is frozen
291
     * @throws \LogicException        if the container is frozen
292
     *
293
     * @api
294
     */
295
    public function loadFromExtension($extension, array $values = array())
296
    {
297
        if ($this->isFrozen()) {
298
            throw new BadMethodCallException('Cannot load from an extension on a frozen container.');
299
        }
300
301
        $namespace = $this->getExtension($extension)->getAlias();
302
303
        $this->extensionConfigs[$namespace][] = $values;
304
305
        return $this;
306
    }
307
308
    /**
309
     * Adds a compiler pass.
310
     *
311
     * @param CompilerPassInterface $pass A compiler pass
312
     * @param string                $type The type of compiler pass
313
     *
314
     * @return ContainerBuilder The current instance
315
     *
316
     * @api
317
     */
318
    public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION)
319
    {
320
        $this->getCompiler()->addPass($pass, $type);
321
322
        $this->addObjectResource($pass);
323
324
        return $this;
325
    }
326
327
    /**
328
     * Returns the compiler pass config which can then be modified.
329
     *
330
     * @return PassConfig The compiler pass config
331
     *
332
     * @api
333
     */
334
    public function getCompilerPassConfig()
335
    {
336
        return $this->getCompiler()->getPassConfig();
337
    }
338
339
    /**
340
     * Returns the compiler.
341
     *
342
     * @return Compiler The compiler
343
     *
344
     * @api
345
     */
346
    public function getCompiler()
347
    {
348
        if (null === $this->compiler) {
349
            $this->compiler = new Compiler();
350
        }
351
352
        return $this->compiler;
353
    }
354
355
    /**
356
     * Returns all Scopes.
357
     *
358
     * @return array An array of scopes
359
     *
360
     * @api
361
     */
362
    public function getScopes()
363
    {
364
        return $this->scopes;
365
    }
366
367
    /**
368
     * Returns all Scope children.
369
     *
370
     * @return array An array of scope children.
371
     *
372
     * @api
373
     */
374
    public function getScopeChildren()
375
    {
376
        return $this->scopeChildren;
377
    }
378
379
    /**
380
     * Sets a service.
381
     *
382
     * @param string $id      The service identifier
383
     * @param object $service The service instance
384
     * @param string $scope   The scope
385
     *
386
     * @throws BadMethodCallException When this ContainerBuilder is frozen
387
     *
388
     * @api
389
     */
390
    public function set($id, $service, $scope = self::SCOPE_CONTAINER)
391
    {
392
        $id = strtolower($id);
393
394
        if ($this->isFrozen()) {
395
            // setting a synthetic service on a frozen container is alright
396
            if (
397
                (!isset($this->definitions[$id]) && !isset($this->obsoleteDefinitions[$id]))
398
                    ||
399
                (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())
400
                    ||
401
                (isset($this->obsoleteDefinitions[$id]) && !$this->obsoleteDefinitions[$id]->isSynthetic())
402
            ) {
403
                throw new BadMethodCallException(sprintf('Setting service "%s" on a frozen container is not allowed.', $id));
404
            }
405
        }
406
407
        if (isset($this->definitions[$id])) {
408
            $this->obsoleteDefinitions[$id] = $this->definitions[$id];
409
        }
410
411
        unset($this->definitions[$id], $this->aliasDefinitions[$id]);
412
413
        parent::set($id, $service, $scope);
414
415
        if (isset($this->obsoleteDefinitions[$id]) && $this->obsoleteDefinitions[$id]->isSynchronized(false)) {
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...ition::isSynchronized() has been deprecated with message: since version 2.7, will be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
416
            $this->synchronize($id);
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...rBuilder::synchronize() has been deprecated with message: since version 2.7, will be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
417
        }
418
    }
419
420
    /**
421
     * Removes a service definition.
422
     *
423
     * @param string $id The service identifier
424
     *
425
     * @api
426
     */
427
    public function removeDefinition($id)
428
    {
429
        unset($this->definitions[strtolower($id)]);
430
    }
431
432
    /**
433
     * Returns true if the given service is defined.
434
     *
435
     * @param string $id The service identifier
436
     *
437
     * @return bool true if the service is defined, false otherwise
438
     *
439
     * @api
440
     */
441
    public function has($id)
442
    {
443
        $id = strtolower($id);
444
445
        return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || parent::has($id);
446
    }
447
448
    /**
449
     * Gets a service.
450
     *
451
     * @param string $id              The service identifier
452
     * @param int    $invalidBehavior The behavior when the service does not exist
453
     *
454
     * @return object The associated service
455
     *
456
     * @throws InvalidArgumentException when no definitions are available
457
     * @throws InactiveScopeException   when the current scope is not active
458
     * @throws LogicException           when a circular dependency is detected
459
     * @throws \Exception
460
     *
461
     * @see Reference
462
     *
463
     * @api
464
     */
465
    public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE)
466
    {
467
        $id = strtolower($id);
468
469
        if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
470
            return $service;
471
        }
472
473
        if (!array_key_exists($id, $this->definitions) && isset($this->aliasDefinitions[$id])) {
474
            return $this->get($this->aliasDefinitions[$id]);
475
        }
476
477
        try {
478
            $definition = $this->getDefinition($id);
479
        } catch (InvalidArgumentException $e) {
480
            if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) {
481
                return;
482
            }
483
484
            throw $e;
485
        }
486
487
        $this->loading[$id] = true;
488
489
        try {
490
            $service = $this->createService($definition, $id);
491
        } catch (\Exception $e) {
492
            unset($this->loading[$id]);
493
494
            if ($e instanceof InactiveScopeException && self::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) {
495
                return;
496
            }
497
498
            throw $e;
499
        }
500
501
        unset($this->loading[$id]);
502
503
        return $service;
504
    }
505
506
    /**
507
     * Merges a ContainerBuilder with the current ContainerBuilder configuration.
508
     *
509
     * Service definitions overrides the current defined ones.
510
     *
511
     * But for parameters, they are overridden by the current ones. It allows
512
     * the parameters passed to the container constructor to have precedence
513
     * over the loaded ones.
514
     *
515
     * $container = new ContainerBuilder(array('foo' => 'bar'));
516
     * $loader = new LoaderXXX($container);
517
     * $loader->load('resource_name');
518
     * $container->register('foo', new stdClass());
519
     *
520
     * In the above example, even if the loaded resource defines a foo
521
     * parameter, the value will still be 'bar' as defined in the ContainerBuilder
522
     * constructor.
523
     *
524
     * @param ContainerBuilder $container The ContainerBuilder instance to merge.
525
     *
526
     * @throws BadMethodCallException When this ContainerBuilder is frozen
527
     *
528
     * @api
529
     */
530
    public function merge(ContainerBuilder $container)
531
    {
532
        if ($this->isFrozen()) {
533
            throw new BadMethodCallException('Cannot merge on a frozen container.');
534
        }
535
536
        $this->addDefinitions($container->getDefinitions());
537
        $this->addAliases($container->getAliases());
538
        $this->getParameterBag()->add($container->getParameterBag()->all());
539
540
        if ($this->trackResources) {
541
            foreach ($container->getResources() as $resource) {
542
                $this->addResource($resource);
543
            }
544
        }
545
546
        foreach ($this->extensions as $name => $extension) {
547
            if (!isset($this->extensionConfigs[$name])) {
548
                $this->extensionConfigs[$name] = array();
549
            }
550
551
            $this->extensionConfigs[$name] = array_merge($this->extensionConfigs[$name], $container->getExtensionConfig($name));
552
        }
553
    }
554
555
    /**
556
     * Returns the configuration array for the given extension.
557
     *
558
     * @param string $name The name of the extension
559
     *
560
     * @return array An array of configuration
561
     *
562
     * @api
563
     */
564
    public function getExtensionConfig($name)
565
    {
566
        if (!isset($this->extensionConfigs[$name])) {
567
            $this->extensionConfigs[$name] = array();
568
        }
569
570
        return $this->extensionConfigs[$name];
571
    }
572
573
    /**
574
     * Prepends a config array to the configs of the given extension.
575
     *
576
     * @param string $name   The name of the extension
577
     * @param array  $config The config to set
578
     */
579
    public function prependExtensionConfig($name, array $config)
580
    {
581
        if (!isset($this->extensionConfigs[$name])) {
582
            $this->extensionConfigs[$name] = array();
583
        }
584
585
        array_unshift($this->extensionConfigs[$name], $config);
586
    }
587
588
    /**
589
     * Compiles the container.
590
     *
591
     * This method passes the container to compiler
592
     * passes whose job is to manipulate and optimize
593
     * the container.
594
     *
595
     * The main compiler passes roughly do four things:
596
     *
597
     *  * The extension configurations are merged;
598
     *  * Parameter values are resolved;
599
     *  * The parameter bag is frozen;
600
     *  * Extension loading is disabled.
601
     *
602
     * @api
603
     */
604
    public function compile()
605
    {
606
        $compiler = $this->getCompiler();
607
608
        if ($this->trackResources) {
609
            foreach ($compiler->getPassConfig()->getPasses() as $pass) {
610
                $this->addObjectResource($pass);
611
            }
612
        }
613
614
        $compiler->compile($this);
615
616
        if ($this->trackResources) {
617
            foreach ($this->definitions as $definition) {
618
                if ($definition->isLazy() && ($class = $definition->getClass()) && class_exists($class)) {
619
                    $this->addClassResource(new \ReflectionClass($class));
620
                }
621
            }
622
        }
623
624
        $this->extensionConfigs = array();
625
626
        parent::compile();
627
    }
628
629
    /**
630
     * Gets all service ids.
631
     *
632
     * @return array An array of all defined service ids
633
     */
634
    public function getServiceIds()
635
    {
636
        return array_unique(array_merge(array_keys($this->getDefinitions()), array_keys($this->aliasDefinitions), parent::getServiceIds()));
637
    }
638
639
    /**
640
     * Adds the service aliases.
641
     *
642
     * @param array $aliases An array of aliases
643
     *
644
     * @api
645
     */
646
    public function addAliases(array $aliases)
647
    {
648
        foreach ($aliases as $alias => $id) {
649
            $this->setAlias($alias, $id);
650
        }
651
    }
652
653
    /**
654
     * Sets the service aliases.
655
     *
656
     * @param array $aliases An array of aliases
657
     *
658
     * @api
659
     */
660
    public function setAliases(array $aliases)
661
    {
662
        $this->aliasDefinitions = array();
663
        $this->addAliases($aliases);
664
    }
665
666
    /**
667
     * Sets an alias for an existing service.
668
     *
669
     * @param string       $alias The alias to create
670
     * @param string|Alias $id    The service to alias
671
     *
672
     * @throws InvalidArgumentException if the id is not a string or an Alias
673
     * @throws InvalidArgumentException if the alias is for itself
674
     *
675
     * @api
676
     */
677
    public function setAlias($alias, $id)
678
    {
679
        $alias = strtolower($alias);
680
681
        if (is_string($id)) {
682
            $id = new Alias($id);
683
        } elseif (!$id instanceof Alias) {
684
            throw new InvalidArgumentException('$id must be a string, or an Alias object.');
685
        }
686
687
        if ($alias === (string) $id) {
688
            throw new InvalidArgumentException(sprintf('An alias can not reference itself, got a circular reference on "%s".', $alias));
689
        }
690
691
        unset($this->definitions[$alias]);
692
693
        $this->aliasDefinitions[$alias] = $id;
694
    }
695
696
    /**
697
     * Removes an alias.
698
     *
699
     * @param string $alias The alias to remove
700
     *
701
     * @api
702
     */
703
    public function removeAlias($alias)
704
    {
705
        unset($this->aliasDefinitions[strtolower($alias)]);
706
    }
707
708
    /**
709
     * Returns true if an alias exists under the given identifier.
710
     *
711
     * @param string $id The service identifier
712
     *
713
     * @return bool true if the alias exists, false otherwise
714
     *
715
     * @api
716
     */
717
    public function hasAlias($id)
718
    {
719
        return isset($this->aliasDefinitions[strtolower($id)]);
720
    }
721
722
    /**
723
     * Gets all defined aliases.
724
     *
725
     * @return Alias[] An array of aliases
726
     *
727
     * @api
728
     */
729
    public function getAliases()
730
    {
731
        return $this->aliasDefinitions;
732
    }
733
734
    /**
735
     * Gets an alias.
736
     *
737
     * @param string $id The service identifier
738
     *
739
     * @return Alias An Alias instance
740
     *
741
     * @throws InvalidArgumentException if the alias does not exist
742
     *
743
     * @api
744
     */
745
    public function getAlias($id)
746
    {
747
        $id = strtolower($id);
748
749
        if (!isset($this->aliasDefinitions[$id])) {
750
            throw new InvalidArgumentException(sprintf('The service alias "%s" does not exist.', $id));
751
        }
752
753
        return $this->aliasDefinitions[$id];
754
    }
755
756
    /**
757
     * Registers a service definition.
758
     *
759
     * This methods allows for simple registration of service definition
760
     * with a fluid interface.
761
     *
762
     * @param string $id    The service identifier
763
     * @param string $class The service class
764
     *
765
     * @return Definition A Definition instance
766
     *
767
     * @api
768
     */
769
    public function register($id, $class = null)
770
    {
771
        return $this->setDefinition($id, new Definition($class));
772
    }
773
774
    /**
775
     * Adds the service definitions.
776
     *
777
     * @param Definition[] $definitions An array of service definitions
778
     *
779
     * @api
780
     */
781
    public function addDefinitions(array $definitions)
782
    {
783
        foreach ($definitions as $id => $definition) {
784
            $this->setDefinition($id, $definition);
785
        }
786
    }
787
788
    /**
789
     * Sets the service definitions.
790
     *
791
     * @param Definition[] $definitions An array of service definitions
792
     *
793
     * @api
794
     */
795
    public function setDefinitions(array $definitions)
796
    {
797
        $this->definitions = array();
798
        $this->addDefinitions($definitions);
799
    }
800
801
    /**
802
     * Gets all service definitions.
803
     *
804
     * @return Definition[] An array of Definition instances
805
     *
806
     * @api
807
     */
808
    public function getDefinitions()
809
    {
810
        return $this->definitions;
811
    }
812
813
    /**
814
     * Sets a service definition.
815
     *
816
     * @param string     $id         The service identifier
817
     * @param Definition $definition A Definition instance
818
     *
819
     * @return Definition the service definition
820
     *
821
     * @throws BadMethodCallException When this ContainerBuilder is frozen
822
     *
823
     * @api
824
     */
825
    public function setDefinition($id, Definition $definition)
826
    {
827
        if ($this->isFrozen()) {
828
            throw new BadMethodCallException('Adding definition to a frozen container is not allowed');
829
        }
830
831
        $id = strtolower($id);
832
833
        unset($this->aliasDefinitions[$id]);
834
835
        return $this->definitions[$id] = $definition;
836
    }
837
838
    /**
839
     * Returns true if a service definition exists under the given identifier.
840
     *
841
     * @param string $id The service identifier
842
     *
843
     * @return bool true if the service definition exists, false otherwise
844
     *
845
     * @api
846
     */
847
    public function hasDefinition($id)
848
    {
849
        return array_key_exists(strtolower($id), $this->definitions);
850
    }
851
852
    /**
853
     * Gets a service definition.
854
     *
855
     * @param string $id The service identifier
856
     *
857
     * @return Definition A Definition instance
858
     *
859
     * @throws InvalidArgumentException if the service definition does not exist
860
     *
861
     * @api
862
     */
863 View Code Duplication
    public function getDefinition($id)
864
    {
865
        $id = strtolower($id);
866
867
        if (!array_key_exists($id, $this->definitions)) {
868
            throw new InvalidArgumentException(sprintf('The service definition "%s" does not exist.', $id));
869
        }
870
871
        return $this->definitions[$id];
872
    }
873
874
    /**
875
     * Gets a service definition by id or alias.
876
     *
877
     * The method "unaliases" recursively to return a Definition instance.
878
     *
879
     * @param string $id The service identifier or alias
880
     *
881
     * @return Definition A Definition instance
882
     *
883
     * @throws InvalidArgumentException if the service definition does not exist
884
     *
885
     * @api
886
     */
887
    public function findDefinition($id)
888
    {
889
        $id = strtolower($id);
890
891
        while (isset($this->aliasDefinitions[$id])) {
892
            $id = (string) $this->aliasDefinitions[$id];
893
        }
894
895
        return $this->getDefinition($id);
896
    }
897
898
    /**
899
     * Creates a service for a service definition.
900
     *
901
     * @param Definition $definition A service definition instance
902
     * @param string     $id         The service identifier
903
     * @param bool       $tryProxy   Whether to try proxying the service with a lazy proxy
904
     *
905
     * @return object The service described by the service definition
906
     *
907
     * @throws RuntimeException         When the scope is inactive
908
     * @throws RuntimeException         When the factory definition is incomplete
909
     * @throws RuntimeException         When the service is a synthetic service
910
     * @throws InvalidArgumentException When configure callable is not callable
911
     *
912
     * @internal this method is public because of PHP 5.3 limitations, do not use it explicitly in your code
913
     */
914
    public function createService(Definition $definition, $id, $tryProxy = true)
915
    {
916
        if ($definition->isSynthetic()) {
917
            throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The DIC does not know how to construct this service.', $id));
918
        }
919
920
        if ($tryProxy && $definition->isLazy()) {
921
            $container = $this;
922
923
            $proxy = $this
924
                ->getProxyInstantiator()
925
                ->instantiateProxy(
926
                    $container,
927
                    $definition,
928
                    $id, function () use ($definition, $id, $container) {
929
                        return $container->createService($definition, $id, false);
930
                    }
931
                );
932
            $this->shareService($definition, $proxy, $id);
933
934
            return $proxy;
935
        }
936
937
        $parameterBag = $this->getParameterBag();
938
939
        if (null !== $definition->getFile()) {
940
            require_once $parameterBag->resolveValue($definition->getFile());
941
        }
942
943
        $arguments = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())));
944
945
        if (null !== $factory = $definition->getFactory()) {
946
            if (is_array($factory)) {
947
                $factory = array($this->resolveServices($parameterBag->resolveValue($factory[0])), $factory[1]);
948
            } elseif (!is_string($factory)) {
949
                throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory', $id));
950
            }
951
952
            $service = call_user_func_array($factory, $arguments);
953
        } elseif (null !== $definition->getFactoryMethod(false)) {
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...ion::getFactoryMethod() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
954
            if (null !== $definition->getFactoryClass(false)) {
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...tion::getFactoryClass() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
955
                $factory = $parameterBag->resolveValue($definition->getFactoryClass(false));
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...tion::getFactoryClass() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
956
            } elseif (null !== $definition->getFactoryService(false)) {
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...on::getFactoryService() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
957
                $factory = $this->get($parameterBag->resolveValue($definition->getFactoryService(false)));
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...on::getFactoryService() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
958
            } else {
959
                throw new RuntimeException(sprintf('Cannot create service "%s" from factory method without a factory service or factory class.', $id));
960
            }
961
962
            $service = call_user_func_array(array($factory, $definition->getFactoryMethod(false)), $arguments);
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...ion::getFactoryMethod() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
963
        } else {
964
            $r = new \ReflectionClass($parameterBag->resolveValue($definition->getClass()));
965
966
            $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments);
967
        }
968
969
        if ($tryProxy || !$definition->isLazy()) {
970
            // share only if proxying failed, or if not a proxy
971
            $this->shareService($definition, $service, $id);
972
        }
973
974
        foreach ($definition->getMethodCalls() as $call) {
975
            $this->callMethod($service, $call);
976
        }
977
978
        $properties = $this->resolveServices($parameterBag->resolveValue($definition->getProperties()));
979
        foreach ($properties as $name => $value) {
980
            $service->$name = $value;
981
        }
982
983
        if ($callable = $definition->getConfigurator()) {
984
            if (is_array($callable)) {
985
                $callable[0] = $callable[0] instanceof Reference ? $this->get((string) $callable[0]) : $parameterBag->resolveValue($callable[0]);
986
            }
987
988
            if (!is_callable($callable)) {
989
                throw new InvalidArgumentException(sprintf('The configure callable for class "%s" is not a callable.', get_class($service)));
990
            }
991
992
            call_user_func($callable, $service);
993
        }
994
995
        return $service;
996
    }
997
998
    /**
999
     * Replaces service references by the real service instance and evaluates expressions.
1000
     *
1001
     * @param mixed $value A value
1002
     *
1003
     * @return mixed The same value with all service references replaced by
1004
     *               the real service instances and all expressions evaluated
1005
     */
1006
    public function resolveServices($value)
1007
    {
1008
        if (is_array($value)) {
1009
            $value = array_map(array($this, 'resolveServices'), $value);
1010
        } elseif ($value instanceof Reference) {
1011
            $value = $this->get((string) $value, $value->getInvalidBehavior());
1012
        } elseif ($value instanceof Definition) {
1013
            $value = $this->createService($value, null);
1014
        } elseif ($value instanceof Expression) {
0 ignored issues
show
Bug introduced by
The class Symfony\Component\ExpressionLanguage\Expression does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
1015
            $value = $this->getExpressionLanguage()->evaluate($value, array('container' => $this));
1016
        }
1017
1018
        return $value;
1019
    }
1020
1021
    /**
1022
     * Returns service ids for a given tag.
1023
     *
1024
     * Example:
1025
     *
1026
     * $container->register('foo')->addTag('my.tag', array('hello' => 'world'));
1027
     *
1028
     * $serviceIds = $container->findTaggedServiceIds('my.tag');
1029
     * foreach ($serviceIds as $serviceId => $tags) {
1030
     *     foreach ($tags as $tag) {
1031
     *         echo $tag['hello'];
1032
     *     }
1033
     * }
1034
     *
1035
     * @param string $name The tag name
1036
     *
1037
     * @return array An array of tags with the tagged service as key, holding a list of attribute arrays.
1038
     *
1039
     * @api
1040
     */
1041
    public function findTaggedServiceIds($name)
1042
    {
1043
        $tags = array();
1044
        foreach ($this->getDefinitions() as $id => $definition) {
1045
            if ($definition->hasTag($name)) {
1046
                $tags[$id] = $definition->getTag($name);
1047
            }
1048
        }
1049
1050
        return $tags;
1051
    }
1052
1053
    /**
1054
     * Returns all tags the defined services use.
1055
     *
1056
     * @return array An array of tags
1057
     */
1058
    public function findTags()
1059
    {
1060
        $tags = array();
1061
        foreach ($this->getDefinitions() as $id => $definition) {
1062
            $tags = array_merge(array_keys($definition->getTags()), $tags);
1063
        }
1064
1065
        return array_unique($tags);
1066
    }
1067
1068
    public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
1069
    {
1070
        $this->expressionLanguageProviders[] = $provider;
1071
    }
1072
1073
    /**
1074
     * @return ExpressionFunctionProviderInterface[]
1075
     */
1076
    public function getExpressionLanguageProviders()
1077
    {
1078
        return $this->expressionLanguageProviders;
1079
    }
1080
1081
    /**
1082
     * Returns the Service Conditionals.
1083
     *
1084
     * @param mixed $value An array of conditionals to return.
1085
     *
1086
     * @return array An array of Service conditionals
1087
     */
1088
    public static function getServiceConditionals($value)
1089
    {
1090
        $services = array();
1091
1092
        if (is_array($value)) {
1093
            foreach ($value as $v) {
1094
                $services = array_unique(array_merge($services, self::getServiceConditionals($v)));
1095
            }
1096
        } elseif ($value instanceof Reference && $value->getInvalidBehavior() === ContainerInterface::IGNORE_ON_INVALID_REFERENCE) {
1097
            $services[] = (string) $value;
1098
        }
1099
1100
        return $services;
1101
    }
1102
1103
    /**
1104
     * Retrieves the currently set proxy instantiator or instantiates one.
1105
     *
1106
     * @return InstantiatorInterface
1107
     */
1108
    private function getProxyInstantiator()
1109
    {
1110
        if (!$this->proxyInstantiator) {
1111
            $this->proxyInstantiator = new RealServiceInstantiator();
1112
        }
1113
1114
        return $this->proxyInstantiator;
1115
    }
1116
1117
    /**
1118
     * Synchronizes a service change.
1119
     *
1120
     * This method updates all services that depend on the given
1121
     * service by calling all methods referencing it.
1122
     *
1123
     * @param string $id A service id
1124
     *
1125
     * @deprecated since version 2.7, will be removed in 3.0.
1126
     */
1127
    private function synchronize($id)
1128
    {
1129
        if ('request' !== $id) {
1130
            trigger_error('The '.__METHOD__.' method is deprecated in version 2.7 and will be removed in version 3.0.', E_USER_DEPRECATED);
1131
        }
1132
1133 View Code Duplication
        foreach ($this->definitions as $definitionId => $definition) {
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...
1134
            // only check initialized services
1135
            if (!$this->initialized($definitionId)) {
1136
                continue;
1137
            }
1138
1139
            foreach ($definition->getMethodCalls() as $call) {
1140
                foreach ($call[1] as $argument) {
1141
                    if ($argument instanceof Reference && $id == (string) $argument) {
1142
                        $this->callMethod($this->get($definitionId), $call);
1143
                    }
1144
                }
1145
            }
1146
        }
1147
    }
1148
1149 View Code Duplication
    private function callMethod($service, $call)
1150
    {
1151
        $services = self::getServiceConditionals($call[1]);
1152
1153
        foreach ($services as $s) {
1154
            if (!$this->has($s)) {
1155
                return;
1156
            }
1157
        }
1158
1159
        call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->resolveValue($call[1])));
1160
    }
1161
1162
    /**
1163
     * Shares a given service in the container.
1164
     *
1165
     * @param Definition $definition
1166
     * @param mixed      $service
1167
     * @param string     $id
1168
     *
1169
     * @throws InactiveScopeException
1170
     */
1171
    private function shareService(Definition $definition, $service, $id)
1172
    {
1173
        if (self::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) {
1174
            if (self::SCOPE_CONTAINER !== $scope && !isset($this->scopedServices[$scope])) {
1175
                throw new InactiveScopeException($id, $scope);
1176
            }
1177
1178
            $this->services[$lowerId = strtolower($id)] = $service;
1179
1180
            if (self::SCOPE_CONTAINER !== $scope) {
1181
                $this->scopedServices[$scope][$lowerId] = $service;
1182
            }
1183
        }
1184
    }
1185
1186
    private function getExpressionLanguage()
1187
    {
1188
        if (null === $this->expressionLanguage) {
1189
            if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
1190
                throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
1191
            }
1192
            $this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders);
1193
        }
1194
1195
        return $this->expressionLanguage;
1196
    }
1197
}
1198