diff --git a/Neos.Flow/Classes/Cli/CommandRequestHandler.php b/Neos.Flow/Classes/Cli/CommandRequestHandler.php index 810bfd00f9..fbeb1e027d 100644 --- a/Neos.Flow/Classes/Cli/CommandRequestHandler.php +++ b/Neos.Flow/Classes/Cli/CommandRequestHandler.php @@ -75,7 +75,7 @@ public function canHandleRequest(): bool * * @return integer The priority of the request handler. */ - public function getPriority(): int + public static function getPriority(): int { return 100; } diff --git a/Neos.Flow/Classes/Cli/SlaveRequestHandler.php b/Neos.Flow/Classes/Cli/SlaveRequestHandler.php index 794e96bf48..d8ad4f0913 100644 --- a/Neos.Flow/Classes/Cli/SlaveRequestHandler.php +++ b/Neos.Flow/Classes/Cli/SlaveRequestHandler.php @@ -61,7 +61,7 @@ public function canHandleRequest(): bool * * @return integer The priority of the request handler. */ - public function getPriority(): int + public static function getPriority(): int { return 200; } diff --git a/Neos.Flow/Classes/Core/Bootstrap.php b/Neos.Flow/Classes/Core/Bootstrap.php index 267233ca2e..f40b6b7527 100644 --- a/Neos.Flow/Classes/Core/Bootstrap.php +++ b/Neos.Flow/Classes/Core/Bootstrap.php @@ -31,28 +31,22 @@ */ class Bootstrap { - const RUNLEVEL_COMPILETIME = 'Compiletime'; - const RUNLEVEL_RUNTIME = 'Runtime'; + public const RUNLEVEL_COMPILETIME = 'Compiletime'; + public const RUNLEVEL_RUNTIME = 'Runtime'; - /** - * @var ApplicationContext - */ - protected $context; + protected ApplicationContext $context; /** - * @var array, RequestHandlerInterface> + * @var array */ - protected $requestHandlers = []; + private array $requestHandlerClassNames = []; /** - * @var class-string + * @var class-string|null */ - protected $preselectedRequestHandlerClassName; + protected string|null $preselectedRequestHandlerClassName = null; - /** - * @var RequestHandlerInterface - */ - protected $activeRequestHandler; + protected RequestHandlerInterface $activeRequestHandler; /** * The same instance like $objectManager, but static, for use in the proxy classes. @@ -144,17 +138,17 @@ public function getContext(): ApplicationContext } /** - * Registers a request handler which can possibly handle a request. - * All registered request handlers will be queried if they can handle a request - * when the bootstrap's run() method is called. + * Registers a request handler which can possibly handle a request. + * All registered request handlers will be queried if they can handle a request + * when the bootstrap's run() method is called. * - * @param RequestHandlerInterface $requestHandler + * @param class-string $className * @return void - * @api */ - public function registerRequestHandler(RequestHandlerInterface $requestHandler) + public function registerRequestHandlerClassName(string $className): void { - $this->requestHandlers[get_class($requestHandler)] = $requestHandler; + // No checks at this point in time as we might not be able to autolaod the class yet. + $this->requestHandlerClassNames[] = $className; } /** @@ -162,10 +156,15 @@ public function registerRequestHandler(RequestHandlerInterface $requestHandler) * it will be used if it can handle the request – regardless of the priority * of this or other request handlers. * + * This will also register the classname as a possible request handler. + * * @param class-string $className */ - public function setPreselectedRequestHandlerClassName(string $className) + public function setPreselectedRequestHandlerClassName(string $className): void { + if (!in_array($className, $this->requestHandlerClassNames, true)) { + $this->registerRequestHandlerClassName($className); + } $this->preselectedRequestHandlerClassName = $className; } @@ -191,7 +190,7 @@ public function getActiveRequestHandler(): RequestHandlerInterface * @param RequestHandlerInterface $requestHandler * @return void */ - public function setActiveRequestHandler(RequestHandlerInterface $requestHandler) + public function setActiveRequestHandler(RequestHandlerInterface $requestHandler): void { $this->activeRequestHandler = $requestHandler; } @@ -404,27 +403,54 @@ public function getObjectManager(): ObjectManagerInterface */ protected function resolveRequestHandler(): RequestHandlerInterface { - if ($this->preselectedRequestHandlerClassName !== null && isset($this->requestHandlers[$this->preselectedRequestHandlerClassName])) { - $requestHandler = $this->requestHandlers[$this->preselectedRequestHandlerClassName]; + if ($this->preselectedRequestHandlerClassName !== null) { + $requestHandler = $this->getRequestHandlerInstance($this->preselectedRequestHandlerClassName); if ($requestHandler->canHandleRequest()) { return $requestHandler; } } - foreach ($this->requestHandlers as $requestHandler) { + /** @var RequestHandlerInterface|null $bestMatchingRequestHandler */ + $bestMatchingRequestHandler = null; + $bestMatchingPriority = -9999; + foreach($this->requestHandlerClassNames as $requestHandlerClassName) { + if ($bestMatchingPriority > $requestHandlerClassName::getPriority()) { + continue; + } + + $requestHandler = $this->getRequestHandlerInstance($requestHandlerClassName); if ($requestHandler->canHandleRequest() > 0) { - $priority = $requestHandler->getPriority(); - if (isset($suitableRequestHandlers[$priority])) { + $priority = $requestHandler::getPriority(); + if ($bestMatchingPriority === $priority) { throw new FlowException('More than one request handler with the same priority can handle the request, but only one handler may be active at a time!', 1176475350); } - $suitableRequestHandlers[$priority] = $requestHandler; + $bestMatchingPriority = $priority; + $bestMatchingRequestHandler = $requestHandler; } } - if (empty($suitableRequestHandlers)) { + + if (is_null($bestMatchingRequestHandler)) { throw new FlowException('No suitable request handler could be found for the current request. This is most likely a setup-problem, so please check your composer.json and/or try removing Configuration/PackageStates.php', 1464882543); } - ksort($suitableRequestHandlers); - return array_pop($suitableRequestHandlers); + return $bestMatchingRequestHandler; + } + + /** + * @param class-string $className + * @return RequestHandlerInterface + */ + private function getRequestHandlerInstance(string $className): RequestHandlerInterface + { + if (!in_array($className, $this->requestHandlerClassNames, true)) { + throw new FlowException(sprintf('A request handler "%s" was never registered', $className), 1719060769); + } + + $requestHandler = new $className($this); + if (!$requestHandler instanceof RequestHandlerInterface) { + throw new FlowException(sprintf('The class "%s" registered as request handler must implement the RequestHandlerInterface interface.', $className), 1719053345); + } + + return $requestHandler; } /** diff --git a/Neos.Flow/Classes/Core/RequestHandlerInterface.php b/Neos.Flow/Classes/Core/RequestHandlerInterface.php index fa208e65ad..6813b7aa3b 100644 --- a/Neos.Flow/Classes/Core/RequestHandlerInterface.php +++ b/Neos.Flow/Classes/Core/RequestHandlerInterface.php @@ -42,5 +42,5 @@ public function canHandleRequest(); * @return integer The priority of the request handler * @api */ - public function getPriority(); + public static function getPriority(): int; } diff --git a/Neos.Flow/Classes/Http/RequestHandler.php b/Neos.Flow/Classes/Http/RequestHandler.php index 249780b287..722574f103 100644 --- a/Neos.Flow/Classes/Http/RequestHandler.php +++ b/Neos.Flow/Classes/Http/RequestHandler.php @@ -81,7 +81,7 @@ public function canHandleRequest() * @return integer The priority of the request handler. * @api */ - public function getPriority() + public static function getPriority(): int { return 100; } diff --git a/Neos.Flow/Classes/Package.php b/Neos.Flow/Classes/Package.php index acdfdc0118..e1b5d4575b 100644 --- a/Neos.Flow/Classes/Package.php +++ b/Neos.Flow/Classes/Package.php @@ -49,17 +49,17 @@ public function boot(Core\Bootstrap $bootstrap) $context = $bootstrap->getContext(); if (PHP_SAPI === 'cli') { - $bootstrap->registerRequestHandler(new Cli\SlaveRequestHandler($bootstrap)); - $bootstrap->registerRequestHandler(new Cli\CommandRequestHandler($bootstrap)); + $bootstrap->registerRequestHandlerClassName(Cli\SlaveRequestHandler::class); + $bootstrap->registerRequestHandlerClassName(Cli\CommandRequestHandler::class); } else { - $bootstrap->registerRequestHandler(new Http\RequestHandler($bootstrap)); + $bootstrap->registerRequestHandlerClassName(Http\RequestHandler::class); } if ($context->isTesting()) { // TODO: This is technically not necessary as we can register the request handler in the functional bootstrap // A future commit will remove this aftter BuildEssentials is adapted /** @phpstan-ignore-next-line composer doesnt autoload this class */ - $bootstrap->registerRequestHandler(new Tests\FunctionalTestRequestHandler($bootstrap)); + $bootstrap->registerRequestHandlerClassName(Tests\FunctionalTestRequestHandler::class); } $bootstrap->registerCompiletimeCommand('neos.flow:core:*'); diff --git a/Neos.Flow/Classes/Testing/RequestHandler/EmptyRequestHandler.php b/Neos.Flow/Classes/Testing/RequestHandler/EmptyRequestHandler.php index c74f5e90a1..7fa9fc952c 100644 --- a/Neos.Flow/Classes/Testing/RequestHandler/EmptyRequestHandler.php +++ b/Neos.Flow/Classes/Testing/RequestHandler/EmptyRequestHandler.php @@ -33,7 +33,7 @@ public function canHandleRequest(): bool return true; } - public function getPriority(): int + public static function getPriority(): int { return 0; } diff --git a/Neos.Flow/Classes/Testing/RequestHandler/RuntimeSequenceInvokingRequestHandler.php b/Neos.Flow/Classes/Testing/RequestHandler/RuntimeSequenceInvokingRequestHandler.php index c284d0789e..c9cb598624 100644 --- a/Neos.Flow/Classes/Testing/RequestHandler/RuntimeSequenceInvokingRequestHandler.php +++ b/Neos.Flow/Classes/Testing/RequestHandler/RuntimeSequenceInvokingRequestHandler.php @@ -56,7 +56,7 @@ public function canHandleRequest(): bool * * @return integer The priority of the request handler. */ - public function getPriority(): int + public static function getPriority(): int { return 0; } diff --git a/Neos.Flow/Tests/FunctionalTestRequestHandler.php b/Neos.Flow/Tests/FunctionalTestRequestHandler.php index 6fdbe76f6f..519cdfa355 100644 --- a/Neos.Flow/Tests/FunctionalTestRequestHandler.php +++ b/Neos.Flow/Tests/FunctionalTestRequestHandler.php @@ -74,7 +74,7 @@ public function canHandleRequest() * * @return integer The priority of the request handler. */ - public function getPriority() + public static function getPriority(): int { return 0; } diff --git a/Neos.Flow/Tests/PhpBench/Core/BootstrapBench.php b/Neos.Flow/Tests/PhpBench/Core/BootstrapBench.php index 7706edb0b1..12f4ce1160 100644 --- a/Neos.Flow/Tests/PhpBench/Core/BootstrapBench.php +++ b/Neos.Flow/Tests/PhpBench/Core/BootstrapBench.php @@ -41,7 +41,6 @@ public function benchBootstrapConstruct(): void public function benchBootstrapRunWithoutBootSequence(): void { $flowBootstrap = new Bootstrap(TestableFramework::getApplicationContext()); - $flowBootstrap->registerRequestHandler(new EmptyRequestHandler()); $flowBootstrap->setPreselectedRequestHandlerClassName(EmptyRequestHandler::class); $flowBootstrap->run(); } @@ -57,7 +56,6 @@ public function benchBootstrapRunWithoutBootSequence(): void public function benchBootstrapRunWithRuntimeBootSequence(): void { $flowBootstrap = new Bootstrap(TestableFramework::getApplicationContext()); - $flowBootstrap->registerRequestHandler(new RuntimeSequenceInvokingRequestHandler($flowBootstrap)); $flowBootstrap->setPreselectedRequestHandlerClassName(RuntimeSequenceInvokingRequestHandler::class); $flowBootstrap->run(); }