diff --git a/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php b/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php index 3d103e215c..617dd5c3fa 100644 --- a/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php +++ b/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php @@ -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 { diff --git a/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.php b/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.php index 741c894e33..0c39008d49 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.php @@ -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' => '')), ); } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index b383fa0445..2cce294054 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -521,7 +521,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()) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php index 100a29eeb2..2ccd9a07e4 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php @@ -46,7 +46,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() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services11.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services11.php index a51e8cc9a0..361b352c74 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services11.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services11.php @@ -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 getFooService() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php index 0e5f4dc53e..c7f4ddc967 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php @@ -52,7 +52,7 @@ class ProjectServiceContainer extends Container * This service is shared. * This method always returns the same instance of the service. * - * @return Bar\FooClass A Bar\FooClass instance. + * @return \Bar\FooClass A Bar\FooClass instance. */ protected function getBarService() { @@ -71,7 +71,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() { @@ -88,7 +88,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 getConfiguredServiceService() { @@ -105,7 +105,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 getDecoratedService() { @@ -118,7 +118,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 getDecoratorServiceService() { @@ -131,7 +131,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 getDecoratorServiceWithNameService() { @@ -144,7 +144,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() { @@ -161,7 +161,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() { @@ -174,7 +174,7 @@ class ProjectServiceContainer extends Container * This service is shared. * This method always returns the same instance of the service. * - * @return Bar\FooClass A Bar\FooClass instance. + * @return \Bar\FooClass A Bar\FooClass instance. */ protected function getFooService() { @@ -227,7 +227,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() { @@ -244,7 +244,7 @@ class ProjectServiceContainer extends Container * This service is shared. * This method always returns the same instance of the service. * - * @return Bar\FooClass A Bar\FooClass instance. + * @return \Bar\FooClass A Bar\FooClass instance. */ protected function getMethodCall1Service() { @@ -298,7 +298,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 ConfClass A ConfClass instance. + * @return \ConfClass A ConfClass instance. */ protected function getConfiguratorServiceService() { @@ -319,7 +319,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() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php index bab12bf2c9..219d4d3415 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -61,7 +61,7 @@ class ProjectServiceContainer extends Container * This service is shared. * This method always returns the same instance of the service. * - * @return Bar\FooClass A Bar\FooClass instance. + * @return \Bar\FooClass A Bar\FooClass instance. */ protected function getBarService() { @@ -80,7 +80,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() { @@ -97,7 +97,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 getConfiguredServiceService() { @@ -117,7 +117,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 getDecoratorServiceService() { @@ -130,7 +130,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 getDecoratorServiceWithNameService() { @@ -143,7 +143,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() { @@ -160,7 +160,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() { @@ -173,7 +173,7 @@ class ProjectServiceContainer extends Container * This service is shared. * This method always returns the same instance of the service. * - * @return Bar\FooClass A Bar\FooClass instance. + * @return \Bar\FooClass A Bar\FooClass instance. */ protected function getFooService() { @@ -197,7 +197,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() { @@ -211,7 +211,7 @@ class ProjectServiceContainer extends Container /** * Gets the 'foo_bar' service. * - * @return Bar\FooClass A Bar\FooClass instance. + * @return \Bar\FooClass A Bar\FooClass instance. */ protected function getFooBarService() { @@ -224,7 +224,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() { @@ -246,7 +246,7 @@ class ProjectServiceContainer extends Container * This service is shared. * This method always returns the same instance of the service. * - * @return Bar\FooClass A Bar\FooClass instance. + * @return \Bar\FooClass A Bar\FooClass instance. */ protected function getMethodCall1Service() { diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php index 9cad463b2b..11519972d8 100755 --- a/src/Symfony/Component/DomCrawler/Crawler.php +++ b/src/Symfony/Component/DomCrawler/Crawler.php @@ -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); + } } } @@ -898,18 +900,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; @@ -917,9 +923,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; } diff --git a/src/Symfony/Component/DomCrawler/Form.php b/src/Symfony/Component/DomCrawler/Form.php index 955b243fdc..5caea539db 100644 --- a/src/Symfony/Component/DomCrawler/Form.php +++ b/src/Symfony/Component/DomCrawler/Form.php @@ -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)); diff --git a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php index 202c5d9b19..81c9773e69 100755 --- a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php @@ -422,8 +422,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 )')); @@ -494,72 +494,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')); } /** @@ -969,6 +1001,7 @@ HTML;
+
diff --git a/src/Symfony/Component/DomCrawler/Tests/FormTest.php b/src/Symfony/Component/DomCrawler/Tests/FormTest.php index 8f18f01086..4e3cad74b1 100644 --- a/src/Symfony/Component/DomCrawler/Tests/FormTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/FormTest.php @@ -802,6 +802,28 @@ class FormTest extends \PHPUnit_Framework_TestCase )); } + public function testDifferentFieldTypesWithSameName() + { + $dom = new \DOMDocument(); + $dom->loadHTML(' + + +
+ + + + + + +
+ + + '); + $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 diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php index 618fc6b4a4..ffc9915fa4 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php @@ -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' diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToDuplicatesTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToDuplicatesTransformerTest.php index 2c5298da50..a8a088a95a 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToDuplicatesTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToDuplicatesTransformerTest.php @@ -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 */ diff --git a/src/Symfony/Component/Routing/Loader/XmlFileLoader.php b/src/Symfony/Component/Routing/Loader/XmlFileLoader.php index 5838b8d341..8a95f51286 100644 --- a/src/Symfony/Component/Routing/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/Routing/Loader/XmlFileLoader.php @@ -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'); + } } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/null_values.xml b/src/Symfony/Component/Routing/Tests/Fixtures/null_values.xml new file mode 100644 index 0000000000..f9e2aa24df --- /dev/null +++ b/src/Symfony/Component/Routing/Tests/Fixtures/null_values.xml @@ -0,0 +1,12 @@ + + + + + + + foo + bar + + diff --git a/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php index e6c80290e1..c488c43bcc 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php @@ -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')); + } } diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php b/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php index abc18b438b..79fa69f8a9 100644 --- a/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php +++ b/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php @@ -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 diff --git a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php index 81cc89536d..8b1ca067ef 100644 --- a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php @@ -17,8 +17,6 @@ use Symfony\Component\Validator\Constraints\Traverse; use Symfony\Component\Validator\Constraints\Valid; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Exception\GroupDefinitionException; -use Symfony\Component\Validator\MetadataInterface as LegacyMetadataInterface; -use Symfony\Component\Validator\PropertyMetadataContainerInterface; use Symfony\Component\Validator\ValidationVisitorInterface; /** @@ -29,7 +27,7 @@ use Symfony\Component\Validator\ValidationVisitorInterface; * @author Bernhard Schussek * @author Fabien Potencier */ -class ClassMetadata extends ElementMetadata implements LegacyMetadataInterface, PropertyMetadataContainerInterface, ClassMetadataInterface +class ClassMetadata extends ElementMetadata implements ClassMetadataInterface { /** * @var string @@ -499,7 +497,7 @@ class ClassMetadata extends ElementMetadata implements LegacyMetadataInterface, /** * Class nodes are never cascaded. * - * @return bool Always returns false. + * {@inheritdoc} */ public function getCascadingStrategy() {