Skip to content

Commit 86c93e4

Browse files
committed
[Routing] data type support for defaults
As pointed out in symfony/symfony-docs#4017, the XmlFileLoader was not capable of defining array default values. Additionally, this commit adds support for handling associative arrays, boolean, integer, float and string data types.
1 parent 3baea1f commit 86c93e4

14 files changed

+531
-9
lines changed

src/Symfony/Component/Routing/CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
CHANGELOG
22
=========
33

4+
2.6.0
5+
-----
6+
7+
* Added support for `boolean`, `integer`, `float`, `string`, `list` and `map`
8+
defaults.
9+
410
2.5.0
511
-----
612

@@ -164,6 +170,6 @@ CHANGELOG
164170
been used anyway without creating inconsistencies
165171
* [BC BREAK] RouteCollection::remove also removes a route from parent
166172
collections (not only from its children)
167-
* added ConfigurableRequirementsInterface that allows to disable exceptions
173+
* added ConfigurableRequirementsInterface that allows to disable exceptions
168174
(and generate empty URLs instead) when generating a route with an invalid
169175
parameter value

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

Lines changed: 102 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -217,14 +217,13 @@ private function parseConfigs(\DOMElement $node, $path)
217217
$condition = null;
218218

219219
foreach ($node->getElementsByTagNameNS(self::NAMESPACE_URI, '*') as $n) {
220+
if ($node !== $n->parentNode) {
221+
continue;
222+
}
223+
220224
switch ($n->localName) {
221225
case 'default':
222-
if ($n->hasAttribute('xsi:nil') && 'true' == $n->getAttribute('xsi:nil')) {
223-
$defaults[$n->getAttribute('key')] = null;
224-
} else {
225-
$defaults[$n->getAttribute('key')] = trim($n->textContent);
226-
}
227-
226+
$defaults[$n->getAttribute('key')] = $this->parseDefaultsConfig($n, $path);
228227
break;
229228
case 'requirement':
230229
$requirements[$n->getAttribute('key')] = trim($n->textContent);
@@ -242,4 +241,101 @@ private function parseConfigs(\DOMElement $node, $path)
242241

243242
return array($defaults, $requirements, $options, $condition);
244243
}
244+
245+
/**
246+
* Parses the "default" elements.
247+
*
248+
* @param \DOMElement $element The "default" element to parse
249+
* @param string $path Full path of the XML file being processed
250+
*
251+
* @return array|bool|float|int|string|null The parsed value of the "default" element
252+
*/
253+
private function parseDefaultsConfig(\DOMElement $element, $path)
254+
{
255+
if ($element->hasAttribute('xsi:nil') && 'true' == $element->getAttribute('xsi:nil')) {
256+
return;
257+
}
258+
259+
// Check for existing element nodes in the default element. There can
260+
// only be a single element inside a default element. So this element
261+
// (if one was found) can safely be returned.
262+
foreach ($element->childNodes as $child) {
263+
if (!$child instanceof \DOMElement) {
264+
continue;
265+
}
266+
267+
if (self::NAMESPACE_URI !== $child->namespaceURI) {
268+
continue;
269+
}
270+
271+
return $this->parseDefaultNode($child, $path);
272+
}
273+
274+
// If the default element doesn't contain a nested "boolean", "integer",
275+
// "float", "string", "list" or "map" element, the element contents will
276+
// be treated as the string value of the associated default option.
277+
return trim($element->textContent);
278+
}
279+
280+
/**
281+
* Recursively parses the value of a "default" element.
282+
*
283+
* @param \DOMElement $node The node value
284+
* @param string $path Full path of the XML file being processed
285+
*
286+
* @return array|bool|float|int|string The parsed value
287+
*
288+
* @throws \InvalidArgumentException when the XML is invalid
289+
*/
290+
private function parseDefaultNode(\DOMElement $node, $path)
291+
{
292+
if ($node->hasAttribute('xsi:nil') && 'true' == $node->getAttribute('xsi:nil')) {
293+
return;
294+
}
295+
296+
switch ($node->localName) {
297+
case 'boolean':
298+
return 'true' === trim($node->nodeValue) || '1' === trim($node->nodeValue);
299+
case 'integer':
300+
return (int) trim($node->nodeValue);
301+
case 'float':
302+
return (float) trim($node->nodeValue);
303+
case 'string':
304+
return trim($node->nodeValue);
305+
case 'list':
306+
$list = array();
307+
308+
foreach ($node->childNodes as $element) {
309+
if (!$element instanceof \DOMElement) {
310+
continue;
311+
}
312+
313+
if (self::NAMESPACE_URI !== $element->namespaceURI) {
314+
continue;
315+
}
316+
317+
$list[] = $this->parseDefaultNode($element, $path);
318+
}
319+
320+
return $list;
321+
case 'map':
322+
$map = array();
323+
324+
foreach ($node->childNodes as $element) {
325+
if (!$element instanceof \DOMElement) {
326+
continue;
327+
}
328+
329+
if (self::NAMESPACE_URI !== $element->namespaceURI) {
330+
continue;
331+
}
332+
333+
$map[$element->getAttribute('key')] = $this->parseDefaultNode($element, $path);
334+
}
335+
336+
return $map;
337+
default:
338+
throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "boolean", "integer", "float", "string", "list" or "map".', $node->localName, $path));
339+
}
340+
}
245341
}

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

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
<xsd:group name="configs">
2828
<xsd:choice>
29-
<xsd:element name="default" nillable="true" type="element" />
29+
<xsd:element name="default" nillable="true" type="default" />
3030
<xsd:element name="requirement" type="element" />
3131
<xsd:element name="option" type="element" />
3232
<xsd:element name="condition" type="xsd:string" />
@@ -55,11 +55,93 @@
5555
<xsd:attribute name="methods" type="xsd:string" />
5656
</xsd:complexType>
5757

