Dispatcher can dispatch PSR-15 HandlerInterface and MiddlewareInterface (Drafts)

This commit is contained in:
PJ Dietz 2017-07-31 22:14:46 -04:00
parent af3eef4657
commit 1dd9bf0f9c
3 changed files with 233 additions and 73 deletions

View File

@ -4,40 +4,49 @@ namespace WellRESTed\Dispatching;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\ServerMiddleware\MiddlewareInterface;
class Dispatcher implements DispatcherInterface
{
/**
* @param mixed $middleware
* @param mixed $dispatchable
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
* @return ResponseInterface
* @throws DispatchException Unable to dispatch $middleware
*/
public function dispatch($middleware, ServerRequestInterface $request, ResponseInterface $response, $next)
{
if (is_callable($middleware)) {
$middleware = $middleware($request, $response, $next);
} elseif (is_string($middleware)) {
$middleware = new $middleware();
} elseif (is_array($middleware)) {
$middleware = $this->getDispatchStack($middleware);
public function dispatch(
$dispatchable,
ServerRequestInterface $request,
ResponseInterface $response,
$next
) {
if (is_callable($dispatchable)) {
$dispatchable = $dispatchable($request, $response, $next);
} elseif (is_string($dispatchable)) {
$dispatchable = new $dispatchable();
} elseif (is_array($dispatchable)) {
$dispatchable = $this->getDispatchStack($dispatchable);
}
if (is_callable($middleware)) {
return $middleware($request, $response, $next);
} elseif ($middleware instanceof ResponseInterface) {
return $middleware;
if (is_callable($dispatchable)) {
return $dispatchable($request, $response, $next);
} elseif ($dispatchable instanceof MiddlewareInterface) {
$delegate = new DispatcherDelegate($response, $next);
return $dispatchable->process($request, $delegate);
} elseif ($dispatchable instanceof ResponseInterface) {
return $dispatchable;
} else {
throw new DispatchException("Unable to dispatch middleware.");
}
}
protected function getDispatchStack($middlewares)
protected function getDispatchStack($dispatchables)
{
$stack = new DispatchStack($this);
foreach ($middlewares as $middleware) {
$stack->add($middleware);
foreach ($dispatchables as $dispatchable) {
$stack->add($dispatchable);
}
return $stack;
}

View File

@ -0,0 +1,29 @@
<?php
namespace WellRESTed\Dispatching;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\ServerMiddleware\DelegateInterface;
/**
* Adapter to allow use of PSR-15 Middleware with double pass implementations.
*/
class DispatcherDelegate implements DelegateInterface
{
/** @var ResponseInterface */
private $response;
/** @var callable */
private $next;
public function __construct(ResponseInterface $response, callable $next)
{
$this->response = $response;
$this->next = $next;
}
public function __invoke(ServerRequestInterface $request): ResponseInterface
{
return call_user_func($this->next, $request, $this->response);
}
}

View File

@ -4,6 +4,7 @@ namespace WellRESTed\Test\Unit\Dispatching;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\ServerMiddleware\DelegateInterface;
use WellRESTed\Dispatching\Dispatcher;
use WellRESTed\Message\Response;
use WellRESTed\Message\ServerRequest;
@ -11,85 +12,206 @@ use WellRESTed\MiddlewareInterface;
use WellRESTed\Test\Doubles\NextMock;
use WellRESTed\Test\TestCase;
use Psr\Http\ServerMiddleware\HandleInterface;
class DispatcherTest extends TestCase
{
/** @var ServerRequestInterface */
private $request;
/** @var ResponseInterface */
private $response;
/** @var NextMock */
private $next;
/** @var ResponseInterface */
private $stubResponse;
public function setUp()
{
$this->request = new ServerRequest();
$this->response = new Response();
$this->next = new NextMock();
}
public function testDispatchesCallableThatReturnsResponse()
{
$middleware = function ($request, $response, $next) {
return $next($request, $response->withStatus(200));
};
$dispatcher = new Dispatcher();
$response = $dispatcher->dispatch($middleware, $this->request, $this->response, $this->next);
$this->assertEquals(200, $response->getStatusCode());
}
public function testDispatchesMiddlewareInstanceFromCallable()
{
$middleware = function () {
return new DispatcherTest_Middleware();
};
$dispatcher = new Dispatcher();
$response = $dispatcher->dispatch($middleware, $this->request, $this->response, $this->next);
$this->assertEquals(200, $response->getStatusCode());
}
public function testDispatchesMiddlewareFromClassNameString()
{
$middleware = __NAMESPACE__ . '\DispatcherTest_Middleware';
$dispatcher = new Dispatcher();
$response = $dispatcher->dispatch($middleware, $this->request, $this->response, $this->next);
$this->assertEquals(200, $response->getStatusCode());
}
public function testDispatchesMiddlewareInstance()
{
$middleware = new DispatcherTest_Middleware();
$dispatcher = new Dispatcher();
$response = $dispatcher->dispatch($middleware, $this->request, $this->response, $this->next);
$this->assertEquals(200, $response->getStatusCode());
}
public function testDispatchesArrayAsDispatchStack()
{
$middleware = new DispatcherTest_Middleware();
$dispatcher = new Dispatcher();
$response = $dispatcher->dispatch([$middleware], $this->request, $this->response, $this->next);
$this->assertEquals(200, $response->getStatusCode());
$this->stubResponse = new Response();
}
/**
* @expectedException \WellRESTed\Dispatching\DispatchException
* Dispatch the provided dispatchable using the class under test and the
* ivars $request, $response, and $next. Return the response.
*/
private function dispatch($dispatchable): ResponseInterface
{
$dispatcher = new Dispatcher();
return $dispatcher->dispatch(
$dispatchable,
$this->request,
$this->response,
$this->next
);
}
// -------------------------------------------------------------------------
// PSR-15 Handler
public function testDispatchesPsr15Handler()
{
$handler = new HandlerDouble($this->stubResponse);
$response = $this->dispatch($handler);
$this->assertSame($this->stubResponse, $response);
}
public function testDispatchesPsr15HandlerFromFactory()
{
$factory = function () {
return new HandlerDouble($this->stubResponse);
};
$response = $this->dispatch($factory);
$this->assertSame($this->stubResponse, $response);
}
// -------------------------------------------------------------------------
// PSR-15 Middleware
public function testDispatchesPsr15MiddlewareWithDelegate() {
$this->next->upstreamResponse = $this->stubResponse;
$middleware = new MiddlewareDouble();
$response = $this->dispatch($middleware);
$this->assertSame($this->stubResponse, $response);
}
public function testDispatchesPsr15MiddlewareFromFactoryWithDelegate() {
$this->next->upstreamResponse = $this->stubResponse;
$factory = function () {
return new MiddlewareDouble();
};
$response = $this->dispatch($factory);
$this->assertSame($this->stubResponse, $response);
}
// -------------------------------------------------------------------------
// Double-Pass Middleware Callable
public function testDispatchesDoublePassMiddlewareCallable()
{
$doublePass = function ($request, $response, $next) {
return $next($request, $this->stubResponse);
};
$response = $this->dispatch($doublePass);
$this->assertSame($this->stubResponse, $response);
}
public function testDispatchesDoublePassMiddlewareCallableFromFactory()
{
$factory = function () {
return function ($request, $response, $next) {
return $next($request, $this->stubResponse);
};
};
$response = $this->dispatch($factory);
$this->assertSame($this->stubResponse, $response);
}
// -------------------------------------------------------------------------
// Double-Pass Middleware Instance
public function testDispatchesDoublePassMiddlewareInstance()
{
$doublePass = new DoublePassMiddlewareDouble();
$response = $this->dispatch($doublePass);
$this->assertEquals(200, $response->getStatusCode());
}
public function testDispatchesDoublePassMiddlewareInstanceFromFactory()
{
$factory = function () {
return new DoublePassMiddlewareDouble();
};
$response = $this->dispatch($factory);
$this->assertEquals(200, $response->getStatusCode());
}
// -------------------------------------------------------------------------
// String
public function testDispatchesInstanceFromStringName()
{
$response = $this->dispatch(DoublePassMiddlewareDouble::class);
$this->assertEquals(200, $response->getStatusCode());
}
// -------------------------------------------------------------------------
// Arrays
public function testDispatchesArrayAsDispatchStack()
{
$doublePass = new DoublePassMiddlewareDouble();
$response = $this->dispatch([$doublePass]);
$this->assertEquals(200, $response->getStatusCode());
}
/** @expectedException \WellRESTed\Dispatching\DispatchException */
public function testThrowsExceptionWhenUnableToDispatch()
{
$middleware = null;
$dispatcher = new Dispatcher();
$dispatcher->dispatch($middleware, $this->request, $this->response, $this->next);
$this->dispatch(null);
}
}
class DispatcherTest_Middleware implements MiddlewareInterface
// -----------------------------------------------------------------------------
// Doubles
/**
* Double pass middleware that sends a response with a 200 status to $next
* and return the response.
*
* This class has no constructor so that we can test instantiating from string.
*/
class DoublePassMiddlewareDouble implements MiddlewareInterface
{
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
{
public function __invoke(
ServerRequestInterface $request,
ResponseInterface $response,
$next
) {
$response = $response->withStatus(200);
return $next($request, $response);
}
}
// -----------------------------------------------------------------------------
/**
* PSR-15 Handler that returns a ResponseInterface stub
*/
class HandlerDouble implements HandleInterface
{
/** @var ResponseInterface */
private $response;
public function __construct(ResponseInterface $response)
{
$this->response = $response;
}
public function __invoke(ServerRequestInterface $request): ResponseInterface
{
return $this->response;
}
}
// -----------------------------------------------------------------------------
/**
* PSR-15 Middleware that passes the request to the delegate and returns the
* delegate's response
*/
class MiddlewareDouble implements \Psr\Http\ServerMiddleware\MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
DelegateInterface $delegate
): ResponseInterface {
return $delegate($request);
}
}