diff --git a/config/services.php b/config/services.php index 68dc18a..0a333e2 100644 --- a/config/services.php +++ b/config/services.php @@ -35,9 +35,9 @@ ->args([ service('sofascore.purgatory.mapping.configuration'), service('router'), - service('controller_resolver'), service('sofascore.purgatory.annotation_reader'), service('doctrine.orm.entity_manager'), + abstract_arg('ControllerClassMapPass'), ]) ->set('sofascore.purgatory.mapping.annotation_loader.warmer', AnnotationsLoaderWarmer::class) diff --git a/src/DependencyInjection/CompilerPass/ControllerClassMapPass.php b/src/DependencyInjection/CompilerPass/ControllerClassMapPass.php new file mode 100644 index 0000000..725c0cb --- /dev/null +++ b/src/DependencyInjection/CompilerPass/ControllerClassMapPass.php @@ -0,0 +1,22 @@ +findTaggedServiceIds('controller.service_arguments', true) as $id => $tags) { + $classMap[$id] = $container->getDefinition($id)->getClass(); + } + + $container->getDefinition('sofascore.purgatory.mapping.annotation_loader')->replaceArgument(4, $classMap); + } +} diff --git a/src/Mapping/Loader/AnnotationsLoader.php b/src/Mapping/Loader/AnnotationsLoader.php index 76cdd30..98704b8 100644 --- a/src/Mapping/Loader/AnnotationsLoader.php +++ b/src/Mapping/Loader/AnnotationsLoader.php @@ -14,9 +14,7 @@ use Sofascore\PurgatoryBundle\Mapping\MappingValue; use Sofascore\PurgatoryBundle\Mapping\PropertySubscription; use Symfony\Component\Config\ConfigCache; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; -use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouterInterface; @@ -24,22 +22,22 @@ class AnnotationsLoader implements LoaderInterface, WarmableInterface { protected RouterInterface $router; protected Configuration $config; - protected ControllerResolverInterface $controllerResolver; protected Reader $annotationReader; protected ObjectManager $manager; + protected array $serviceClassMap; public function __construct( Configuration $config, RouterInterface $router, - ControllerResolverInterface $controllerResolver, Reader $annotationReader, ObjectManager $manager, + array $serviceClassMap, ) { $this->config = $config; $this->router = $router; - $this->controllerResolver = $controllerResolver; $this->annotationReader = $annotationReader; $this->manager = $manager; + $this->serviceClassMap = $serviceClassMap; } /** @@ -118,15 +116,17 @@ private function loadMappings(): MappingCollection } } - $controllerCallable = $this->resolveController($route->getDefault('_controller')); + if (null === $routeController = $route->getDefault('_controller')) { + continue; + } // if controller cannot be resolved, skip route - if (false === $controllerCallable) { + if (null === $controllerCallableReflection = $this->resolveControllerCallable($routeController)) { continue; } // get class/property subscriptions - $this->parseControllerMappings($controllerCallable, $routeName, $route, $subscriptions); + $this->parseControllerMappings($controllerCallableReflection, $routeName, $route, $subscriptions); } // resolve subscription classes and properties @@ -154,22 +154,46 @@ private function loadMappings(): MappingCollection return $mappingCollection; } - protected function resolveController(?string $controllerPath): callable|false + private function resolveControllerCallable(array|object|string $controller): ?\ReflectionMethod { - if (null === $controllerPath) { - return false; + if (\is_array($controller) && isset($controller[0]) && \is_string($controller[0]) && isset($controller[1])) { + $resolvedClass = $this->resolveClass($controller[0]); + + return $resolvedClass ? new \ReflectionMethod($resolvedClass, $controller[1]) : null; } - // set controller path - $request = new Request([], [], ['_controller' => $controllerPath]); + if (\is_object($controller) && \is_callable($controller)) { + return new \ReflectionMethod($controller, '__invoke'); + } - try { - // resolve controller path - return $this->controllerResolver->getController($request); - } catch (\Exception $e) { - // if error happens skip route - return false; + if (\function_exists($controller)) { + return null; } + + if (!str_contains($controller, '::')) { + $resolvedClass = $this->resolveClass($controller); + + return $resolvedClass ? new \ReflectionMethod($resolvedClass, '__invoke') : null; + } + + [$class, $method] = explode('::', $controller, 2); + + $resolvedClass = $this->resolveClass($class); + + return $resolvedClass ? new \ReflectionMethod($resolvedClass, $method) : null; + } + + private function resolveClass(string $serviceIdOrClass): ?string + { + if (isset($this->serviceClassMap[$serviceIdOrClass])) { + return $this->serviceClassMap[$serviceIdOrClass]; + } + + if (class_exists($serviceIdOrClass)) { + return $serviceIdOrClass; + } + + return null; } /** @@ -178,19 +202,12 @@ protected function resolveController(?string $controllerPath): callable|false * @throws \ReflectionException|ReaderException */ private function parseControllerMappings( - callable $controllerCallable, + \ReflectionMethod $controllerCallableReflection, string $routeName, Route $route, array &$subscriptions, ): array { - if (!\is_array($controllerCallable)) { - return []; - } - - [$controller, $method] = $controllerCallable; - - $reflectionMethod = new \ReflectionMethod($controller, $method); - $methodAnnotations = $this->annotationReader->getAnnotations($reflectionMethod); + $methodAnnotations = $this->annotationReader->getAnnotations($controllerCallableReflection); foreach ($methodAnnotations as $class => $annotations) { if (PurgeOn::class === $class) { diff --git a/src/PurgatoryBundle.php b/src/PurgatoryBundle.php index dfdce88..833b3f7 100644 --- a/src/PurgatoryBundle.php +++ b/src/PurgatoryBundle.php @@ -4,6 +4,7 @@ namespace Sofascore\PurgatoryBundle; +use Sofascore\PurgatoryBundle\DependencyInjection\CompilerPass\ControllerClassMapPass; use Sofascore\PurgatoryBundle\DependencyInjection\CompilerPass\RegisterPurgerImplementationCompilerPass; use Sofascore\PurgatoryBundle\DependencyInjection\CompilerPass\SymfonyPurgerRemovalCompilerPass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -20,5 +21,6 @@ public function build(ContainerBuilder $container): void $container->addCompilerPass(new RegisterPurgerImplementationCompilerPass()); $container->addCompilerPass(new SymfonyPurgerRemovalCompilerPass()); + $container->addCompilerPass(new ControllerClassMapPass()); } }