Skip to content

Instantly share code, notes, and snippets.

@jm42
Created September 14, 2016 01:17
Show Gist options
  • Save jm42/ebf80cf5e40d062e54bdeea6f3720a89 to your computer and use it in GitHub Desktop.
Save jm42/ebf80cf5e40d062e54bdeea6f3720a89 to your computer and use it in GitHub Desktop.
Old http middleware impl for guide42/ochenta
<?php declare(strict_types=1);
namespace ochenta\psr15;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use ochenta\psr7\Response;
use ochenta\psr7\ServerRequest;
use ochenta\psr7\Stream;
use function ochenta\stream_of;
/** PSR-15 stack with a base responder to be called last.
* This a responder and acts like a middleware for `ochenta\emit`.
*/
class Stack/* implements StackInterface*/
{
protected $responder;
protected $stack = [];
function __construct(callable $responder, array $stack=[]) {
$this->responder = $responder;
$this->stack = array_filter($stack, function(MiddlewareInterface $middleware): bool {
return TRUE;
});
}
function withMiddleware(MiddlewareInterface $middleware): self {
$new = clone $this;
$new->stack[] = $middleware;
return $new;
}
function withoutMiddleware(MiddlewareInterface $middleware): self {
$new = clone $this;
$new->stack = [];
foreach ($this->stack as $index => $source) {
if ($source !== $middleware) {
$new->stack[] = $source;
}
}
return $new;
}
function process(RequestInterface $request): ResponseInterface {
return $this->resolve(0)->next($request);
}
private function resolve(int $index)/*: FrameInterface*/ {
$middleware = $this->stack[$index] ?? NULL;
$next = is_null($middleware) ? NULL : $this->resolve($index + 1);
return new class($this->responder, $middleware, $next)/* implements FrameInterface*/ {
private $responder;
private $middleware;
private $next;
function __construct(
callable $responder,
MiddlewareInterface $middleware=NULL,
FrameInterface $next=NULL
) {
$this->responder = $responder;
$this->middleware = $middleware;
$this->next = $next;
}
function next(RequestInterface $request): ResponseInterface {
if (is_null($this->middleware) || is_null($this->next)) {
$response = new Response;
$response = $response->withBody(new Stream(stream_of('')));
$responder = $this->responder;
$generator = $responder($request, function(int $status, array $headers) use(&$response) {
$response = $response->withStatus($status);
foreach ($headers as $header => $values) {
$response = $response->withHeader($header, $values);
}
});
try {
foreach ($generator as $output) {
$response->getBody()->write($output);
}
} finally {
try {
$close = $generator->getReturn();
} catch (\Exception $ex) {
$close = NULL;
}
if (is_callable($close)) {
$close();
}
}
} else {
$response = $middleware($request, $this->next);
}
return $response;
}
};
}
function __invoke(ServerRequest $req, callable $open) {
$res = $this->process($req);
$res->prepare($req);
$open($res->getStatusCode(), $res->getHeaders());
$body = $req->getBody();
$body->rewind();
yield $body->getContents();
}
}
<?php declare(strict_types=1);
use ochenta\psr15\Stack;
use ochenta\psr7\Response;
use ochenta\psr7\ServerRequest;
describe('psr15\\Stack', function() {
describe('->process', function() {
it('transforms a responder into a response', function() {
$stack = new Stack(function(ServerRequest $req, callable $open) {
$open(200, ['X-Hello' => ['World']]);
yield 'Hello World';
});
$response = $stack->process(new ServerRequest);
expect($response)->toBeAnInstanceOf(Response::class);
expect($response->getStatusCode())->toBe(200);
expect($response->getHeaders())->toContainKey('X-Hello');
expect($response->getHeaders()['X-Hello'])->toBe(['World']);
expect($response->getBody()->__toString())->toBe('Hello World');
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment