* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ declare(strict_types=1); namespace OCC\PSR15; use Exception; use DomainException; use RuntimeException; use GuzzleHttp\Psr7\Response; use GuzzleHttp\Psr7\ServerRequest; use OCC\Basics\Traits\Getter; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; /** * A queue-based PSR-15 HTTP Server Request Handler. * * @author Sebastian Meyer * @package opencultureconsulting/psr15 */ class QueueRequestHandler implements RequestHandlerInterface { use Getter; /** * The PSR-7 HTTP Server Request. */ protected ServerRequestInterface $request; /** * The queue of middlewares to process the server request. */ protected MiddlewareQueue $queue; /** * The PSR-7 HTTP Response. */ protected ResponseInterface $response; /** * Handles a request by invoking a queue of middlewares. * * @param ?ServerRequestInterface $request The PSR-7 server request to handle * * @return ResponseInterface A PSR-7 compatible HTTP response */ public function handle(?ServerRequestInterface $request = null): ResponseInterface { $this->request = $request ?? $this->request; // It is RECOMMENDED that any application using middleware includes a // component that catches exceptions and converts them into responses. // This middleware SHOULD be the first component executed and wrap all // further processing to ensure that a response is always generated. try { if (count($this->queue) > 0) { $middleware = $this->queue->dequeue(); if (!is_object($middleware) || !is_a($middleware, MiddlewareInterface::class)) { throw new DomainException( sprintf( 'Middleware must be of type %s, %s given.', MiddlewareInterface::class, get_debug_type($middleware) ), 500 ); } $this->response = $middleware->process($this->request, $this); } } catch(Exception $exception) { $options = [ 'options' => [ 'default' => 500, 'min_range' => 100, 'max_range' => 599 ] ]; $statusCode = filter_var($exception->getCode(), FILTER_VALIDATE_INT, $options); $this->response = new Response($statusCode, [], $exception->getMessage()); $this->respond(1); } return $this->response; } /** * Return the current response to the client. * * @param ?int $exitCode Exit code after sending out the response or NULL to continue * * @return void */ public function respond(?int $exitCode = null): void { $file = 'unknown file'; $line = 0; if (headers_sent($file, $line)) { throw new RuntimeException( sprintf( 'Headers already sent in %s on line %d', $file, $line ) ); } header( sprintf( 'HTTP/%s %s %s', $this->response->getProtocolVersion(), $this->response->getStatusCode(), $this->response->getReasonPhrase() ), true ); foreach (array_keys($this->response->getHeaders()) as $name) { $header = sprintf('%s: %s', $name, $this->response->getHeaderLine($name)); header($header, false); } echo $this->response->getBody(); if (!is_null($exitCode)) { exit($exitCode); } } /** * Magic getter method for $this->queue. * @see Getter * * @return MiddlewareQueue The queue of PSR-15 middlewares */ protected function magicGetQueue(): MiddlewareQueue { return $this->queue; } /** * Magic getter method for $this->request. * @see Getter * * @return ServerRequestInterface The PSR-7 server request */ protected function magicGetRequest(): ServerRequestInterface { return $this->request; } /** * Create a queue-based PSR-15 HTTP Server Request Handler. * * @param iterable $middlewares Initial set of middlewares */ public function __construct(?iterable $middlewares = []) { $this->request = ServerRequest::fromGlobals(); $this->queue = MiddlewareQueue::getInstance($middlewares); $this->response = new Response(200); } /** * Allow the request handler to be invoked directly. * @see QueueRequestHandler::handle() * * @param ?ServerRequestInterface $request The PSR-7 server request to handle * * @return ResponseInterface A PSR-7 compatible HTTP response */ public function __invoke(?ServerRequestInterface $request = null): ResponseInterface { return $this->handle($request); } }