Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

[Routing] support for array values in route defaults #11394

Open
wants to merge 1 commit into from

7 participants

@xabbuh
Collaborator
Q A
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets
License MIT
Doc PR

As pointed out in symfony/symfony-docs#4017, the XmlFileLoader was not capable of defining array default values.

  • array values
  • integer values
  • float values
  • boolean
@xabbuh xabbuh referenced this pull request in symfony/symfony-docs
Merged

Clarify that route defaults don't need a placeholder #4017

@Tobion
Collaborator

We would also need to find a way to represent other datatypes in xml like integer, float and boolean. Only supporting array without the other datatypes would be arbitrary.
Also possibly DateTime etc.

@xabbuh
Collaborator

@Tobion Do you think that both array types and different scalar types should be done in the same pull request?

@Tobion
Collaborator

Yes because if we don't find a solution for all of them, I'd argue it's not worth implementing only one part.
Then we could just say, xml only supports flat structure with strings.

@xabbuh
Collaborator

I don't fully agree. Even if you were not able to use integer, float or boolean values natively in XML, you would at least be able to retain the array structure. Scalar types can be casted in the controller. Achieving the same with arrays is not always that easy (at least with nested arrays).

But I also don't think that it is too hard adding support for the other data types. So I will look into this tonight.

@stof
Collaborator

@Tobion the XML loading in Symfony already converts values to boolean or number types when they look like that in the DI component. However, this is not applied in the routing loader (and applying it could be considered a BC break btw).

However, this way to represent arrays looks good (a bit painful if you want to represent lists as you have to map the keys explicitly though)

@xabbuh
Collaborator

@Tobion the XML loading in Symfony already converts values to boolean or number types when they look like that in the DI component. However, this is not applied in the routing loader (and applying it could be considered a BC break btw).

@stof I thought about adding an optional type attribute to the default element. If present, the content will be casted accordingly. Otherwise it is treated as a string. This way we can maintain backward compatibility.

However, this way to represent arrays looks good (a bit painful if you want to represent lists as you have to map the keys explicitly though)

We can make the key attribute optional. This should work and shouldn't cause any BC issue. What do you think?

@Tobion
Collaborator

We can make the key attribute optional. This should work and shouldn't cause any BC issue. What do you think?

The problem is that the first level needs a string key name. Otherwise you would have numeric defaults which won't work (because they get removed since preg_match also adds them).

@Tobion
Collaborator

I thought about adding an optional type attribute to the default element. If present, the content will be casted accordingly.

This will not support XSD validation with types, can it? So you could add a string within a type=integer and XSD cannot raise an error.

@xabbuh
Collaborator

The problem is that the first level needs a string key name. Otherwise you would have numeric defaults which won't work (because they get removed since preg_match also adds them).

Should be possible to validate that in the XSD.

This will not support XSD validation with types, can it? So you could add a string within a type=integer and XSD cannot raise an error.

No, unfortunately not. If we want that, we'll have to use different XML elements for each data type I guess.

Another possibility I see is to introduce a new attribute like infer-type which defaults to false. When this is set to true, the loader can try to infer the data type from the actual value instead of simply assuming a string value.

@xabbuh
Collaborator

I think a solution that fully supports the mentioned data types both in the XML schema and in PHP and which is backward compatible requires some more work.

What I would suggest for the moment, is creating a new defaults (the name may need to be discussed) element on the same level as the current default elements. Inside it we would nest collection, string, double, integer and boolean elements. Keys can be defined the same way as before with the key attribute. To allow numerical indices, this attribute would be optional on all but the top level of the tree.

An example configuration may then look like this:

<defaults>
  <string key="foo">bar</string>
  <collection key="list">
    <boolean>true</boolean>
    <boolean>false</boolean>
    <collection>
      <string key="foobar">hash value</string>
    </collection>
  </collection>
  <integer key="baz">10</integer>
</defaults>

The process defaults would then be equal to this array:

$defaults = array(
    'foo' => 'bar',
    'list' => array(
        true,
        false,
        array('foobar' => 'hash value'),
    ),
    'baz' => 10,
);

The already existing default element would be kept with the current behaviour for backward compatibility reasons.

What do you think?

@stof
Collaborator

@xabbuh using a single <defaults> instead of multiple default elements would be totally inconsistent with the way other places look in Symfony XML files. for me, it is a -1.

@xabbuh
Collaborator

Staying with multiple default elements would mean something like this (the attribute should then be forbidden on the top level default child elements):

<default key="foo">
  <string>bar</string>
</default>
<default key="list">
  <collection>
    <boolean>true</boolean>
    <boolean>false</boolean>
    <collection>
      <string key="foobar">hash value</string>
    </collection>
  </collection>
</default>
<default key="baz">
  <integer>10</integer>
</default>

For backward compatibility,

<default key="foo">
  <string>bar</string>
</default>

and

<default key="foo">bar</default>

would be equivalent.

@Tobion
Collaborator

The last proposal was also what I had in mind. But I would propose to distinguish between arrays (as data structure, not PHPs implementation) and maps/associative arrays. So you can validate in XSD that elements inside maps require a key and inside arrays must not have a key.
Otherwise you could mix elements with key and without inside collections which is possible in PHP but rather magical in general and not needed here. This would also prevent accidential user errors.

@xabbuh
Collaborator

That's a good point since mixing list and maps also isn't possible with YAML. So, we can simply use a list and a map element.

If we agree on this, I will update the pull request to match this structure.

@Tobion
Collaborator

Yeah I agree with list and map. In php the list will still be an array that allows random access, so is not really a list. But that can be considered implementation detail and semantically people usually want to express a list a values in XML when not using an associative array/map.

