ac4072a84902326d0dfa197e41b92273ffcdaa3c
[yaffs-website] / vendor / symfony / dependency-injection / Compiler / CheckReferenceValidityPass.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
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\Compiler;
13
14 use Symfony\Component\DependencyInjection\Definition;
15 use Symfony\Component\DependencyInjection\ContainerInterface;
16 use Symfony\Component\DependencyInjection\Reference;
17 use Symfony\Component\DependencyInjection\ContainerBuilder;
18 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
19 use Symfony\Component\DependencyInjection\Exception\ScopeCrossingInjectionException;
20 use Symfony\Component\DependencyInjection\Exception\ScopeWideningInjectionException;
21
22 /**
23  * Checks the validity of references.
24  *
25  * The following checks are performed by this pass:
26  * - target definitions are not abstract
27  * - target definitions are of equal or wider scope
28  * - target definitions are in the same scope hierarchy
29  *
30  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
31  */
32 class CheckReferenceValidityPass implements CompilerPassInterface
33 {
34     private $container;
35     private $currentId;
36     private $currentScope;
37     private $currentScopeAncestors;
38     private $currentScopeChildren;
39
40     /**
41      * Processes the ContainerBuilder to validate References.
42      *
43      * @param ContainerBuilder $container
44      */
45     public function process(ContainerBuilder $container)
46     {
47         $this->container = $container;
48
49         $children = $this->container->getScopeChildren(false);
50         $ancestors = array();
51
52         $scopes = $this->container->getScopes(false);
53         foreach ($scopes as $name => $parent) {
54             $ancestors[$name] = array($parent);
55
56             while (isset($scopes[$parent])) {
57                 $ancestors[$name][] = $parent = $scopes[$parent];
58             }
59         }
60
61         foreach ($container->getDefinitions() as $id => $definition) {
62             if ($definition->isSynthetic() || $definition->isAbstract()) {
63                 continue;
64             }
65
66             $this->currentId = $id;
67             $this->currentScope = $scope = $definition->getScope(false);
68
69             if (ContainerInterface::SCOPE_CONTAINER === $scope) {
70                 $this->currentScopeChildren = array_keys($scopes);
71                 $this->currentScopeAncestors = array();
72             } elseif (ContainerInterface::SCOPE_PROTOTYPE !== $scope) {
73                 $this->currentScopeChildren = isset($children[$scope]) ? $children[$scope] : array();
74                 $this->currentScopeAncestors = isset($ancestors[$scope]) ? $ancestors[$scope] : array();
75             }
76
77             $this->validateReferences($definition->getArguments());
78             $this->validateReferences($definition->getMethodCalls());
79             $this->validateReferences($definition->getProperties());
80         }
81     }
82
83     /**
84      * Validates an array of References.
85      *
86      * @param array $arguments An array of Reference objects
87      *
88      * @throws RuntimeException when there is a reference to an abstract definition.
89      */
90     private function validateReferences(array $arguments)
91     {
92         foreach ($arguments as $argument) {
93             if (is_array($argument)) {
94                 $this->validateReferences($argument);
95             } elseif ($argument instanceof Reference) {
96                 $targetDefinition = $this->getDefinition((string) $argument);
97
98                 if (null !== $targetDefinition && $targetDefinition->isAbstract()) {
99                     throw new RuntimeException(sprintf(
100                         'The definition "%s" has a reference to an abstract definition "%s". '
101                        .'Abstract definitions cannot be the target of references.',
102                        $this->currentId,
103                        $argument
104                     ));
105                 }
106
107                 $this->validateScope($argument, $targetDefinition);
108             }
109         }
110     }
111
112     /**
113      * Validates the scope of a single Reference.
114      *
115      * @param Reference  $reference
116      * @param Definition $definition
117      *
118      * @throws ScopeWideningInjectionException when the definition references a service of a narrower scope
119      * @throws ScopeCrossingInjectionException when the definition references a service of another scope hierarchy
120      */
121     private function validateScope(Reference $reference, Definition $definition = null)
122     {
123         if (ContainerInterface::SCOPE_PROTOTYPE === $this->currentScope) {
124             return;
125         }
126
127         if (!$reference->isStrict(false)) {
128             return;
129         }
130
131         if (null === $definition) {
132             return;
133         }
134
135         if ($this->currentScope === $scope = $definition->getScope(false)) {
136             return;
137         }
138
139         $id = (string) $reference;
140
141         if (in_array($scope, $this->currentScopeChildren, true)) {
142             throw new ScopeWideningInjectionException($this->currentId, $this->currentScope, $id, $scope);
143         }
144
145         if (!in_array($scope, $this->currentScopeAncestors, true)) {
146             throw new ScopeCrossingInjectionException($this->currentId, $this->currentScope, $id, $scope);
147         }
148     }
149
150     /**
151      * Returns the Definition given an id.
152      *
153      * @param string $id Definition identifier
154      *
155      * @return Definition
156      */
157     private function getDefinition($id)
158     {
159         if (!$this->container->hasDefinition($id)) {
160             return;
161         }
162
163         return $this->container->getDefinition($id);
164     }
165 }