Add Route::register method to delegate to MethodMap

This commit is contained in:
PJ Dietz 2018-06-22 14:44:43 -04:00
parent 72d5df244d
commit 9b29f2a09e
5 changed files with 93 additions and 61 deletions

View File

@ -23,33 +23,28 @@ class MethodMap implements MethodMapInterface
// MethodMapInterface // MethodMapInterface
/** /**
* Register middleware with a method. * Register a dispatchable (handler or middleware) with a method.
* *
* $method may be: * $method may be:
* - A single verb ("GET"), * - A single verb ("GET"),
* - A comma-separated list of verbs ("GET,PUT,DELETE") * - A comma-separated list of verbs ("GET,PUT,DELETE")
* - "*" to indicate any method. * - "*" to indicate any method.
* *
* $middleware may be: * $dispatchable may be anything a Dispatcher can dispatch.
* - An instance implementing MiddlewareInterface * @see DispatcherInterface::dispatch
* - A string containing the fully qualified class name of a class
* implementing MiddlewareInterface
* - A callable that returns an instance implementing MiddleInterface
* - A callable matching the signature of MiddlewareInterface::dispatch
* @see DispatchedInterface::dispatch
* *
* $middleware may also be null, in which case any previously set * $dispatchable may also be null, in which case any previously set
* middleware for that method or methods will be unset. * handlers and middle for that method or methods will be unset.
* *
* @param string $method * @param string $method
* @param mixed $middleware * @param mixed $dispatchable
*/ */
public function register($method, $middleware) public function register($method, $dispatchable)
{ {
$methods = explode(",", $method); $methods = explode(",", $method);
$methods = array_map("trim", $methods); $methods = array_map("trim", $methods);
foreach ($methods as $method) { foreach ($methods as $method) {
$this->map[$method] = $middleware; $this->map[$method] = $dispatchable;
} }
} }
@ -62,8 +57,11 @@ class MethodMap implements MethodMapInterface
* @param callable $next * @param callable $next
* @return ResponseInterface * @return ResponseInterface
*/ */
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next) public function __invoke(
{ ServerRequestInterface $request,
ResponseInterface $response,
$next
) {
$method = $request->getMethod(); $method = $request->getMethod();
// Dispatch middleware registered with the explicitly matching method. // Dispatch middleware registered with the explicitly matching method.
if (isset($this->map[$method])) { if (isset($this->map[$method])) {
@ -119,8 +117,12 @@ class MethodMap implements MethodMapInterface
* @param $next * @param $next
* @return ResponseInterface * @return ResponseInterface
*/ */
private function dispatchMiddleware($middleware, ServerRequestInterface $request, ResponseInterface &$response, $next) private function dispatchMiddleware(
{ $middleware,
ServerRequestInterface $request,
ResponseInterface &$response,
$next
) {
return $this->dispatcher->dispatch($middleware, $request, $response, $next); return $this->dispatcher->dispatch($middleware, $request, $response, $next);
} }
} }

View File

@ -7,15 +7,12 @@ use Psr\Http\Message\ServerRequestInterface;
use WellRESTed\MiddlewareInterface; use WellRESTed\MiddlewareInterface;
/** /**
* Maps HTTP methods to middleware * Maps HTTP methods to handlers and middleware
*/ */
interface MethodMapInterface extends MiddlewareInterface interface MethodMapInterface extends MiddlewareInterface
{ {
/** /**
* Evaluate $request's method and dispatches matching middleware. * Evaluate $request's method and dispatches matching dispatchable.
*
* Implementations MUST pass $request, $response, and $next to the matching
* middleware.
* *
* @param ServerRequestInterface $request * @param ServerRequestInterface $request
* @param ResponseInterface $response * @param ResponseInterface $response
@ -25,23 +22,18 @@ interface MethodMapInterface extends MiddlewareInterface
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next); public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next);
/** /**
* Register middleware with a method. * Register a dispatchable (handler or middleware) with a method.
* *
* $method may be: * $method may be:
* - A single verb ("GET"), * - A single verb ("GET"),
* - A comma-separated list of verbs ("GET,PUT,DELETE") * - A comma-separated list of verbs ("GET,PUT,DELETE")
* - "*" to indicate any method. * - "*" to indicate any method.
* *
* $middleware may be: * $dispatchable may be anything a Dispatcher can dispatch.
* - An instance implementing MiddlewareInterface
* - A string containing the fully qualified class name of a class
* implementing MiddlewareInterface
* - A callable that returns an instance implementing MiddleInterface
* - A callable matching the signature of MiddlewareInterface::dispatch
* @see DispatcherInterface::dispatch * @see DispatcherInterface::dispatch
* *
* @param string $method * @param string $method
* @param mixed $middleware * @param mixed $dispatchable
*/ */
public function register($method, $middleware); public function register($method, $dispatchable);
} }

View File

@ -37,6 +37,25 @@ abstract class Route implements RouteInterface
return $this->target; return $this->target;
} }
/**
* Register a dispatchable (handler or middleware) with a method.
*
* $method may be:
* - A single verb ("GET"),
* - A comma-separated list of verbs ("GET,PUT,DELETE")
* - "*" to indicate any method.
*
* $dispatchable may be anything a Dispatcher can dispatch.
* @see DispatcherInterface::dispatch
*
* @param string $method
* @param mixed $dispatchable
*/
public function register($method, $dispatchable)
{
$this->methodMap->register($method, $dispatchable);
}
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next) public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
{ {
$map = $this->methodMap; $map = $this->methodMap;

View File

@ -64,4 +64,20 @@ interface RouteInterface extends MiddlewareInterface
* invalid regular expression * invalid regular expression
*/ */
public function matchesRequestTarget($requestTarget); public function matchesRequestTarget($requestTarget);
/**
* Register a dispatchable (handler or middleware) with a method.
*
* $method may be:
* - A single verb ("GET"),
* - A comma-separated list of verbs ("GET,PUT,DELETE")
* - "*" to indicate any method.
*
* $dispatchable may be anything a Dispatcher can dispatch.
* @see DispatcherInterface::dispatch
*
* @param string $method
* @param mixed $dispatchable
*/
public function register($method, $dispatchable);
} }

View File

@ -3,53 +3,56 @@
namespace WellRESTed\Test\Unit\Routing\Route; namespace WellRESTed\Test\Unit\Routing\Route;
use Prophecy\Argument; use Prophecy\Argument;
use Psr\Http\Server\RequestHandlerInterface;
use WellRESTed\Message\Response;
use WellRESTed\Message\ServerRequest;
use WellRESTed\Routing\MethodMap;
use WellRESTed\Routing\Route\StaticRoute;
use WellRESTed\Test\TestCase; use WellRESTed\Test\TestCase;
class RouteTest extends TestCase class RouteTest extends TestCase
{ {
public function testCreatesInstance() const TARGET = '/target';
private $methodMap;
private $route;
public function setUp()
{ {
$methodMap = $this->prophesize('WellRESTed\Routing\MethodMapInterface'); $this->methodMap = $this->prophesize(MethodMap::class);
$route = $this->getMockForAbstractClass( $this->methodMap->register(Argument::cetera())
'WellRESTed\Routing\Route\Route', ->willReturn();
["/target", $methodMap->reveal()]); $this->methodMap->__invoke(Argument::cetera())
$this->assertNotNull($route); ->willReturn(new Response());
$this->route = new StaticRoute(
self::TARGET, $this->methodMap->reveal());
} }
public function testReturnsTarget() public function testReturnsTarget()
{ {
$methodMap = $this->prophesize('WellRESTed\Routing\MethodMapInterface'); $this->assertSame(self::TARGET, $this->route->getTarget());
$route = $this->getMockForAbstractClass(
'WellRESTed\Routing\Route\Route',
["/target", $methodMap->reveal()]);
$this->assertSame("/target", $route->getTarget());
} }
public function testReturnsMethodMap() public function testRegistersDispatchableWithMethodMap()
{ {
$methodMap = $this->prophesize('WellRESTed\Routing\MethodMapInterface'); $handler = $this->prophesize(RequestHandlerInterface::class)->reveal();
$route = $this->getMockForAbstractClass(
'WellRESTed\Routing\Route\Route', $this->route->register('GET', $handler);
["/target", $methodMap->reveal()]);
$this->assertSame($methodMap->reveal(), $route->getMethodMap()); $this->methodMap->register('GET', $handler)->shouldHaveBeenCalled();
} }
public function testDispatchesMethodMap() public function testDispatchesMethodMap()
{ {
$methodMap = $this->prophesize('WellRESTed\Routing\MethodMapInterface'); $request = new ServerRequest();
$methodMap->__invoke(Argument::cetera())->willReturn(); $response = new Response();
$next = function ($rqst, $resp) {
$route = $this->getMockForAbstractClass( return $resp;
'WellRESTed\Routing\Route\Route',
["/target", $methodMap->reveal()]);
$request = $this->prophesize('Psr\Http\Message\ServerRequestInterface')->reveal();
$response = $this->prophesize('Psr\Http\Message\ResponseInterface')->reveal();
$next = function ($request, $response) {
return $response;
}; };
$route->__invoke($request, $response, $next);
$methodMap->__invoke(Argument::cetera())->shouldHaveBeenCalled(); $this->route->__invoke($request, $response, $next);
$this->methodMap->__invoke(Argument::cetera())->shouldHaveBeenCalled();
} }
} }