Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ jobs:
run: ./vendor/bin/phpunit tests --testdox

- name: Run static analysis
run: ./vendor/bin/phpstan analyse src --level 6 --no-progress --no-interaction
run: ./vendor/bin/phpstan analyse src --no-progress --no-interaction
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@
},
"autoload": {
"psr-4": {
"Borsch\\": "src/"
"Borsch\\RequestHandler\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"BorschTest\\": "tests/"
}
}
}
}
3 changes: 3 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
parameters:
level: 6
treatPhpDocTypesAsCertain: false
62 changes: 62 additions & 0 deletions src/Emitter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php declare(strict_types=1);

namespace Borsch\RequestHandler;

use Psr\Http\Message\{ResponseInterface, StreamInterface};
use function header, sprintf, ucwords;

class Emitter implements EmitterInterface
{

/**
* @link https://github.com/http-interop/response-sender/blob/master/src/functions.php
*/
public function emit(ResponseInterface $response): void
{
$this->emitHeaders($response);
$this->emitStatusLine($response);
$this->emitBody($response->getBody());
}

private function emitHeaders(ResponseInterface $response): void
{
foreach ($response->getHeaders() as $name => $values) {
foreach ($values as $value) {
header(sprintf(
'%s: %s',
ucwords($name, '-'), $value),
false
);
}
}
}

private function emitStatusLine(ResponseInterface $response): void
{
$http_line = sprintf(
'HTTP/%s %s %s',
$response->getProtocolVersion(),
$response->getStatusCode(),
$response->getReasonPhrase()
);

header($http_line, true, $response->getStatusCode());
}

private function emitBody(StreamInterface $stream): void
{
if ($stream->isSeekable()) {
$stream->rewind();
}

if (!$stream->isReadable()) {
echo $stream;
return;
}

$length = 1024 * 8;
while (!$stream->eof()) {
echo $stream->read($length);
}
}
}
16 changes: 16 additions & 0 deletions src/EmitterInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php declare(strict_types=1);

namespace Borsch\RequestHandler;

use Psr\Http\Message\ResponseInterface;

interface EmitterInterface
{

/**
* Emits the given response.
*
* @param ResponseInterface $response The response to emit.
*/
public function emit(ResponseInterface $response): void;
}
13 changes: 10 additions & 3 deletions src/RequestHandler/RequestHandler.php → src/RequestHandler.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<?php
<?php declare(strict_types=1);

namespace Borsch\RequestHandler;

use Borsch\RequestHandler\Exception\RequestHandlerRuntimeException;
use Psr\Http\Message\{ResponseInterface, ServerRequestInterface};
use Psr\Http\Server\MiddlewareInterface;
use SplStack;
use function get_class, gettype, is_object;

class RequestHandler implements RequestHandlerInterface
{
Expand Down Expand Up @@ -44,6 +44,13 @@ public function handle(ServerRequestInterface $request): ResponseInterface
throw RequestHandlerRuntimeException::emptyStack();
}

return $this->stack->shift()->process($request, $this);
$middleware = $this->stack->shift();
if (!$middleware instanceof MiddlewareInterface) {
throw RequestHandlerRuntimeException::invalidMiddleware(
(string)(is_object($middleware) ? get_class($middleware) : gettype($middleware))
);
}

return $middleware->process($request, $this);
}
}
40 changes: 0 additions & 40 deletions src/RequestHandler/Emitter.php

This file was deleted.

18 changes: 0 additions & 18 deletions src/RequestHandler/Exception/RequestHandlerRuntimeException.php

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);

namespace Borsch\RequestHandler;

Expand All @@ -11,8 +11,8 @@ interface RequestHandlerInterface extends PsrRequestHandlerInterface
{

/**
* Add a middleware to the request handler.
* Add middleware to the request handler.
* Must return itself to allow method chaining (if desired).
*/
public function middleware(MiddlewareInterface $middleware): RequestHandlerInterface;
}
}
51 changes: 51 additions & 0 deletions src/RequestHandlerRunner.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php declare(strict_types=1);

namespace Borsch\RequestHandler;

use Psr\Http\Message\{ResponseInterface, ServerRequestInterface};
use Psr\Http\Server\RequestHandlerInterface;
use Throwable;

final class RequestHandlerRunner implements RequestHandlerRunnerInterface
{

/** @var callable(): ServerRequestInterface */
private $server_request_factory;

/** @var callable(Throwable $e): ResponseInterface */
private $error_response_factory;

/**
* @param callable(): ServerRequestInterface $server_request_factory
* @param callable(Throwable $e): ResponseInterface $error_response_factory
*/
public function __construct(
private readonly RequestHandlerInterface $handler,
private readonly EmitterInterface $emitter,
callable $server_request_factory,
callable $error_response_factory,
) {
$this->server_request_factory = $server_request_factory;
$this->error_response_factory = $error_response_factory;
}

/**
* @inheritDoc
*/
public function run(): void
{
try {
$request = ($this->server_request_factory)();
} catch (Throwable $e) {
$this->emitter->emit(
($this->error_response_factory)($e)
);

return;
}

$this->emitter->emit(
$this->handler->handle($request)
);
}
}
13 changes: 13 additions & 0 deletions src/RequestHandlerRunnerInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php declare(strict_types=1);

namespace Borsch\RequestHandler;

interface RequestHandlerRunnerInterface
{

/**
* Run the request handler.
* This method should handle the request and send the response.
*/
public function run(): void;
}
29 changes: 29 additions & 0 deletions src/RequestHandlerRuntimeException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php declare(strict_types=1);

namespace Borsch\RequestHandler;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\MiddlewareInterface;
use RuntimeException;
use function sprintf;

class RequestHandlerRuntimeException extends RuntimeException
{

public static function emptyStack(): self
{
return new self(sprintf(
'The middleware stack is empty and no %s has been returned',
ResponseInterface::class
));
}

public static function invalidMiddleware(string $middleware): self
{
return new self(sprintf(
'The middleware "%s" must implement %s',
$middleware,
MiddlewareInterface::class
));
}
}