Skip to content

Commit 547184f

Browse files
[Routing] Allow defining and reusing subroutines
1 parent b2fafc6 commit 547184f

File tree

16 files changed

+140
-3
lines changed

16 files changed

+140
-3
lines changed

src/Symfony/Component/Routing/Loader/Configurator/RoutingConfigurator.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,11 @@ final public function collection($name = '')
5151
{
5252
return new CollectionConfigurator($this->collection, $name);
5353
}
54+
55+
final public function subroutine(string $name, string $pattern): self
56+
{
57+
$this->collection->setSubroutine($name, $pattern);
58+
59+
return $this;
60+
}
5461
}

src/Symfony/Component/Routing/Loader/XmlFileLoader.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ protected function parseNode(RouteCollection $collection, \DOMElement $node, $pa
8383
case 'import':
8484
$this->parseImport($collection, $node, $path, $file);
8585
break;
86+
case 'subroutine':
87+
$collection->setSubroutine($node->getAttribute('id'), $node->getAttribute('pattern'));
88+
break;
8689
default:
8790
throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "route" or "import".', $node->localName, $path));
8891
}

src/Symfony/Component/Routing/Loader/YamlFileLoader.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ public function load($file, $type = null)
7878
}
7979

8080
foreach ($parsedConfig as $name => $config) {
81+
if ('_subroutines' === $name && \is_array($config) && !isset($config['resource']) && !isset($config['path'])) {
82+
foreach ($config as $name => $pattern) {
83+
$collection->setSubroutine($name, $pattern);
84+
}
85+
continue;
86+
}
8187
$this->validate($config, $name, $path);
8288

8389
if (isset($config['resource'])) {

src/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
<xsd:choice minOccurs="0" maxOccurs="unbounded">
2222
<xsd:element name="import" type="import" />
2323
<xsd:element name="route" type="route" />
24+
<xsd:element name="subroutine" type="subroutine" />
2425
</xsd:choice>
2526
</xsd:complexType>
2627

@@ -69,6 +70,11 @@
6970
<xsd:attribute name="controller" type="xsd:string" />
7071
</xsd:complexType>
7172

73+
<xsd:complexType name="subroutine">
74+
<xsd:attribute name="id" type="xsd:string" use="required" />
75+
<xsd:attribute name="pattern" type="xsd:string" use="required" />
76+
</xsd:complexType>
77+
7278
<xsd:complexType name="default" mixed="true">
7379
<xsd:choice minOccurs="0" maxOccurs="1">
7480
<xsd:element name="bool" type="xsd:boolean" />

src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,11 @@ private function compileDynamicRoutes(RouteCollection $collection, bool $matchHo
404404
$code .= "\n .')'";
405405
$state->regex .= ')';
406406
}
407+
foreach ($this->getRoutes()->getSubroutines() as $name => $rx) {
408+
$rx = sprintf('(?(DEFINE)(?P<%s>%s))', $name, $rx);
409+
$code .= "\n .'{$rx}'";
410+
$state->regex .= $rx;
411+
}
407412
$rx = ")$}{$modifiers}";
408413
$code .= "\n .'{$rx}',";
409414
$state->regex .= $rx;

src/Symfony/Component/Routing/Matcher/UrlMatcher.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ public function addExpressionLanguageProvider(ExpressionFunctionProviderInterfac
130130
*/
131131
protected function matchCollection($pathinfo, RouteCollection $routes)
132132
{
133+
$subroutines = '';
134+
foreach ($routes->getSubroutines() as $name => $pattern) {
135+
$subroutines .= sprintf('(?(DEFINE)(?P<%s>%s))', $name, $pattern);
136+
}
137+
133138
foreach ($routes as $name => $route) {
134139
$compiledRoute = $route->compile();
135140

@@ -138,13 +143,22 @@ protected function matchCollection($pathinfo, RouteCollection $routes)
138143
continue;
139144
}
140145

141-
if (!preg_match($compiledRoute->getRegex(), $pathinfo, $matches)) {
146+
$rx = $compiledRoute->getRegex();
147+
if ('' !== $subroutines) {
148+
$rx = substr_replace($rx, $subroutines, strrpos($rx, '#'), 0);
149+
}
150+
if (!preg_match($rx, $pathinfo, $matches)) {
142151
continue;
143152
}
144153

145154
$hostMatches = array();
146-
if ($compiledRoute->getHostRegex() && !preg_match($compiledRoute->getHostRegex(), $this->context->getHost(), $hostMatches)) {
147-
continue;
155+
if ($rx = $compiledRoute->getHostRegex()) {
156+
if ('' !== $subroutines) {
157+
$rx = substr_replace($rx, $subroutines, strrpos($rx, '#'), 0);
158+
}
159+
if (!preg_match($rx, $this->context->getHost(), $hostMatches)) {
160+
continue;
161+
}
148162
}
149163

150164
$status = $this->handleRouteRequirements($pathinfo, $name, $route);

src/Symfony/Component/Routing/RouteCollection.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ class RouteCollection implements \IteratorAggregate, \Countable
3535
*/
3636
private $resources = array();
3737

38+
/**
39+
* @var array
40+
*/
41+
private $subroutines = array();
42+
3843
public function __clone()
3944
{
4045
foreach ($this->routes as $name => $route) {
@@ -129,6 +134,10 @@ public function addCollection(self $collection)
129134
foreach ($collection->getResources() as $resource) {
130135
$this->addResource($resource);
131136
}
137+
138+
foreach ($collection->getSubroutines() as $name => $pattern) {
139+
$this->setSubroutine($name, $pattern);
140+
}
132141
}
133142

134143
/**
@@ -291,4 +300,23 @@ public function addResource(ResourceInterface $resource)
291300
$this->resources[$key] = $resource;
292301
}
293302
}
303+
304+
/**
305+
* Sets a subroutine that can be reused in requirements.
306+
*/
307+
public function setSubroutine(string $name, string $pattern)
308+
{
309+
if (\strlen($name) > RouteCompiler::VARIABLE_MAXIMUM_LENGTH) {
310+
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));
311+
}
312+
$this->subroutines[$name] = $pattern;
313+
}
314+
315+
/**
316+
* Returns the defined subroutines.
317+
*/
318+
public function getSubroutines(): array
319+
{
320+
return $this->subroutines;
321+
}
294322
}

src/Symfony/Component/Routing/RouteCollectionBuilder.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class RouteCollectionBuilder
3737
private $schemes;
3838
private $methods;
3939
private $resources = array();
40+
private $subroutines = array();
4041

4142
public function __construct(LoaderInterface $loader = null)
4243
{
@@ -76,6 +77,10 @@ public function import($resource, $prefix = '/', $type = null)
7677
foreach ($collection->getResources() as $resource) {
7778
$builder->addResource($resource);
7879
}
80+
81+
foreach ($collection->getSubroutines() as $name => $pattern) {
82+
$builder->setSubroutine($name, $pattern);
83+
}
7984
}
8085

8186
// mount into this builder
@@ -262,6 +267,22 @@ private function addResource(ResourceInterface $resource): RouteCollectionBuilde
262267
return $this;
263268
}
264269

270+
/**
271+
* Sets a subroutine that can be reused in requirements.
272+
*/
273+
public function setSubroutine(string $name, string $pattern)
274+
{
275+
$this->subroutines[$name] = $pattern;
276+
}
277+
278+
/**
279+
* Returns the defined subroutines.
280+
*/
281+
public function getSubroutines()
282+
{
283+
return $this->subroutines;
284+
}
285+
265286
/**
266287
* Creates the final RouteCollection and returns it.
267288
*
@@ -321,6 +342,10 @@ public function build()
321342
$routeCollection->addResource($resource);
322343
}
323344

345+
foreach ($this->subroutines as $name => $pattern) {
346+
$routeCollection->setSubroutine($name, $pattern);
347+
}
348+
324349
return $routeCollection;
325350
}
326351

src/Symfony/Component/Routing/Tests/Fixtures/controller/routing.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
xsi:schemaLocation="http://symfony.com/schema/routing
55
http://symfony.com/schema/routing/routing-1.0.xsd">
66

7+
<subroutine id="number" pattern="\d" />
8+
79
<route id="app_homepage" path="/" controller="AppBundle:Homepage:show" />
810

911
<route id="app_blog" path="/blog">

src/Symfony/Component/Routing/Tests/Fixtures/controller/routing.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
_subroutines:
2+
number: \d
3+
14
app_homepage:
25
path: /
36
controller: AppBundle:Homepage:show

0 commit comments

Comments
 (0)
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