Dispatcher can dispatch PSR-15 HandlerInterface and MiddlewareInterface (Drafts)
This commit is contained in:
parent
af3eef4657
commit
1dd9bf0f9c
|
|
@ -4,40 +4,49 @@ namespace WellRESTed\Dispatching;
|
||||||
|
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Psr\Http\ServerMiddleware\MiddlewareInterface;
|
||||||
|
|
||||||
class Dispatcher implements DispatcherInterface
|
class Dispatcher implements DispatcherInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param mixed $middleware
|
* @param mixed $dispatchable
|
||||||
* @param ServerRequestInterface $request
|
* @param ServerRequestInterface $request
|
||||||
* @param ResponseInterface $response
|
* @param ResponseInterface $response
|
||||||
* @param callable $next
|
* @param callable $next
|
||||||
* @return ResponseInterface
|
* @return ResponseInterface
|
||||||
* @throws DispatchException Unable to dispatch $middleware
|
* @throws DispatchException Unable to dispatch $middleware
|
||||||
*/
|
*/
|
||||||
public function dispatch($middleware, ServerRequestInterface $request, ResponseInterface $response, $next)
|
public function dispatch(
|
||||||
{
|
$dispatchable,
|
||||||
if (is_callable($middleware)) {
|
ServerRequestInterface $request,
|
||||||
$middleware = $middleware($request, $response, $next);
|
ResponseInterface $response,
|
||||||
} elseif (is_string($middleware)) {
|
$next
|
||||||
$middleware = new $middleware();
|
) {
|
||||||
} elseif (is_array($middleware)) {
|
if (is_callable($dispatchable)) {
|
||||||
$middleware = $this->getDispatchStack($middleware);
|
$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);
|
if (is_callable($dispatchable)) {
|
||||||
} elseif ($middleware instanceof ResponseInterface) {
|
return $dispatchable($request, $response, $next);
|
||||||
return $middleware;
|
} elseif ($dispatchable instanceof MiddlewareInterface) {
|
||||||
|
$delegate = new DispatcherDelegate($response, $next);
|
||||||
|
return $dispatchable->process($request, $delegate);
|
||||||
|
} elseif ($dispatchable instanceof ResponseInterface) {
|
||||||
|
return $dispatchable;
|
||||||
} else {
|
} else {
|
||||||
throw new DispatchException("Unable to dispatch middleware.");
|
throw new DispatchException("Unable to dispatch middleware.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getDispatchStack($middlewares)
|
protected function getDispatchStack($dispatchables)
|
||||||
{
|
{
|
||||||
$stack = new DispatchStack($this);
|
$stack = new DispatchStack($this);
|
||||||
foreach ($middlewares as $middleware) {
|
foreach ($dispatchables as $dispatchable) {
|
||||||
$stack->add($middleware);
|
$stack->add($dispatchable);
|
||||||
}
|
}
|
||||||
return $stack;
|
return $stack;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ namespace WellRESTed\Test\Unit\Dispatching;
|
||||||
|
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Psr\Http\ServerMiddleware\DelegateInterface;
|
||||||
use WellRESTed\Dispatching\Dispatcher;
|
use WellRESTed\Dispatching\Dispatcher;
|
||||||
use WellRESTed\Message\Response;
|
use WellRESTed\Message\Response;
|
||||||
use WellRESTed\Message\ServerRequest;
|
use WellRESTed\Message\ServerRequest;
|
||||||
|
|
@ -11,85 +12,206 @@ use WellRESTed\MiddlewareInterface;
|
||||||
use WellRESTed\Test\Doubles\NextMock;
|
use WellRESTed\Test\Doubles\NextMock;
|
||||||
use WellRESTed\Test\TestCase;
|
use WellRESTed\Test\TestCase;
|
||||||
|
|
||||||
|
use Psr\Http\ServerMiddleware\HandleInterface;
|
||||||
|
|
||||||
class DispatcherTest extends TestCase
|
class DispatcherTest extends TestCase
|
||||||
{
|
{
|
||||||
|
/** @var ServerRequestInterface */
|
||||||
private $request;
|
private $request;
|
||||||
|
/** @var ResponseInterface */
|
||||||
private $response;
|
private $response;
|
||||||
|
/** @var NextMock */
|
||||||
private $next;
|
private $next;
|
||||||
|
/** @var ResponseInterface */
|
||||||
|
private $stubResponse;
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
$this->request = new ServerRequest();
|
$this->request = new ServerRequest();
|
||||||
$this->response = new Response();
|
$this->response = new Response();
|
||||||
$this->next = new NextMock();
|
$this->next = new NextMock();
|
||||||
}
|
$this->stubResponse = new Response();
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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()
|
public function testThrowsExceptionWhenUnableToDispatch()
|
||||||
{
|
{
|
||||||
$middleware = null;
|
$this->dispatch(null);
|
||||||
|
|
||||||
$dispatcher = new Dispatcher();
|
|
||||||
$dispatcher->dispatch($middleware, $this->request, $this->response, $this->next);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
$response = $response->withStatus(200);
|
||||||
return $next($request, $response);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue