Rework MethodMap
This commit is contained in:
parent
7cbbe6d7c5
commit
ccbe8bb2e0
|
|
@ -9,34 +9,40 @@ class MethodMap implements MiddlewareInterface, MethodMapInterface
|
|||
{
|
||||
protected $map;
|
||||
|
||||
/**
|
||||
* @param array $map
|
||||
*/
|
||||
public function __construct($map = null)
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->map = [];
|
||||
if ($map) {
|
||||
$this->addMap($map);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $map
|
||||
*/
|
||||
public function addMap($map)
|
||||
{
|
||||
foreach ($map as $method => $middleware) {
|
||||
$this->add($method, $middleware);
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
// MethodMapInterface
|
||||
|
||||
/**
|
||||
* Register middleware with a method.
|
||||
*
|
||||
* $method may be:
|
||||
* - A single verb ("GET"),
|
||||
* - A comma-separated list of verbs ("GET,PUT,DELETE")
|
||||
* - "*" to indicate any method.
|
||||
*
|
||||
* $middleware may be:
|
||||
* - 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 maching the signature of MiddlewareInteraface::dispatch
|
||||
* @see DispatchedInterface::dispatch
|
||||
*
|
||||
* $middleware may also be null, in which case any previously set
|
||||
* middleware for that method or methods will be unset.
|
||||
*
|
||||
* @param string $method
|
||||
* @param mixed $middleware
|
||||
*/
|
||||
public function add($method, $middleware)
|
||||
public function setMethod($method, $middleware)
|
||||
{
|
||||
$method = strtoupper($method);
|
||||
$methods = explode(",", $method);
|
||||
$methods = array_map("trim", $methods);
|
||||
foreach ($methods as $method) {
|
||||
|
|
@ -44,23 +50,32 @@ class MethodMap implements MiddlewareInterface, MethodMapInterface
|
|||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// MiddlewareInterface
|
||||
|
||||
public function dispatch(ServerRequestInterface $request, ResponseInterface &$response)
|
||||
{
|
||||
$method = strtoupper($request->getMethod());
|
||||
$method = $request->getMethod();
|
||||
// Dispatch middleware registered with the explicitly matching method.
|
||||
if (isset($this->map[$method])) {
|
||||
$middleware = $this->map[$method];
|
||||
$this->disptchMiddleware($middleware, $request, $response);
|
||||
$this->dispatchMiddleware($middleware, $request, $response);
|
||||
return;
|
||||
}
|
||||
// For HEAD, dispatch GET by default.
|
||||
if ($method === "HEAD" && isset($this->map["GET"])) {
|
||||
$middleware = $this->map["GET"];
|
||||
$this->disptchMiddleware($middleware, $request, $response);
|
||||
$this->dispatchMiddleware($middleware, $request, $response);
|
||||
return;
|
||||
}
|
||||
// Method is not defined. Respond describing the allowed methods,
|
||||
// either as a 405 response or in response to an OPTIONS request.
|
||||
// Dispatch * middleware, if registered.
|
||||
if (isset($this->map["*"])) {
|
||||
$middleware = $this->map["*"];
|
||||
$this->dispatchMiddleware($middleware, $request, $response);
|
||||
return;
|
||||
}
|
||||
// Respond describing the allowed methods, either as a 405 response or
|
||||
// in response to an OPTIONS request.
|
||||
if ($method === "OPTIONS") {
|
||||
$response = $response->withStatus(200);
|
||||
} else {
|
||||
|
|
@ -69,6 +84,8 @@ class MethodMap implements MiddlewareInterface, MethodMapInterface
|
|||
$this->addAllowHeader($response);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
protected function addAllowHeader(ResponseInterface &$response)
|
||||
{
|
||||
$methods = join(",", $this->getAllowedMethods());
|
||||
|
|
@ -100,7 +117,7 @@ class MethodMap implements MiddlewareInterface, MethodMapInterface
|
|||
return new Dispatcher();
|
||||
}
|
||||
|
||||
private function disptchMiddleware($middleware, ServerRequestInterface $request, ResponseInterface &$response)
|
||||
private function dispatchMiddleware($middleware, ServerRequestInterface $request, ResponseInterface &$response)
|
||||
{
|
||||
$dispatcher = $this->getDispatcher();
|
||||
$dispatcher->dispatch($middleware, $request, $response);
|
||||
|
|
|
|||
|
|
@ -2,16 +2,29 @@
|
|||
|
||||
namespace WellRESTed\Routing;
|
||||
|
||||
interface MethodMapInterface
|
||||
interface MethodMapInterface extends MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* @param array $map
|
||||
*/
|
||||
public function addMap($map);
|
||||
|
||||
/**
|
||||
* Register middleware with a method.
|
||||
*
|
||||
* $method may be:
|
||||
* - A single verb ("GET"),
|
||||
* - A comma-separated list of verbs ("GET,PUT,DELETE")
|
||||
* - "*" to indicate any method.
|
||||
*
|
||||
* $middleware may be:
|
||||
* - 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 maching the signature of MiddlewareInteraface::dispatch
|
||||
* @see DispatchedInterface::dispatch
|
||||
*
|
||||
* $middleware may also be null, in which case any previously set
|
||||
* middleware for that method or methods will be unset.
|
||||
*
|
||||
* @param string $method
|
||||
* @param mixed $middleware
|
||||
*/
|
||||
public function add($method, $middleware);
|
||||
public function setMethod($method, $middleware);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class RouteMap implements RouteMapInterface
|
|||
* - A single verb ("GET"),
|
||||
* - A comma-separated list of verbs ("GET,PUT,DELETE")
|
||||
* - "*" to indicate any method.
|
||||
* @see MethodMapInterface::addMethod
|
||||
* @see MethodMapInterface::setMethod
|
||||
*
|
||||
* $target may be:
|
||||
* - An exact path (e.g., "/path/")
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ interface RouteMapInterface extends MiddlewareInterface
|
|||
* - A single verb ("GET"),
|
||||
* - A comma-separated list of verbs ("GET,PUT,DELETE")
|
||||
* - "*" to indicate any method.
|
||||
* @see MethodMapInterface::addMethod
|
||||
* @see MethodMapInterface::setMethod
|
||||
*
|
||||
* $target may be:
|
||||
* - An exact path (e.g., "/path/")
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ class Router implements MiddlewareInterface, RouteMapInterface
|
|||
* - A single verb ("GET"),
|
||||
* - A comma-separated list of verbs ("GET,PUT,DELETE")
|
||||
* - "*" to indicate any method.
|
||||
* @see MethodMapInterface::addMethod
|
||||
* @see MethodMapInterface::setMethod
|
||||
*
|
||||
* $target may be:
|
||||
* - An exact path (e.g., "/path/")
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ use Prophecy\Argument;
|
|||
use WellRESTed\Routing\MethodMap;
|
||||
|
||||
/**
|
||||
* @covers WellRESTed\Routing\MethodMap
|
||||
* @coversDefaultClass WellRESTed\Routing\MethodMap
|
||||
* @uses WellRESTed\Routing\MethodMap
|
||||
* @uses WellRESTed\Routing\Dispatcher
|
||||
*/
|
||||
class MethodMapTest extends \PHPUnit_Framework_TestCase
|
||||
|
|
@ -22,14 +23,30 @@ class MethodMapTest extends \PHPUnit_Framework_TestCase
|
|||
$this->response->withHeader(Argument::cetera())->willReturn($this->response->reveal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::__construct
|
||||
*/
|
||||
public function testCreatesInstance()
|
||||
{
|
||||
$methodMap = new MethodMap();
|
||||
$this->assertNotNull($methodMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::dispatch
|
||||
* @covers ::dispatchMiddleware
|
||||
* @covers ::getDispatcher
|
||||
* @covers ::setMethod
|
||||
*/
|
||||
public function testDispatchesMiddlewareWithMatchingMethod()
|
||||
{
|
||||
$this->request->getMethod()->willReturn("GET");
|
||||
|
||||
$middleware = $this->prophesize("\\WellRESTed\\Routing\\MiddlewareInterface");
|
||||
$middleware = $this->prophesize('WellRESTed\Routing\MiddlewareInterface');
|
||||
$middleware->dispatch(Argument::cetera())->willReturn();
|
||||
|
||||
$map = new MethodMap(["GET" => $middleware->reveal()]);
|
||||
$map = new MethodMap();
|
||||
$map->setMethod("GET", $middleware->reveal());
|
||||
|
||||
$request = $this->request->reveal();
|
||||
$response = $this->response->reveal();
|
||||
|
|
@ -38,14 +55,65 @@ class MethodMapTest extends \PHPUnit_Framework_TestCase
|
|||
$middleware->dispatch($request, $response)->shouldHaveBeenCalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* @coversNothing
|
||||
*/
|
||||
public function testTreatsMethodNamesCaseSensitively()
|
||||
{
|
||||
$this->request->getMethod()->willReturn("get");
|
||||
|
||||
$middlewareUpper = $this->prophesize('WellRESTed\Routing\MiddlewareInterface');
|
||||
$middlewareUpper->dispatch(Argument::cetera())->willReturn();
|
||||
|
||||
$middlewareLower = $this->prophesize('WellRESTed\Routing\MiddlewareInterface');
|
||||
$middlewareLower->dispatch(Argument::cetera())->willReturn();
|
||||
|
||||
$map = new MethodMap();
|
||||
$map->setMethod("GET", $middlewareUpper->reveal());
|
||||
$map->setMethod("get", $middlewareLower->reveal());
|
||||
|
||||
$request = $this->request->reveal();
|
||||
$response = $this->response->reveal();
|
||||
$map->dispatch($request, $response);
|
||||
|
||||
$middlewareLower->dispatch($request, $response)->shouldHaveBeenCalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::dispatch
|
||||
* @covers ::dispatchMiddleware
|
||||
* @covers ::getDispatcher
|
||||
* @covers ::setMethod
|
||||
*/
|
||||
public function testDispatchesWildcardMiddlewareWithNonMatchingMethod()
|
||||
{
|
||||
$this->request->getMethod()->willReturn("GET");
|
||||
|
||||
$middleware = $this->prophesize('WellRESTed\Routing\MiddlewareInterface');
|
||||
$middleware->dispatch(Argument::cetera())->willReturn();
|
||||
|
||||
$map = new MethodMap();
|
||||
$map->setMethod("*", $middleware->reveal());
|
||||
|
||||
$request = $this->request->reveal();
|
||||
$response = $this->response->reveal();
|
||||
$map->dispatch($request, $response);
|
||||
|
||||
$middleware->dispatch($request, $response)->shouldHaveBeenCalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::dispatch
|
||||
*/
|
||||
public function testDispatchesGetMiddlewareForHeadByDefault()
|
||||
{
|
||||
$this->request->getMethod()->willReturn("HEAD");
|
||||
|
||||
$middleware = $this->prophesize("\\WellRESTed\\Routing\\MiddlewareInterface");
|
||||
$middleware = $this->prophesize('WellRESTed\Routing\MiddlewareInterface');
|
||||
$middleware->dispatch(Argument::cetera())->willReturn();
|
||||
|
||||
$map = new MethodMap(["GET" => $middleware->reveal()]);
|
||||
$map = new MethodMap();
|
||||
$map->setMethod("GET", $middleware->reveal());
|
||||
|
||||
$request = $this->request->reveal();
|
||||
$response = $this->response->reveal();
|
||||
|
|
@ -54,13 +122,16 @@ class MethodMapTest extends \PHPUnit_Framework_TestCase
|
|||
$middleware->dispatch($request, $response)->shouldHaveBeenCalled();
|
||||
}
|
||||
|
||||
/*
|
||||
* @covers ::setMethod
|
||||
*/
|
||||
public function testRegistersMiddlewareForMultipleMethods()
|
||||
{
|
||||
$middleware = $this->prophesize("\\WellRESTed\\Routing\\MiddlewareInterface");
|
||||
$middleware = $this->prophesize('WellRESTed\Routing\MiddlewareInterface');
|
||||
$middleware->dispatch(Argument::cetera())->willReturn();
|
||||
|
||||
$map = new MethodMap();
|
||||
$map->add("GET,POST", $middleware->reveal());
|
||||
$map->setMethod("GET,POST", $middleware->reveal());
|
||||
|
||||
$request = $this->request->reveal();
|
||||
$response = $this->response->reveal();
|
||||
|
|
@ -74,13 +145,36 @@ class MethodMapTest extends \PHPUnit_Framework_TestCase
|
|||
$middleware->dispatch($request, $response)->shouldHaveBeenCalledTimes(2);
|
||||
}
|
||||
|
||||
public function testSettingNullUnregistersMiddleware()
|
||||
{
|
||||
$this->request->getMethod()->willReturn("POST");
|
||||
|
||||
$middleware = $this->prophesize('WellRESTed\Routing\MiddlewareInterface');
|
||||
|
||||
$map = new MethodMap();
|
||||
$map->setMethod("POST", $middleware->reveal());
|
||||
$map->setMethod("POST", null);
|
||||
|
||||
$request = $this->request->reveal();
|
||||
$response = $this->response->reveal();
|
||||
$map->dispatch($request, $response);
|
||||
|
||||
$this->response->withStatus(405)->shouldHaveBeenCalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::dispatch
|
||||
* @covers ::addAllowHeader
|
||||
* @covers ::getAllowedMethods
|
||||
*/
|
||||
public function testSetsStatusTo200ForOptions()
|
||||
{
|
||||
$this->request->getMethod()->willReturn("OPTIONS");
|
||||
|
||||
$middleware = $this->prophesize("\\WellRESTed\\Routing\\MiddlewareInterface");
|
||||
$middleware = $this->prophesize('WellRESTed\Routing\MiddlewareInterface');
|
||||
|
||||
$map = new MethodMap(["GET" => $middleware->reveal()]);
|
||||
$map = new MethodMap();
|
||||
$map->setMethod("GET", $middleware->reveal());
|
||||
|
||||
$request = $this->request->reveal();
|
||||
$response = $this->response->reveal();
|
||||
|
|
@ -90,17 +184,20 @@ class MethodMapTest extends \PHPUnit_Framework_TestCase
|
|||
}
|
||||
|
||||
/**
|
||||
* @covers ::dispatch
|
||||
* @covers ::addAllowHeader
|
||||
* @covers ::getAllowedMethods
|
||||
* @dataProvider allowedMethodProvider
|
||||
*/
|
||||
public function testSetsAllowHeaderForOptions($methodsDeclared, $methodsAllowed)
|
||||
{
|
||||
$this->request->getMethod()->willReturn("OPTIONS");
|
||||
|
||||
$middleware = $this->prophesize("\\WellRESTed\\Routing\\MiddlewareInterface");
|
||||
$middleware = $this->prophesize('WellRESTed\Routing\MiddlewareInterface');
|
||||
|
||||
$map = new MethodMap();
|
||||
foreach ($methodsDeclared as $method) {
|
||||
$map->add($method, $middleware->reveal());
|
||||
$map->setMethod($method, $middleware->reveal());
|
||||
}
|
||||
|
||||
$request = $this->request->reveal();
|
||||
|
|
@ -118,13 +215,20 @@ class MethodMapTest extends \PHPUnit_Framework_TestCase
|
|||
$this->response->withHeader("Allow", Argument::that($containsAllMethods))->shouldHaveBeenCalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::dispatch
|
||||
* @covers ::addAllowHeader
|
||||
* @covers ::getAllowedMethods
|
||||
* @dataProvider allowedMethodProvider
|
||||
*/
|
||||
public function testSetsStatusTo405ForBadMethod()
|
||||
{
|
||||
$this->request->getMethod()->willReturn("POST");
|
||||
|
||||
$middleware = $this->prophesize("\\WellRESTed\\Routing\\MiddlewareInterface");
|
||||
$middleware = $this->prophesize('WellRESTed\Routing\MiddlewareInterface');
|
||||
|
||||
$map = new MethodMap(["GET" => $middleware->reveal()]);
|
||||
$map = new MethodMap();
|
||||
$map->setMethod("GET", $middleware->reveal());
|
||||
|
||||
$request = $this->request->reveal();
|
||||
$response = $this->response->reveal();
|
||||
|
|
@ -134,17 +238,20 @@ class MethodMapTest extends \PHPUnit_Framework_TestCase
|
|||
}
|
||||
|
||||
/**
|
||||
* @covers ::dispatch
|
||||
* @covers ::addAllowHeader
|
||||
* @covers ::getAllowedMethods
|
||||
* @dataProvider allowedMethodProvider
|
||||
*/
|
||||
public function testSetsAlloweHeaderForBadMethod($methodsDeclared, $methodsAllowed)
|
||||
public function testSetsAllowHeaderForBadMethod($methodsDeclared, $methodsAllowed)
|
||||
{
|
||||
$this->request->getMethod()->willReturn("BAD");
|
||||
|
||||
$middleware = $this->prophesize("\\WellRESTed\\Routing\\MiddlewareInterface");
|
||||
$middleware = $this->prophesize('WellRESTed\Routing\MiddlewareInterface');
|
||||
|
||||
$map = new MethodMap();
|
||||
foreach ($methodsDeclared as $method) {
|
||||
$map->add($method, $middleware->reveal());
|
||||
$map->setMethod($method, $middleware->reveal());
|
||||
}
|
||||
|
||||
$request = $this->request->reveal();
|
||||
|
|
|
|||
Loading…
Reference in New Issue