[Routing] Allow inline definition of requirements and defaults

This commit is contained in:
Nicolas Grekas 2018-03-14 09:42:44 +01:00
parent b2fafc6a0f
commit 67559e1f97
2 changed files with 31 additions and 2 deletions

View File

@ -53,8 +53,8 @@ class Route implements \Serializable
public function __construct(string $path, array $defaults = array(), array $requirements = array(), array $options = array(), ?string $host = '', $schemes = array(), $methods = array(), ?string $condition = '')
{
$this->setPath($path);
$this->setDefaults($defaults);
$this->setRequirements($requirements);
$this->addDefaults($defaults);
$this->addRequirements($requirements);
$this->setOptions($options);
$this->setHost($host);
$this->setSchemes($schemes);
@ -123,6 +123,19 @@ class Route implements \Serializable
*/
public function setPath($pattern)
{
if (false !== strpbrk($pattern, '?<')) {
$pattern = preg_replace_callback('#\{(\w++)(<.*?>)?(\?[^\}]*+)?\}#', function ($m) {
if (isset($m[3][0])) {
$this->setDefault($m[1], '?' !== $m[3] ? substr($m[3], 1) : null);
}
if (isset($m[2][0])) {
$this->setRequirement($m[1], substr($m[2], 1, -1));
}
return '{'.$m[1].'}';
}, $pattern);
}
// A pattern must start with a slash and must not have multiple slashes at the beginning because the
// generated path for this route would be confused with a network path, e.g. '//domain.com/path'.
$this->path = '/'.ltrim(trim($pattern), '/');

View File

@ -203,6 +203,22 @@ class RouteTest extends TestCase
$this->assertNotSame($route, $unserialized);
}
public function testInlineDefaultAndRequirement()
{
$this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', null), new Route('/foo/{bar?}'));
$this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', 'baz'), new Route('/foo/{bar?baz}'));
$this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', 'baz<buz>'), new Route('/foo/{bar?baz<buz>}'));
$this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', 'baz'), new Route('/foo/{bar?}', array('bar' => 'baz')));
$this->assertEquals((new Route('/foo/{bar}'))->setRequirement('bar', '.*'), new Route('/foo/{bar<.*>}'));
$this->assertEquals((new Route('/foo/{bar}'))->setRequirement('bar', '>'), new Route('/foo/{bar<>>}'));
$this->assertEquals((new Route('/foo/{bar}'))->setRequirement('bar', '\d+'), new Route('/foo/{bar<.*>}', array(), array('bar' => '\d+')));
$this->assertEquals((new Route('/foo/{bar}'))->setRequirement('bar', '[a-z]{2}'), new Route('/foo/{bar<[a-z]{2}>}'));
$this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', null)->setRequirement('bar', '.*'), new Route('/foo/{bar<.*>?}'));
$this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', '<>')->setRequirement('bar', '>'), new Route('/foo/{bar<>>?<>}'));
}
/**
* Tests that the compiled version is also serialized to prevent the overhead
* of compiling it again after unserialize.