matomo
/
app
/
vendor
/
prefixed
/
symfony
/
http-kernel
/
DependencyInjection
/
RegisterControllerArgumentLocatorsPass.php
AddAnnotatedClassesToCachePass.php
1 year ago
ConfigurableExtension.php
2 years ago
ControllerArgumentValueResolverPass.php
2 years ago
Extension.php
2 years ago
FragmentRendererPass.php
1 year ago
LazyLoadingFragmentHandler.php
1 year ago
LoggerPass.php
1 year ago
MergeExtensionConfigurationPass.php
2 years ago
RegisterControllerArgumentLocatorsPass.php
1 year ago
RegisterLocaleAwareServicesPass.php
2 years ago
RemoveEmptyControllerArgumentLocatorsPass.php
1 year ago
ResettableServicePass.php
1 year ago
ServicesResetter.php
2 years ago
RegisterControllerArgumentLocatorsPass.php
191 lines
| 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 | namespace Matomo\Dependencies\Symfony\Component\HttpKernel\DependencyInjection; |
| 12 | |
| 13 | use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; |
| 14 | use Symfony\Component\DependencyInjection\Attribute\Target; |
| 15 | use Symfony\Component\DependencyInjection\ChildDefinition; |
| 16 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; |
| 17 | use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; |
| 18 | use Symfony\Component\DependencyInjection\ContainerAwareInterface; |
| 19 | use Symfony\Component\DependencyInjection\ContainerBuilder; |
| 20 | use Symfony\Component\DependencyInjection\ContainerInterface; |
| 21 | use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; |
| 22 | use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper; |
| 23 | use Symfony\Component\DependencyInjection\Reference; |
| 24 | use Symfony\Component\DependencyInjection\TypedReference; |
| 25 | use Matomo\Dependencies\Symfony\Component\HttpFoundation\Request; |
| 26 | use Matomo\Dependencies\Symfony\Component\HttpFoundation\Response; |
| 27 | use Matomo\Dependencies\Symfony\Component\HttpFoundation\Session\SessionInterface; |
| 28 | /** |
| 29 | * Creates the service-locators required by ServiceValueResolver. |
| 30 | * |
| 31 | * @author Nicolas Grekas <p@tchwork.com> |
| 32 | */ |
| 33 | class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface |
| 34 | { |
| 35 | private $resolverServiceId; |
| 36 | private $controllerTag; |
| 37 | private $controllerLocator; |
| 38 | private $notTaggedControllerResolverServiceId; |
| 39 | public function __construct(string $resolverServiceId = 'argument_resolver.service', string $controllerTag = 'controller.service_arguments', string $controllerLocator = 'argument_resolver.controller_locator', string $notTaggedControllerResolverServiceId = 'argument_resolver.not_tagged_controller') |
| 40 | { |
| 41 | if (0 < \func_num_args()) { |
| 42 | trigger_deprecation('symfony/http-kernel', '5.3', 'Configuring "%s" is deprecated.', __CLASS__); |
| 43 | } |
| 44 | $this->resolverServiceId = $resolverServiceId; |
| 45 | $this->controllerTag = $controllerTag; |
| 46 | $this->controllerLocator = $controllerLocator; |
| 47 | $this->notTaggedControllerResolverServiceId = $notTaggedControllerResolverServiceId; |
| 48 | } |
| 49 | public function process(ContainerBuilder $container) |
| 50 | { |
| 51 | if (\false === $container->hasDefinition($this->resolverServiceId) && \false === $container->hasDefinition($this->notTaggedControllerResolverServiceId)) { |
| 52 | return; |
| 53 | } |
| 54 | $parameterBag = $container->getParameterBag(); |
| 55 | $controllers = []; |
| 56 | $publicAliases = []; |
| 57 | foreach ($container->getAliases() as $id => $alias) { |
| 58 | if ($alias->isPublic() && !$alias->isPrivate()) { |
| 59 | $publicAliases[(string) $alias][] = $id; |
| 60 | } |
| 61 | } |
| 62 | foreach ($container->findTaggedServiceIds($this->controllerTag, \true) as $id => $tags) { |
| 63 | $def = $container->getDefinition($id); |
| 64 | $def->setPublic(\true); |
| 65 | $def->setLazy(\false); |
| 66 | $class = $def->getClass(); |
| 67 | $autowire = $def->isAutowired(); |
| 68 | $bindings = $def->getBindings(); |
| 69 | // resolve service class, taking parent definitions into account |
| 70 | while ($def instanceof ChildDefinition) { |
| 71 | $def = $container->findDefinition($def->getParent()); |
| 72 | $class = $class ?: $def->getClass(); |
| 73 | $bindings += $def->getBindings(); |
| 74 | } |
| 75 | $class = $parameterBag->resolveValue($class); |
| 76 | if (!($r = $container->getReflectionClass($class))) { |
| 77 | throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); |
| 78 | } |
| 79 | $isContainerAware = $r->implementsInterface(ContainerAwareInterface::class) || is_subclass_of($class, AbstractController::class); |
| 80 | // get regular public methods |
| 81 | $methods = []; |
| 82 | $arguments = []; |
| 83 | foreach ($r->getMethods(\ReflectionMethod::IS_PUBLIC) as $r) { |
| 84 | if ('setContainer' === $r->name && $isContainerAware) { |
| 85 | continue; |
| 86 | } |
| 87 | if (!$r->isConstructor() && !$r->isDestructor() && !$r->isAbstract()) { |
| 88 | $methods[strtolower($r->name)] = [$r, $r->getParameters()]; |
| 89 | } |
| 90 | } |
| 91 | // validate and collect explicit per-actions and per-arguments service references |
| 92 | foreach ($tags as $attributes) { |
| 93 | if (!isset($attributes['action']) && !isset($attributes['argument']) && !isset($attributes['id'])) { |
| 94 | $autowire = \true; |
| 95 | continue; |
| 96 | } |
| 97 | foreach (['action', 'argument', 'id'] as $k) { |
| 98 | if (!isset($attributes[$k][0])) { |
| 99 | throw new InvalidArgumentException(sprintf('Missing "%s" attribute on tag "%s" %s for service "%s".', $k, $this->controllerTag, json_encode($attributes, \JSON_UNESCAPED_UNICODE), $id)); |
| 100 | } |
| 101 | } |
| 102 | if (!isset($methods[$action = strtolower($attributes['action'])])) { |
| 103 | throw new InvalidArgumentException(sprintf('Invalid "action" attribute on tag "%s" for service "%s": no public "%s()" method found on class "%s".', $this->controllerTag, $id, $attributes['action'], $class)); |
| 104 | } |
| 105 | [$r, $parameters] = $methods[$action]; |
| 106 | $found = \false; |
| 107 | foreach ($parameters as $p) { |
| 108 | if ($attributes['argument'] === $p->name) { |
| 109 | if (!isset($arguments[$r->name][$p->name])) { |
| 110 | $arguments[$r->name][$p->name] = $attributes['id']; |
| 111 | } |
| 112 | $found = \true; |
| 113 | break; |
| 114 | } |
| 115 | } |
| 116 | if (!$found) { |
| 117 | throw new InvalidArgumentException(sprintf('Invalid "%s" tag for service "%s": method "%s()" has no "%s" argument on class "%s".', $this->controllerTag, $id, $r->name, $attributes['argument'], $class)); |
| 118 | } |
| 119 | } |
| 120 | foreach ($methods as [$r, $parameters]) { |
| 121 | /** @var \ReflectionMethod $r */ |
| 122 | // create a per-method map of argument-names to service/type-references |
| 123 | $args = []; |
| 124 | foreach ($parameters as $p) { |
| 125 | /** @var \ReflectionParameter $p */ |
| 126 | $type = ltrim($target = (string) ProxyHelper::getTypeHint($r, $p), '\\'); |
| 127 | $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; |
| 128 | if (isset($arguments[$r->name][$p->name])) { |
| 129 | $target = $arguments[$r->name][$p->name]; |
| 130 | if ('?' !== $target[0]) { |
| 131 | $invalidBehavior = ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE; |
| 132 | } elseif ('' === ($target = (string) substr($target, 1))) { |
| 133 | throw new InvalidArgumentException(sprintf('A "%s" tag must have non-empty "id" attributes for service "%s".', $this->controllerTag, $id)); |
| 134 | } elseif ($p->allowsNull() && !$p->isOptional()) { |
| 135 | $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; |
| 136 | } |
| 137 | } elseif (isset($bindings[$bindingName = $type . ' $' . ($name = Target::parseName($p))]) || isset($bindings[$bindingName = '$' . $name]) || isset($bindings[$bindingName = $type])) { |
| 138 | $binding = $bindings[$bindingName]; |
| 139 | [$bindingValue, $bindingId, , $bindingType, $bindingFile] = $binding->getValues(); |
| 140 | $binding->setValues([$bindingValue, $bindingId, \true, $bindingType, $bindingFile]); |
| 141 | if (!$bindingValue instanceof Reference) { |
| 142 | $args[$p->name] = new Reference('.value.' . $container->hash($bindingValue)); |
| 143 | $container->register((string) $args[$p->name], 'mixed')->setFactory('current')->addArgument([$bindingValue]); |
| 144 | } else { |
| 145 | $args[$p->name] = $bindingValue; |
| 146 | } |
| 147 | continue; |
| 148 | } elseif (!$type || !$autowire || '\\' !== $target[0]) { |
| 149 | continue; |
| 150 | } elseif (is_subclass_of($type, \UnitEnum::class)) { |
| 151 | // do not attempt to register enum typed arguments if not already present in bindings |
| 152 | continue; |
| 153 | } elseif (!$p->allowsNull()) { |
| 154 | $invalidBehavior = ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE; |
| 155 | } |
| 156 | if (Request::class === $type || SessionInterface::class === $type || Response::class === $type) { |
| 157 | continue; |
| 158 | } |
| 159 | if ($type && !$p->isOptional() && !$p->allowsNull() && !class_exists($type) && !interface_exists($type, \false)) { |
| 160 | $message = sprintf('Cannot determine controller argument for "%s::%s()": the $%s argument is type-hinted with the non-existent class or interface: "%s".', $class, $r->name, $p->name, $type); |
| 161 | // see if the type-hint lives in the same namespace as the controller |
| 162 | if (0 === strncmp($type, $class, strrpos($class, '\\'))) { |
| 163 | $message .= ' Did you forget to add a use statement?'; |
| 164 | } |
| 165 | $container->register($erroredId = '.errored.' . $container->hash($message), $type)->addError($message); |
| 166 | $args[$p->name] = new Reference($erroredId, ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE); |
| 167 | } else { |
| 168 | $target = ltrim($target, '\\'); |
| 169 | $args[$p->name] = $type ? new TypedReference($target, $type, $invalidBehavior, Target::parseName($p)) : new Reference($target, $invalidBehavior); |
| 170 | } |
| 171 | } |
| 172 | // register the maps as a per-method service-locators |
| 173 | if ($args) { |
| 174 | $controllers[$id . '::' . $r->name] = ServiceLocatorTagPass::register($container, $args); |
| 175 | foreach ($publicAliases[$id] ?? [] as $alias) { |
| 176 | $controllers[$alias . '::' . $r->name] = clone $controllers[$id . '::' . $r->name]; |
| 177 | } |
| 178 | } |
| 179 | } |
| 180 | } |
| 181 | $controllerLocatorRef = ServiceLocatorTagPass::register($container, $controllers); |
| 182 | if ($container->hasDefinition($this->resolverServiceId)) { |
| 183 | $container->getDefinition($this->resolverServiceId)->replaceArgument(0, $controllerLocatorRef); |
| 184 | } |
| 185 | if ($container->hasDefinition($this->notTaggedControllerResolverServiceId)) { |
| 186 | $container->getDefinition($this->notTaggedControllerResolverServiceId)->replaceArgument(0, $controllerLocatorRef); |
| 187 | } |
| 188 | $container->setAlias($this->controllerLocator, (string) $controllerLocatorRef); |
| 189 | } |
| 190 | } |
| 191 |