Merge branch '2.3' into 2.4
* 2.3: [DependencyInjection] fix @return anno created by PhpDumper Fixed the phpdoc of the VoterInterface [DoctrineBridge] Fix empty parameter logging in the dbal logger Fixed #11675 ValueToDuplicatesTransformer accept "0" value check for the correct field type fix handling of nullable XML attributes [DomCrawler] fix the axes handling in a bc way Conflicts: src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php
This commit is contained in:
commit
16e3637ec1
|
@ -56,15 +56,15 @@ class DbalLogger implements SQLLogger
|
|||
}
|
||||
|
||||
// non utf-8 strings break json encoding
|
||||
if (!preg_match('#[\p{L}\p{N} ]#u', $params[$index])) {
|
||||
if (!preg_match('//u', $params[$index])) {
|
||||
$params[$index] = self::BINARY_DATA_VALUE;
|
||||
continue;
|
||||
}
|
||||
|
||||
// detect if the too long string must be shorten
|
||||
if (function_exists('mb_detect_encoding') && false !== $encoding = mb_detect_encoding($params[$index])) {
|
||||
if (self::MAX_STRING_LENGTH < mb_strlen($params[$index], $encoding)) {
|
||||
$params[$index] = mb_substr($params[$index], 0, self::MAX_STRING_LENGTH - 6, $encoding).' [...]';
|
||||
if (function_exists('mb_strlen')) {
|
||||
if (self::MAX_STRING_LENGTH < mb_strlen($params[$index], 'UTF-8')) {
|
||||
$params[$index] = mb_substr($params[$index], 0, self::MAX_STRING_LENGTH - 6, 'UTF-8').' [...]';
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -43,7 +43,10 @@ class DbalLoggerTest extends \PHPUnit_Framework_TestCase
|
|||
return array(
|
||||
array('SQL', null, array()),
|
||||
array('SQL', array(), array()),
|
||||
array('SQL', array('foo' => 'bar'), array('foo' => 'bar'))
|
||||
array('SQL', array('foo' => 'bar'), array('foo' => 'bar')),
|
||||
array('SQL', array('foo' => "\x7F\xFF"), array('foo' => DbalLogger::BINARY_DATA_VALUE)),
|
||||
array('SQL', array('foo' => "bar\x7F\xFF"), array('foo' => DbalLogger::BINARY_DATA_VALUE)),
|
||||
array('SQL', array('foo' => ''), array('foo' => '')),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -513,7 +513,7 @@ class PhpDumper extends Dumper
|
|||
if ($definition->isSynthetic()) {
|
||||
$return[] = '@throws RuntimeException always since this service is expected to be injected dynamically';
|
||||
} elseif ($class = $definition->getClass()) {
|
||||
$return[] = sprintf("@return %s A %s instance.", 0 === strpos($class, '%') ? 'object' : $class, $class);
|
||||
$return[] = sprintf("@return %s A %s instance.", 0 === strpos($class, '%') ? 'object' : "\\".$class, $class);
|
||||
} elseif ($definition->getFactoryClass()) {
|
||||
$return[] = sprintf('@return object An instance returned by %s::%s().', $definition->getFactoryClass(), $definition->getFactoryMethod());
|
||||
} elseif ($definition->getFactoryService()) {
|
||||
|
|
|
@ -44,7 +44,7 @@ class ProjectServiceContainer extends Container
|
|||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return stdClass A stdClass instance.
|
||||
* @return \stdClass A stdClass instance.
|
||||
*/
|
||||
protected function getTestService()
|
||||
{
|
||||
|
|
|
@ -42,7 +42,7 @@ class ProjectServiceContainer extends Container
|
|||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return stdClass A stdClass instance.
|
||||
* @return \stdClass A stdClass instance.
|
||||
*/
|
||||
protected function getFooService()
|
||||
{
|
||||
|
|
|
@ -47,7 +47,7 @@ class ProjectServiceContainer extends Container
|
|||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return FooClass A FooClass instance.
|
||||
* @return \FooClass A FooClass instance.
|
||||
*/
|
||||
protected function getBarService()
|
||||
{
|
||||
|
@ -64,7 +64,7 @@ class ProjectServiceContainer extends Container
|
|||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return Baz A Baz instance.
|
||||
* @return \Baz A Baz instance.
|
||||
*/
|
||||
protected function getBazService()
|
||||
{
|
||||
|
@ -81,7 +81,7 @@ class ProjectServiceContainer extends Container
|
|||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return stdClass A stdClass instance.
|
||||
* @return \stdClass A stdClass instance.
|
||||
*/
|
||||
protected function getDependsOnRequestService()
|
||||
{
|
||||
|
@ -98,7 +98,7 @@ class ProjectServiceContainer extends Container
|
|||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return Bar A Bar instance.
|
||||
* @return \Bar A Bar instance.
|
||||
*/
|
||||
protected function getFactoryServiceService()
|
||||
{
|
||||
|
@ -111,7 +111,7 @@ class ProjectServiceContainer extends Container
|
|||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return FooClass A FooClass instance.
|
||||
* @return \FooClass A FooClass instance.
|
||||
*/
|
||||
protected function getFooService()
|
||||
{
|
||||
|
@ -164,7 +164,7 @@ class ProjectServiceContainer extends Container
|
|||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return Foo A Foo instance.
|
||||
* @return \Foo A Foo instance.
|
||||
*/
|
||||
protected function getFooWithInlineService()
|
||||
{
|
||||
|
@ -181,7 +181,7 @@ class ProjectServiceContainer extends Container
|
|||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return FooClass A FooClass instance.
|
||||
* @return \FooClass A FooClass instance.
|
||||
*/
|
||||
protected function getMethodCall1Service()
|
||||
{
|
||||
|
@ -235,7 +235,7 @@ class ProjectServiceContainer extends Container
|
|||
* If you want to be able to request this service from the container directly,
|
||||
* make it public, otherwise you might end up with broken code.
|
||||
*
|
||||
* @return Bar A Bar instance.
|
||||
* @return \Bar A Bar instance.
|
||||
*/
|
||||
protected function getInlinedService()
|
||||
{
|
||||
|
|
|
@ -55,7 +55,7 @@ class ProjectServiceContainer extends Container
|
|||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return FooClass A FooClass instance.
|
||||
* @return \FooClass A FooClass instance.
|
||||
*/
|
||||
protected function getBarService()
|
||||
{
|
||||
|
@ -72,7 +72,7 @@ class ProjectServiceContainer extends Container
|
|||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return Baz A Baz instance.
|
||||
* @return \Baz A Baz instance.
|
||||
*/
|
||||
protected function getBazService()
|
||||
{
|
||||
|
@ -89,7 +89,7 @@ class ProjectServiceContainer extends Container
|
|||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return stdClass A stdClass instance.
|
||||
* @return \stdClass A stdClass instance.
|
||||
*/
|
||||
protected function getDependsOnRequestService()
|
||||
{
|
||||
|
@ -106,7 +106,7 @@ class ProjectServiceContainer extends Container
|
|||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return Bar A Bar instance.
|
||||
* @return \Bar A Bar instance.
|
||||
*/
|
||||
protected function getFactoryServiceService()
|
||||
{
|
||||
|
@ -119,7 +119,7 @@ class ProjectServiceContainer extends Container
|
|||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return FooClass A FooClass instance.
|
||||
* @return \FooClass A FooClass instance.
|
||||
*/
|
||||
protected function getFooService()
|
||||
{
|
||||
|
@ -143,7 +143,7 @@ class ProjectServiceContainer extends Container
|
|||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return BazClass A BazClass instance.
|
||||
* @return \BazClass A BazClass instance.
|
||||
*/
|
||||
protected function getFoo_BazService()
|
||||
{
|
||||
|
@ -157,7 +157,7 @@ class ProjectServiceContainer extends Container
|
|||
/**
|
||||
* Gets the 'foo_bar' service.
|
||||
*
|
||||
* @return FooClass A FooClass instance.
|
||||
* @return \FooClass A FooClass instance.
|
||||
*/
|
||||
protected function getFooBarService()
|
||||
{
|
||||
|
@ -170,7 +170,7 @@ class ProjectServiceContainer extends Container
|
|||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return Foo A Foo instance.
|
||||
* @return \Foo A Foo instance.
|
||||
*/
|
||||
protected function getFooWithInlineService()
|
||||
{
|
||||
|
@ -192,7 +192,7 @@ class ProjectServiceContainer extends Container
|
|||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return FooClass A FooClass instance.
|
||||
* @return \FooClass A FooClass instance.
|
||||
*/
|
||||
protected function getMethodCall1Service()
|
||||
{
|
||||
|
|
|
@ -259,7 +259,9 @@ class Crawler extends \SplObjectStorage
|
|||
public function addNodeList(\DOMNodeList $nodes)
|
||||
{
|
||||
foreach ($nodes as $node) {
|
||||
$this->addNode($node);
|
||||
if ($node instanceof \DOMNode) {
|
||||
$this->addNode($node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -871,18 +873,22 @@ class Crawler extends \SplObjectStorage
|
|||
// BC for Symfony 2.4 and lower were elements were adding in a fake _root parent
|
||||
if (0 === strpos($expression, '/_root/')) {
|
||||
$expression = './'.substr($expression, 7);
|
||||
} elseif (0 === strpos($expression, 'self::*/')) {
|
||||
$expression = './'.substr($expression, 8);
|
||||
}
|
||||
|
||||
// add prefix before absolute element selector
|
||||
if (empty($expression)) {
|
||||
$expression = $nonMatchingExpression;
|
||||
} elseif (0 === strpos($expression, '//')) {
|
||||
$expression = 'descendant-or-self::' . substr($expression, 2);
|
||||
$expression = 'descendant-or-self::'.substr($expression, 2);
|
||||
} elseif (0 === strpos($expression, './/')) {
|
||||
$expression = 'descendant-or-self::' . substr($expression, 3);
|
||||
$expression = 'descendant-or-self::'.substr($expression, 3);
|
||||
} elseif (0 === strpos($expression, './')) {
|
||||
$expression = 'self::' . substr($expression, 2);
|
||||
} elseif ('/' === $expression[0]) {
|
||||
$expression = 'self::'.substr($expression, 2);
|
||||
} elseif (0 === strpos($expression, 'child::')) {
|
||||
$expression = 'self::'.substr($expression, 7);
|
||||
} elseif ('/' === $expression[0] || 0 === strpos($expression, 'self::')) {
|
||||
// the only direct child in Symfony 2.4 and lower is _root, which is already handled previously
|
||||
// so let's drop the expression entirely
|
||||
$expression = $nonMatchingExpression;
|
||||
|
@ -890,9 +896,12 @@ class Crawler extends \SplObjectStorage
|
|||
// '.' is the fake root element in Symfony 2.4 and lower, which is excluded from results
|
||||
$expression = $nonMatchingExpression;
|
||||
} elseif (0 === strpos($expression, 'descendant::')) {
|
||||
$expression = 'descendant-or-self::' . substr($expression, strlen('descendant::'));
|
||||
} elseif (!preg_match('/^(ancestor|ancestor-or-self|attribute|child|descendant-or-self|following|following-sibling|parent|preceding|preceding-sibling|self)::/', $expression)) {
|
||||
$expression = 'self::' .$expression;
|
||||
$expression = 'descendant-or-self::'.substr($expression, strlen('descendant::'));
|
||||
} elseif (preg_match('/^(ancestor|ancestor-or-self|attribute|following|following-sibling|namespace|parent|preceding|preceding-sibling)::/', $expression)) {
|
||||
// the fake root has no parent, preceding or following nodes and also no attributes (even no namespace attributes)
|
||||
$expression = $nonMatchingExpression;
|
||||
} elseif (0 !== strpos($expression, 'descendant-or-self::')) {
|
||||
$expression = 'self::'.$expression;
|
||||
}
|
||||
$expressions[] = $parenthesis.$expression;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
namespace Symfony\Component\DomCrawler;
|
||||
|
||||
use Symfony\Component\DomCrawler\Field\ChoiceFormField;
|
||||
use Symfony\Component\DomCrawler\Field\FormField;
|
||||
|
||||
/**
|
||||
|
@ -460,7 +461,9 @@ class Form extends Link implements \ArrayAccess
|
|||
if ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == strtolower($node->getAttribute('type'))) {
|
||||
$this->set(new Field\ChoiceFormField($node));
|
||||
} elseif ('input' == $nodeName && 'radio' == strtolower($node->getAttribute('type'))) {
|
||||
if ($this->has($node->getAttribute('name'))) {
|
||||
// there may be other fields with the same name that are no choice
|
||||
// fields already registered (see https://github.com/symfony/symfony/issues/11689)
|
||||
if ($this->has($node->getAttribute('name')) && $this->get($node->getAttribute('name')) instanceof ChoiceFormField) {
|
||||
$this->get($node->getAttribute('name'))->addChoice($node);
|
||||
} else {
|
||||
$this->set(new Field\ChoiceFormField($node));
|
||||
|
|
|
@ -400,8 +400,8 @@ EOF
|
|||
$this->assertCount(1, $crawler->filterXPath('//body'));
|
||||
$this->assertCount(1, $crawler->filterXPath('descendant-or-self::body'));
|
||||
$this->assertCount(1, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('./div'), 'A child selection finds only the current div');
|
||||
$this->assertCount(2, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('descendant::div'), 'A descendant selector matches the current div and its child');
|
||||
$this->assertCount(2, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('//div'), 'A descendant selector matches the current div and its child');
|
||||
$this->assertCount(3, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('descendant::div'), 'A descendant selector matches the current div and its child');
|
||||
$this->assertCount(3, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('//div'), 'A descendant selector matches the current div and its child');
|
||||
$this->assertCount(5, $crawler->filterXPath('(//a | //div)//img'));
|
||||
$this->assertCount(7, $crawler->filterXPath('((//a | //div)//img | //ul)'));
|
||||
$this->assertCount(7, $crawler->filterXPath('( ( //a | //div )//img | //ul )'));
|
||||
|
@ -472,72 +472,104 @@ EOF
|
|||
$this->assertSame('Music', $crawler->text());
|
||||
}
|
||||
|
||||
public function testFilterXPathWithFakeRoot()
|
||||
{
|
||||
$crawler = $this->createTestCrawler();
|
||||
$this->assertCount(0, $crawler->filterXPath('.'), '->filterXPath() returns an empty result if the XPath references the fake root node');
|
||||
$this->assertCount(0, $crawler->filterXPath('/_root'), '->filterXPath() returns an empty result if the XPath references the fake root node');
|
||||
$this->assertCount(0, $crawler->filterXPath('self::*'), '->filterXPath() returns an empty result if the XPath references the fake root node');
|
||||
$this->assertCount(0, $crawler->filterXPath('self::_root'), '->filterXPath() returns an empty result if the XPath references the fake root node');
|
||||
}
|
||||
|
||||
public function testFilterXPathWithAncestorAxis()
|
||||
{
|
||||
$crawler = $this->createTestCrawler()->filterXPath('//form');
|
||||
|
||||
$this->assertCount(2, $crawler->filterXPath('ancestor::*'));
|
||||
$this->assertCount(0, $crawler->filterXPath('ancestor::*'), 'The fake root node has no ancestor nodes');
|
||||
}
|
||||
|
||||
public function testFilterXPathWithAncestorOrSelfAxis()
|
||||
{
|
||||
$crawler = $this->createTestCrawler()->filterXPath('//form');
|
||||
|
||||
$this->assertCount(3, $crawler->filterXPath('ancestor-or-self::*'));
|
||||
$this->assertCount(0, $crawler->filterXPath('ancestor-or-self::*'), 'The fake root node has no ancestor nodes');
|
||||
}
|
||||
|
||||
public function testFilterXPathWithAttributeAxis()
|
||||
{
|
||||
$crawler = $this->createTestCrawler()->filterXPath('//form');
|
||||
|
||||
$this->assertCount(2, $crawler->filterXPath('attribute::*'));
|
||||
$this->assertCount(0, $crawler->filterXPath('attribute::*'), 'The fake root node has no attribute nodes');
|
||||
}
|
||||
|
||||
public function testFilterXPathWithAttributeAxisAfterElementAxis()
|
||||
{
|
||||
$this->assertCount(3, $this->createTestCrawler()->filterXPath('//form/button/attribute::*'), '->filterXPath() handles attribute axes properly when they are preceded by an element filtering axis');
|
||||
}
|
||||
|
||||
public function testFilterXPathWithChildAxis()
|
||||
{
|
||||
$crawler = $this->createTestCrawler()->filterXPath('//body');
|
||||
$crawler = $this->createTestCrawler()->filterXPath('//div[@id="parent"]');
|
||||
|
||||
$this->assertCount(2, $crawler->filterXPath('child::input'));
|
||||
$this->assertCount(1, $crawler->filterXPath('child::div'), 'A child selection finds only the current div');
|
||||
}
|
||||
|
||||
public function testFilterXPathWithFollowingAxis()
|
||||
{
|
||||
$crawler = $this->createTestCrawler()->filterXPath('//a');
|
||||
|
||||
$this->assertCount(3, $crawler->filterXPath('following::div'));
|
||||
$this->assertCount(0, $crawler->filterXPath('following::div'), 'The fake root node has no following nodes');
|
||||
}
|
||||
|
||||
public function testFilterXPathWithFollowingSiblingAxis()
|
||||
{
|
||||
$crawler = $this->createTestCrawler()->filterXPath('//a');
|
||||
|
||||
$this->assertCount(2, $crawler->filterXPath('following-sibling::div'));
|
||||
$this->assertCount(0, $crawler->filterXPath('following-sibling::div'), 'The fake root node has no following nodes');
|
||||
}
|
||||
|
||||
public function testFilterXPathWithNamespaceAxis()
|
||||
{
|
||||
$crawler = $this->createTestCrawler()->filterXPath('//button');
|
||||
|
||||
$this->assertCount(0, $crawler->filterXPath('namespace::*'), 'The fake root node has no namespace nodes');
|
||||
}
|
||||
|
||||
public function testFilterXPathWithNamespaceAxisAfterElementAxis()
|
||||
{
|
||||
$crawler = $this->createTestCrawler()->filterXPath('//div[@id="parent"]/namespace::*');
|
||||
|
||||
$this->assertCount(0, $crawler->filterXPath('namespace::*'), 'Namespace axes cannot be requested');
|
||||
}
|
||||
|
||||
public function testFilterXPathWithParentAxis()
|
||||
{
|
||||
$crawler = $this->createTestCrawler()->filterXPath('//button');
|
||||
|
||||
$this->assertEquals('foo', $crawler->filterXPath('parent::*')->attr('action'));
|
||||
$this->assertCount(0, $crawler->filterXPath('parent::*'), 'The fake root node has no parent nodes');
|
||||
}
|
||||
|
||||
public function testFilterXPathWithPrecedingAxis()
|
||||
{
|
||||
$crawler = $this->createTestCrawler()->filterXPath('//form');
|
||||
|
||||
$this->assertCount(13, $crawler->filterXPath('preceding::*'));
|
||||
$this->assertCount(0, $crawler->filterXPath('preceding::*'), 'The fake root node has no preceding nodes');
|
||||
}
|
||||
|
||||
public function testFilterXPathWithPrecedingSiblingAxis()
|
||||
{
|
||||
$crawler = $this->createTestCrawler()->filterXPath('//form');
|
||||
|
||||
$this->assertCount(9, $crawler->filterXPath('preceding-sibling::*'));
|
||||
$this->assertCount(0, $crawler->filterXPath('preceding-sibling::*'), 'The fake root node has no preceding nodes');
|
||||
}
|
||||
|
||||
public function testFilterXPathWithSelfAxes()
|
||||
{
|
||||
$this->assertCount(1, $this->createTestCrawler()->filterXPath('self::*'));
|
||||
$crawler = $this->createTestCrawler()->filterXPath('//a');
|
||||
|
||||
$this->assertCount(0, $crawler->filterXPath('self::a'), 'The fake root node has no "real" element name');
|
||||
$this->assertCount(0, $crawler->filterXPath('self::a/img'), 'The fake root node has no "real" element name');
|
||||
$this->assertCount(9, $crawler->filterXPath('self::*/a'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -947,6 +979,7 @@ HTML;
|
|||
</ul>
|
||||
<div id="parent">
|
||||
<div id="child"></div>
|
||||
<div id="child2" xmlns:foo="http://example.com"></div>
|
||||
</div>
|
||||
<div id="sibling"><img /></div>
|
||||
</body>
|
||||
|
|
|
@ -802,6 +802,28 @@ class FormTest extends \PHPUnit_Framework_TestCase
|
|||
));
|
||||
}
|
||||
|
||||
public function testDifferentFieldTypesWithSameName()
|
||||
{
|
||||
$dom = new \DOMDocument();
|
||||
$dom->loadHTML('
|
||||
<html>
|
||||
<body>
|
||||
<form action="/">
|
||||
<input type="hidden" name="option" value="default">
|
||||
<input type="radio" name="option" value="A">
|
||||
<input type="radio" name="option" value="B">
|
||||
<input type="hidden" name="settings[1]" value="0">
|
||||
<input type="checkbox" name="settings[1]" value="1" id="setting-1">
|
||||
<button>klickme</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
');
|
||||
$form = new Form($dom->getElementsByTagName('form')->item(0), 'http://example.com');
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\DomCrawler\Field\ChoiceFormField', $form->get('option'));
|
||||
}
|
||||
|
||||
protected function getFormFieldMock($name, $value = null)
|
||||
{
|
||||
$field = $this
|
||||
|
|
|
@ -64,7 +64,7 @@ class ValueToDuplicatesTransformer implements DataTransformerInterface
|
|||
$emptyKeys = array();
|
||||
|
||||
foreach ($this->keys as $key) {
|
||||
if (!empty($array[$key])) {
|
||||
if (isset($array[$key]) && '' !== $array[$key] && false !== $array[$key] && array() !== $array[$key]) {
|
||||
if ($array[$key] !== $result) {
|
||||
throw new TransformationFailedException(
|
||||
'All values in the array should be the same'
|
||||
|
|
|
@ -82,6 +82,29 @@ class ValueToDuplicatesTransformerTest extends \PHPUnit_Framework_TestCase
|
|||
$this->assertNull($this->transformer->reverseTransform($input));
|
||||
}
|
||||
|
||||
public function testReverseTransformEmptyArray()
|
||||
{
|
||||
$input = array(
|
||||
'a' => array(),
|
||||
'b' => array(),
|
||||
'c' => array(),
|
||||
);
|
||||
|
||||
$this->assertNull($this->transformer->reverseTransform($input));
|
||||
}
|
||||
|
||||
|
||||
public function testReverseTransformZeroString()
|
||||
{
|
||||
$input = array(
|
||||
'a' => '0',
|
||||
'b' => '0',
|
||||
'c' => '0'
|
||||
);
|
||||
|
||||
$this->assertSame('0', $this->transformer->reverseTransform($input));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
|
||||
*/
|
||||
|
|
|
@ -219,7 +219,7 @@ class XmlFileLoader extends FileLoader
|
|||
foreach ($node->getElementsByTagNameNS(self::NAMESPACE_URI, '*') as $n) {
|
||||
switch ($n->localName) {
|
||||
case 'default':
|
||||
if ($n->hasAttribute('xsi:nil') && 'true' == $n->getAttribute('xsi:nil')) {
|
||||
if ($this->isElementValueNull($n)) {
|
||||
$defaults[$n->getAttribute('key')] = null;
|
||||
} else {
|
||||
$defaults[$n->getAttribute('key')] = trim($n->textContent);
|
||||
|
@ -242,4 +242,15 @@ class XmlFileLoader extends FileLoader
|
|||
|
||||
return array($defaults, $requirements, $options, $condition);
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<?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_show" path="/blog/{slug}">
|
||||
<default key="foo" xsi:nil="true" />
|
||||
<default key="bar" xsi:nil="1" />
|
||||
<default key="foobar" xsi:nil="false">foo</default>
|
||||
<default key="baz" xsi:nil="0">bar</default>
|
||||
</route>
|
||||
</routes>
|
|
@ -121,4 +121,18 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
|||
$loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
|
||||
$loader->load('withdoctype.xml');
|
||||
}
|
||||
|
||||
public function testNullValues()
|
||||
{
|
||||
$loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
|
||||
$routeCollection = $loader->load('null_values.xml');
|
||||
$route = $routeCollection->get('blog_show');
|
||||
|
||||
$this->assertTrue($route->hasDefault('foo'));
|
||||
$this->assertNull($route->getDefault('foo'));
|
||||
$this->assertTrue($route->hasDefault('bar'));
|
||||
$this->assertNull($route->getDefault('bar'));
|
||||
$this->assertEquals('foo', $route->getDefault('foobar'));
|
||||
$this->assertEquals('bar', $route->getDefault('baz'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ interface VoterInterface
|
|||
* ACCESS_GRANTED, ACCESS_DENIED, or ACCESS_ABSTAIN.
|
||||
*
|
||||
* @param TokenInterface $token A TokenInterface instance
|
||||
* @param object $object The object to secure
|
||||
* @param object|null $object The object to secure
|
||||
* @param array $attributes An array of attributes associated with the method being invoked
|
||||
*
|
||||
* @return int either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED
|
||||
|
|
Reference in New Issue