Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 9913fc6

Browse filesBrowse files
[Routing] allow no-slash root on imported routes
1 parent a38cbd0 commit 9913fc6
Copy full SHA for 9913fc6

File tree

13 files changed

+60
-22
lines changed
Filter options

13 files changed

+60
-22
lines changed

‎src/Symfony/Component/Routing/Annotation/Route.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Annotation/Route.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class Route
3838
*/
3939
public function __construct(array $data)
4040
{
41-
if (isset($data['value'])) {
41+
if (array_key_exists('value', $data)) {
4242
$data['path'] = $data['value'];
4343
unset($data['value']);
4444
}

‎src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php
+5-4Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public function load($class, $type = null)
120120
}
121121

122122
if (0 === $collection->count() && $class->hasMethod('__invoke') && $annot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass)) {
123-
$globals['path'] = '';
123+
$globals['path'] = null;
124124
$globals['name'] = '';
125125
$this->addRoute($collection, $annot, $globals, $class, $class->getMethod('__invoke'));
126126
}
@@ -135,10 +135,11 @@ protected function addRoute(RouteCollection $collection, $annot, $globals, \Refl
135135
$name = $this->getDefaultRouteName($class, $method);
136136
}
137137
$name = $globals['name'].$name;
138+
$path = isset($globals['path']) ? $globals['path'].$annot->getPath() : $annot->getPath();
138139

139140
$defaults = array_replace($globals['defaults'], $annot->getDefaults());
140141
foreach ($method->getParameters() as $param) {
141-
if (false !== strpos($globals['path'].$annot->getPath(), sprintf('{%s}', $param->getName())) && !isset($defaults[$param->getName()]) && $param->isDefaultValueAvailable()) {
142+
if (null !== $path && false !== strpos($path, sprintf('{%s}', $param->getName())) && !isset($defaults[$param->getName()]) && $param->isDefaultValueAvailable()) {
142143
$defaults[$param->getName()] = $param->getDefaultValue();
143144
}
144145
}
@@ -157,7 +158,7 @@ protected function addRoute(RouteCollection $collection, $annot, $globals, \Refl
157158
$condition = $globals['condition'];
158159
}
159160

160-
$route = $this->createRoute($globals['path'].$annot->getPath(), $defaults, $requirements, $options, $host, $schemes, $methods, $condition);
161+
$route = $this->createRoute($path, $defaults, $requirements, $options, $host, $schemes, $methods, $condition);
161162

162163
$this->configureRoute($route, $class, $method, $annot);
163164

@@ -208,7 +209,7 @@ protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMetho
208209
protected function getGlobals(\ReflectionClass $class)
209210
{
210211
$globals = array(
211-
'path' => '',
212+
'path' => null,
212213
'requirements' => array(),
213214
'options' => array(),
214215
'defaults' => array(),

‎src/Symfony/Component/Routing/Loader/Configurator/CollectionConfigurator.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Loader/Configurator/CollectionConfigurator.php
+5-3Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,14 @@ public function __construct(RouteCollection $parent, string $name)
2929
$this->parent = $parent;
3030
$this->name = $name;
3131
$this->collection = new RouteCollection();
32-
$this->route = new Route('');
32+
$this->route = new Route(null);
3333
}
3434

3535
public function __destruct()
3636
{
37-
$this->collection->addPrefix(rtrim($this->route->getPath(), '/'));
37+
if ($this->route->hasTrailingSlash() || '/' !== $this->route->getPath()) {
38+
$this->collection->addPrefix(rtrim($this->route->getPath(), '/'));
39+
}
3840
$this->parent->addCollection($this->collection);
3941
}
4042

@@ -63,7 +65,7 @@ final public function collection($name = '')
6365
*
6466
* @return $this
6567
*/
66-
final public function prefix(string $prefix)
68+
final public function prefix(?string $prefix)
6769
{
6870
$this->route->setPath($prefix);
6971

‎src/Symfony/Component/Routing/Loader/Configurator/Traits/AddTrait.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Loader/Configurator/Traits/AddTrait.php
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ trait AddTrait
2727
/**
2828
* Adds a route.
2929
*/
30-
final public function add(string $name, string $path): RouteConfigurator
30+
final public function add(string $name, ?string $path): RouteConfigurator
3131
{
3232
$this->collection->add($this->name.$name, $route = new Route($path));
3333

@@ -37,7 +37,7 @@ final public function add(string $name, string $path): RouteConfigurator
3737
/**
3838
* Adds a route.
3939
*/
40-
final public function __invoke(string $name, string $path): RouteConfigurator
40+
final public function __invoke(string $name, ?string $path): RouteConfigurator
4141
{
4242
return $this->add($name, $path);
4343
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Loader/XmlFileLoader.php
+7-3Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,16 +107,20 @@ public function supports($resource, $type = null)
107107
*/
108108
protected function parseRoute(RouteCollection $collection, \DOMElement $node, $path)
109109
{
110-
if ('' === ($id = $node->getAttribute('id')) || !$node->hasAttribute('path')) {
111-
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must have an "id" and a "path" attribute.', $path));
110+
if ('' === ($id = $node->getAttribute('id')) || (!$node->hasAttribute('path') && 'true' !== $node->getAttribute('empty-path'))) {
111+
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must have an "id" and a "path" (or "empty-path") attribute.', $path));
112+
}
113+
114+
if ($node->hasAttribute('path') && 'true' === $node->getAttribute('empty-path')) {
115+
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" cannot have a "path" and "empty-path" set to true.', $path));
112116
}
113117

114118
$schemes = preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY);
115119
$methods = preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY);
116120

117121
list($defaults, $requirements, $options, $condition) = $this->parseConfigs($node, $path);
118122

119-
$route = new Route($node->getAttribute('path'), $defaults, $requirements, $options, $node->getAttribute('host'), $schemes, $methods, $condition);
123+
$route = new Route($node->hasAttribute('path') ? $node->getAttribute('path') : null, $defaults, $requirements, $options, $node->getAttribute('host'), $schemes, $methods, $condition);
120124
$collection->add($id, $route);
121125
}
122126

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Loader/YamlFileLoader.php
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ protected function validate($config, $name, $path)
198198
$path, $name, implode('", "', $extraKeys), implode('", "', self::$availableKeys)
199199
));
200200
}
201-
if (isset($config['resource']) && isset($config['path'])) {
201+
if (isset($config['resource']) && array_key_exists('path', $config)) {
202202
throw new \InvalidArgumentException(sprintf(
203203
'The routing file "%s" must not specify both the "resource" key and the "path" key for "%s". Choose between an import and a route definition.',
204204
$path, $name
@@ -210,7 +210,7 @@ protected function validate($config, $name, $path)
210210
$name, $path
211211
));
212212
}
213-
if (!isset($config['resource']) && !isset($config['path'])) {
213+
if (!isset($config['resource']) && !array_key_exists('path', $config)) {
214214
throw new \InvalidArgumentException(sprintf(
215215
'You must define a "path" for the route "%s" in file "%s".',
216216
$name, $path

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
<xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" />
3838

3939
<xsd:attribute name="id" type="xsd:string" use="required" />
40-
<xsd:attribute name="path" type="xsd:string" use="required" />
40+
<xsd:attribute name="path" type="xsd:string" />
41+
<xsd:attribute name="empty-path" type="xsd:boolean" />
4142
<xsd:attribute name="host" type="xsd:string" />
4243
<xsd:attribute name="schemes" type="xsd:string" />
4344
<xsd:attribute name="methods" type="xsd:string" />

‎src/Symfony/Component/Routing/Route.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Route.php
+10-5Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class Route implements \Serializable
4141
* * compiler_class: A class name able to compile this route instance (RouteCompiler by default)
4242
* * utf8: Whether UTF-8 matching is enforced ot not
4343
*
44-
* @param string $path The path pattern to match
44+
* @param string|null $path The path pattern to match, or null to declare a trailing-slash-less root of a collection
4545
* @param array $defaults An array of default parameter values
4646
* @param array $requirements An array of requirements for parameters (regexes)
4747
* @param array $options An array of options
@@ -50,7 +50,7 @@ class Route implements \Serializable
5050
* @param string|string[] $methods A required HTTP method or an array of restricted methods
5151
* @param string $condition A condition that should evaluate to true for the route to match
5252
*/
53-
public function __construct(string $path, array $defaults = array(), array $requirements = array(), array $options = array(), ?string $host = '', $schemes = array(), $methods = array(), ?string $condition = '')
53+
public function __construct(?string $path, array $defaults = array(), array $requirements = array(), array $options = array(), ?string $host = '', $schemes = array(), $methods = array(), ?string $condition = '')
5454
{
5555
$this->setPath($path);
5656
$this->setDefaults($defaults);
@@ -109,28 +109,33 @@ public function unserialize($serialized)
109109
*/
110110
public function getPath()
111111
{
112-
return $this->path;
112+
return null === $this->path ? '/' : $this->path;
113113
}
114114

115115
/**
116116
* Sets the pattern for the path.
117117
*
118118
* This method implements a fluent interface.
119119
*
120-
* @param string $pattern The path pattern
120+
* @param string|null $pattern The path pattern
121121
*
122122
* @return $this
123123
*/
124124
public function setPath($pattern)
125125
{
126126
// A pattern must start with a slash and must not have multiple slashes at the beginning because the
127127
// generated path for this route would be confused with a network path, e.g. '//domain.com/path'.
128-
$this->path = '/'.ltrim(trim($pattern), '/');
128+
$this->path = null !== $pattern ? '/'.ltrim(trim($pattern), '/') : null;
129129
$this->compiled = null;
130130

131131
return $this;
132132
}
133133

134+
public function hasTrailingSlash(): bool
135+
{
136+
return null !== $this->path && '/' === $this->path[-1];
137+
}
138+
134139
/**
135140
* Returns the pattern for the host.
136141
*

‎src/Symfony/Component/Routing/RouteCollection.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/RouteCollection.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ public function addPrefix($prefix, array $defaults = array(), array $requirement
147147
}
148148

149149
foreach ($this->routes as $route) {
150-
$route->setPath('/'.$prefix.$route->getPath());
150+
$route->setPath('/'.$prefix.(!$route->hasTrailingSlash() && '/' === $route->getPath() ? '' : $route->getPath()));
151151
$route->addDefaults($defaults);
152152
$route->addRequirements($requirements);
153153
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Tests/Fixtures/controller/routing.xml
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
<route id="app_homepage" path="/" controller="AppBundle:Homepage:show" />
88

9+
<route id="app_homepage_no_slash" empty-path="true" />
10+
911
<route id="app_blog" path="/blog">
1012
<default key="_controller">AppBundle:Blog:list</default>
1113
</route>

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Tests/Fixtures/controller/routing.yml
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ app_homepage:
22
path: /
33
controller: AppBundle:Homepage:show
44

5+
app_homepage_no_slash:
6+
path: ~
7+
58
app_blog:
69
path: /blog
710
defaults:

‎src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,10 @@ public function testLoadRouteWithControllerAttribute()
296296
$route = $routeCollection->get('app_homepage');
297297

298298
$this->assertSame('AppBundle:Homepage:show', $route->getDefault('_controller'));
299+
300+
$route = $routeCollection->get('app_homepage_no_slash');
301+
302+
$this->assertSame('/', $route->getPath());
299303
}
300304

301305
public function testLoadRouteWithoutControllerAttribute()
@@ -371,5 +375,11 @@ public function testImportRouteWithNamePrefix()
371375
$this->assertEquals('/blog', $routeCollection->get('app_blog')->getPath());
372376
$this->assertNotNull($routeCollection->get('api_app_blog'));
373377
$this->assertEquals('/api/blog', $routeCollection->get('api_app_blog')->getPath());
378+
379+
$route = $routeCollection->get('api_app_homepage');
380+
$this->assertSame('/api/', $route->getPath());
381+
382+
$route = $routeCollection->get('api_app_homepage_no_slash');
383+
$this->assertSame('/api', $route->getPath());
374384
}
375385
}

‎src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ public function testLoadRouteWithControllerAttribute()
117117
$route = $routeCollection->get('app_homepage');
118118

119119
$this->assertSame('AppBundle:Homepage:show', $route->getDefault('_controller'));
120+
121+
$route = $routeCollection->get('app_homepage_no_slash');
122+
123+
$this->assertSame('/', $route->getPath());
120124
}
121125

122126
public function testLoadRouteWithoutControllerAttribute()
@@ -192,5 +196,11 @@ public function testImportRouteWithNamePrefix()
192196
$this->assertEquals('/blog', $routeCollection->get('app_blog')->getPath());
193197
$this->assertNotNull($routeCollection->get('api_app_blog'));
194198
$this->assertEquals('/api/blog', $routeCollection->get('api_app_blog')->getPath());
199+
200+
$route = $routeCollection->get('api_app_homepage');
201+
$this->assertSame('/api/', $route->getPath());
202+
203+
$route = $routeCollection->get('api_app_homepage_no_slash');
204+
$this->assertSame('/api', $route->getPath());
195205
}
196206
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.