[ Index ] |
PHP Cross Reference of phpBB-3.2.11-deutsch |
[Summary view] [Print] [Text view]
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\ContainerBuilder; 15 use Symfony\Component\DependencyInjection\Definition; 16 use Symfony\Component\DependencyInjection\Exception\RuntimeException; 17 use Symfony\Component\DependencyInjection\Reference; 18 19 /** 20 * Guesses constructor arguments of services definitions and try to instantiate services if necessary. 21 * 22 * @author Kévin Dunglas <dunglas@gmail.com> 23 */ 24 class AutowirePass implements CompilerPassInterface 25 { 26 private $container; 27 private $reflectionClasses = array(); 28 private $definedTypes = array(); 29 private $types; 30 private $notGuessableTypes = array(); 31 private $autowired = array(); 32 33 /** 34 * {@inheritdoc} 35 */ 36 public function process(ContainerBuilder $container) 37 { 38 $throwingAutoloader = function ($class) { throw new \ReflectionException(sprintf('Class %s does not exist', $class)); }; 39 spl_autoload_register($throwingAutoloader); 40 41 try { 42 $this->container = $container; 43 foreach ($container->getDefinitions() as $id => $definition) { 44 if ($definition->isAutowired()) { 45 $this->completeDefinition($id, $definition); 46 } 47 } 48 } catch (\Exception $e) { 49 } catch (\Throwable $e) { 50 } 51 52 spl_autoload_unregister($throwingAutoloader); 53 54 // Free memory and remove circular reference to container 55 $this->container = null; 56 $this->reflectionClasses = array(); 57 $this->definedTypes = array(); 58 $this->types = null; 59 $this->notGuessableTypes = array(); 60 $this->autowired = array(); 61 62 if (isset($e)) { 63 throw $e; 64 } 65 } 66 67 /** 68 * Wires the given definition. 69 * 70 * @param string $id 71 * @param Definition $definition 72 * 73 * @throws RuntimeException 74 */ 75 private function completeDefinition($id, Definition $definition) 76 { 77 if ($definition->getFactory() || $definition->getFactoryClass(false) || $definition->getFactoryService(false)) { 78 throw new RuntimeException(sprintf('Service "%s" can use either autowiring or a factory, not both.', $id)); 79 } 80 81 if (!$reflectionClass = $this->getReflectionClass($id, $definition)) { 82 return; 83 } 84 85 $this->container->addClassResource($reflectionClass); 86 87 if (!$constructor = $reflectionClass->getConstructor()) { 88 return; 89 } 90 $parameters = $constructor->getParameters(); 91 if (method_exists('ReflectionMethod', 'isVariadic') && $constructor->isVariadic()) { 92 array_pop($parameters); 93 } 94 95 $arguments = $definition->getArguments(); 96 foreach ($parameters as $index => $parameter) { 97 if (array_key_exists($index, $arguments) && '' !== $arguments[$index]) { 98 continue; 99 } 100 101 try { 102 if (!$typeHint = $parameter->getClass()) { 103 if (isset($arguments[$index])) { 104 continue; 105 } 106 107 // no default value? Then fail 108 if (!$parameter->isOptional()) { 109 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)); 110 } 111 112 // specifically pass the default value 113 $arguments[$index] = $parameter->getDefaultValue(); 114 115 continue; 116 } 117 118 if (isset($this->autowired[$typeHint->name])) { 119 $arguments[$index] = $this->autowired[$typeHint->name] ? new Reference($this->autowired[$typeHint->name]) : null; 120 continue; 121 } 122 123 if (null === $this->types) { 124 $this->populateAvailableTypes(); 125 } 126 127 if (isset($this->types[$typeHint->name]) && !isset($this->notGuessableTypes[$typeHint->name])) { 128 $value = new Reference($this->types[$typeHint->name]); 129 } else { 130 try { 131 $value = $this->createAutowiredDefinition($typeHint, $id); 132 } catch (RuntimeException $e) { 133 if ($parameter->isDefaultValueAvailable()) { 134 $value = $parameter->getDefaultValue(); 135 } elseif ($parameter->allowsNull()) { 136 $value = null; 137 } else { 138 throw $e; 139 } 140 $this->autowired[$typeHint->name] = false; 141 } 142 } 143 } catch (\ReflectionException $e) { 144 // Typehint against a non-existing class 145 146 if (!$parameter->isDefaultValueAvailable()) { 147 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); 148 } 149 150 $value = $parameter->getDefaultValue(); 151 } 152 153 $arguments[$index] = $value; 154 } 155 156 if ($parameters && !isset($arguments[++$index])) { 157 while (0 <= --$index) { 158 $parameter = $parameters[$index]; 159 if (!$parameter->isDefaultValueAvailable() || $parameter->getDefaultValue() !== $arguments[$index]) { 160 break; 161 } 162 unset($arguments[$index]); 163 } 164 } 165 166 // it's possible index 1 was set, then index 0, then 2, etc 167 // make sure that we re-order so they're injected as expected 168 ksort($arguments); 169 $definition->setArguments($arguments); 170 } 171 172 /** 173 * Populates the list of available types. 174 */ 175 private function populateAvailableTypes() 176 { 177 $this->types = array(); 178 179 foreach ($this->container->getDefinitions() as $id => $definition) { 180 $this->populateAvailableType($id, $definition); 181 } 182 } 183 184 /** 185 * Populates the list of available types for a given definition. 186 * 187 * @param string $id 188 * @param Definition $definition 189 */ 190 private function populateAvailableType($id, Definition $definition) 191 { 192 // Never use abstract services 193 if ($definition->isAbstract()) { 194 return; 195 } 196 197 foreach ($definition->getAutowiringTypes() as $type) { 198 $this->definedTypes[$type] = true; 199 $this->types[$type] = $id; 200 unset($this->notGuessableTypes[$type]); 201 } 202 203 if (!$reflectionClass = $this->getReflectionClass($id, $definition)) { 204 return; 205 } 206 207 foreach ($reflectionClass->getInterfaces() as $reflectionInterface) { 208 $this->set($reflectionInterface->name, $id); 209 } 210 211 do { 212 $this->set($reflectionClass->name, $id); 213 } while ($reflectionClass = $reflectionClass->getParentClass()); 214 } 215 216 /** 217 * Associates a type and a service id if applicable. 218 * 219 * @param string $type 220 * @param string $id 221 */ 222 private function set($type, $id) 223 { 224 if (isset($this->definedTypes[$type])) { 225 return; 226 } 227 228 if (!isset($this->types[$type])) { 229 $this->types[$type] = $id; 230 231 return; 232 } 233 234 if ($this->types[$type] === $id) { 235 return; 236 } 237 238 if (!isset($this->notGuessableTypes[$type])) { 239 $this->notGuessableTypes[$type] = true; 240 $this->types[$type] = (array) $this->types[$type]; 241 } 242 243 $this->types[$type][] = $id; 244 } 245 246 /** 247 * Registers a definition for the type if possible or throws an exception. 248 * 249 * @param \ReflectionClass $typeHint 250 * @param string $id 251 * 252 * @return Reference A reference to the registered definition 253 * 254 * @throws RuntimeException 255 */ 256 private function createAutowiredDefinition(\ReflectionClass $typeHint, $id) 257 { 258 if (isset($this->notGuessableTypes[$typeHint->name])) { 259 $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class'; 260 $matchingServices = implode(', ', $this->types[$typeHint->name]); 261 262 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)); 263 } 264 265 if (!$typeHint->isInstantiable()) { 266 $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class'; 267 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)); 268 } 269 270 $this->autowired[$typeHint->name] = $argumentId = sprintf('autowired.%s', $typeHint->name); 271 272 $argumentDefinition = $this->container->register($argumentId, $typeHint->name); 273 $argumentDefinition->setPublic(false); 274 275 try { 276 $this->completeDefinition($argumentId, $argumentDefinition); 277 } catch (RuntimeException $e) { 278 $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class'; 279 $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); 280 throw new RuntimeException($message, 0, $e); 281 } 282 283 return new Reference($argumentId); 284 } 285 286 /** 287 * Retrieves the reflection class associated with the given service. 288 * 289 * @param string $id 290 * @param Definition $definition 291 * 292 * @return \ReflectionClass|false 293 */ 294 private function getReflectionClass($id, Definition $definition) 295 { 296 if (isset($this->reflectionClasses[$id])) { 297 return $this->reflectionClasses[$id]; 298 } 299 300 // Cannot use reflection if the class isn't set 301 if (!$class = $definition->getClass()) { 302 return false; 303 } 304 305 $class = $this->container->getParameterBag()->resolveValue($class); 306 307 if ($deprecated = $definition->isDeprecated()) { 308 $prevErrorHandler = set_error_handler(function ($level, $message, $file, $line) use (&$prevErrorHandler) { 309 return (E_USER_DEPRECATED === $level || !$prevErrorHandler) ? false : $prevErrorHandler($level, $message, $file, $line); 310 }); 311 } 312 313 $e = null; 314 315 try { 316 $reflector = new \ReflectionClass($class); 317 } catch (\Exception $e) { 318 } catch (\Throwable $e) { 319 } 320 321 if ($deprecated) { 322 restore_error_handler(); 323 } 324 325 if (null !== $e) { 326 if (!$e instanceof \ReflectionException) { 327 throw $e; 328 } 329 $reflector = false; 330 } 331 332 return $this->reflectionClasses[$id] = $reflector; 333 } 334 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Nov 11 20:33:01 2020 | Cross-referenced by PHPXref 0.7.1 |