58+
<xsd:complexType name="default" mixed="true">
59+
<xsd:choice minOccurs="0" maxOccurs="1">
60+
<xsd:element name="boolean" type="xsd:boolean" />
61+
<xsd:element name="integer" type="xsd:integer" />
62+
<xsd:element name="float" type="xsd:float" />
63+
<xsd:element name="string" type="xsd:string" />
64+
<xsd:element name="list" type="list" />
65+
<xsd:element name="map" type="map" />
66+
</xsd:choice>
67+
<xsd:attribute name="key" type="xsd:string" use="required" />
68+
</xsd:complexType>
69+
5870
<xsd:complexType name="element">
5971
<xsd:simpleContent>
6072
<xsd:extension base="xsd:string">
6173
<xsd:attribute name="key" type="xsd:string" use="required" />
6274
</xsd:extension>
6375
</xsd:simpleContent>
6476
</xsd:complexType>
77+
78+
<xsd:complexType name="list">
79+
<xsd:choice minOccurs="0" maxOccurs="unbounded">
80+
<xsd:element name="boolean" nillable="true" type="xsd:boolean" />
81+
<xsd:element name="integer" nillable="true" type="xsd:integer" />
82+
<xsd:element name="float" nillable="true" type="xsd:float" />
83+
<xsd:element name="string" nillable="true" type="xsd:string" />
84+
<xsd:element name="list" nillable="true" type="list" />
85+
<xsd:element name="map" nillable="true" type="map" />
86+
</xsd:choice>
87+
</xsd:complexType>
88+
89+
<xsd:complexType name="map">
90+
<xsd:choice minOccurs="0" maxOccurs="unbounded">
91+
<xsd:element name="boolean" nillable="true" type="map-boolean-entry" />
92+
<xsd:element name="integer" nillable="true" type="map-integer-entry" />
93+
<xsd:element name="float" nillable="true" type="map-float-entry" />
94+
<xsd:element name="string" nillable="true" type="map-string-entry" />
95+
<xsd:element name="list" nillable="true" type="map-list-entry" />
96+
<xsd:element name="map" nillable="true" type="map-map-entry" />
97+
</xsd:choice>
98+
</xsd:complexType>
99+
100+
<xsd:complexType name="map-boolean-entry">
101+
<xsd:simpleContent>
102+
<xsd:extension base="xsd:boolean">
103+
<xsd:attribute name="key" type="xsd:string" use="required" />
104+
</xsd:extension>
105+
</xsd:simpleContent>
106+
</xsd:complexType>
107+
108+
<xsd:complexType name="map-integer-entry">
109+
<xsd:simpleContent>
110+
<xsd:extension base="xsd:integer">
111+
<xsd:attribute name="key" type="xsd:string" use="required" />
112+
</xsd:extension>
113+
</xsd:simpleContent>
114+
</xsd:complexType>
115+
116+
<xsd:complexType name="map-float-entry">
117+
<xsd:simpleContent>
118+
<xsd:extension base="xsd:float">
119+
<xsd:attribute name="key" type="xsd:string" use="required" />
120+
</xsd:extension>
121+
</xsd:simpleContent>
122+
</xsd:complexType>
123+
124+
<xsd:complexType name="map-string-entry">
125+
<xsd:simpleContent>
126+
<xsd:extension base="xsd:string">
127+
<xsd:attribute name="key" type="xsd:string" use="required" />
128+
</xsd:extension>
129+
</xsd:simpleContent>
130+
</xsd:complexType>
131+
132+
<xsd:complexType name="map-list-entry">
133+
<xsd:complexContent>
134+
<xsd:extension base="list">
135+
<xsd:attribute name="key" type="xsd:string" use="required" />
136+
</xsd:extension>
137+
</xsd:complexContent>
138+
</xsd:complexType>
139+
140+
<xsd:complexType name="map-map-entry">
141+
<xsd:complexContent>
142+
<xsd:extension base="map">
143+
<xsd:attribute name="key" type="xsd:string" use="required" />
144+
</xsd:extension>
145+
</xsd:complexContent>
146+
</xsd:complexType>
65147
</xsd:schema>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<routes xmlns="http://symfony.com/schema/routing"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://symfony.com/schema/routing
5+
http://symfony.com/schema/routing/routing-1.0.xsd">
6+
7+
<route id="blog" path="/blog">
8+
<default key="_controller">
9+
<string>AcmeBlogBundle:Blog:index</string>
10+
</default>
11+
<default key="values">
12+
<list>
13+
<boolean>true</boolean>
14+
<integer>1</integer>
15+
<float>3.5</float>
16+
<string>foo</string>
17+
</list>
18+
</default>
19+
</route>
20+
</routes>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<routes xmlns="http://symfony.com/schema/routing"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://symfony.com/schema/routing
5+
http://symfony.com/schema/routing/routing-1.0.xsd">
6+
7+
<route id="blog" path="/blog">
8+
<default key="_controller">
9+
<string>AcmeBlogBundle:Blog:index</string>
10+
</default>
11+
<default key="values">
12+
<list>
13+
<list>
14+
<boolean>true</boolean>
15+
<integer>1</integer>
16+
<float>3.5</float>
17+
<string>foo</string>
18+
</list>
19+
</list>
20+
</default>
21+
</route>
22+
</routes>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<routes xmlns="http://symfony.com/schema/routing"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://symfony.com/schema/routing
5+
http://symfony.com/schema/routing/routing-1.0.xsd">
6+
7+
<route id="blog" path="/blog">
8+
<default key="_controller">
9+
<string>AcmeBlogBundle:Blog:index</string>
10+
</default>
11+
<default key="values">
12+
<map>
13+
<list key="list">
14+
<boolean>true</boolean>
15+
<integer>1</integer>
16+
<float>3.5</float>
17+
<string>foo</string>
18+
</list>
19+
</map>
20+
</default>
21+
</route>
22+
</routes>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<routes xmlns="http://symfony.com/schema/routing"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://symfony.com/schema/routing
5+
http://symfony.com/schema/routing/routing-1.0.xsd">
6+
7+
<route id="blog" path="/blog">
8+
<default key="_controller">
9+
<string>AcmeBlogBundle:Blog:index</string>
10+
</default>
11+
<default key="values">
12+
<map>
13+
<boolean key="public">true</boolean>
14+
<integer key="page">1</integer>
15+
<float key="price">3.5</float>
16+
<string key="title">foo</string>
17+
</map>
18+
</default>
19+
</route>
20+
</routes>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<routes xmlns="http://symfony.com/schema/routing"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://symfony.com/schema/routing
5+
http://symfony.com/schema/routing/routing-1.0.xsd">
6+
7+
<route id="blog" path="/blog">
8+
<default key="_controller">
9+
<string>AcmeBlogBundle:Blog:index</string>
10+
</default>
11+
<default key="values">
12+
<list>
13+
<map>
14+
<boolean key="public">true</boolean>
15+
<integer key="page">1</integer>
16+
<float key="price">3.5</float>
17+
<string key="title">foo</string>
18+
</map>
19+
</list>
20+
</default>
21+
</route>
22+
</routes>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<routes xmlns="http://symfony.com/schema/routing"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://symfony.com/schema/routing
5+
http://symfony.com/schema/routing/routing-1.0.xsd">
6+
7+
<route id="blog" path="/blog">
8+
<default key="_controller">
9+
<string>AcmeBlogBundle:Blog:index</string>
10+
</default>
11+
<default key="values">
12+
<map>
13+
<map key="map">
14+
<boolean key="public">true</boolean>
15+
<integer key="page">1</integer>
16+
<float key="price">3.5</float>
17+
<string key="title">foo</string>
18+
</map>
19+
</map>
20+
</default>
21+
</route>
22+
</routes>

0 commit comments

Comments
 (0)