From 38639d9ee485106a66e722c8959b5c48aaec6c4f Mon Sep 17 00:00:00 2001 From: PJ Dietz Date: Mon, 2 Feb 2015 17:08:25 -0500 Subject: [PATCH] Update TemplateRouter to better match templates with variables in more complicated paths --- .../WellRESTed/Routes/TemplateRoute.php | 64 +++++++------------ test/Routes/TemplateRouteTest.php | 6 +- 2 files changed, 26 insertions(+), 44 deletions(-) diff --git a/src/pjdietz/WellRESTed/Routes/TemplateRoute.php b/src/pjdietz/WellRESTed/Routes/TemplateRoute.php index f6e2358..69ae59e 100644 --- a/src/pjdietz/WellRESTed/Routes/TemplateRoute.php +++ b/src/pjdietz/WellRESTed/Routes/TemplateRoute.php @@ -59,65 +59,45 @@ class TemplateRoute extends RegexRoute */ private function buildPattern($template, $defaultPattern, $variablePatterns) { + // Ensure $variablePatterns is an array. if (is_null($variablePatterns)) { $variablePatterns = array(); } elseif (is_object($variablePatterns)) { $variablePatterns = (array) $variablePatterns; } + // Ensure a default is set. if (!$defaultPattern) { $defaultPattern = self::RE_SLUG; } - $pattern = ''; + // Convert the template into the pattern + $pattern = $template; - // Explode the template into an array of path segments. - if ($template[0] === '/') { - $parts = explode('/', substr($template, 1)); - } else { - $parts = explode('/', $template); - } + // Escape allowable characters with regex meaning. + $pattern = str_replace( + array("-", "."), + array("\\-", "\\."), + $pattern); - foreach ($parts as $part) { + // Replace * with .* AFTER escaping to avoid escaping .* + $pattern = str_replace("*", ".*", $pattern); - $pattern .= '\/'; - - // Is this part an expression or a literal? - if (preg_match(self::URI_TEMPLATE_EXPRESSION_RE, $part, $matches)) { - - // Locate the name for the variable from the template. - $variableName = $matches[1]; - - // If the caller passed an array with this variable name - // as a key, use its value for the pattern here. - // Otherwise, use the class's current default. - if (isset($variablePatterns[$variableName])) { - $variablePattern = $variablePatterns[$variableName]; - } else { - $variablePattern = $defaultPattern; - } - - $pattern .= sprintf( - '(?<%s>%s)', - $variableName, - $variablePattern - ); + // Surroung the pattern with delimiters + $pattern = "~^{$pattern}$~"; + // Replace all template variables with matching subpatterns. + $callback = function ($matches) use ($variablePatterns, $defaultPattern) { + $key = $matches[1]; + if (isset($variablePatterns[$key])) { + $pattern = $variablePatterns[$key]; } else { - // This part is a literal. - $pattern .= $part; + $pattern = $defaultPattern; } + return "(?<{$key}>{$pattern})"; + }; + $pattern = preg_replace_callback(self::URI_TEMPLATE_EXPRESSION_RE, $callback, $pattern); - } - - $pattern = '/^' . $pattern; - if (substr($pattern, -1) === "*") { - // Allow path to include characters passed the pattern. - $pattern = rtrim($pattern, "*") . '/'; - } else { - // Path must end at the end of the pattern. - $pattern .= "$/"; - } return $pattern; } diff --git a/test/Routes/TemplateRouteTest.php b/test/Routes/TemplateRouteTest.php index 7feac4b..c89a036 100644 --- a/test/Routes/TemplateRouteTest.php +++ b/test/Routes/TemplateRouteTest.php @@ -34,11 +34,13 @@ class TemplateRouteTest extends \PHPUnit_Framework_TestCase "catId" => TemplateRoute::RE_SLUG, "dogId" => TemplateRoute::RE_SLUG], "/cat/molly/bear", "dogId", "bear"], - ["cat/{catId}/{dogId}", TemplateRoute::RE_NUM, (object) [ + ["/cat/{catId}/{dogId}", TemplateRoute::RE_NUM, (object) [ "catId" => TemplateRoute::RE_SLUG, "dogId" => TemplateRoute::RE_SLUG], "/cat/molly/bear", "dogId", "bear"], - ["/cat/{id}/*", null, null, "/cat/12/molly", "id", "12"] + ["/cat/{id}/*", null, null, "/cat/12/molly", "id", "12"], + ["/cat/{id}-{width}x{height}.jpg", TemplateRoute::RE_NUM, null, "/cat/17-100x100.jpg", "id", "17"], + ["/cat/{path}", ".*", null, "/cat/this/section/has/slashes", "path", "this/section/has/slashes"] ]; }