diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index b0d6a031f8..2242d12c7b 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -350,19 +350,37 @@ class Inline switch ($mapping[$i]) { case '[': // nested sequence - $output[$key] = self::parseSequence($mapping, $i); + $value = self::parseSequence($mapping, $i); + // Spec: Keys MUST be unique; first one wins. + // Parser cannot abort this mapping earlier, since lines + // are processed sequentially. + if (!isset($output[$key])) { + $output[$key] = $value; + } $done = true; break; case '{': // nested mapping - $output[$key] = self::parseMapping($mapping, $i); + $value = self::parseMapping($mapping, $i); + // Spec: Keys MUST be unique; first one wins. + // Parser cannot abort this mapping earlier, since lines + // are processed sequentially. + if (!isset($output[$key])) { + $output[$key] = $value; + } $done = true; break; case ':': case ' ': break; default: - $output[$key] = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i); + $value = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i); + // Spec: Keys MUST be unique; first one wins. + // Parser cannot abort this mapping earlier, since lines + // are processed sequentially. + if (!isset($output[$key])) { + $output[$key] = $value; + } $done = true; --$i; } diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 9bde67b57b..9539e36aa3 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -178,18 +178,35 @@ class Parser } elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { // if next line is less indented or equal, then it means that the current value is null if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) { - $data[$key] = null; + // Spec: Keys MUST be unique; first one wins. + // Parser cannot abort this mapping earlier, since lines + // are processed sequentially. + if (!isset($data[$key])) { + $data[$key] = null; + } } else { $c = $this->getRealCurrentLineNb() + 1; $parser = new Parser($c); $parser->refs =& $this->refs; - $data[$key] = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport); + $value = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport); + // Spec: Keys MUST be unique; first one wins. + // Parser cannot abort this mapping earlier, since lines + // are processed sequentially. + if (!isset($data[$key])) { + $data[$key] = $value; + } } } else { if ($isInPlace) { $data = $this->refs[$isInPlace]; } else { - $data[$key] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport); + $value = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport);; + // Spec: Keys MUST be unique; first one wins. + // Parser cannot abort this mapping earlier, since lines + // are processed sequentially. + if (!isset($data[$key])) { + $data[$key] = $value; + } } } } else { diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 2335efc559..a4db960729 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -508,6 +508,53 @@ EOF ); } + /** + * > It is an error for two equal keys to appear in the same mapping node. + * > In such a case the YAML processor may continue, ignoring the second + * > `key: value` pair and issuing an appropriate warning. This strategy + * > preserves a consistent information model for one-pass and random access + * > applications. + * + * @see http://yaml.org/spec/1.2/spec.html#id2759572 + * @see http://yaml.org/spec/1.1/#id932806 + * + * @covers \Symfony\Component\Yaml\Parser::parse + */ + public function testMappingDuplicateKeyBlock() + { + $input = << array( + 'child' => 'first', + ), + ); + $this->assertSame($expected, Yaml::parse($input)); + } + + /** + * @covers \Symfony\Component\Yaml\Inline::parseMapping + */ + public function testMappingDuplicateKeyFlow() + { + $input = << array( + 'child' => 'first', + ), + ); + $this->assertSame($expected, Yaml::parse($input)); + } + public function testEmptyValue() { $input = <<