Verzeichnisstruktur phpBB-3.2.0
- Veröffentlicht
- 06.01.2017
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
AutowirePass.php
001 <?php
002
003 /*
004 * This file is part of the Symfony package.
005 *
006 * (c) Fabien Potencier <fabien@symfony.com>
007 *
008 * For the full copyright and license information, please view the LICENSE
009 * file that was distributed with this source code.
010 */
011
012 namespace Symfony\Component\DependencyInjection\Compiler;
013
014 use Symfony\Component\DependencyInjection\ContainerBuilder;
015 use Symfony\Component\DependencyInjection\Definition;
016 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
017 use Symfony\Component\DependencyInjection\Reference;
018
019 /**
020 * Guesses constructor arguments of services definitions and try to instantiate services if necessary.
021 *
022 * @author Kévin Dunglas <dunglas@gmail.com>
023 */
024 class AutowirePass implements CompilerPassInterface
025 {
026 private $container;
027 private $reflectionClasses = array();
028 private $definedTypes = array();
029 private $types;
030 private $notGuessableTypes = array();
031
032 /**
033 * {@inheritdoc}
034 */
035 public function process(ContainerBuilder $container)
036 {
037 $throwingAutoloader = function ($class) { throw new \ReflectionException(sprintf('Class %s does not exist', $class)); };
038 spl_autoload_register($throwingAutoloader);
039
040 try {
041 $this->container = $container;
042 foreach ($container->getDefinitions() as $id => $definition) {
043 if ($definition->isAutowired()) {
044 $this->completeDefinition($id, $definition);
045 }
046 }
047 } catch (\Exception $e) {
048 } catch (\Throwable $e) {
049 }
050
051 spl_autoload_unregister($throwingAutoloader);
052
053 // Free memory and remove circular reference to container
054 $this->container = null;
055 $this->reflectionClasses = array();
056 $this->definedTypes = array();
057 $this->types = null;
058 $this->notGuessableTypes = array();
059
060 if (isset($e)) {
061 throw $e;
062 }
063 }
064
065 /**
066 * Wires the given definition.
067 *
068 * @param string $id
069 * @param Definition $definition
070 *
071 * @throws RuntimeException
072 */
073 private function completeDefinition($id, Definition $definition)
074 {
075 if (!$reflectionClass = $this->getReflectionClass($id, $definition)) {
076 return;
077 }
078
079 $this->container->addClassResource($reflectionClass);
080
081 if (!$constructor = $reflectionClass->getConstructor()) {
082 return;
083 }
084
085 $arguments = $definition->getArguments();
086 foreach ($constructor->getParameters() as $index => $parameter) {
087 if (array_key_exists($index, $arguments) && '' !== $arguments[$index]) {
088 continue;
089 }
090
091 try {
092 if (!$typeHint = $parameter->getClass()) {
093 // no default value? Then fail
094 if (!$parameter->isOptional()) {
095 throw new RuntimeException(sprintf('Unable to autowire argument index %d ($%s) for the service "%s". If this is an object, give it a type-hint. Otherwise, specify this argument\'s value explicitly.', $index, $parameter->name, $id));
096 }
097
098 // specifically pass the default value
099 $arguments[$index] = $parameter->getDefaultValue();
100
101 continue;
102 }
103
104 if (null === $this->types) {
105 $this->populateAvailableTypes();
106 }
107
108 if (isset($this->types[$typeHint->name]) && !isset($this->notGuessableTypes[$typeHint->name])) {
109 $value = new Reference($this->types[$typeHint->name]);
110 } else {
111 try {
112 $value = $this->createAutowiredDefinition($typeHint, $id);
113 } catch (RuntimeException $e) {
114 if ($parameter->allowsNull()) {
115 $value = null;
116 } elseif ($parameter->isDefaultValueAvailable()) {
117 $value = $parameter->getDefaultValue();
118 } else {
119 throw $e;
120 }
121 }
122 }
123 } catch (\ReflectionException $e) {
124 // Typehint against a non-existing class
125
126 if (!$parameter->isDefaultValueAvailable()) {
127 throw new RuntimeException(sprintf('Cannot autowire argument %s for %s because the type-hinted class does not exist (%s).', $index + 1, $definition->getClass(), $e->getMessage()), 0, $e);
128 }
129
130 $value = $parameter->getDefaultValue();
131 }
132
133 $arguments[$index] = $value;
134 }
135
136 // it's possible index 1 was set, then index 0, then 2, etc
137 // make sure that we re-order so they're injected as expected
138 ksort($arguments);
139 $definition->setArguments($arguments);
140 }
141
142 /**
143 * Populates the list of available types.
144 */
145 private function populateAvailableTypes()
146 {
147 $this->types = array();
148
149 foreach ($this->container->getDefinitions() as $id => $definition) {
150 $this->populateAvailableType($id, $definition);
151 }
152 }
153
154 /**
155 * Populates the list of available types for a given definition.
156 *
157 * @param string $id
158 * @param Definition $definition
159 */
160 private function populateAvailableType($id, Definition $definition)
161 {
162 // Never use abstract services
163 if ($definition->isAbstract()) {
164 return;
165 }
166
167 foreach ($definition->getAutowiringTypes() as $type) {
168 $this->definedTypes[$type] = true;
169 $this->types[$type] = $id;
170 }
171
172 if (!$reflectionClass = $this->getReflectionClass($id, $definition)) {
173 return;
174 }
175
176 foreach ($reflectionClass->getInterfaces() as $reflectionInterface) {
177 $this->set($reflectionInterface->name, $id);
178 }
179
180 do {
181 $this->set($reflectionClass->name, $id);
182 } while ($reflectionClass = $reflectionClass->getParentClass());
183 }
184
185 /**
186 * Associates a type and a service id if applicable.
187 *
188 * @param string $type
189 * @param string $id
190 */
191 private function set($type, $id)
192 {
193 if (isset($this->definedTypes[$type])) {
194 return;
195 }
196
197 if (!isset($this->types[$type])) {
198 $this->types[$type] = $id;
199
200 return;
201 }
202
203 if ($this->types[$type] === $id) {
204 return;
205 }
206
207 if (!isset($this->notGuessableTypes[$type])) {
208 $this->notGuessableTypes[$type] = true;
209 $this->types[$type] = (array) $this->types[$type];
210 }
211
212 $this->types[$type][] = $id;
213 }
214
215 /**
216 * Registers a definition for the type if possible or throws an exception.
217 *
218 * @param \ReflectionClass $typeHint
219 * @param string $id
220 *
221 * @return Reference A reference to the registered definition
222 *
223 * @throws RuntimeException
224 */
225 private function createAutowiredDefinition(\ReflectionClass $typeHint, $id)
226 {
227 if (isset($this->notGuessableTypes[$typeHint->name])) {
228 $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class';
229 $matchingServices = implode(', ', $this->types[$typeHint->name]);
230
231 throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". Multiple services exist for this %s (%s).', $typeHint->name, $id, $classOrInterface, $matchingServices));
232 }
233
234 if (!$typeHint->isInstantiable()) {
235 $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class';
236 throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". No services were found matching this %s and it cannot be auto-registered.', $typeHint->name, $id, $classOrInterface));
237 }
238
239 $argumentId = sprintf('autowired.%s', $typeHint->name);
240
241 $argumentDefinition = $this->container->register($argumentId, $typeHint->name);
242 $argumentDefinition->setPublic(false);
243
244 $this->populateAvailableType($argumentId, $argumentDefinition);
245
246 try {
247 $this->completeDefinition($argumentId, $argumentDefinition);
248 } catch (RuntimeException $e) {
249 $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class';
250 $message = sprintf('Unable to autowire argument of type "%s" for the service "%s". No services were found matching this %s and it cannot be auto-registered.', $typeHint->name, $id, $classOrInterface);
251 throw new RuntimeException($message, 0, $e);
252 }
253
254 return new Reference($argumentId);
255 }
256
257 /**
258 * Retrieves the reflection class associated with the given service.
259 *
260 * @param string $id
261 * @param Definition $definition
262 *
263 * @return \ReflectionClass|false
264 */
265 private function getReflectionClass($id, Definition $definition)
266 {
267 if (isset($this->reflectionClasses[$id])) {
268 return $this->reflectionClasses[$id];
269 }
270
271 // Cannot use reflection if the class isn't set
272 if (!$class = $definition->getClass()) {
273 return false;
274 }
275
276 $class = $this->container->getParameterBag()->resolveValue($class);
277
278 try {
279 $reflector = new \ReflectionClass($class);
280 } catch (\ReflectionException $e) {
281 $reflector = false;
282 }
283
284 return $this->reflectionClasses[$id] = $reflector;
285 }
286 }
287