@xabbuh xabbuh commented on the diff
src/Symfony/Component/Routing/Loader/XmlFileLoader.php
@@ -217,14 +217,13 @@ private function parseConfigs(\DOMElement $node, $path)
$condition = null;
foreach ($node->getElementsByTagNameNS(self::NAMESPACE_URI, '*') as $n) {
@xabbuh Collaborator
xabbuh added a note

Does anyone of you know if there was a special reason to use getElementsByTagNameNs() instead of iterating over the DOMNodeList in $node->childNodes?

@Tobion Collaborator
Tobion added a note

to support namespaces

@xabbuh Collaborator
xabbuh added a note

Isn't that covered by the elements from the childNodes too? It could then need some check for the namespaces afterwards but doesn't require to scan the complete subtree.

@Tobion Collaborator
Tobion added a note

The idea was to ignore elements from another namespace and not throw an exception for unknown tag. So one could extend the loader and xsd to support custom elements, like in FosRestBundle.
I think this won't work with childNodes because it also returns element from other namespaces within the current node.

@xabbuh Collaborator
xabbuh added a note

But throwing an exception contradicts this, doesn't it?

By the way, the new method just test for the elements local name. Should I add an additional check for the element's namespace?

@Tobion Collaborator
Tobion added a note

only throw for unkown elements in same namespace

@xabbuh Collaborator
xabbuh added a note

Sorry, of course you're right. I updated the new code to also ignore elements with a different namespace.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@xabbuh
Collaborator

I updated the code to support the several data types.

.../Component/Routing/Tests/Loader/XmlFileLoaderTest.php
((43 lines not shown))
+ $this->assertEquals(
+ array(
+ '_controller' => 'AcmeBlogBundle:Blog:index',
+ 'values' => array(array(true, 1, 3.5, 'foo')),
+ ),
+ $route->getDefaults()
+ );
+ }
+
+ public function testListInMapDefaults()
+ {
+ $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+ $routeCollection = $loader->load('list_in_map_defaults.xml');
+ $route = $routeCollection->get('blog');
+
+ $this->assertEquals(
@Tobion Collaborator
Tobion added a note

please use assertSame everywhere, so the type and order is checked

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/Symfony/Component/Routing/Loader/XmlFileLoader.php
@@ -242,4 +241,81 @@ private function parseConfigs(\DOMElement $node, $path)
return array($defaults, $requirements, $options, $condition);
}
+
+ /**
+ * Parses the "default" elements.
+ *
+ * @param \DOMElement $element The "default" element to parse
+ * @param string $path Full path of the XML file being processed
+ *
+ * @return array|bool|float|int|string The parsed value of the "default" element
@Tobion Collaborator
Tobion added a note

null

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/Symfony/Component/Routing/Loader/XmlFileLoader.php
((13 lines not shown))
+ private function parseDefaultsConfig(\DOMElement $element, $path)
+ {
+ if ($element->hasAttribute('xsi:nil') && 'true' == $element->getAttribute('xsi:nil')) {
+ return;
+ }
+
+ // Check for existing element nodes in the default element. There can
+ // only be a single element inside a default element. So this element
+ // (if one was found) can savely be returned.
+ foreach ($element->childNodes as $child) {
+ if ($child instanceof \DOMElement) {
+ return $this->parseDefaultNode($child, $path);
+ }
+ }
+
+ // For backward compatibility: If the default element doesn't contain
@Tobion Collaborator
Tobion added a note

It's a feature, not only for BC. So you can remove "For backward compatibility".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/Symfony/Component/Routing/Loader/XmlFileLoader.php
((34 lines not shown))
+
+ /**
+ * Recursively parses the value of a "default" element.
+ *
+ * @param \DOMElement $node The node value
+ * @param string $path Full path of the XML file being processed
+ *
+ * @return array|bool|float|int|string The parsed value
+ *
+ * @throws \InvalidArgumentException when the XML is invalid
+ */
+ private function parseDefaultNode(\DOMElement $node, $path)
+ {
+ switch ($node->nodeName) {
+ case 'boolean':
+ return (bool) trim($node->nodeValue);
@Tobion Collaborator
Tobion added a note

This does not look like it works with 'false' correctly.

@xabbuh Collaborator
xabbuh added a note

You're right. Fixed it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Tobion Tobion commented on the diff
...mponent/Routing/Loader/schema/routing/routing-1.0.xsd
@@ -26,7 +26,7 @@
<xsd:group name="configs">
<xsd:choice>
- <xsd:element name="default" nillable="true" type="element" />
+ <xsd:element name="default" nillable="true" type="default" />
<xsd:element name="requirement" type="element" />
@Tobion Collaborator
Tobion added a note

could you also add the required "key" attribute for "requirement" and "option" to the xsd? this does seem to be there yet. but xsd seems to be easy for you :)

@xabbuh Collaborator
xabbuh added a note

This is already be covered by the element definition in the XSD, isn't it?

@Tobion Collaborator
Tobion added a note

Oh true, didn't see that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...mponent/Routing/Loader/schema/routing/routing-1.0.xsd
@@ -26,7 +26,7 @@
<xsd:group name="configs">
<xsd:choice>
- <xsd:element name="default" nillable="true" type="element" />
+ <xsd:element name="default" nillable="true" type="default" />
<xsd:element name="requirement" type="element" />
<xsd:element name="option" type="element" />
<xsd:element name="condition" type="condition" />
@Tobion Collaborator
Tobion added a note

I don't see why condition has a special type. Just to ensure it only accepts strings? This would also apply for option and requirement.

@xabbuh Collaborator
xabbuh added a note

It doesn't have the key attribute. So it cannot be of type element. xsd:string should be sufficient though. What do you think?

@Tobion Collaborator
Tobion added a note

I think so too.

@xabbuh Collaborator
xabbuh added a note

Should it then be changed in a separate pull request to also address current Symfony versions?

@Tobion Collaborator
Tobion added a note

ok

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/Symfony/Component/Routing/Loader/XmlFileLoader.php
((45 lines not shown))
+ private function parseDefaultNode(\DOMElement $node, $path)
+ {
+ switch ($node->nodeName) {
+ case 'boolean':
+ return (bool) trim($node->nodeValue);
+ case 'integer':
+ return (int) trim($node->nodeValue);
+ case 'float':
+ return (float) trim($node->nodeValue);
+ case 'string':
+ return trim($node->nodeValue);
+ case 'list':
+ $list = array();
+ $xpath = new \DOMXPath($node->ownerDocument);
+
+ foreach ($xpath->query('*', $node) as $element) {
@Tobion Collaborator
Tobion added a note

does this work with namespaces? what if an element from another namespace is inside? does the xsd allow that and what would happen here?

@xabbuh Collaborator
xabbuh added a note

It should. Though I don't think that the XPath is necessary at all. I changed that to use the childNodes attributes like it is done in the map part. The schema doesn't allow to add other elements. But one could just not specify the schema in their routing XML file. That could be a problem. I'll check that.

@xabbuh Collaborator
xabbuh added a note

The thing with additional element that are not allowed by the XSD is that they are rejected by the loader. That's because it throws exception if it detects elemens it does not know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@xabbuh
Collaborator

I just noticed that the new parseDefaultNode() method was capable to properly parse nodes with a namespace prefix. I fixed that and modified the already existing testLoadWithNamespacePrefix() to cover this too.

src/Symfony/Component/Routing/Loader/XmlFileLoader.php
((33 lines not shown))
+
+ /**
+ * Recursively parses the value of a "default" element.
+ *
+ * @param \DOMElement $node The node value
+ * @param string $path Full path of the XML file being processed
+ *
+ * @return array|bool|float|int|string The parsed value
+ *
+ * @throws \InvalidArgumentException when the XML is invalid
+ */
+ private function parseDefaultNode(\DOMElement $node, $path)
+ {
+ switch ($node->localName) {
+ case 'boolean':
+ return 'true' === trim($node->nodeValue);
@Tobion Collaborator
Tobion added a note

xsd:boolean also allwos 0 and 1 AFAIK. So this won't work again.

@Tobion Collaborator
Tobion added a note

Also please add a test for all values.

@xabbuh Collaborator
xabbuh added a note

There should be a test for false. And as far as I know, you cannot use 1 and 0.

@Tobion Collaborator
Tobion added a note

I see you added a test for false :+1:

@Tobion Collaborator
Tobion added a note

Note: Legal values for boolean are true, false, 1 (which indicates true), and 0 (which indicates false).
http://www.w3schools.com/schema/schema_dtypes_misc.asp

@xabbuh Collaborator
xabbuh added a note

I can check that again. But I'm pretty sure that the schemaValidateSource() method reported an error for 0 and 1. But I can of course add support for 0 and 1 though.

@xabbuh Collaborator
xabbuh added a note

Done. It works with 0 and 1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Tobion Tobion commented on the diff
.../Component/Routing/Tests/Fixtures/scalar_defaults.xml
((6 lines not shown))
+
+ <route id="blog" path="/blog">
+ <default key="_controller">
+ <string>AcmeBlogBundle:Blog:index</string>
+ </default>
+ <default key="public">
+ <boolean>true</boolean>
+ </default>
+ <default key="page">
+ <integer>1</integer>
+ </default>
+ <default key="price">
+ <float>3.5</float>
+ </default>
+ <default key="archived">
+ <boolean>false</boolean>
@xabbuh Collaborator
xabbuh added a note

Done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Tobion
Collaborator

Can you please add a note about this in the routing changelog. Then I'm :+1: to merge this.

@xabbuh
Collaborator

Of course, done.

@Tobion
Collaborator

It's not there.

@xabbuh
Collaborator

Sorry, I forgot to push.

@Tobion
Collaborator

:+1: now lets hear the other deciders

@xabbuh
Collaborator

@Tobion Thanks for your support!

@fabpot fabpot commented on the diff
src/Symfony/Component/Routing/CHANGELOG.md
@@ -1,6 +1,12 @@
CHANGELOG
=========
+2.6.0
+-----
+
+* Added support for `boolean`, `integer`, `float`, `string`, `list` and `map`
+ defaults.
@fabpot Owner
fabpot added a note

You should be clear that this is only for the XML loader.

@xabbuh Collaborator
xabbuh added a note

Done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@fabpot
Owner

We already have support for different types in the service container... but the implementation is quite different. I think that being consistent between the two components is a good idea. The service container XML loader uses Symfony\Component\Config\Util\XmlUtils to parse complex data structure.

@Tobion
Collaborator

@fabpot My opinion:
1. the xml config parsing is magic (auto-convert types)
2. AFAIK it does not all allow many things, like a string with null: 'null'.
3. it does not support xsd validation because you cannot explicitly type things
4. introducing the same in routing would be a BC break because defaults would be casted automatically now

I agree it would be nice to have the same logic everywhere, but I feel the better version is this one here. So I'd rather have the feature we implemented here inside the config component generalized.

@fabpot
Owner

@Tobion I understand your points. But let's be pragmatic here, we are talking about very rare cases. Nobody ever had this need. Using a default value as an array is a hack in the first place anyway. Using anything besides strings is also a hack (I know that null/booleans can have special meaning here.)

So I'm :-1:.

@Tobion
Collaborator

Then we need to validate defaults, so other loaders only allow plain strings as well. Like YAML or plain PHP.

@Tobion
Collaborator

It's probably gonna be a bc break for many.

@xabbuh
Collaborator

I'm not sure if nobody ever had the need because they didn't have the need or if it's just because they didn't know that the defaults could be used in such a way (we are just changing documentation towards adding notices on the usage of the defaults). Also, the current solution is rather inconsistent since you can do more things in the YAML and PHP loaders.

@sstok

Using an array as default-value is used by Sylius for controller reusing, by using the route defaults as a configuration. So removing support would be a big BC break.

@fabpot fabpot referenced this pull request from a commit
@fabpot fabpot minor #11482 [Routing] simplify the XML schema file (xabbuh)
This PR was merged into the 2.4 branch.

Discussion
----------

[Routing] simplify the XML schema file

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        |

Conditions that are defined for certain routes are just simple strings.
Thus, there is no need to use a dedicated element when xsd:string does
the same (as mentioned by @tobion in #11394).

Commits
-------

dbac46a [Routing] simplify the XML schema file
9b5f56c
@sstok

What is the status of this PR?

@stof stof commented on the diff
src/Symfony/Component/Routing/Loader/XmlFileLoader.php
((74 lines not shown))
+ }
+
+ return $list;
+ case 'map':
+ $map = array();
+
+ foreach ($node->childNodes as $element) {
+ if (!$element instanceof \DOMElement) {
+ continue;
+ }
+
+ if (self::NAMESPACE_URI !== $element->namespaceURI) {
+ continue;
+ }
+
+ $map[$element->getAttribute('key')] = $this->parseDefaultNode($element, $path);
@stof Collaborator
stof added a note

you should check that the key attribute is set

@xabbuh Collaborator
xabbuh added a note

The key attribute for the map elements is mandatory.

@WouterJ
WouterJ added a note

That's even a better reason to check it, isn't it?

@xabbuh Collaborator
xabbuh added a note

But we use the schema definition to validate the XML before.

@stof Collaborator
stof added a note

yeah, I forgot that the XSD already checked it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...mponent/Routing/Loader/schema/routing/routing-1.0.xsd
@@ -55,6 +55,18 @@
<xsd:attribute name="methods" type="xsd:string" />
</xsd:complexType>
+ <xsd:complexType name="default" mixed="true">
+ <xsd:choice minOccurs="0" maxOccurs="unbounded">
@stof Collaborator
stof added a note

are you sure about maxOccurs="unbounded" ? You have a comment in the code saying that there can only be 1 element in it, which means maxOccurs="1"

@xabbuh Collaborator
xabbuh added a note

Good catch, thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@stof
Collaborator

does this syntax allow to put a null value inside a list or a map ?

@xabbuh
Collaborator

@stof List and maps can now be null.

src/Symfony/Component/Routing/Loader/XmlFileLoader.php
@@ -242,4 +241,101 @@ private function parseConfigs(\DOMElement $node, $path)
return array($defaults, $requirements, $options, $condition);
}
+
+ /**
+ * Parses the "default" elements.
+ *
+ * @param \DOMElement $element The "default" element to parse
+ * @param string $path Full path of the XML file being processed
+ *
+ * @return array|bool|float|int|string|null The parsed value of the "default" element
+ */
+ private function parseDefaultsConfig(\DOMElement $element, $path)
+ {
+ if ($element->hasAttribute('xsi:nil') && 'true' == $element->getAttribute('xsi:nil')) {
@Tobion Collaborator
Tobion added a note

Actually I think we should use http://php.net/manual/en/domelement.getattributens.php to retrieve the attribute in the namespace.

@xabbuh Collaborator
xabbuh added a note

Good idea. Changed it here and in line 292.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/Symfony/Component/Routing/Loader/XmlFileLoader.php
@@ -242,4 +241,101 @@ private function parseConfigs(\DOMElement $node, $path)
return array($defaults, $requirements, $options, $condition);
}
+
+ /**
+ * Parses the "default" elements.
+ *
+ * @param \DOMElement $element The "default" element to parse
+ * @param string $path Full path of the XML file being processed
+ *
+ * @return array|bool|float|int|string|null The parsed value of the "default" element
+ */
+ private function parseDefaultsConfig(\DOMElement $element, $path)
+ {
+ if ($element->hasAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'nil') && 'true' == $element->getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'nil')) {
@Tobion Collaborator
Tobion added a note

what happens when 1 or 0 is used for xsi:nil as a boolean http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#boolean

@xabbuh Collaborator
xabbuh added a note

Thanks, you're right. This didn't work in 2.3 before. I submitted a fix for this (see #11672).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@xabbuh xabbuh referenced this pull request from a commit in xabbuh/symfony
@xabbuh xabbuh fix handling of nullable XML attributes
As @Tobion pointed out in #11394, true and 1 are valid values in
boolean XML attributes. The XmlFileLoader didn't handle 1 values
properly.
6deca28
@xabbuh xabbuh [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.
72a3f87
@xabbuh xabbuh referenced this pull request from a commit in xabbuh/symfony
@xabbuh xabbuh fix handling of nullable XML attributes
As @Tobion pointed out in #11394, true and 1 are valid values in
boolean XML attributes. The XmlFileLoader didn't handle 1 values
properly.
29e452d
@xabbuh xabbuh referenced this pull request from a commit in xabbuh/symfony
@xabbuh xabbuh fix handling of nullable XML attributes
As @Tobion pointed out in #11394, true and 1 are valid values in
boolean XML attributes. The XmlFileLoader didn't handle 1 values
properly.
55eeb89
@xabbuh xabbuh referenced this pull request from a commit in xabbuh/symfony
@xabbuh xabbuh fix handling of nullable XML attributes
As @Tobion pointed out in #11394, true and 1 are valid values in
boolean XML attributes. The XmlFileLoader didn't handle 1 values
properly.
7b4d4b6
@Tobion Tobion referenced this pull request from a commit
@Tobion Tobion bug #11672 [Routing] fix handling of nullable XML attributes (xabbuh)
This PR was merged into the 2.3 branch.

Discussion
----------

[Routing] fix handling of nullable XML attributes

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        |

As @Tobion pointed out in #11394, ``true`` and ``1`` are valid values in boolean XML attributes. The XmlFileLoader didn't handle ``1`` values properly.

Commits
-------

7b4d4b6 fix handling of nullable XML attributes
f262b01
@Tobion
Collaborator

@fabpot considering all other loaders except xml already "supported" array defaults, we have two ways to deal with this
1. "remove" array default support in the other loaders, which will probably be a bc break for some
2. add support for array defaults in xml to be consistent and merge this PR

I'd vote for 2. because its not a bc break and it should not be a problem in php to support arrays. we deprecated apache dumper where it would probably be tedious to support arrays. so thats not a problem. also the xml loader already explicitly supported null, so also supporting other scalars like int makes sense. fabien, what do you prefer?

@fabpot
Owner

The support for non-string values in other formats was not a conscious decision; it just happens to work because there is no restrictions in place. So, I'm still against this change.

@Tobion
Collaborator

Well actually it was, at least for null: #7635

@Tobion
Collaborator

And according to RequestContext::setParameter a parameter can be mixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Aug 14, 2014
  1. @xabbuh

    [Routing] data type support for defaults

    xabbuh authored
    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.
This page is out of date. Refresh to see the latest.
View
8 src/Symfony/Component/Routing/CHANGELOG.md
@@ -1,6 +1,12 @@
CHANGELOG
=========
+2.6.0
+-----
+
+* Added support for `boolean`, `integer`, `float`, `string`, `list` and `map`
+ defaults.
@fabpot Owner
fabpot added a note

You should be clear that this is only for the XML loader.

@xabbuh Collaborator
xabbuh added a note

Done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
2.5.0
-----
@@ -164,6 +170,6 @@ CHANGELOG
been used anyway without creating inconsistencies
* [BC BREAK] RouteCollection::remove also removes a route from parent
collections (not only from its children)
- * added ConfigurableRequirementsInterface that allows to disable exceptions
+ * added ConfigurableRequirementsInterface that allows to disable exceptions
(and generate empty URLs instead) when generating a route with an invalid
parameter value
View
119 src/Symfony/Component/Routing/Loader/XmlFileLoader.php
@@ -217,14 +217,13 @@ private function parseConfigs(\DOMElement $node, $path)
$condition = null;
foreach ($node->getElementsByTagNameNS(self::NAMESPACE_URI, '*') as $n) {
@xabbuh Collaborator
xabbuh added a note

Does anyone of you know if there was a special reason to use getElementsByTagNameNs() instead of iterating over the DOMNodeList in $node->childNodes?

@Tobion Collaborator
Tobion added a note

to support namespaces

@xabbuh Collaborator
xabbuh added a note

Isn't that covered by the elements from the childNodes too? It could then need some check for the namespaces afterwards but doesn't require to scan the complete subtree.

@Tobion Collaborator
Tobion added a note

The idea was to ignore elements from another namespace and not throw an exception for unknown tag. So one could extend the loader and xsd to support custom elements, like in FosRestBundle.
I think this won't work with childNodes because it also returns element from other namespaces within the current node.

@xabbuh Collaborator
xabbuh added a note

But throwing an exception contradicts this, doesn't it?

By the way, the new method just test for the elements local name. Should I add an additional check for the element's namespace?

@Tobion Collaborator
Tobion added a note

only throw for unkown elements in same namespace

@xabbuh Collaborator
xabbuh added a note

Sorry, of course you're right. I updated the new code to also ignore elements with a different namespace.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ if ($node !== $n->parentNode) {
+ continue;
+ }
+
switch ($n->localName) {
case 'default':
- if ($n->hasAttribute('xsi:nil') && 'true' == $n->getAttribute('xsi:nil')) {
- $defaults[$n->getAttribute('key')] = null;
- } else {
- $defaults[$n->getAttribute('key')] = trim($n->textContent);
- }
-
+ $defaults[$n->getAttribute('key')] = $this->parseDefaultsConfig($n, $path);
break;
case 'requirement':
$requirements[$n->getAttribute('key')] = trim($n->textContent);
@@ -242,4 +241,112 @@ private function parseConfigs(\DOMElement $node, $path)
return array($defaults, $requirements, $options, $condition);
}
+
+ /**
+ * Parses the "default" elements.
+ *
+ * @param \DOMElement $element The "default" element to parse
+ * @param string $path Full path of the XML file being processed
+ *
+ * @return array|bool|float|int|string|null The parsed value of the "default" element
+ */
+ private function parseDefaultsConfig(\DOMElement $element, $path)
+ {
+ if ($this->isElementValueNull($element)) {
+ return;
+ }
+
+ // Check for existing element nodes in the default element. There can
+ // only be a single element inside a default element. So this element
+ // (if one was found) can safely be returned.
+ foreach ($element->childNodes as $child) {
+ if (!$child instanceof \DOMElement) {
+ continue;
+ }
+
+ if (self::NAMESPACE_URI !== $child->namespaceURI) {
+ continue;
+ }
+
+ return $this->parseDefaultNode($child, $path);
+ }
+
+ // If the default element doesn't contain a nested "boolean", "integer",
+ // "float", "string", "list" or "map" element, the element contents will
+ // be treated as the string value of the associated default option.
+ return trim($element->textContent);
+ }
+
+ /**
+ * Recursively parses the value of a "default" element.
+ *
+ * @param \DOMElement $node The node value
+ * @param string $path Full path of the XML file being processed
+ *
+ * @return array|bool|float|int|string The parsed value
+ *
+ * @throws \InvalidArgumentException when the XML is invalid
+ */
+ private function parseDefaultNode(\DOMElement $node, $path)
+ {
+ if ($this->isElementValueNull($node)) {
+ return;
+ }
+
+ switch ($node->localName) {
+ case 'boolean':
+ return 'true' === trim($node->nodeValue) || '1' === trim($node->nodeValue);
+ case 'integer':
+ return (int) trim($node->nodeValue);
+ case 'float':
+ return (float) trim($node->nodeValue);
+ case 'string':
+ return trim($node->nodeValue);
+ case 'list':
+ $list = array();
+
+ foreach ($node->childNodes as $element) {
+ if (!$element instanceof \DOMElement) {
+ continue;
+ }
+
+ if (self::NAMESPACE_URI !== $element->namespaceURI) {
+ continue;
+ }
+
+ $list[] = $this->parseDefaultNode($element, $path);
+ }
+
+ return $list;
+ case 'map':
+ $map = array();
+
+ foreach ($node->childNodes as $element) {
+ if (!$element instanceof \DOMElement) {
+ continue;
+ }
+
+ if (self::NAMESPACE_URI !== $element->namespaceURI) {
+ continue;
+ }
+
+ $map[$element->getAttribute('key')] = $this->parseDefaultNode($element, $path);
@stof Collaborator
stof added a note

you should check that the key attribute is set

@xabbuh Collaborator
xabbuh added a note

The key attribute for the map elements is mandatory.

@WouterJ
WouterJ added a note

That's even a better reason to check it, isn't it?

@xabbuh Collaborator
xabbuh added a note

But we use the schema definition to validate the XML before.

@stof Collaborator
stof added a note

yeah, I forgot that the XSD already checked it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ }
+
+ return $map;
+ default:
+ throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "boolean", "integer", "float", "string", "list" or "map".', $node->localName, $path));
+ }
+ }
+
+ private function isElementValueNull(\DOMElement $element)
+ {
+ $namespaceUri = 'http://www.w3.org/2001/XMLSchema-instance';
+
+ if (!$element->hasAttributeNS($namespaceUri, 'nil')) {
+ return false;
+ }
+
+ return 'true' === $element->getAttributeNS($namespaceUri, 'nil') || '1' === $element->getAttributeNS($namespaceUri, 'nil');
+ }
}
View
84 src/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd
@@ -26,7 +26,7 @@
<xsd:group name="configs">
<xsd:choice>
- <xsd:element name="default" nillable="true" type="element" />
+ <xsd:element name="default" nillable="true" type="default" />
<xsd:element name="requirement" type="element" />
@Tobion Collaborator
Tobion added a note

could you also add the required "key" attribute for "requirement" and "option" to the xsd? this does seem to be there yet. but xsd seems to be easy for you :)

@xabbuh Collaborator
xabbuh added a note

This is already be covered by the element definition in the XSD, isn't it?

@Tobion Collaborator
Tobion added a note

Oh true, didn't see that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
<xsd:element name="option" type="element" />
<xsd:element name="condition" type="xsd:string" />
@@ -55,6 +55,18 @@
<xsd:attribute name="methods" type="xsd:string" />
</xsd:complexType>
+ <xsd:complexType name="default" mixed="true">
+ <xsd:choice minOccurs="0" maxOccurs="1">
+ <xsd:element name="boolean" type="xsd:boolean" />
+ <xsd:element name="integer" type="xsd:integer" />
+ <xsd:element name="float" type="xsd:float" />
+ <xsd:element name="string" type="xsd:string" />
+ <xsd:element name="list" type="list" />
+ <xsd:element name="map" type="map" />
+ </xsd:choice>
+ <xsd:attribute name="key" type="xsd:string" use="required" />
+ </xsd:complexType>
+
<xsd:complexType name="element">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
@@ -62,4 +74,74 @@
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
+
+ <xsd:complexType name="list">
+ <xsd:choice minOccurs="0" maxOccurs="unbounded">
+ <xsd:element name="boolean" nillable="true" type="xsd:boolean" />
+ <xsd:element name="integer" nillable="true" type="xsd:integer" />
+ <xsd:element name="float" nillable="true" type="xsd:float" />
+ <xsd:element name="string" nillable="true" type="xsd:string" />
+ <xsd:element name="list" nillable="true" type="list" />
+ <xsd:element name="map" nillable="true" type="map" />
+ </xsd:choice>
+ </xsd:complexType>
+
+ <xsd:complexType name="map">
+ <xsd:choice minOccurs="0" maxOccurs="unbounded">
+ <xsd:element name="boolean" nillable="true" type="map-boolean-entry" />
+ <xsd:element name="integer" nillable="true" type="map-integer-entry" />
+ <xsd:element name="float" nillable="true" type="map-float-entry" />
+ <xsd:element name="string" nillable="true" type="map-string-entry" />
+ <xsd:element name="list" nillable="true" type="map-list-entry" />
+ <xsd:element name="map" nillable="true" type="map-map-entry" />
+ </xsd:choice>
+ </xsd:complexType>
+
+ <xsd:complexType name="map-boolean-entry">
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:boolean">
+ <xsd:attribute name="key" type="xsd:string" use="required" />
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+ <xsd:complexType name="map-integer-entry">
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:integer">
+ <xsd:attribute name="key" type="xsd:string" use="required" />
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+ <xsd:complexType name="map-float-entry">
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:float">
+ <xsd:attribute name="key" type="xsd:string" use="required" />
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+ <xsd:complexType name="map-string-entry">
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <xsd:attribute name="key" type="xsd:string" use="required" />
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+ <xsd:complexType name="map-list-entry">
+ <xsd:complexContent>
+ <xsd:extension base="list">
+ <xsd:attribute name="key" type="xsd:string" use="required" />
+ </xsd:extension>
+ </xsd:complexContent>
+ </xsd:complexType>
+
+ <xsd:complexType name="map-map-entry">
+ <xsd:complexContent>
+ <xsd:extension base="map">
+ <xsd:attribute name="key" type="xsd:string" use="required" />
+ </xsd:extension>
+ </xsd:complexContent>
+ </xsd:complexType>
</xsd:schema>
View
20 src/Symfony/Component/Routing/Tests/Fixtures/list_defaults.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<routes xmlns="http://symfony.com/schema/routing"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://symfony.com/schema/routing
+ http://symfony.com/schema/routing/routing-1.0.xsd">
+
+ <route id="blog" path="/blog">
+ <default key="_controller">
+ <string>AcmeBlogBundle:Blog:index</string>
+ </default>
+ <default key="values">
+ <list>
+ <boolean>true</boolean>
+ <integer>1</integer>
+ <float>3.5</float>
+ <string>foo</string>
+ </list>
+ </default>
+ </route>
+</routes>
View
22 src/Symfony/Component/Routing/Tests/Fixtures/list_in_list_defaults.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<routes xmlns="http://symfony.com/schema/routing"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://symfony.com/schema/routing
+ http://symfony.com/schema/routing/routing-1.0.xsd">
+
+ <route id="blog" path="/blog">
+ <default key="_controller">
+ <string>AcmeBlogBundle:Blog:index</string>
+ </default>
+ <default key="values">
+ <list>
+ <list>
+ <boolean>true</boolean>
+ <integer>1</integer>
+ <float>3.5</float>
+ <string>foo</string>
+ </list>
+ </list>
+ </default>
+ </route>
+</routes>
View
22 src/Symfony/Component/Routing/Tests/Fixtures/list_in_map_defaults.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<routes xmlns="http://symfony.com/schema/routing"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://symfony.com/schema/routing
+ http://symfony.com/schema/routing/routing-1.0.xsd">
+
+ <route id="blog" path="/blog">
+ <default key="_controller">
+ <string>AcmeBlogBundle:Blog:index</string>
+ </default>
+ <default key="values">
+ <map>
+ <list key="list">
+ <boolean>true</boolean>
+ <integer>1</integer>
+ <float>3.5</float>
+ <string>foo</string>
+ </list>
+ </map>
+ </default>
+ </route>
+</routes>
View
22 src/Symfony/Component/Routing/Tests/Fixtures/list_null_values.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<routes xmlns="http://symfony.com/schema/routing"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://symfony.com/schema/routing
+ http://symfony.com/schema/routing/routing-1.0.xsd">
+
+ <route id="blog" path="/blog">
+ <default key="_controller">
+ <string>AcmeBlogBundle:Blog:index</string>
+ </default>
+ <default key="list">
+ <list>
+ <boolean xsi:nil="true" />
+ <integer xsi:nil="true" />
+ <float xsi:nil="1" />
+ <string xsi:nil="true" />
+ <list xsi:nil="true" />
+ <map xsi:nil="true" />
+ </list>
+ </default>
+ </route>
+</routes>
View
20 src/Symfony/Component/Routing/Tests/Fixtures/map_defaults.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<routes xmlns="http://symfony.com/schema/routing"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://symfony.com/schema/routing
+ http://symfony.com/schema/routing/routing-1.0.xsd">
+
+ <route id="blog" path="/blog">
+ <default key="_controller">
+ <string>AcmeBlogBundle:Blog:index</string>
+ </default>
+ <default key="values">
+ <map>
+ <boolean key="public">true</boolean>
+ <integer key="page">1</integer>
+ <float key="price">3.5</float>
+ <string key="title">foo</string>
+ </map>
+ </default>
+ </route>
+</routes>
View
22 src/Symfony/Component/Routing/Tests/Fixtures/map_in_list_defaults.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<routes xmlns="http://symfony.com/schema/routing"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://symfony.com/schema/routing
+ http://symfony.com/schema/routing/routing-1.0.xsd">
+
+ <route id="blog" path="/blog">
+ <default key="_controller">
+ <string>AcmeBlogBundle:Blog:index</string>
+ </default>
+ <default key="values">
+ <list>
+ <map>
+ <boolean key="public">true</boolean>
+ <integer key="page">1</integer>
+ <float key="price">3.5</float>
+ <string key="title">foo</string>
+ </map>
+ </list>
+ </default>
+ </route>
+</routes>
View
22 src/Symfony/Component/Routing/Tests/Fixtures/map_in_map_defaults.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<routes xmlns="http://symfony.com/schema/routing"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://symfony.com/schema/routing
+ http://symfony.com/schema/routing/routing-1.0.xsd">
+
+ <route id="blog" path="/blog">
+ <default key="_controller">
+ <string>AcmeBlogBundle:Blog:index</string>
+ </default>
+ <default key="values">
+ <map>
+ <map key="map">
+ <boolean key="public">true</boolean>
+ <integer key="page">1</integer>
+ <float key="price">3.5</float>
+ <string key="title">foo</string>
+ </map>
+ </map>
+ </default>
+ </route>
+</routes>
View
22 src/Symfony/Component/Routing/Tests/Fixtures/map_null_values.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<routes xmlns="http://symfony.com/schema/routing"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://symfony.com/schema/routing
+ http://symfony.com/schema/routing/routing-1.0.xsd">
+
+ <route id="blog" path="/blog">
+ <default key="_controller">
+ <string>AcmeBlogBundle:Blog:index</string>
+ </default>
+ <default key="map">
+ <map>
+ <boolean key="boolean" xsi:nil="true" />
+ <integer key="integer" xsi:nil="true" />
+ <float key="float" xsi:nil="true" />
+ <string key="string" xsi:nil="1" />
+ <list key="list" xsi:nil="true" />
+ <map key="map" xsi:nil="true" />
+ </map>
+ </default>
+ </route>
+</routes>
View
3  src/Symfony/Component/Routing/Tests/Fixtures/namespaceprefix.xml
@@ -9,5 +9,8 @@
<requirement xmlns="http://symfony.com/schema/routing" key="slug">\w+</requirement>
<r2:requirement xmlns:r2="http://symfony.com/schema/routing" key="_locale">en|fr|de</r2:requirement>
<r:option key="compiler_class">RouteCompiler</r:option>
+ <r:default key="page">
+ <r3:integer xmlns:r3="http://symfony.com/schema/routing">1</r3:integer>
+ </r:default>
</r:route>
</r:routes>
View
33 src/Symfony/Component/Routing/Tests/Fixtures/scalar_defaults.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<routes xmlns="http://symfony.com/schema/routing"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://symfony.com/schema/routing
+ http://symfony.com/schema/routing/routing-1.0.xsd">
+
+ <route id="blog" path="/blog">
+ <default key="_controller">
+ <string>AcmeBlogBundle:Blog:index</string>
+ </default>
+ <default key="slug" xsi:nil="true" />
+ <default key="published">
+ <boolean>true</boolean>
+ </default>
+ <default key="page">
+ <integer>1</integer>
+ </default>
+ <default key="price">
+ <float>3.5</float>
+ </default>
+ <default key="archived">
+ <boolean>false</boolean>
@xabbuh Collaborator
xabbuh added a note

Done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ </default>
+ <default key="free">
+ <boolean>1</boolean>
+ </default>
+ <default key="locked">
+ <boolean>0</boolean>
+ </default>
+ <default key="foo" xsi:nil="true" />
+ <default key="bar" xsi:nil="1" />
+ </route>
+</routes>
View
1  src/Symfony/Component/Routing/Tests/Fixtures/validpattern.xml
@@ -13,7 +13,6 @@
<route id="blog_show_legacy" pattern="/blog/{slug}" host="{locale}.example.com">
<default key="_controller">MyBundle:Blog:show</default>
- <default key="slug" xsi:nil="true" />
<requirement key="_method">GET|POST|put|OpTiOnS</requirement>
<requirement key="_scheme">hTTps</requirement>
<requirement key="locale">\w+</requirement>
View
157 src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php
@@ -66,6 +66,7 @@ public function testLoadWithNamespacePrefix()
$this->assertSame('en|fr|de', $route->getRequirement('_locale'));
$this->assertNull($route->getDefault('slug'));
$this->assertSame('RouteCompiler', $route->getOption('compiler_class'));
+ $this->assertSame(1, $route->getDefault('page'));
}
public function testLoadWithImport()
@@ -121,4 +122,160 @@ public function testDocTypeIsNotAllowed()
$loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
$loader->load('withdoctype.xml');
}
+
+ public function testScalarDataTypeDefaults()
+ {
+ $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+ $routeCollection = $loader->load('scalar_defaults.xml');
+ $route = $routeCollection->get('blog');
+
+ $this->assertSame(
+ array(
+ '_controller' => 'AcmeBlogBundle:Blog:index',
+ 'slug' => null,
+ 'published' => true,
+ 'page' => 1,
+ 'price' => 3.5,
+ 'archived' => false,
+ 'free' => true,
+ 'locked' => false,
+ 'foo' => null,
+ 'bar' => null,
+ ),
+ $route->getDefaults()
+ );
+ }
+
+ public function testListDefaults()
+ {
+ $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+ $routeCollection = $loader->load('list_defaults.xml');
+ $route = $routeCollection->get('blog');
+
+ $this->assertSame(
+ array(
+ '_controller' => 'AcmeBlogBundle:Blog:index',
+ 'values' => array(true, 1, 3.5, 'foo'),
+ ),
+ $route->getDefaults()
+ );
+ }
+
+ public function testListInListDefaults()
+ {
+ $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+ $routeCollection = $loader->load('list_in_list_defaults.xml');
+ $route = $routeCollection->get('blog');
+
+ $this->assertSame(
+ array(
+ '_controller' => 'AcmeBlogBundle:Blog:index',
+ 'values' => array(array(true, 1, 3.5, 'foo')),
+ ),
+ $route->getDefaults()
+ );
+ }
+
+ public function testListInMapDefaults()
+ {
+ $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+ $routeCollection = $loader->load('list_in_map_defaults.xml');
+ $route = $routeCollection->get('blog');
+
+ $this->assertSame(
+ array(
+ '_controller' => 'AcmeBlogBundle:Blog:index',
+ 'values' => array('list' => array(true, 1, 3.5, 'foo')),
+ ),
+ $route->getDefaults()
+ );
+ }
+
+ public function testMapDefaults()
+ {
+ $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+ $routeCollection = $loader->load('map_defaults.xml');
+ $route = $routeCollection->get('blog');
+
+ $this->assertSame(
+ array(
+ '_controller' => 'AcmeBlogBundle:Blog:index',
+ 'values' => array(
+ 'public' => true,
+ 'page' => 1,
+ 'price' => 3.5,
+ 'title' => 'foo',
+ ),
+ ),
+ $route->getDefaults()
+ );
+ }
+
+ public function testMapInListDefaults()
+ {
+ $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+ $routeCollection = $loader->load('map_in_list_defaults.xml');
+ $route = $routeCollection->get('blog');
+
+ $this->assertSame(
+ array(
+ '_controller' => 'AcmeBlogBundle:Blog:index',
+ 'values' => array(array(
+ 'public' => true,
+ 'page' => 1,
+ 'price' => 3.5,
+ 'title' => 'foo',
+ )),
+ ),
+ $route->getDefaults()
+ );
+ }
+
+ public function testMapInMapDefaults()
+ {
+ $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+ $routeCollection = $loader->load('map_in_map_defaults.xml');
+ $route = $routeCollection->get('blog');
+
+ $this->assertSame(
+ array(
+ '_controller' => 'AcmeBlogBundle:Blog:index',
+ 'values' => array('map' => array(
+ 'public' => true,
+ 'page' => 1,
+ 'price' => 3.5,
+ 'title' => 'foo',
+ )),
+ ),
+ $route->getDefaults()
+ );
+ }
+
+ public function testNullValuesInList()
+ {
+ $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+ $routeCollection = $loader->load('list_null_values.xml');
+ $route = $routeCollection->get('blog');
+
+ $this->assertSame(array(null, null, null, null, null, null), $route->getDefault('list'));
+ }
+
+ public function testNullValuesInMap()
+ {
+ $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+ $routeCollection = $loader->load('map_null_values.xml');
+ $route = $routeCollection->get('blog');
+
+ $this->assertSame(
+ array(
+ 'boolean' => null,
+ 'integer' => null,
+ 'float' => null,
+ 'string' => null,
+ 'list' => null,
+ 'map' => null
+ ),
+ $route->getDefault('map')
+ );
+ }
}
Something went wrong with that request. Please try again.