TemplateRoute more throughly implements URI Templates as defined in RFC 6570
Template support:
- Simple strings /{var}
- Reserved string /{+var}
- Multiple variables per expression /{hello,larry}
- Dot-prefixes /{.filename,extension}
- Slash-prefiex {/path,to,here}
- Explosion {/paths*}, /cats/{ids*} explode to list arrays
This commit is contained in:
parent
1bb93434b2
commit
61fd0f3354
|
|
@ -2,54 +2,160 @@
|
||||||
|
|
||||||
namespace WellRESTed\Routing\Route;
|
namespace WellRESTed\Routing\Route;
|
||||||
|
|
||||||
class TemplateRoute extends RegexRoute
|
class TemplateRoute extends Route
|
||||||
{
|
{
|
||||||
|
private $pathVariables;
|
||||||
|
private $explosions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Regular expression matching 1 or more unreserved characters.
|
* Regular expression matching 1 or more unreserved characters.
|
||||||
* ALPHA / DIGIT / "-" / "." / "_" / "~"
|
* ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||||
*/
|
*/
|
||||||
const RE_UNRESERVED = '[0-9a-zA-Z\-._\~]+';
|
const RE_UNRESERVED = '[0-9a-zA-Z\-._\~%]*';
|
||||||
/** Regular expression matching a URI template variable (e.g., {id}) */
|
/** Regular expression matching a URI template variable (e.g., {id}) */
|
||||||
const URI_TEMPLATE_EXPRESSION_RE = '/{([[a-zA-Z][a-zA-Z0-_]*)}/';
|
const URI_TEMPLATE_EXPRESSION_RE = '/{([+.\/]?[a-zA-Z0-9_,]+\*?)}/';
|
||||||
|
|
||||||
public function __construct($target, $methodMap)
|
public function getType()
|
||||||
{
|
{
|
||||||
$pattern = $this->buildPattern($target);
|
return RouteInterface::TYPE_PATTERN;
|
||||||
parent::__construct($pattern, $methodMap);
|
}
|
||||||
|
|
||||||
|
public function getPathVariables()
|
||||||
|
{
|
||||||
|
return $this->pathVariables ?: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate the URI template into a regular expression.
|
* Examines a request target to see if it is a match for the route.
|
||||||
*
|
*
|
||||||
* @param string $template URI template the path must match
|
* @param string $requestTarget
|
||||||
* @return string
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
private function buildPattern($template)
|
public function matchesRequestTarget($requestTarget)
|
||||||
|
{
|
||||||
|
$this->pathVariables = [];
|
||||||
|
$this->explosions = [];
|
||||||
|
|
||||||
|
if (!$this->matchesStartOfRequestTarget($requestTarget)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$matchingPattern = $this->getMatchingPattern();
|
||||||
|
|
||||||
|
if (preg_match($matchingPattern, $requestTarget, $captures)) {
|
||||||
|
$this->pathVariables = $this->processMatches($captures);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function matchesStartOfRequestTarget($requestTarget)
|
||||||
|
{
|
||||||
|
$firstVarPos = strpos($this->target, "{");
|
||||||
|
return (substr($requestTarget, 0, $firstVarPos) === substr($this->target, 0, $firstVarPos));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function processMatches($matches)
|
||||||
|
{
|
||||||
|
$variables = [];
|
||||||
|
|
||||||
|
// Isolate the named captures.
|
||||||
|
$keys = array_filter(array_keys($matches), "is_string");
|
||||||
|
|
||||||
|
// Store named captures to the variables.
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
|
||||||
|
$value = $matches[$key];
|
||||||
|
|
||||||
|
if (isset($this->explosions[$key])) {
|
||||||
|
$values = explode($this->explosions[$key], $value);
|
||||||
|
$variables[$key] = array_map("urldecode", $values);
|
||||||
|
} else {
|
||||||
|
$value = urldecode($value);
|
||||||
|
$variables[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $variables;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMatchingPattern()
|
||||||
{
|
{
|
||||||
// Convert the template into the pattern
|
// Convert the template into the pattern
|
||||||
$pattern = $template;
|
$pattern = $this->target;
|
||||||
|
|
||||||
// Escape allowable characters with regex meaning.
|
// Escape allowable characters with regex meaning.
|
||||||
$pattern = str_replace(
|
$escape = [
|
||||||
array("-", "."),
|
"." => "\\.",
|
||||||
array("\\-", "\\."),
|
"-" => "\\-",
|
||||||
$pattern);
|
"+" => "\\+",
|
||||||
|
"*" => "\\*"
|
||||||
// Replace * with .* AFTER escaping to avoid escaping .*
|
];
|
||||||
$pattern = str_replace("*", ".*", $pattern);
|
$pattern = str_replace(array_keys($escape), array_values($escape), $pattern);
|
||||||
|
$unescape = [
|
||||||
|
"{\\+" => "{+",
|
||||||
|
"{\\." => "{.",
|
||||||
|
"\\*}" => "*}"
|
||||||
|
];
|
||||||
|
$pattern = str_replace(array_keys($unescape), array_values($unescape), $pattern);
|
||||||
|
|
||||||
// Surround the pattern with delimiters.
|
// Surround the pattern with delimiters.
|
||||||
$pattern = "~^{$pattern}$~";
|
$pattern = "~^{$pattern}$~";
|
||||||
|
|
||||||
// Replace all template variables with matching subpatterns.
|
$pattern = preg_replace_callback(
|
||||||
$callback = function ($matches) {
|
self::URI_TEMPLATE_EXPRESSION_RE,
|
||||||
$key = $matches[1];
|
[$this, "uriVariableReplacementCallback"],
|
||||||
// TODO Check for reserved characters, etc.
|
$pattern
|
||||||
$pattern = self::RE_UNRESERVED;
|
);
|
||||||
return "(?<{$key}>{$pattern})";
|
|
||||||
};
|
|
||||||
$pattern = preg_replace_callback(self::URI_TEMPLATE_EXPRESSION_RE, $callback, $pattern);
|
|
||||||
|
|
||||||
return $pattern;
|
return $pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function uriVariableReplacementCallback($matches)
|
||||||
|
{
|
||||||
|
$name = $matches[1];
|
||||||
|
$pattern = self::RE_UNRESERVED;
|
||||||
|
|
||||||
|
$prefix = "";
|
||||||
|
$delimiter = ",";
|
||||||
|
$explodeDelimiter = ",";
|
||||||
|
|
||||||
|
// Read the first character as an operator. This determines which
|
||||||
|
// characters to allow in the match.
|
||||||
|
$operator = $name[0];
|
||||||
|
|
||||||
|
switch ($operator) {
|
||||||
|
case "+":
|
||||||
|
$name = substr($name, 1);
|
||||||
|
$pattern = ".*";
|
||||||
|
break;
|
||||||
|
case ".":
|
||||||
|
$name = substr($name, 1);
|
||||||
|
$prefix = "\\.";
|
||||||
|
$delimiter = "\\.";
|
||||||
|
$explodeDelimiter = ".";
|
||||||
|
break;
|
||||||
|
case "/":
|
||||||
|
$name = substr($name, 1);
|
||||||
|
$prefix = "\\/";
|
||||||
|
$delimiter = "\\/";
|
||||||
|
$explodeDelimiter = "/";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explosion
|
||||||
|
if (substr($name, -1, 1) === "*") {
|
||||||
|
$name = substr($name, 0, -1);
|
||||||
|
$pattern = ".*";
|
||||||
|
$this->explosions[$name] = $explodeDelimiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
$names = explode(",", $name);
|
||||||
|
$results = [];
|
||||||
|
foreach ($names as $name) {
|
||||||
|
$results[] = "(?<{$name}>{$pattern})";
|
||||||
|
}
|
||||||
|
return $prefix . join($delimiter, $results);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ use WellRESTed\Routing\Route\RouteInterface;
|
||||||
use WellRESTed\Routing\Route\TemplateRoute;
|
use WellRESTed\Routing\Route\TemplateRoute;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers WellRESTed\Routing\Route\TemplateRoute
|
* @coversDefaultClass WellRESTed\Routing\Route\TemplateRoute
|
||||||
|
* @uses WellRESTed\Routing\Route\TemplateRoute
|
||||||
* @uses WellRESTed\Routing\Route\RegexRoute
|
* @uses WellRESTed\Routing\Route\RegexRoute
|
||||||
* @uses WellRESTed\Routing\Route\Route
|
* @uses WellRESTed\Routing\Route\Route
|
||||||
* @group route
|
* @group route
|
||||||
|
|
@ -22,8 +23,34 @@ class TemplateRouteTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->methodMap = $this->prophesize('WellRESTed\Routing\MethodMapInterface');
|
$this->methodMap = $this->prophesize('WellRESTed\Routing\MethodMapInterface');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getExpectedValues($keys)
|
||||||
|
{
|
||||||
|
$expectedValues = [
|
||||||
|
"var" => "value",
|
||||||
|
"hello" => "Hello World!",
|
||||||
|
"x" => "1024",
|
||||||
|
"y" => "768",
|
||||||
|
"path" => "/foo/bar",
|
||||||
|
"who" => "fred",
|
||||||
|
"half" => "50%",
|
||||||
|
"empty" => "",
|
||||||
|
"count" => ["one", "two", "three"],
|
||||||
|
"list" => ["red", "green", "blue"]
|
||||||
|
];
|
||||||
|
return array_intersect_key($expectedValues, array_flip($keys));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertArrayHasSameContents($expected, $actual)
|
||||||
|
{
|
||||||
|
ksort($expected);
|
||||||
|
ksort($actual);
|
||||||
|
$this->assertEquals($expected, $actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @coversNothing
|
* @covers ::getType
|
||||||
*/
|
*/
|
||||||
public function testReturnsPatternType()
|
public function testReturnsPatternType()
|
||||||
{
|
{
|
||||||
|
|
@ -31,133 +58,241 @@ class TemplateRouteTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertSame(RouteInterface::TYPE_PATTERN, $route->getType());
|
$this->assertSame(RouteInterface::TYPE_PATTERN, $route->getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// ------------------------------------------------------------------------
|
||||||
* @dataProvider matchingTemplateProvider
|
// Matching
|
||||||
*/
|
|
||||||
public function testMatchesTemplate($template, $requestTarget)
|
|
||||||
{
|
|
||||||
$route = new TemplateRoute($template, $this->methodMap->reveal());
|
|
||||||
$this->assertTrue($route->matchesRequestTarget($requestTarget));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider matchingTemplateProvider
|
* @covers ::matchesRequestTarget
|
||||||
|
* @covers ::matchesStartOfRequestTarget
|
||||||
|
* @covers ::getMatchingPattern
|
||||||
|
* @dataProvider nonMatchingTargetProvider
|
||||||
|
* @param string $template
|
||||||
|
* @param string $target
|
||||||
*/
|
*/
|
||||||
public function testProvidesCapturesAsRequestAttributes($template, $path, $expectedCaptures)
|
public function testFailsToMatchNonMatchingTarget($template, $target)
|
||||||
{
|
{
|
||||||
$request = $this->prophesize('Psr\Http\Message\ServerRequestInterface');
|
$route = new TemplateRoute($template, $this->methodMap);
|
||||||
$request->withAttribute(Argument::cetera())->willReturn($request->reveal());
|
$this->assertFalse($route->matchesRequestTarget($target));
|
||||||
$response = $this->prophesize('Psr\Http\Message\ResponseInterface');
|
|
||||||
$next = function ($request, $response) {
|
|
||||||
return $response;
|
|
||||||
};
|
|
||||||
|
|
||||||
$route = new TemplateRoute($template, $this->methodMap->reveal());
|
|
||||||
$route->matchesRequestTarget($path);
|
|
||||||
$route->dispatch($request->reveal(), $response->reveal(), $next);
|
|
||||||
|
|
||||||
$request->withAttribute("uriVariables", Argument::that(function ($path) use ($expectedCaptures) {
|
|
||||||
return array_intersect_assoc($path, $expectedCaptures) == $expectedCaptures;
|
|
||||||
}))->shouldHaveBeenCalled();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function matchingTemplateProvider()
|
public function nonMatchingTargetProvider()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
["/cat/{id}", "/cat/12", ["id" => "12"]],
|
["/foo/{var}", "/bar/12", false, "Mismatch before first template expression"],
|
||||||
["/unreserved/{id}", "/unreserved/az0-._~", ["id" => "az0-._~"]],
|
["/foo/{foo}/bar/{bar}", "/foo/12/13", false, "Mismatch after first template expression"],
|
||||||
["/cat/{catId}/{dogId}",
|
["/hello/{hello}", "/hello/Hello%20World!", false, "Requires + operator to match reserver characters"]
|
||||||
"/cat/molly/bear",
|
|
||||||
[
|
|
||||||
"catId" => "molly",
|
|
||||||
"dogId" => "bear"
|
|
||||||
]
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"/cat/{catId}/{dogId}",
|
|
||||||
"/cat/molly/bear",
|
|
||||||
[
|
|
||||||
"catId" => "molly",
|
|
||||||
"dogId" => "bear"
|
|
||||||
]
|
|
||||||
],
|
|
||||||
["/cat/{id}/*", "/cat/12/molly", ["id" => "12"]],
|
|
||||||
[
|
|
||||||
"/cat/{id}-{width}x{height}.jpg",
|
|
||||||
"/cat/17-200x100.jpg",
|
|
||||||
[
|
|
||||||
"id" => "17",
|
|
||||||
"width" => "200",
|
|
||||||
"height" => "100"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// ------------------------------------------------------------------------
|
||||||
* @dataProvider allowedVariableNamesProvider
|
// Matching :: Simple Strings
|
||||||
*/
|
|
||||||
public function testMatchesAllowedVariablesNames($template, $path, $expectedCaptures)
|
|
||||||
{
|
|
||||||
$request = $this->prophesize('Psr\Http\Message\ServerRequestInterface');
|
|
||||||
$request->withAttribute(Argument::cetera())->willReturn($request->reveal());
|
|
||||||
$response = $this->prophesize('Psr\Http\Message\ResponseInterface');
|
|
||||||
$next = function ($request, $response) {
|
|
||||||
return $response;
|
|
||||||
};
|
|
||||||
$route = new TemplateRoute($template, $this->methodMap->reveal());
|
|
||||||
$route->matchesRequestTarget($path);
|
|
||||||
$route->dispatch($request->reveal(), $response->reveal(), $next);
|
|
||||||
|
|
||||||
$request->withAttribute("uriVariables", Argument::that(function ($path) use ($expectedCaptures) {
|
/**
|
||||||
return array_intersect_assoc($path, $expectedCaptures) == $expectedCaptures;
|
* @covers ::matchesRequestTarget
|
||||||
}))->shouldHaveBeenCalled();
|
* @covers ::getMatchingPattern
|
||||||
|
* @covers ::uriVariableReplacementCallback
|
||||||
|
* @dataProvider simpleStringProvider
|
||||||
|
* @param string $template
|
||||||
|
* @param string $target
|
||||||
|
*/
|
||||||
|
public function testMatchesSimpleStrings($template, $target)
|
||||||
|
{
|
||||||
|
$route = new TemplateRoute($template, $this->methodMap);
|
||||||
|
$this->assertTrue($route->matchesRequestTarget($target));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function allowedVariableNamesProvider()
|
/**
|
||||||
|
* @covers ::getPathVariables
|
||||||
|
* @covers ::processMatches
|
||||||
|
* @covers ::uriVariableReplacementCallback
|
||||||
|
* @dataProvider simpleStringProvider
|
||||||
|
* @param string $template
|
||||||
|
* @param string $target
|
||||||
|
* @param string[] List of variables that should be extracted
|
||||||
|
*/
|
||||||
|
public function testCapturesFromSimpleStrings($template, $target, $variables)
|
||||||
|
{
|
||||||
|
$route = new TemplateRoute($template, $this->methodMap);
|
||||||
|
$route->matchesRequestTarget($target);
|
||||||
|
$this->assertArrayHasSameContents($this->getExpectedValues($variables), $route->getPathVariables());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function simpleStringProvider()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
["/{n}", "/lower", ["n" => "lower"]],
|
["/foo", "/foo", []],
|
||||||
["/{N}", "/UPPER", ["N" => "UPPER"]],
|
["/{var}", "/value", ["var"]],
|
||||||
["/{var1024}", "/digits", ["var1024" => "digits"]],
|
["/{hello}", "/Hello%20World%21", ["hello"]],
|
||||||
["/{variable_name}", "/underscore", ["variable_name" => "underscore"]],
|
["/{x,hello,y}", "/1024,Hello%20World%21,768", ["x", "hello", "y"]],
|
||||||
|
["/{x,hello,y}", "/1024,Hello%20World%21,768", ["x", "hello", "y"]],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// Matching :: Reservered
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider illegalVariableNamesProvider
|
* @covers ::matchesRequestTarget
|
||||||
|
* @covers ::getMatchingPattern
|
||||||
|
* @covers ::uriVariableReplacementCallback
|
||||||
|
* @dataProvider reservedStringProvider
|
||||||
|
* @param string $template
|
||||||
|
* @param string $target
|
||||||
*/
|
*/
|
||||||
public function testFailsToMatchIllegalVariablesNames($template, $path)
|
public function testMatchesReserveredStrings($template, $target)
|
||||||
{
|
{
|
||||||
$route = new TemplateRoute($template, $this->methodMap->reveal());
|
$route = new TemplateRoute($template, $this->methodMap);
|
||||||
$this->assertFalse($route->matchesRequestTarget($path));
|
$this->assertTrue($route->matchesRequestTarget($target));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function illegalVariableNamesProvider()
|
/**
|
||||||
|
* @covers ::getPathVariables
|
||||||
|
* @covers ::processMatches
|
||||||
|
* @covers ::uriVariableReplacementCallback
|
||||||
|
* @dataProvider reservedStringProvider
|
||||||
|
* @param string $template
|
||||||
|
* @param string $target
|
||||||
|
* @param string[] List of variables that should be extracted
|
||||||
|
*/
|
||||||
|
public function testCapturesFromReservedStrings($template, $target, $variables)
|
||||||
|
{
|
||||||
|
$route = new TemplateRoute($template, $this->methodMap);
|
||||||
|
$route->matchesRequestTarget($target);
|
||||||
|
$this->assertSame($this->getExpectedValues($variables), $route->getPathVariables());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reservedStringProvider()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
["/{not-legal}", "/hyphen"],
|
["/{+var}", "/value", ["var"]],
|
||||||
["/{1digitfirst}", "/digitfirst"],
|
["/{+hello}", "/Hello%20World!", ["hello"]],
|
||||||
["/{%2f}", "/percent-encoded"],
|
["{+path}/here", "/foo/bar/here", ["path"]],
|
||||||
["/{}", "/empty"],
|
|
||||||
["/{{nested}}", "/nested"]
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// Matching :: Label Expansion
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider nonmatchingTemplateProvider
|
* @covers ::matchesRequestTarget
|
||||||
|
* @covers ::getMatchingPattern
|
||||||
|
* @covers ::uriVariableReplacementCallback
|
||||||
|
* @dataProvider labelWithDotPrefixProvider
|
||||||
|
* @param string $template
|
||||||
|
* @param string $target
|
||||||
*/
|
*/
|
||||||
public function testFailsToMatchNonmatchingTemplate($template, $path)
|
public function testMatchesLabelWithDotPrefix($template, $target)
|
||||||
{
|
{
|
||||||
$route = new TemplateRoute($template, $this->methodMap->reveal());
|
$route = new TemplateRoute($template, $this->methodMap);
|
||||||
$this->assertFalse($route->matchesRequestTarget($path));
|
$this->assertTrue($route->matchesRequestTarget($target));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function nonmatchingTemplateProvider()
|
/**
|
||||||
|
* @covers ::getPathVariables
|
||||||
|
* @covers ::processMatches
|
||||||
|
* @covers ::uriVariableReplacementCallback
|
||||||
|
* @dataProvider labelWithDotPrefixProvider
|
||||||
|
* @param string $template
|
||||||
|
* @param string $target
|
||||||
|
* @param string[] List of variables that should be extracted
|
||||||
|
*/
|
||||||
|
public function testCapturesFromLabelWithDotPrefix($template, $target, $variables)
|
||||||
|
{
|
||||||
|
$route = new TemplateRoute($template, $this->methodMap);
|
||||||
|
$route->matchesRequestTarget($target);
|
||||||
|
$this->assertArrayHasSameContents($this->getExpectedValues($variables), $route->getPathVariables());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function labelWithDotPrefixProvider()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
["/cat/{id}", "/cat/molly/the/cat"],
|
["/{.who}", "/.fred", ["who"]],
|
||||||
["/cat/{catId}/{dogId}", "/dog/12/13"]
|
["/{.half,who}", "/.50%25.fred", ["half", "who"]],
|
||||||
|
["/X{.empty}", "/X.", ["empty"]]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// Matching :: Path Segments
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::matchesRequestTarget
|
||||||
|
* @covers ::getMatchingPattern
|
||||||
|
* @covers ::uriVariableReplacementCallback
|
||||||
|
* @dataProvider pathSegmentProvider
|
||||||
|
* @param string $template
|
||||||
|
* @param string $target
|
||||||
|
*/
|
||||||
|
public function testMatchesPathSegments($template, $target)
|
||||||
|
{
|
||||||
|
$route = new TemplateRoute($template, $this->methodMap);
|
||||||
|
$this->assertTrue($route->matchesRequestTarget($target));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::getPathVariables
|
||||||
|
* @covers ::processMatches
|
||||||
|
* @covers ::uriVariableReplacementCallback
|
||||||
|
* @dataProvider pathSegmentProvider
|
||||||
|
* @param string $template
|
||||||
|
* @param string $target
|
||||||
|
* @param string[] List of variables that should be extracted
|
||||||
|
*/
|
||||||
|
public function testCapturesFromPathSegments($template, $target, $variables)
|
||||||
|
{
|
||||||
|
$route = new TemplateRoute($template, $this->methodMap);
|
||||||
|
$route->matchesRequestTarget($target);
|
||||||
|
$this->assertArrayHasSameContents($this->getExpectedValues($variables), $route->getPathVariables());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pathSegmentProvider()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
["{/who}", "/fred", ["who"]],
|
||||||
|
["{/half,who}", "/50%25/fred", ["half", "who"]],
|
||||||
|
["{/var,empty}", "/value/", ["var", "empty"]]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// Matching :: Explosion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::matchesRequestTarget
|
||||||
|
* @covers ::getMatchingPattern
|
||||||
|
* @covers ::uriVariableReplacementCallback
|
||||||
|
* @dataProvider pathExplosionProvider
|
||||||
|
* @param string $template
|
||||||
|
* @param string $target
|
||||||
|
*/
|
||||||
|
public function testMatchesExplosion($template, $target)
|
||||||
|
{
|
||||||
|
$route = new TemplateRoute($template, $this->methodMap);
|
||||||
|
$this->assertTrue($route->matchesRequestTarget($target));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::getPathVariables
|
||||||
|
* @covers ::processMatches
|
||||||
|
* @covers ::uriVariableReplacementCallback
|
||||||
|
* @dataProvider pathExplosionProvider
|
||||||
|
* @param string $template
|
||||||
|
* @param string $target
|
||||||
|
* @param string[] List of variables that should be extracted
|
||||||
|
*/
|
||||||
|
public function testCapturesFromExplosion($template, $target, $variables)
|
||||||
|
{
|
||||||
|
$route = new TemplateRoute($template, $this->methodMap);
|
||||||
|
$route->matchesRequestTarget($target);
|
||||||
|
$this->assertArrayHasSameContents($this->getExpectedValues($variables), $route->getPathVariables());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pathExplosionProvider()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
["/{count*}", "/one,two,three", ["count"]],
|
||||||
|
["{/count*}", "/one/two/three", ["count"]],
|
||||||
|
["X{.list*}", "X.red.green.blue", ["list"]]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue