From 67559e1f974afa851fd6f55d9bed1d68bef14e88 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 14 Mar 2018 09:42:44 +0100 Subject: [PATCH] [Routing] Allow inline definition of requirements and defaults --- src/Symfony/Component/Routing/Route.php | 17 +++++++++++++++-- .../Component/Routing/Tests/RouteTest.php | 16 ++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Routing/Route.php b/src/Symfony/Component/Routing/Route.php index 29d352aebd..09ac13de09 100644 --- a/src/Symfony/Component/Routing/Route.php +++ b/src/Symfony/Component/Routing/Route.php @@ -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), '/'); diff --git a/src/Symfony/Component/Routing/Tests/RouteTest.php b/src/Symfony/Component/Routing/Tests/RouteTest.php index c7af058e3b..e28cdaf593 100644 --- a/src/Symfony/Component/Routing/Tests/RouteTest.php +++ b/src/Symfony/Component/Routing/Tests/RouteTest.php @@ -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'), new Route('/foo/{bar?baz}')); + $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.