[ Index ]

PHP Cross Reference of phpBB-3.2.8-deutsch

title

Body

[close]

/vendor/symfony/dependency-injection/Compiler/ -> AutowirePass.php (source)

   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  }


Generated: Tue Apr 7 19:42:26 2020 Cross-referenced by PHPXref 0.7.1