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
|
||||
*
|
||||
* @author PJ Dietz <pj@pjdietz.com>
|
||||
* @copyright Copyright 2014 by PJ Dietz
|
||||
* @copyright Copyright 2015 by PJ Dietz
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
|
|
@ -14,6 +14,7 @@ use pjdietz\WellRESTed\Exceptions\HttpExceptions\HttpException;
|
|||
use pjdietz\WellRESTed\Interfaces\HandlerInterface;
|
||||
use pjdietz\WellRESTed\Interfaces\RequestInterface;
|
||||
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
|
||||
use pjdietz\WellRESTed\Interfaces\Routes\PrefixRouteInterface;
|
||||
use pjdietz\WellRESTed\Interfaces\Routes\StaticRouteInterface;
|
||||
|
||||
/**
|
||||
|
|
@ -26,6 +27,9 @@ class Router implements HandlerInterface
|
|||
/** @var array Array of Route objects */
|
||||
private $routes;
|
||||
|
||||
/** @var array Hash array mapping path prefixes to handlers */
|
||||
private $prefixRoutes;
|
||||
|
||||
/** @var array Hash array mapping exact paths to handlers */
|
||||
private $staticRoutes;
|
||||
|
||||
|
|
@ -36,6 +40,7 @@ class Router implements HandlerInterface
|
|||
public function __construct()
|
||||
{
|
||||
$this->routes = array();
|
||||
$this->prefixRoutes = array();
|
||||
$this->staticRoutes = array();
|
||||
$this->errorHandlers = array();
|
||||
}
|
||||
|
|
@ -76,6 +81,8 @@ class Router implements HandlerInterface
|
|||
{
|
||||
if ($route instanceof StaticRouteInterface) {
|
||||
$this->setStaticRoute($route->getPaths(), $route->getHandler());
|
||||
} elseif ($route instanceof PrefixRouteInterface) {
|
||||
$this->setPrefixRoute($route->getPrefixes(), $route->getHandler());
|
||||
} else {
|
||||
$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 $handler Fully qualified name to an autoloadable handler class
|
||||
|
|
@ -163,14 +186,48 @@ class Router implements HandlerInterface
|
|||
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)
|
||||
{
|
||||
if (array_key_exists($path, $this->staticRoutes)) {
|
||||
// Instantiate and return the handler identified by the path.
|
||||
return new $this->staticRoutes[$path]();
|
||||
}
|
||||
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)
|
||||
{
|
||||
$response = null;
|
||||
|
|
@ -183,6 +240,12 @@ class Router implements HandlerInterface
|
|||
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.
|
||||
foreach ($this->routes as $route) {
|
||||
/** @var HandlerInterface $route */
|
||||
|
|
|
|||
|
|
@ -1,22 +1,46 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* pjdietz\WellRESTed\PrefixRoute
|
||||
* pjdietz\WellRESTed\Routes\PrefixRoute
|
||||
*
|
||||
* @author PJ Dietz <pj@pjdietz.com>
|
||||
* @copyright Copyright 2014 by PJ Dietz
|
||||
* @copyright Copyright 2015 by PJ Dietz
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
namespace pjdietz\WellRESTed\Routes;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use pjdietz\WellRESTed\Interfaces\RequestInterface;
|
||||
use pjdietz\WellRESTed\Interfaces\Routes\PrefixRouteInterface;
|
||||
|
||||
/**
|
||||
* 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 */
|
||||
|
||||
|
|
@ -32,12 +56,32 @@ class PrefixRoute extends StaticRoute
|
|||
public function getResponse(RequestInterface $request, array $args = null)
|
||||
{
|
||||
$requestPath = $request->getPath();
|
||||
foreach ($this->paths as $path) {
|
||||
if (substr($requestPath, 0, strlen($path)) === $path) {
|
||||
foreach ($this->prefixes as $prefix) {
|
||||
if (strrpos($requestPath, $prefix, -strlen($requestPath)) !== false) {
|
||||
$target = $this->getTarget();
|
||||
return $target->getResponse($request, $args);
|
||||
}
|
||||
}
|
||||
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\Response;
|
||||
use pjdietz\WellRESTed\Router;
|
||||
use pjdietz\WellRESTed\Routes\PrefixRoute;
|
||||
use pjdietz\WellRESTed\Routes\StaticRoute;
|
||||
use pjdietz\WellRESTed\Routes\TemplateRoute;
|
||||
|
||||
|
|
@ -48,6 +49,81 @@ class RouterTest extends \PHPUnit_Framework_TestCase
|
|||
$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()
|
||||
{
|
||||
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface');
|
||||
|
|
@ -337,7 +413,6 @@ class InjectionErrorHandler implements HandlerInterface
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
class InjectionHandler implements HandlerInterface
|
||||
{
|
||||
public function getResponse(RequestInterface $request, array $args = null)
|
||||
|
|
|
|||
Loading…
Reference in New Issue