Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

!!!TASK: Bootstrap instantiates request handlers internally #3371

Open
wants to merge 1 commit into
base: 9.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions Neos.Flow/Classes/Cli/CommandRequestHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,12 @@ class CommandRequestHandler implements RequestHandlerInterface
*/
protected $response;

/**
* @param Bootstrap $bootstrap
*/
public function __construct(Bootstrap $bootstrap)
public static function fromBootstrap(Bootstrap $bootstrap): RequestHandlerInterface
{
return new self($bootstrap);
}

private function __construct(Bootstrap $bootstrap)
{
$this->bootstrap = $bootstrap;
}
Expand All @@ -75,7 +77,7 @@ public function canHandleRequest(): bool
*
* @return integer The priority of the request handler.
*/
public function getPriority(): int
public static function getPriority(): int
{
return 100;
}
Expand Down
14 changes: 7 additions & 7 deletions Neos.Flow/Classes/Cli/SlaveRequestHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ class SlaveRequestHandler implements RequestHandlerInterface
*/
protected $bootstrap;

/**
* Constructor
*
* @param Bootstrap $bootstrap
*/
public function __construct(Bootstrap $bootstrap)
public static function fromBootstrap(Bootstrap $bootstrap): RequestHandlerInterface
{
return new self($bootstrap);
}

private function __construct(Bootstrap $bootstrap)
{
$this->bootstrap = $bootstrap;
}
Expand All @@ -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;
}
Expand Down
95 changes: 62 additions & 33 deletions Neos.Flow/Classes/Core/Bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -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<class-string<RequestHandlerInterface>, RequestHandlerInterface>
* @var array<int, class-string>
*/
protected $requestHandlers = [];
private array $requestHandlerClassNames = [];

/**
* @var class-string<RequestHandlerInterface>
* @var class-string<RequestHandlerInterface>|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.
Expand Down Expand Up @@ -144,28 +138,33 @@ 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<RequestHandlerInterface> $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;
}

/**
* Preselects a specific request handler. If such a request handler exists,
* 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<RequestHandlerInterface> $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;
}

Expand All @@ -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;
}
Expand Down Expand Up @@ -231,7 +230,7 @@ public function isCompiletimeCommand(string $commandIdentifier): bool
$shortControllerIdentifier = implode(':', $commandIdentifierParts);

foreach ($this->compiletimeCommands as $fullControllerIdentifier => $isCompiletimeCommandController) {
list($packageKey, $controllerName, $commandName) = explode(':', $fullControllerIdentifier);
[$packageKey, $controllerName, $commandName] = explode(':', $fullControllerIdentifier);
$packageKeyParts = explode('.', $packageKey);
$packageKeyPartsCount = count($packageKeyParts);
for ($offset = 0; $offset < $packageKeyPartsCount; $offset++) {
Expand Down Expand Up @@ -404,27 +403,57 @@ 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);
}

if (isset($this->earlyInstances[$className]) && $this->earlyInstances[$className] instanceof RequestHandlerInterface) {
return $this->earlyInstances[$className];
}

if (!in_array(RequestHandlerInterface::class, class_implements($className), true)) {
throw new FlowException(sprintf('The class "%s" registered as request handler must implement the RequestHandlerInterface interface.', $className), 1719132434);
}

return $className::fromBootstrap($this);
}

/**
Expand Down
11 changes: 10 additions & 1 deletion Neos.Flow/Classes/Core/RequestHandlerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,14 @@ public function canHandleRequest();
* @return integer The priority of the request handler
* @api
*/
public function getPriority();
public static function getPriority(): int;

/**
* This is the API to create an instance of a request handler the bootstrap will use internally.
*
* @param Bootstrap $bootstrap
* @return self
* @api
*/
public static function fromBootstrap(Bootstrap $bootstrap): self;
}
15 changes: 9 additions & 6 deletions Neos.Flow/Classes/Http/RequestHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use GuzzleHttp\Psr7\ServerRequest;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Core\Bootstrap;
use Neos\Flow\Core\RequestHandlerInterface;
use Neos\Flow\Http\Helper\ResponseInformationHelper;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
Expand Down Expand Up @@ -52,13 +53,15 @@ class RequestHandler implements HttpRequestHandlerInterface
*/
public $exit;

/**
* @param Bootstrap $bootstrap
*/
public function __construct(Bootstrap $bootstrap)
public static function fromBootstrap(Bootstrap $bootstrap): RequestHandlerInterface
{
return new self($bootstrap);
}

private function __construct(Bootstrap $bootstrap)
{
$this->bootstrap = $bootstrap;
$this->exit = function () {
$this->exit = static function () {
exit();
};
}
Expand All @@ -81,7 +84,7 @@ public function canHandleRequest()
* @return integer The priority of the request handler.
* @api
*/
public function getPriority()
public static function getPriority(): int
{
return 100;
}
Expand Down
8 changes: 4 additions & 4 deletions Neos.Flow/Classes/Package.php
Original file line number Diff line number Diff line change
Expand Up @@ -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:*');
Expand Down
12 changes: 11 additions & 1 deletion Neos.Flow/Classes/Testing/RequestHandler/EmptyRequestHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
namespace Neos\Flow\Testing\RequestHandler;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Core\Bootstrap;
use Neos\Flow\Core\RequestHandlerInterface;
use Neos\Flow\Tests\PhpBench\Core\BootstrapBench;

Expand All @@ -24,6 +25,15 @@
*/
final class EmptyRequestHandler implements RequestHandlerInterface
{
public static function fromBootstrap(Bootstrap $bootstrap): RequestHandlerInterface
{
return new self();
}

private function __construct()
{
}

public function handleRequest(): void
{
}
Expand All @@ -33,7 +43,7 @@ public function canHandleRequest(): bool
return true;
}

public function getPriority(): int
public static function getPriority(): int
{
return 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ class RuntimeSequenceInvokingRequestHandler implements RequestHandlerInterface
{
protected Bootstrap $bootstrap;

/**
* Constructor
*
* @param \Neos\Flow\Core\Bootstrap $bootstrap
*/
public function __construct(Bootstrap $bootstrap)
public static function fromBootstrap(Bootstrap $bootstrap): RequestHandlerInterface
{
return new self($bootstrap);
}

private function __construct(Bootstrap $bootstrap)
{
$this->bootstrap = $bootstrap;
}
Expand All @@ -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;
}
Expand Down
15 changes: 8 additions & 7 deletions Neos.Flow/Tests/FunctionalTestRequestHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use GuzzleHttp\Psr7\ServerRequest;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Core\Bootstrap;
use Neos\Flow\Core\RequestHandlerInterface;
use Psr\Http\Message\ServerRequestInterface;

/**
Expand Down Expand Up @@ -45,12 +46,12 @@ class FunctionalTestRequestHandler implements \Neos\Flow\Http\HttpRequestHandler
*/
protected $httpRequest;

/**
* Constructor
*
* @param \Neos\Flow\Core\Bootstrap $bootstrap
*/
public function __construct(Bootstrap $bootstrap)
public static function fromBootstrap(Bootstrap $bootstrap): RequestHandlerInterface
{
return new self($bootstrap);
}

private function __construct(Bootstrap $bootstrap)
{
$this->bootstrap = $bootstrap;
}
Expand All @@ -74,7 +75,7 @@ public function canHandleRequest()
*
* @return integer The priority of the request handler.
*/
public function getPriority()
public static function getPriority(): int
{
return 0;
}
Expand Down
Loading
Loading