From 09ea17d349828f80aa353ba232456edefda1ca0f Mon Sep 17 00:00:00 2001 From: PJ Dietz Date: Fri, 8 May 2015 00:25:15 -0400 Subject: [PATCH] Update TemplateRoute --- src/Routing/Route/TemplateRoute.php | 58 +++--------- .../unit/Routing/Route/TemplateRouteTest.php | 93 ++++++++++--------- 2 files changed, 61 insertions(+), 90 deletions(-) diff --git a/src/Routing/Route/TemplateRoute.php b/src/Routing/Route/TemplateRoute.php index 98f787e..4838239 100644 --- a/src/Routing/Route/TemplateRoute.php +++ b/src/Routing/Route/TemplateRoute.php @@ -5,60 +5,27 @@ namespace WellRESTed\Routing\Route; class TemplateRoute extends RegexRoute { /** - * Regular expression matching URL friendly characters (i.e., letters, - * digits, hyphen and underscore) + * Regular expression matching 1 or more unreserved characters. + * ALPHA / DIGIT / "-" / "." / "_" / "~" */ - const RE_SLUG = '[0-9a-zA-Z\-_]+'; - /** Regular expression matching digitis */ - const RE_NUM = '[0-9]+'; - /** Regular expression matching letters */ - const RE_ALPHA = '[a-zA-Z]+'; - /** Regular expression matching letters and digits */ - const RE_ALPHANUM = '[0-9a-zA-Z]+'; + const RE_UNRESERVED = '[0-9a-zA-Z\-._\~]+'; /** Regular expression matching a URI template variable (e.g., {id}) */ const URI_TEMPLATE_EXPRESSION_RE = '/{([[a-zA-Z][a-zA-Z0-_]*)}/'; - /** - * Create a new route that matches a URI template to a handler. - * - * Optionally provide patterns for the variables in the template. - * - * @param string $template URI template the path must match - * @param mixed $target Handler to dispatch - * @param string|array $variablePattern Regular expression for variables - * - * @see BaseRoute for details about $target - */ - public function __construct( - $template, - $middleware, - $variablePattern = self::RE_SLUG - ) { - $pattern = $this->buildPattern($template, $variablePattern); - parent::__construct($pattern, $middleware); + public function __construct($target, $methodMap) + { + $pattern = $this->buildPattern($target); + parent::__construct($pattern, $methodMap); } /** * Translate the URI template into a regular expression. * * @param string $template URI template the path must match - * @param string|array $variablePattern Regular expression for variables * @return string */ - private function buildPattern($template, $variablePattern) + private function buildPattern($template) { - $defaultPattern = self::RE_SLUG; - $variablePatterns = []; - - if (is_string($variablePattern)) { - $defaultPattern = $variablePattern; - } elseif (is_array($variablePattern)) { - $variablePatterns = $variablePattern; - if (isset($variablePatterns["*"])) { - $defaultPattern = $variablePatterns["*"]; - } - } - // Convert the template into the pattern $pattern = $template; @@ -75,13 +42,10 @@ class TemplateRoute extends RegexRoute $pattern = "~^{$pattern}$~"; // Replace all template variables with matching subpatterns. - $callback = function ($matches) use ($variablePatterns, $defaultPattern) { + $callback = function ($matches) { $key = $matches[1]; - if (isset($variablePatterns[$key])) { - $pattern = $variablePatterns[$key]; - } else { - $pattern = $defaultPattern; - } + // TODO Check for reserved characters, etc. + $pattern = self::RE_UNRESERVED; return "(?<{$key}>{$pattern})"; }; $pattern = preg_replace_callback(self::URI_TEMPLATE_EXPRESSION_RE, $callback, $pattern); diff --git a/test/tests/unit/Routing/Route/TemplateRouteTest.php b/test/tests/unit/Routing/Route/TemplateRouteTest.php index a64086e..c2ee63c 100644 --- a/test/tests/unit/Routing/Route/TemplateRouteTest.php +++ b/test/tests/unit/Routing/Route/TemplateRouteTest.php @@ -3,6 +3,7 @@ namespace WellRESTed\Test\Unit\Routing\Route; use Prophecy\Argument; +use WellRESTed\Routing\Route\RouteInterface; use WellRESTed\Routing\Route\TemplateRoute; /** @@ -12,42 +13,54 @@ use WellRESTed\Routing\Route\TemplateRoute; */ class TemplateRouteTest extends \PHPUnit_Framework_TestCase { - private $request; - private $response; - private $middleware; + private $methodMap; public function setUp() { - $this->request = $this->prophesize("\\Psr\\Http\\Message\\ServerRequestInterface"); - $this->response = $this->prophesize("\\Psr\\Http\\Message\\ResponseInterface"); - $this->middleware = $this->prophesize("\\WellRESTed\\Routing\\MiddlewareInterface"); + $this->methodMap = $this->prophesize('WellRESTed\Routing\MethodMapInterface'); + } + + /** + * @coversNothing + */ + public function testReturnsPatternType() + { + $route = new TemplateRoute("/", $this->methodMap->reveal()); + $this->assertSame(RouteInterface::TYPE_PATTERN, $route->getType()); } /** * @dataProvider matchingTemplateProvider */ - public function testMatchesTemplate($template, $vars, $path) + public function testMatchesTemplate($template, $requestTarget) { - $route = new TemplateRoute($template, $this->middleware->reveal(), $vars); - $this->assertTrue($route->matchesRequestTarget($path)); + $route = new TemplateRoute($template, $this->methodMap->reveal()); + $this->assertTrue($route->matchesRequestTarget($requestTarget)); } /** - * @dataProvider matchingTemplateProvider + * @dataProvider matchingRouteProvider */ - public function testExtractsCaptures($template, $vars, $path, $expectedCaptures) + public function testProvidesCapturesAsRequestAttributes($template, $path, $expectedCaptures) { - $route = new TemplateRoute($template, $this->middleware->reveal(), $vars); - $route->matchesRequestTarget($path, $captures); - $this->assertEquals(0, count(array_diff_assoc($expectedCaptures, $captures))); + $request = $this->prophesize('Psr\Http\Message\ServerRequestInterface'); + $request->withAttribute(Argument::cetera())->willReturn($request->reveal()); + $response = $this->prophesize('Psr\Http\Message\ResponseInterface'); + $responseReveal = $response->reveal(); + + $route = new TemplateRoute($template, $this->methodMap->reveal()); + $route->matchesRequestTarget($path); + $route->dispatch($request->reveal(), $responseReveal); + + $request->withAttribute("path", $expectedCaptures)->shouldHaveBeenCalled(); } public function matchingTemplateProvider() { return [ - ["/cat/{id}", TemplateRoute::RE_NUM, "/cat/12", ["id" => "12"]], + ["/cat/{id}", "/cat/12", ["id" => "12"]], + ["/unreserved/{id}", "/unreserved/az0-._~", ["id" => "az0-._~"]], ["/cat/{catId}/{dogId}", - TemplateRoute::RE_SLUG, "/cat/molly/bear", [ "catId" => "molly", @@ -56,28 +69,22 @@ class TemplateRouteTest extends \PHPUnit_Framework_TestCase ], [ "/cat/{catId}/{dogId}", - [ - "catId" => TemplateRoute::RE_SLUG, - "dogId" => TemplateRoute::RE_SLUG - ], "/cat/molly/bear", [ "catId" => "molly", "dogId" => "bear" ] ], - ["/cat/{id}/*", null, "/cat/12/molly", ["id" => "12"]], + ["/cat/{id}/*", "/cat/12/molly", ["id" => "12"]], [ "/cat/{id}-{width}x{height}.jpg", - TemplateRoute::RE_NUM, "/cat/17-200x100.jpg", [ "id" => "17", "width" => "200", "height" => "100" ] - ], - ["/cat/{path}", ".*", "/cat/this/section/has/slashes", ["path" => "this/section/has/slashes"]] + ] ]; } @@ -86,9 +93,18 @@ class TemplateRouteTest extends \PHPUnit_Framework_TestCase */ public function testMatchesAllowedVariablesNames($template, $path, $expectedCaptures) { - $route = new TemplateRoute($template, $this->middleware->reveal()); - $route->matchesRequestTarget($path, $captures); - $this->assertEquals(0, count(array_diff_assoc($expectedCaptures, $captures))); + $request = $this->prophesize('Psr\Http\Message\ServerRequestInterface'); + $request->withAttribute(Argument::cetera())->willReturn($request->reveal()); + $response = $this->prophesize('Psr\Http\Message\ResponseInterface'); + $responseReveal = $response->reveal(); + + $route = new TemplateRoute($template, $this->methodMap->reveal()); + $route->matchesRequestTarget($path); + $route->dispatch($request->reveal(), $responseReveal); + + $request->withAttribute("path", Argument::that(function ($path) use ($expectedCaptures) { + return array_intersect_assoc($path, $expectedCaptures) == $expectedCaptures; + }))->shouldHaveBeenCalled(); } public function allowedVariableNamesProvider() @@ -106,8 +122,8 @@ class TemplateRouteTest extends \PHPUnit_Framework_TestCase */ public function testFailsToMatchIllegalVariablesNames($template, $path) { - $route = new TemplateRoute($template, $this->middleware->reveal()); - $this->assertFalse($route->matchesRequestTarget($path, $captures)); + $route = new TemplateRoute($template, $this->methodMap->reveal()); + $this->assertFalse($route->matchesRequestTarget($path)); } public function illegalVariableNamesProvider() @@ -124,26 +140,17 @@ class TemplateRouteTest extends \PHPUnit_Framework_TestCase /** * @dataProvider nonmatchingTemplateProvider */ - public function testFailsToMatchNonmatchingTemplate($template, $vars, $path) + public function testFailsToMatchNonmatchingTemplate($template, $path) { - $route = new TemplateRoute($template, $this->middleware->reveal(), $vars); - $this->assertFalse($route->matchesRequestTarget($path, $captures)); + $route = new TemplateRoute($template, $this->methodMap->reveal()); + $this->assertFalse($route->matchesRequestTarget($path)); } public function nonmatchingTemplateProvider() { return [ - ["/cat/{id}", TemplateRoute::RE_NUM, "/cat/molly"], - ["/cat/{catId}/{dogId}", TemplateRoute::RE_ALPHA, "/cat/12/13"], - [ - "/cat/{catId}/{dogId}", - [ - "*" => TemplateRoute::RE_NUM, - "catId" => TemplateRoute::RE_ALPHA, - "dogId" => TemplateRoute::RE_ALPHA - ], - "/cat/12/13" - ] + ["/cat/{id}", "/cat/molly/the/cat"], + ["/cat/{catId}/{dogId}", "/dog/12/13"] ]; } }