Store PrefixRoutes to a separate array.
Prioritize routes in the order static, prefix, everything else.
This commit is contained in:
parent
caef817535
commit
ca2c8625ec
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pjdietz\WellRESTed\Interfaces\Route\PrefixRouteInterface
|
||||||
|
*
|
||||||
|
* @author PJ Dietz <pj@pjdietz.com>
|
||||||
|
* @copyright Copyright 2015 by PJ Dietz
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace pjdietz\WellRESTed\Interfaces\Routes;
|
||||||
|
|
||||||
|
interface PrefixRouteInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns the target class this maps to.
|
||||||
|
*
|
||||||
|
* @return string Fully qualified name for a HandlerInterface
|
||||||
|
*/
|
||||||
|
public function getHandler();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the path prefixes this maps to a target handler.
|
||||||
|
*
|
||||||
|
* @return array Array of path prefixes.
|
||||||
|
*/
|
||||||
|
public function getPrefixes();
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
* pjdietz\WellRESTed\Router
|
* pjdietz\WellRESTed\Router
|
||||||
*
|
*
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
* @author PJ Dietz <pj@pjdietz.com>
|
||||||
* @copyright Copyright 2014 by PJ Dietz
|
* @copyright Copyright 2015 by PJ Dietz
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -14,6 +14,7 @@ use pjdietz\WellRESTed\Exceptions\HttpExceptions\HttpException;
|
||||||
use pjdietz\WellRESTed\Interfaces\HandlerInterface;
|
use pjdietz\WellRESTed\Interfaces\HandlerInterface;
|
||||||
use pjdietz\WellRESTed\Interfaces\RequestInterface;
|
use pjdietz\WellRESTed\Interfaces\RequestInterface;
|
||||||
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
|
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
|
||||||
|
use pjdietz\WellRESTed\Interfaces\Routes\PrefixRouteInterface;
|
||||||
use pjdietz\WellRESTed\Interfaces\Routes\StaticRouteInterface;
|
use pjdietz\WellRESTed\Interfaces\Routes\StaticRouteInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -26,6 +27,9 @@ class Router implements HandlerInterface
|
||||||
/** @var array Array of Route objects */
|
/** @var array Array of Route objects */
|
||||||
private $routes;
|
private $routes;
|
||||||
|
|
||||||
|
/** @var array Hash array mapping path prefixes to handlers */
|
||||||
|
private $prefixRoutes;
|
||||||
|
|
||||||
/** @var array Hash array mapping exact paths to handlers */
|
/** @var array Hash array mapping exact paths to handlers */
|
||||||
private $staticRoutes;
|
private $staticRoutes;
|
||||||
|
|
||||||
|
|
@ -36,6 +40,7 @@ class Router implements HandlerInterface
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->routes = array();
|
$this->routes = array();
|
||||||
|
$this->prefixRoutes = array();
|
||||||
$this->staticRoutes = array();
|
$this->staticRoutes = array();
|
||||||
$this->errorHandlers = array();
|
$this->errorHandlers = array();
|
||||||
}
|
}
|
||||||
|
|
@ -76,6 +81,8 @@ class Router implements HandlerInterface
|
||||||
{
|
{
|
||||||
if ($route instanceof StaticRouteInterface) {
|
if ($route instanceof StaticRouteInterface) {
|
||||||
$this->setStaticRoute($route->getPaths(), $route->getHandler());
|
$this->setStaticRoute($route->getPaths(), $route->getHandler());
|
||||||
|
} elseif ($route instanceof PrefixRouteInterface) {
|
||||||
|
$this->setPrefixRoute($route->getPrefixes(), $route->getHandler());
|
||||||
} else {
|
} else {
|
||||||
$this->routes[] = $route;
|
$this->routes[] = $route;
|
||||||
}
|
}
|
||||||
|
|
@ -96,7 +103,23 @@ class Router implements HandlerInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A route for an exact match to a path.
|
* Add a route for paths beginning with a given prefix.
|
||||||
|
*
|
||||||
|
* @param string|array $prefixes Prefix of a path to match
|
||||||
|
* @param string $handler Fully qualified name to an autoloadable handler class
|
||||||
|
*/
|
||||||
|
public function setPrefixRoute($prefixes, $handler)
|
||||||
|
{
|
||||||
|
if (is_string($prefixes)) {
|
||||||
|
$prefixes = array($prefixes);
|
||||||
|
}
|
||||||
|
foreach ($prefixes as $prefix) {
|
||||||
|
$this->prefixRoutes[$prefix] = $handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a route for an exact match to a path.
|
||||||
*
|
*
|
||||||
* @param string|array $paths Path component of the URI or a list of paths
|
* @param string|array $paths Path component of the URI or a list of paths
|
||||||
* @param string $handler Fully qualified name to an autoloadable handler class
|
* @param string $handler Fully qualified name to an autoloadable handler class
|
||||||
|
|
@ -163,14 +186,48 @@ class Router implements HandlerInterface
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returning the matching static handler, or null if none match.
|
||||||
|
*
|
||||||
|
* @param $path string The request's path
|
||||||
|
* @return HandlerInterface|null
|
||||||
|
*/
|
||||||
private function getStaticHandler($path)
|
private function getStaticHandler($path)
|
||||||
{
|
{
|
||||||
if (array_key_exists($path, $this->staticRoutes)) {
|
if (array_key_exists($path, $this->staticRoutes)) {
|
||||||
|
// Instantiate and return the handler identified by the path.
|
||||||
return new $this->staticRoutes[$path]();
|
return new $this->staticRoutes[$path]();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returning the best-matching prefix handler, or null if none match.
|
||||||
|
*
|
||||||
|
* @param $path string The request's path
|
||||||
|
* @return HandlerInterface|null
|
||||||
|
*/
|
||||||
|
private function getPrefixHandler($path)
|
||||||
|
{
|
||||||
|
// Find all prefixes that match the start of this path.
|
||||||
|
$prefixes = array_keys($this->prefixRoutes);
|
||||||
|
$matches = array_filter($prefixes, function ($prefix) use ($path) {
|
||||||
|
return (strrpos($path, $prefix, -strlen($path)) !== false);
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($matches) {
|
||||||
|
// If there are multiple matches, sort them to find the one with the longest string length.
|
||||||
|
if (count($matches) > 0) {
|
||||||
|
usort($matches, function ($a, $b) {
|
||||||
|
return strlen($b) - strlen($a);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Instantiate and return the handler identified as the best match.
|
||||||
|
return new $this->prefixRoutes[$matches[0]]();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private function getResponseFromRoutes(RequestInterface $request, array $args = null)
|
private function getResponseFromRoutes(RequestInterface $request, array $args = null)
|
||||||
{
|
{
|
||||||
$response = null;
|
$response = null;
|
||||||
|
|
@ -183,6 +240,12 @@ class Router implements HandlerInterface
|
||||||
return $this->tryResponse($handler, $request, $args);
|
return $this->tryResponse($handler, $request, $args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check prefix routes for any routes that match. Use the longest matching prefix.
|
||||||
|
$handler = $this->getPrefixHandler($path);
|
||||||
|
if ($handler) {
|
||||||
|
return $this->tryResponse($handler, $request, $args);
|
||||||
|
}
|
||||||
|
|
||||||
// Try each of the routes.
|
// Try each of the routes.
|
||||||
foreach ($this->routes as $route) {
|
foreach ($this->routes as $route) {
|
||||||
/** @var HandlerInterface $route */
|
/** @var HandlerInterface $route */
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,46 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pjdietz\WellRESTed\PrefixRoute
|
* pjdietz\WellRESTed\Routes\PrefixRoute
|
||||||
*
|
*
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
* @author PJ Dietz <pj@pjdietz.com>
|
||||||
* @copyright Copyright 2014 by PJ Dietz
|
* @copyright Copyright 2015 by PJ Dietz
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Routes;
|
namespace pjdietz\WellRESTed\Routes;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
use pjdietz\WellRESTed\Interfaces\RequestInterface;
|
use pjdietz\WellRESTed\Interfaces\RequestInterface;
|
||||||
|
use pjdietz\WellRESTed\Interfaces\Routes\PrefixRouteInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps a list of static URI paths to a Handler
|
* Maps a list of static URI paths to a Handler
|
||||||
*/
|
*/
|
||||||
class PrefixRoute extends StaticRoute
|
class PrefixRoute extends BaseRoute implements PrefixRouteInterface
|
||||||
{
|
{
|
||||||
|
/** @var array List of static URI paths */
|
||||||
|
private $prefixes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new StaticRoute for a given path or paths and a handler class.
|
||||||
|
*
|
||||||
|
* @param string|array $prefixes Path or list of paths the request must match
|
||||||
|
* @param string $targetClassName Fully qualified name to an autoloadable handler class.
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function __construct($prefixes, $targetClassName)
|
||||||
|
{
|
||||||
|
parent::__construct($targetClassName);
|
||||||
|
if (is_string($prefixes)) {
|
||||||
|
$this->prefixes = array($prefixes);
|
||||||
|
} elseif (is_array($prefixes)) {
|
||||||
|
$this->prefixes = $prefixes;
|
||||||
|
} else {
|
||||||
|
throw new InvalidArgumentException("$prefixes must be a string or array of string");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
/* HandlerInterface */
|
/* HandlerInterface */
|
||||||
|
|
||||||
|
|
@ -32,12 +56,32 @@ class PrefixRoute extends StaticRoute
|
||||||
public function getResponse(RequestInterface $request, array $args = null)
|
public function getResponse(RequestInterface $request, array $args = null)
|
||||||
{
|
{
|
||||||
$requestPath = $request->getPath();
|
$requestPath = $request->getPath();
|
||||||
foreach ($this->paths as $path) {
|
foreach ($this->prefixes as $prefix) {
|
||||||
if (substr($requestPath, 0, strlen($path)) === $path) {
|
if (strrpos($requestPath, $prefix, -strlen($requestPath)) !== false) {
|
||||||
$target = $this->getTarget();
|
$target = $this->getTarget();
|
||||||
return $target->getResponse($request, $args);
|
return $target->getResponse($request, $args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the path prefixes this maps to a target handler.
|
||||||
|
*
|
||||||
|
* @return array Array of path prefixes.
|
||||||
|
*/
|
||||||
|
public function getPrefixes()
|
||||||
|
{
|
||||||
|
return $this->prefixes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the target class this maps to.
|
||||||
|
*
|
||||||
|
* @return string Fully qualified name for a HandlerInterface
|
||||||
|
*/
|
||||||
|
public function getHandler()
|
||||||
|
{
|
||||||
|
return $this->getTarget();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use pjdietz\WellRESTed\Interfaces\RequestInterface;
|
||||||
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
|
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
|
||||||
use pjdietz\WellRESTed\Response;
|
use pjdietz\WellRESTed\Response;
|
||||||
use pjdietz\WellRESTed\Router;
|
use pjdietz\WellRESTed\Router;
|
||||||
|
use pjdietz\WellRESTed\Routes\PrefixRoute;
|
||||||
use pjdietz\WellRESTed\Routes\StaticRoute;
|
use pjdietz\WellRESTed\Routes\StaticRoute;
|
||||||
use pjdietz\WellRESTed\Routes\TemplateRoute;
|
use pjdietz\WellRESTed\Routes\TemplateRoute;
|
||||||
|
|
||||||
|
|
@ -48,6 +49,81 @@ class RouterTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertEquals(200, $resp->getStatusCode());
|
$this->assertEquals(200, $resp->getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testAddStaticRoute()
|
||||||
|
{
|
||||||
|
$path = "/cats/";
|
||||||
|
|
||||||
|
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface');
|
||||||
|
$mockRequest->expects($this->any())
|
||||||
|
->method('getPath')
|
||||||
|
->will($this->returnValue($path));
|
||||||
|
|
||||||
|
$router = new Router();
|
||||||
|
$router->setStaticRoute($path, __NAMESPACE__ . '\\RouterTestHandler');
|
||||||
|
$resp = $router->getResponse($mockRequest);
|
||||||
|
$this->assertNotNull($resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddPrefixRoute()
|
||||||
|
{
|
||||||
|
$path = "/cats/";
|
||||||
|
|
||||||
|
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface');
|
||||||
|
$mockRequest->expects($this->any())
|
||||||
|
->method('getPath')
|
||||||
|
->will($this->returnValue($path));
|
||||||
|
|
||||||
|
$router = new Router();
|
||||||
|
$router->setPrefixRoute($path, __NAMESPACE__ . '\\RouterTestHandler');
|
||||||
|
$resp = $router->getResponse($mockRequest);
|
||||||
|
$this->assertNotNull($resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMatchStaticRouteBeforePrefixRoute()
|
||||||
|
{
|
||||||
|
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface');
|
||||||
|
$mockRequest->expects($this->any())
|
||||||
|
->method('getPath')
|
||||||
|
->will($this->returnValue("/amimals/cats/molly"));
|
||||||
|
|
||||||
|
$router = new Router();
|
||||||
|
$router->addRoute(new PrefixRoute("/amimals/", __NAMESPACE__ . '\\NotFoundHandler'));
|
||||||
|
$router->addRoute(new PrefixRoute("/amimals/cats/", __NAMESPACE__ . '\\NotFoundHandler'));
|
||||||
|
$router->addRoute(new StaticRoute("/amimals/cats/molly", __NAMESPACE__ . '\\RouterTestHandler'));
|
||||||
|
$resp = $router->getResponse($mockRequest);
|
||||||
|
$this->assertEquals(200, $resp->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMatchBestPrefixRoute()
|
||||||
|
{
|
||||||
|
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface');
|
||||||
|
$mockRequest->expects($this->any())
|
||||||
|
->method('getPath')
|
||||||
|
->will($this->returnValue("/amimals/cats/molly"));
|
||||||
|
|
||||||
|
$router = new Router();
|
||||||
|
$router->addRoute(new PrefixRoute("/amimals/", __NAMESPACE__ . '\\NotFoundHandler'));
|
||||||
|
$router->addRoute(new PrefixRoute("/amimals/dogs/", __NAMESPACE__ . '\\NotFoundHandler'));
|
||||||
|
$router->addRoute(new PrefixRoute("/amimals/cats/", __NAMESPACE__ . '\\RouterTestHandler'));
|
||||||
|
$resp = $router->getResponse($mockRequest);
|
||||||
|
$this->assertEquals(200, $resp->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMatchBestPrefixRouteBeforeTemplateRoute()
|
||||||
|
{
|
||||||
|
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface');
|
||||||
|
$mockRequest->expects($this->any())
|
||||||
|
->method('getPath')
|
||||||
|
->will($this->returnValue("/amimals/cats/molly"));
|
||||||
|
|
||||||
|
$router = new Router();
|
||||||
|
$router->addRoute(new PrefixRoute("/amimals/", __NAMESPACE__ . '\\NotFoundHandler'));
|
||||||
|
$router->addRoute(new PrefixRoute("/amimals/cats/", __NAMESPACE__ . '\\RouterTestHandler'));
|
||||||
|
$router->addRoute(new TemplateRoute("/amimals/cats/*", __NAMESPACE__ . '\\NotFoundHandler'));
|
||||||
|
$resp = $router->getResponse($mockRequest);
|
||||||
|
$this->assertEquals(200, $resp->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
public function testRespondWithDefaultErrorForException()
|
public function testRespondWithDefaultErrorForException()
|
||||||
{
|
{
|
||||||
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface');
|
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface');
|
||||||
|
|
@ -337,7 +413,6 @@ class InjectionErrorHandler implements HandlerInterface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class InjectionHandler implements HandlerInterface
|
class InjectionHandler implements HandlerInterface
|
||||||
{
|
{
|
||||||
public function getResponse(RequestInterface $request, array $args = null)
|
public function getResponse(RequestInterface $request, array $args = null)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue