diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 8ffa8ccf64..93b2fca4d7 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -491,6 +491,59 @@ class Parser return true; } + /** + * Substitute from alias where is used a << + * + * @param mixed $values Parsed YAML in which aliases are not substituted + * + * @return mixed YAML with substituted aliases + * + * @throws Exception\ParseException When indentation problem are detected + */ + private function substituteAliases($values) + { + if (is_array($values)) { + $keys = array(); + foreach ($values as $key => $value) { + if ($key ==='<<' && preg_grep('/^\*.+/', (array) $value) === array_values((array) $value)) { + $values[$key] = array(); + foreach ((array) $value as $ref) { + $refName = substr($ref, 1); + if (!array_key_exists($refName, $this->refs)) { + throw new ParseException( + sprintf('Reference "%s" does not exist.', $refName), + $this->getRealCurrentLineNb() + 1, + $this->currentLine + ); + } + + $keys = array_merge( + $keys, + array_diff(array_keys($this->refs[$refName]), $keys) + ); + $values[$key] = array_replace($this->refs[$refName], $values[$key]); + } + } elseif (!isset($result[$key]) || is_array($result[$key])) { + $keys[] = $key; + $values[$key] = $this->substituteAliases($value); + } + } + + if (isset($values['<<'])) { + $values = array_replace($values['<<'], $values); + unset($values['<<']); + uksort( + $values, + function ($a, $b) use ($keys) { + return array_search($a, $keys, true) - array_search($b, $keys, true); + } + ); + } + } + + return $values; + } + /** * Parses a YAML value. * @@ -526,7 +579,7 @@ class Parser } try { - return Inline::parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs); + return $this->substituteAliases(Inline::parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs)); } catch (ParseException $e) { $e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setSnippet($this->currentLine); diff --git a/src/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml b/src/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml index 59f6125141..a37eca2e3c 100644 --- a/src/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml +++ b/src/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml @@ -22,6 +22,7 @@ yaml: | foo: bar foo: ignore bar: foo + bar_inline: {a: before, d: other, <<: *foo, b: new, x: Oren, c: { foo: bar, foo: ignore, bar: foo}} duplicate: foo: bar foo: ignore @@ -46,15 +47,20 @@ yaml: | p: 12345 z: <<: *nestedref + head_inline: &head_inline { <<: [ *foo , *dong , *foo2 ] } + recursive_inline: { <<: *head_inline, c: { <<: *foo2 } } php: | array( 'foo' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'e' => 'notnull'), 'bar' => array('a' => 'before', 'd' => 'other', 'e' => null, 'b' => 'new', 'c' => array('foo' => 'bar', 'bar' => 'foo'), 'x' => 'Oren'), + 'bar_inline' => array('a' => 'before', 'd' => 'other', 'b' => 'new', 'c' => array('foo' => 'bar', 'bar' => 'foo'), 'x' => 'Oren'), 'duplicate' => array('foo' => 'bar'), 'foo2' => array('a' => 'Ballmer'), 'ding' => array('fi', 'fei', 'fo', 'fam'), 'check' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'e' => 'notnull', 'fi', 'fei', 'fo', 'fam', 'isit' => 'tested'), 'head' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'e' => 'notnull', 'fi', 'fei', 'fo', 'fam'), 'taz' => array('a' => 'Steve', 'w' => array('p' => 1234)), - 'nested' => array('a' => 'Steve', 'w' => array('p' => 12345), 'd' => 'Doug', 'z' => array('p' => 12345)) + 'nested' => array('a' => 'Steve', 'w' => array('p' => 12345), 'd' => 'Doug', 'z' => array('p' => 12345)), + 'head_inline' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam'), + 'recursive_inline' => array('a' => 'Steve', 'b' => 'Clark', 'c' => array('a' => 'Ballmer'), 'fi', 'fei', 'fo', 'fam') ) diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 9be278f67e..97ce0cac0f 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -1207,6 +1207,18 @@ YAML; $this->assertSame($expected, $this->parser->parse($yaml)); } + + /** + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + * @expectedExceptionMessage Reference "foo" does not exist + */ + public function testEvalRefException() + { + $yaml = <<parser->parse($yaml); + } } class B