diff --git a/src/Symfony/Component/Routing/Loader/Configurator/RoutingConfigurator.php b/src/Symfony/Component/Routing/Loader/Configurator/RoutingConfigurator.php index 713fcd2e05190..99bb9d7a9be54 100644 --- a/src/Symfony/Component/Routing/Loader/Configurator/RoutingConfigurator.php +++ b/src/Symfony/Component/Routing/Loader/Configurator/RoutingConfigurator.php @@ -51,4 +51,11 @@ final public function collection($name = '') { return new CollectionConfigurator($this->collection, $name); } + + final public function subroutine(string $name, string $pattern): self + { + $this->collection->setSubroutine($name, $pattern); + + return $this; + } } diff --git a/src/Symfony/Component/Routing/Loader/XmlFileLoader.php b/src/Symfony/Component/Routing/Loader/XmlFileLoader.php index 81a4c94ce06fc..cb02ac5072dbc 100644 --- a/src/Symfony/Component/Routing/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/Routing/Loader/XmlFileLoader.php @@ -83,6 +83,9 @@ protected function parseNode(RouteCollection $collection, \DOMElement $node, $pa case 'import': $this->parseImport($collection, $node, $path, $file); break; + case 'subroutine': + $collection->setSubroutine($node->getAttribute('id'), $node->getAttribute('pattern')); + break; default: throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "route" or "import".', $node->localName, $path)); } diff --git a/src/Symfony/Component/Routing/Loader/YamlFileLoader.php b/src/Symfony/Component/Routing/Loader/YamlFileLoader.php index 30d66d36113bb..a0a146bfd4ae3 100644 --- a/src/Symfony/Component/Routing/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/Routing/Loader/YamlFileLoader.php @@ -78,6 +78,12 @@ public function load($file, $type = null) } foreach ($parsedConfig as $name => $config) { + if ('_subroutines' === $name && \is_array($config) && !isset($config['resource']) && !isset($config['path'])) { + foreach ($config as $name => $pattern) { + $collection->setSubroutine($name, $pattern); + } + continue; + } $this->validate($config, $name, $path); if (isset($config['resource'])) { diff --git a/src/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd b/src/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd index dd2477999df24..494ce332830a0 100644 --- a/src/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd +++ b/src/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd @@ -21,6 +21,7 @@ + @@ -69,6 +70,11 @@ + + + + + diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php index aa8f61cc8b78d..7ebd73d13494b 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php @@ -404,6 +404,11 @@ private function compileDynamicRoutes(RouteCollection $collection, bool $matchHo $code .= "\n .')'"; $state->regex .= ')'; } + foreach ($this->getRoutes()->getSubroutines() as $name => $rx) { + $rx = sprintf('(?(DEFINE)(?P<%s>%s))', $name, $rx); + $code .= "\n .'{$rx}'"; + $state->regex .= $rx; + } $rx = ")$}{$modifiers}"; $code .= "\n .'{$rx}',"; $state->regex .= $rx; diff --git a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php index e37cae0361eb8..d3ea89ef1fa34 100644 --- a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php +++ b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php @@ -130,6 +130,11 @@ public function addExpressionLanguageProvider(ExpressionFunctionProviderInterfac */ protected function matchCollection($pathinfo, RouteCollection $routes) { + $subroutines = ''; + foreach ($routes->getSubroutines() as $name => $pattern) { + $subroutines .= sprintf('(?(DEFINE)(?P<%s>%s))', $name, $pattern); + } + foreach ($routes as $name => $route) { $compiledRoute = $route->compile(); @@ -138,13 +143,22 @@ protected function matchCollection($pathinfo, RouteCollection $routes) continue; } - if (!preg_match($compiledRoute->getRegex(), $pathinfo, $matches)) { + $rx = $compiledRoute->getRegex(); + if ('' !== $subroutines) { + $rx = substr_replace($rx, $subroutines, strrpos($rx, '#'), 0); + } + if (!preg_match($rx, $pathinfo, $matches)) { continue; } $hostMatches = array(); - if ($compiledRoute->getHostRegex() && !preg_match($compiledRoute->getHostRegex(), $this->context->getHost(), $hostMatches)) { - continue; + if ($rx = $compiledRoute->getHostRegex()) { + if ('' !== $subroutines) { + $rx = substr_replace($rx, $subroutines, strrpos($rx, '#'), 0); + } + if (!preg_match($rx, $this->context->getHost(), $hostMatches)) { + continue; + } } $status = $this->handleRouteRequirements($pathinfo, $name, $route); diff --git a/src/Symfony/Component/Routing/RouteCollection.php b/src/Symfony/Component/Routing/RouteCollection.php index 84719e2c82fa5..6a3860fa7fde0 100644 --- a/src/Symfony/Component/Routing/RouteCollection.php +++ b/src/Symfony/Component/Routing/RouteCollection.php @@ -35,6 +35,11 @@ class RouteCollection implements \IteratorAggregate, \Countable */ private $resources = array(); + /** + * @var array + */ + private $subroutines = array(); + public function __clone() { foreach ($this->routes as $name => $route) { @@ -129,6 +134,10 @@ public function addCollection(self $collection) foreach ($collection->getResources() as $resource) { $this->addResource($resource); } + + foreach ($collection->getSubroutines() as $name => $pattern) { + $this->setSubroutine($name, $pattern); + } } /** @@ -291,4 +300,23 @@ public function addResource(ResourceInterface $resource) $this->resources[$key] = $resource; } } + + /** + * Sets a subroutine that can be reused in requirements. + */ + public function setSubroutine(string $name, string $pattern) + { + if (\strlen($name) > RouteCompiler::VARIABLE_MAXIMUM_LENGTH) { + throw new \DomainException(sprintf('Subroutine name "%s" cannot be longer than %s characters. Please use a shorter name for pattern "%s".', $name, RouteCompiler::VARIABLE_MAXIMUM_LENGTH, $pattern)); + } + $this->subroutines[$name] = $pattern; + } + + /** + * Returns the defined subroutines. + */ + public function getSubroutines(): array + { + return $this->subroutines; + } } diff --git a/src/Symfony/Component/Routing/RouteCollectionBuilder.php b/src/Symfony/Component/Routing/RouteCollectionBuilder.php index d63c6138f7983..d40592c8cf3c2 100644 --- a/src/Symfony/Component/Routing/RouteCollectionBuilder.php +++ b/src/Symfony/Component/Routing/RouteCollectionBuilder.php @@ -37,6 +37,7 @@ class RouteCollectionBuilder private $schemes; private $methods; private $resources = array(); + private $subroutines = array(); public function __construct(LoaderInterface $loader = null) { @@ -76,6 +77,10 @@ public function import($resource, $prefix = '/', $type = null) foreach ($collection->getResources() as $resource) { $builder->addResource($resource); } + + foreach ($collection->getSubroutines() as $name => $pattern) { + $builder->setSubroutine($name, $pattern); + } } // mount into this builder @@ -262,6 +267,22 @@ private function addResource(ResourceInterface $resource): RouteCollectionBuilde return $this; } + /** + * Sets a subroutine that can be reused in requirements. + */ + public function setSubroutine(string $name, string $pattern) + { + $this->subroutines[$name] = $pattern; + } + + /** + * Returns the defined subroutines. + */ + public function getSubroutines() + { + return $this->subroutines; + } + /** * Creates the final RouteCollection and returns it. * @@ -321,6 +342,10 @@ public function build() $routeCollection->addResource($resource); } + foreach ($this->subroutines as $name => $pattern) { + $routeCollection->setSubroutine($name, $pattern); + } + return $routeCollection; } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/controller/routing.xml b/src/Symfony/Component/Routing/Tests/Fixtures/controller/routing.xml index 6420138a65072..674b010b64bad 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/controller/routing.xml +++ b/src/Symfony/Component/Routing/Tests/Fixtures/controller/routing.xml @@ -4,6 +4,8 @@ xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd"> + + diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/controller/routing.yml b/src/Symfony/Component/Routing/Tests/Fixtures/controller/routing.yml index cb71ec3b75b79..c4f41c6c8637f 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/controller/routing.yml +++ b/src/Symfony/Component/Routing/Tests/Fixtures/controller/routing.yml @@ -1,3 +1,6 @@ +_subroutines: + number: \d + app_homepage: path: / controller: AppBundle:Homepage:show diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/php_dsl.php b/src/Symfony/Component/Routing/Tests/Fixtures/php_dsl.php index 0b8fa0a9eb339..b017ebda2a6da 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/php_dsl.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/php_dsl.php @@ -3,6 +3,8 @@ namespace Symfony\Component\Routing\Loader\Configurator; return function (RoutingConfigurator $routes) { + $routes->subroutine('number', '\d'); + $routes ->collection() ->add('foo', '/foo') diff --git a/src/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php index c0a72b6f81cbe..1d5ffb858296b 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php @@ -113,6 +113,7 @@ public function testRoutingConfigurator() ->setMethods(array('GET')) ->setDefaults(array('id' => 0)) ); + $expectedCollection->setSubroutine('number', '\d'); $expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl_sub.php'))); $expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl.php'))); diff --git a/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php index 0b2e1a9d79340..c1f00a33fc222 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php @@ -411,4 +411,12 @@ public function testImportRouteWithNamePrefix() $this->assertNotNull($routeCollection->get('api_app_blog')); $this->assertEquals('/api/blog', $routeCollection->get('api_app_blog')->getPath()); } + + public function testSubroutine() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/controller'))); + $routeCollection = $loader->load('routing.xml'); + + $this->assertSame(array('number' => '\d'), $routeCollection->getSubroutines()); + } } diff --git a/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php index 3bcfe1b5b6453..97bb3feb28408 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php @@ -275,4 +275,12 @@ public function testImportingWithControllerDefault() $this->assertEquals('DefaultController::defaultAction', $routes->get('home.nl')->getDefault('_controller')); $this->assertEquals('DefaultController::defaultAction', $routes->get('not_localized')->getDefault('_controller')); } + + public function testSubroutine() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/controller'))); + $routeCollection = $loader->load('routing.yml'); + + $this->assertSame(array('number' => '\d'), $routeCollection->getSubroutines()); + } } diff --git a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php index cf7ded2551cf2..eb9312b6049eb 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php @@ -596,6 +596,16 @@ public function testRequirementWithCapturingGroup() $this->assertEquals(array('_route' => 'a', 'a' => 'a', 'b' => 'b'), $matcher->match('/a/b')); } + public function testSubroutine() + { + $coll = new RouteCollection(); + $coll->setSubroutine('date', '\d{4}-\d{2}-\d{2}'); + $coll->add('a', new Route('/{a}', array(), array('a' => '(?&date)'))); + + $matcher = $this->getUrlMatcher($coll); + $this->assertEquals(array('_route' => 'a', 'a' => '2018-03-14'), $matcher->match('/2018-03-14')); + } + protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) { return new UrlMatcher($routes, $context ?: new RequestContext()); diff --git a/src/Symfony/Component/Routing/Tests/RouteCollectionBuilderTest.php b/src/Symfony/Component/Routing/Tests/RouteCollectionBuilderTest.php index 76a042d670b29..7fa1209ae4f7c 100644 --- a/src/Symfony/Component/Routing/Tests/RouteCollectionBuilderTest.php +++ b/src/Symfony/Component/Routing/Tests/RouteCollectionBuilderTest.php @@ -361,4 +361,13 @@ public function testAddsThePrefixOnlyOnceWhenLoadingMultipleCollections() $this->assertEquals('/other/a', $routes['a']->getPath()); $this->assertEquals('/other/b', $routes['b']->getPath()); } + + public function testSubroutine() + { + $routeCollectionBuilder = new RouteCollectionBuilder(new YamlFileLoader(new FileLocator(array(__DIR__.'/Fixtures/controller')))); + $routeCollectionBuilder->import('routing.yml'); + $routeCollection = $routeCollectionBuilder->build(); + + $this->assertSame(array('number' => '\d'), $routeCollection->getSubroutines()); + } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy