Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

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.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

AutowirePass.php

Zuletzt modifiziert: 02.04.2025, 15:03 - Dateigröße: 21.56 KiB


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\Config\Resource\ClassExistenceResource;
015  use Symfony\Component\DependencyInjection\Config\AutowireServiceResource;
016  use Symfony\Component\DependencyInjection\ContainerBuilder;
017  use Symfony\Component\DependencyInjection\Definition;
018  use Symfony\Component\DependencyInjection\Exception\AutowiringFailedException;
019  use Symfony\Component\DependencyInjection\Exception\RuntimeException;
020  use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper;
021  use Symfony\Component\DependencyInjection\TypedReference;
022   
023  /**
024   * Inspects existing service definitions and wires the autowired ones using the type hints of their classes.
025   *
026   * @author Kévin Dunglas <dunglas@gmail.com>
027   * @author Nicolas Grekas <p@tchwork.com>
028   */
029  class AutowirePass extends AbstractRecursivePass
030  {
031      private $definedTypes = [];
032      private $types;
033      private $ambiguousServiceTypes;
034      private $autowired = [];
035      private $lastFailure;
036      private $throwOnAutowiringException;
037      private $autowiringExceptions = [];
038      private $strictMode;
039   
040      /**
041       * @param bool $throwOnAutowireException Errors can be retrieved via Definition::getErrors()
042       */
043      public function __construct($throwOnAutowireException = true)
044      {
045          $this->throwOnAutowiringException = $throwOnAutowireException;
046      }
047   
048      /**
049       * @deprecated since version 3.4, to be removed in 4.0.
050       *
051       * @return AutowiringFailedException[]
052       */
053      public function getAutowiringExceptions()
054      {
055          @trigger_error('Calling AutowirePass::getAutowiringExceptions() is deprecated since Symfony 3.4 and will be removed in 4.0. Use Definition::getErrors() instead.', \E_USER_DEPRECATED);
056   
057          return $this->autowiringExceptions;
058      }
059   
060      /**
061       * {@inheritdoc}
062       */
063      public function process(ContainerBuilder $container)
064      {
065          // clear out any possibly stored exceptions from before
066          $this->autowiringExceptions = [];
067          $this->strictMode = $container->hasParameter('container.autowiring.strict_mode') && $container->getParameter('container.autowiring.strict_mode');
068   
069          try {
070              parent::process($container);
071          } finally {
072              $this->definedTypes = [];
073              $this->types = null;
074              $this->ambiguousServiceTypes = null;
075              $this->autowired = [];
076          }
077      }
078   
079      /**
080       * Creates a resource to help know if this service has changed.
081       *
082       * @return AutowireServiceResource
083       *
084       * @deprecated since version 3.3, to be removed in 4.0. Use ContainerBuilder::getReflectionClass() instead.
085       */
086      public static function createResourceForClass(\ReflectionClass $reflectionClass)
087      {
088          @trigger_error('The '.__METHOD__.'() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use ContainerBuilder::getReflectionClass() instead.', \E_USER_DEPRECATED);
089   
090          $metadata = [];
091   
092          foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
093              if (!$reflectionMethod->isStatic()) {
094                  $metadata[$reflectionMethod->name] = self::getResourceMetadataForMethod($reflectionMethod);
095              }
096          }
097   
098          return new AutowireServiceResource($reflectionClass->name, $reflectionClass->getFileName(), $metadata);
099      }
100   
101      /**
102       * {@inheritdoc}
103       */
104      protected function processValue($value, $isRoot = false)
105      {
106          try {
107              return $this->doProcessValue($value, $isRoot);
108          } catch (AutowiringFailedException $e) {
109              if ($this->throwOnAutowiringException) {
110                  throw $e;
111              }
112   
113              $this->autowiringExceptions[] = $e;
114              $this->container->getDefinition($this->currentId)->addError($e->getMessage());
115   
116              return parent::processValue($value, $isRoot);
117          }
118      }
119   
120      private function doProcessValue($value, $isRoot = false)
121      {
122          if ($value instanceof TypedReference) {
123              if ($ref = $this->getAutowiredReference($value, $value->getRequiringClass() ? sprintf('for "%s" in "%s"', $value->getType(), $value->getRequiringClass()) : '')) {
124                  return $ref;
125              }
126              $this->container->log($this, $this->createTypeNotFoundMessage($value, 'it'));
127          }
128          $value = parent::processValue($value, $isRoot);
129   
130          if (!$value instanceof Definition || !$value->isAutowired() || $value->isAbstract() || !$value->getClass()) {
131              return $value;
132          }
133          if (!$reflectionClass = $this->container->getReflectionClass($value->getClass(), false)) {
134              $this->container->log($this, sprintf('Skipping service "%s": Class or interface "%s" cannot be loaded.', $this->currentId, $value->getClass()));
135   
136              return $value;
137          }
138   
139          $methodCalls = $value->getMethodCalls();
140   
141          try {
142              $constructor = $this->getConstructor($value, false);
143          } catch (RuntimeException $e) {
144              throw new AutowiringFailedException($this->currentId, $e->getMessage(), 0, $e);
145          }
146   
147          if ($constructor) {
148              array_unshift($methodCalls, [$constructor, $value->getArguments()]);
149          }
150   
151          $methodCalls = $this->autowireCalls($reflectionClass, $methodCalls);
152   
153          if ($constructor) {
154              list(, $arguments) = array_shift($methodCalls);
155   
156              if ($arguments !== $value->getArguments()) {
157                  $value->setArguments($arguments);
158              }
159          }
160   
161          if ($methodCalls !== $value->getMethodCalls()) {
162              $value->setMethodCalls($methodCalls);
163          }
164   
165          return $value;
166      }
167   
168      /**
169       * @return array
170       */
171      private function autowireCalls(\ReflectionClass $reflectionClass, array $methodCalls)
172      {
173          foreach ($methodCalls as $i => $call) {
174              list($method, $arguments) = $call;
175   
176              if ($method instanceof \ReflectionFunctionAbstract) {
177                  $reflectionMethod = $method;
178              } else {
179                  $definition = new Definition($reflectionClass->name);
180                  try {
181                      $reflectionMethod = $this->getReflectionMethod($definition, $method);
182                  } catch (RuntimeException $e) {
183                      if ($definition->getFactory()) {
184                          continue;
185                      }
186                      throw $e;
187                  }
188              }
189   
190              $arguments = $this->autowireMethod($reflectionMethod, $arguments);
191   
192              if ($arguments !== $call[1]) {
193                  $methodCalls[$i][1] = $arguments;
194              }
195          }
196   
197          return $methodCalls;
198      }
199   
200      /**
201       * Autowires the constructor or a method.
202       *
203       * @return array The autowired arguments
204       *
205       * @throws AutowiringFailedException
206       */
207      private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments)
208      {
209          $class = $reflectionMethod instanceof \ReflectionMethod ? $reflectionMethod->class : $this->currentId;
210          $method = $reflectionMethod->name;
211          $parameters = $reflectionMethod->getParameters();
212          if (method_exists('ReflectionMethod', 'isVariadic') && $reflectionMethod->isVariadic()) {
213              array_pop($parameters);
214          }
215   
216          foreach ($parameters as $index => $parameter) {
217              if (\array_key_exists($index, $arguments) && '' !== $arguments[$index]) {
218                  continue;
219              }
220   
221              $type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, true);
222   
223              if (!$type) {
224                  if (isset($arguments[$index])) {
225                      continue;
226                  }
227   
228                  // no default value? Then fail
229                  if (!$parameter->isDefaultValueAvailable()) {
230                      // For core classes, isDefaultValueAvailable() can
231                      // be false when isOptional() returns true. If the
232                      // argument *is* optional, allow it to be missing
233                      if ($parameter->isOptional()) {
234                          continue;
235                      }
236                      $type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, false);
237                      $type = $type ? sprintf('is type-hinted "%s"', $type) : 'has no type-hint';
238   
239                      throw new AutowiringFailedException($this->currentId, sprintf('Cannot autowire service "%s": argument "$%s" of method "%s()" %s, you should configure its value explicitly.', $this->currentId, $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method, $type));
240                  }
241   
242                  // specifically pass the default value
243                  $arguments[$index] = $parameter->getDefaultValue();
244   
245                  continue;
246              }
247   
248              if (!$value = $this->getAutowiredReference($ref = new TypedReference($type, $type, !$parameter->isOptional() ? $class : ''), 'for '.sprintf('argument "$%s" of method "%s()"', $parameter->name, $class.'::'.$method))) {
249                  $failureMessage = $this->createTypeNotFoundMessage($ref, sprintf('argument "$%s" of method "%s()"', $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method));
250   
251                  if ($parameter->isDefaultValueAvailable()) {
252                      $value = $parameter->getDefaultValue();
253                  } elseif (!$parameter->allowsNull()) {
254                      throw new AutowiringFailedException($this->currentId, $failureMessage);
255                  }
256                  $this->container->log($this, $failureMessage);
257              }
258   
259              $arguments[$index] = $value;
260          }
261   
262          if ($parameters && !isset($arguments[++$index])) {
263              while (0 <= --$index) {
264                  $parameter = $parameters[$index];
265                  if (!$parameter->isDefaultValueAvailable() || $parameter->getDefaultValue() !== $arguments[$index]) {
266                      break;
267                  }
268                  unset($arguments[$index]);
269              }
270          }
271   
272          // it's possible index 1 was set, then index 0, then 2, etc
273          // make sure that we re-order so they're injected as expected
274          ksort($arguments);
275   
276          return $arguments;
277      }
278   
279      /**
280       * @return TypedReference|null A reference to the service matching the given type, if any
281       */
282      private function getAutowiredReference(TypedReference $reference, $deprecationMessage)
283      {
284          $this->lastFailure = null;
285          $type = $reference->getType();
286   
287          if ($type !== $this->container->normalizeId($reference) || ($this->container->has($type) && !$this->container->findDefinition($type)->isAbstract())) {
288              return $reference;
289          }
290   
291          if (null === $this->types) {
292              $this->populateAvailableTypes($this->strictMode);
293          }
294   
295          if (isset($this->definedTypes[$type])) {
296              return new TypedReference($this->types[$type], $type);
297          }
298   
299          if (!$this->strictMode && isset($this->types[$type])) {
300              $message = 'Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won\'t be supported in version 4.0.';
301              if ($aliasSuggestion = $this->getAliasesSuggestionForType($type = $reference->getType(), $deprecationMessage)) {
302                  $message .= ' '.$aliasSuggestion;
303              } else {
304                  $message .= sprintf(' You should %s the "%s" service to "%s" instead.', isset($this->types[$this->types[$type]]) ? 'alias' : 'rename (or alias)', $this->types[$type], $type);
305              }
306   
307              @trigger_error($message, \E_USER_DEPRECATED);
308   
309              return new TypedReference($this->types[$type], $type);
310          }
311   
312          if (!$reference->canBeAutoregistered() || isset($this->types[$type]) || isset($this->ambiguousServiceTypes[$type])) {
313              return null;
314          }
315   
316          if (isset($this->autowired[$type])) {
317              return $this->autowired[$type] ? new TypedReference($this->autowired[$type], $type) : null;
318          }
319   
320          if (!$this->strictMode) {
321              return $this->createAutowiredDefinition($type);
322          }
323   
324          return null;
325      }
326   
327      /**
328       * Populates the list of available types.
329       */
330      private function populateAvailableTypes($onlyAutowiringTypes = false)
331      {
332          $this->types = [];
333          if (!$onlyAutowiringTypes) {
334              $this->ambiguousServiceTypes = [];
335          }
336   
337          foreach ($this->container->getDefinitions() as $id => $definition) {
338              $this->populateAvailableType($id, $definition, $onlyAutowiringTypes);
339          }
340      }
341   
342      /**
343       * Populates the list of available types for a given definition.
344       *
345       * @param string $id
346       */
347      private function populateAvailableType($id, Definition $definition, $onlyAutowiringTypes)
348      {
349          // Never use abstract services
350          if ($definition->isAbstract()) {
351              return;
352          }
353   
354          foreach ($definition->getAutowiringTypes(false) as $type) {
355              $this->definedTypes[$type] = true;
356              $this->types[$type] = $id;
357              unset($this->ambiguousServiceTypes[$type]);
358          }
359   
360          if ($onlyAutowiringTypes) {
361              return;
362          }
363   
364          if (preg_match('/^\d+_[^~]++~[._a-zA-Z\d]{7}$/', $id) || $definition->isDeprecated() || !$reflectionClass = $this->container->getReflectionClass($definition->getClass(), false)) {
365              return;
366          }
367   
368          foreach ($reflectionClass->getInterfaces() as $reflectionInterface) {
369              $this->set($reflectionInterface->name, $id);
370          }
371   
372          do {
373              $this->set($reflectionClass->name, $id);
374          } while ($reflectionClass = $reflectionClass->getParentClass());
375      }
376   
377      /**
378       * Associates a type and a service id if applicable.
379       *
380       * @param string $type
381       * @param string $id
382       */
383      private function set($type, $id)
384      {
385          if (isset($this->definedTypes[$type])) {
386              return;
387          }
388   
389          // is this already a type/class that is known to match multiple services?
390          if (isset($this->ambiguousServiceTypes[$type])) {
391              $this->ambiguousServiceTypes[$type][] = $id;
392   
393              return;
394          }
395   
396          // check to make sure the type doesn't match multiple services
397          if (!isset($this->types[$type]) || $this->types[$type] === $id) {
398              $this->types[$type] = $id;
399   
400              return;
401          }
402   
403          // keep an array of all services matching this type
404          if (!isset($this->ambiguousServiceTypes[$type])) {
405              $this->ambiguousServiceTypes[$type] = [$this->types[$type]];
406              unset($this->types[$type]);
407          }
408          $this->ambiguousServiceTypes[$type][] = $id;
409      }
410   
411      /**
412       * Registers a definition for the type if possible or throws an exception.
413       *
414       * @param string $type
415       *
416       * @return TypedReference|null A reference to the registered definition
417       */
418      private function createAutowiredDefinition($type)
419      {
420          if (!($typeHint = $this->container->getReflectionClass($type, false)) || !$typeHint->isInstantiable()) {
421              return null;
422          }
423   
424          $currentId = $this->currentId;
425          $this->currentId = $type;
426          $this->autowired[$type] = $argumentId = sprintf('autowired.%s', $type);
427          $argumentDefinition = new Definition($type);
428          $argumentDefinition->setPublic(false);
429          $argumentDefinition->setAutowired(true);
430   
431          try {
432              $originalThrowSetting = $this->throwOnAutowiringException;
433              $this->throwOnAutowiringException = true;
434              $this->processValue($argumentDefinition, true);
435              $this->container->setDefinition($argumentId, $argumentDefinition);
436          } catch (AutowiringFailedException $e) {
437              $this->autowired[$type] = false;
438              $this->lastFailure = $e->getMessage();
439              $this->container->log($this, $this->lastFailure);
440   
441              return null;
442          } finally {
443              $this->throwOnAutowiringException = $originalThrowSetting;
444              $this->currentId = $currentId;
445          }
446   
447          @trigger_error(sprintf('Relying on service auto-registration for type "%s" is deprecated since Symfony 3.4 and won\'t be supported in 4.0. Create a service named "%s" instead.', $type, $type), \E_USER_DEPRECATED);
448   
449          $this->container->log($this, sprintf('Type "%s" has been auto-registered for service "%s".', $type, $this->currentId));
450   
451          return new TypedReference($argumentId, $type);
452      }
453   
454      private function createTypeNotFoundMessage(TypedReference $reference, $label)
455      {
456          $trackResources = $this->container->isTrackingResources();
457          $this->container->setResourceTracking(false);
458          try {
459              if ($r = $this->container->getReflectionClass($type = $reference->getType(), false)) {
460                  $alternatives = $this->createTypeAlternatives($reference);
461              }
462          } finally {
463              $this->container->setResourceTracking($trackResources);
464          }
465   
466          if (!$r) {
467              // either $type does not exist or a parent class does not exist
468              try {
469                  $resource = new ClassExistenceResource($type, false);
470                  // isFresh() will explode ONLY if a parent class/trait does not exist
471                  $resource->isFresh(0);
472                  $parentMsg = false;
473              } catch (\ReflectionException $e) {
474                  $parentMsg = $e->getMessage();
475              }
476   
477              $message = sprintf('has type "%s" but this class %s.', $type, $parentMsg ? sprintf('is missing a parent class (%s)', $parentMsg) : 'was not found');
478          } else {
479              $message = $this->container->has($type) ? 'this service is abstract' : 'no such service exists';
480              $message = sprintf('references %s "%s" but %s.%s', $r->isInterface() ? 'interface' : 'class', $type, $message, $alternatives);
481   
482              if ($r->isInterface() && !$alternatives) {
483                  $message .= ' Did you create a class that implements this interface?';
484              }
485          }
486   
487          $message = sprintf('Cannot autowire service "%s": %s %s', $this->currentId, $label, $message);
488   
489          if (null !== $this->lastFailure) {
490              $message = $this->lastFailure."\n".$message;
491              $this->lastFailure = null;
492          }
493   
494          return $message;
495      }
496   
497      private function createTypeAlternatives(TypedReference $reference)
498      {
499          // try suggesting available aliases first
500          if ($message = $this->getAliasesSuggestionForType($type = $reference->getType())) {
501              return ' '.$message;
502          }
503          if (null === $this->ambiguousServiceTypes) {
504              $this->populateAvailableTypes();
505          }
506   
507          if (isset($this->ambiguousServiceTypes[$type])) {
508              $message = sprintf('one of these existing services: "%s"', implode('", "', $this->ambiguousServiceTypes[$type]));
509          } elseif (isset($this->types[$type])) {
510              $message = sprintf('the existing "%s" service', $this->types[$type]);
511          } elseif ($reference->getRequiringClass() && !$reference->canBeAutoregistered() && !$this->strictMode) {
512              return ' It cannot be auto-registered because it is from a different root namespace.';
513          } else {
514              return '';
515          }
516   
517          return sprintf(' You should maybe alias this %s to %s.', class_exists($type, false) ? 'class' : 'interface', $message);
518      }
519   
520      /**
521       * @deprecated since version 3.3, to be removed in 4.0.
522       */
523      private static function getResourceMetadataForMethod(\ReflectionMethod $method)
524      {
525          $methodArgumentsMetadata = [];
526          foreach ($method->getParameters() as $parameter) {
527              try {
528                  if (method_exists($parameter, 'getType')) {
529                      $type = $parameter->getType();
530                      if ($type && !$type->isBuiltin()) {
531                          $class = new \ReflectionClass($type instanceof \ReflectionNamedType ? $type->getName() : (string) $type);
532                      } else {
533                          $class = null;
534                      }
535                  } else {
536                      $class = $parameter->getClass();
537                  }
538              } catch (\ReflectionException $e) {
539                  // type-hint is against a non-existent class
540                  $class = false;
541              }
542   
543              $isVariadic = method_exists($parameter, 'isVariadic') && $parameter->isVariadic();
544              $methodArgumentsMetadata[] = [
545                  'class' => $class,
546                  'isOptional' => $parameter->isOptional(),
547                  'defaultValue' => ($parameter->isOptional() && !$isVariadic) ? $parameter->getDefaultValue() : null,
548              ];
549          }
550   
551          return $methodArgumentsMetadata;
552      }
553   
554      private function getAliasesSuggestionForType($type, $extraContext = null)
555      {
556          $aliases = [];
557          foreach (class_parents($type) + class_implements($type) as $parent) {
558              if ($this->container->has($parent) && !$this->container->findDefinition($parent)->isAbstract()) {
559                  $aliases[] = $parent;
560              }
561          }
562   
563          $extraContext = $extraContext ? ' '.$extraContext : '';
564          if (1 < $len = \count($aliases)) {
565              $message = sprintf('Try changing the type-hint%s to one of its parents: ', $extraContext);
566              for ($i = 0, --$len; $i < $len; ++$i) {
567                  $message .= sprintf('%s "%s", ', class_exists($aliases[$i], false) ? 'class' : 'interface', $aliases[$i]);
568              }
569              $message .= sprintf('or %s "%s".', class_exists($aliases[$i], false) ? 'class' : 'interface', $aliases[$i]);
570   
571              return $message;
572          }
573   
574          if ($aliases) {
575              return sprintf('Try changing the type-hint%s to "%s" instead.', $extraContext, $aliases[0]);
576          }
577   
578          return null;
579      }
580  }
581