Skip to content

Commit 414645c

Browse files
committed
parse merge keys with PARSE_OBJECT_FOR_MAP flag
1 parent 3f4c47c commit 414645c

File tree

2 files changed

+64
-20
lines changed

2 files changed

+64
-20
lines changed

src/Symfony/Component/Yaml/Parser.php

+32-20
Original file line numberDiff line numberDiff line change
@@ -249,19 +249,25 @@ private function doParse($value, $flags)
249249
if ('<<' === $key) {
250250
$mergeNode = true;
251251
$allowOverwrite = true;
252-
if (isset($values['value']) && 0 === strpos($values['value'], '*')) {
252+
if (isset($values['value']) && isset($values['value'][0]) && '*' === $values['value'][0]) {
253253
$refName = substr(rtrim($values['value']), 1);
254254
if (!array_key_exists($refName, $this->refs)) {
255255
throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine);
256256
}
257257

258258
$refValue = $this->refs[$refName];
259259

260-
if (!is_array($refValue)) {
260+
if (is_array($refValue)) {
261+
$data += $refValue; // array union
262+
} elseif ((bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags) && $refValue instanceof \stdClass) {
263+
foreach ($refValue as $refValueKey => $refValueValue) {
264+
if (!isset($data[$refValueKey])) {
265+
$data[$refValueKey] = $refValueValue;
266+
}
267+
}
268+
} else {
261269
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
262270
}
263-
264-
$data += $refValue; // array union
265271
} else {
266272
if (isset($values['value']) && $values['value'] !== '') {
267273
$value = $values['value'];
@@ -270,25 +276,31 @@ private function doParse($value, $flags)
270276
}
271277
$parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $flags);
272278

273-
if (!is_array($parsed)) {
274-
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
275-
}
276-
277-
if (isset($parsed[0])) {
278-
// If the value associated with the merge key is a sequence, then this sequence is expected to contain mapping nodes
279-
// and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier
280-
// in the sequence override keys specified in later mapping nodes.
281-
foreach ($parsed as $parsedItem) {
282-
if (!is_array($parsedItem)) {
283-
throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem);
279+
if (is_array($parsed)) {
280+
if (isset($parsed[0])) {
281+
// If the value associated with the merge key is a sequence, then this sequence is expected to contain mapping nodes
282+
// and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier
283+
// in the sequence override keys specified in later mapping nodes.
284+
foreach ($parsed as $parsedItem) {
285+
if (!is_array($parsedItem)) {
286+
throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem);
287+
}
288+
289+
$data += $parsedItem; // array union
290+
}
291+
} else {
292+
// If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the
293+
// current mapping, unless the key already exists in it.
294+
$data += $parsed; // array union
295+
}
296+
} elseif ((bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags) && $parsed instanceof \stdClass) {
297+
foreach ($parsed as $parsedKey => $parsedValue) {
298+
if (!isset($data[$parsedKey])) {
299+
$data[$parsedKey] = $parsedValue;
284300
}
285-
286-
$data += $parsedItem; // array union
287301
}
288302
} else {
289-
// If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the
290-
// current mapping, unless the key already exists in it.
291-
$data += $parsed; // array union
303+
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
292304
}
293305
}
294306
} elseif (isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]++) *+(?P<value>.*)#u', $values['value'], $matches)) {

src/Symfony/Component/Yaml/Tests/ParserTest.php

+32
Original file line numberDiff line numberDiff line change
@@ -1865,6 +1865,38 @@ public function testPhpConstantTagMappingKeyWithKeysCastToStrings()
18651865

18661866
$this->assertSame($expected, $this->parser->parse($yaml, Yaml::PARSE_CONSTANT | Yaml::PARSE_KEYS_AS_STRINGS));
18671867
}
1868+
1869+
public function testMergeKeysWhenMappingsAreParsedAsObjects()
1870+
{
1871+
$yaml = <<<YAML
1872+
aaa: &A
1873+
bbb: 1
1874+
1875+
ccc:
1876+
ddd: 2
1877+
<<: *A
1878+
1879+
eee:
1880+
fff: 3
1881+
<<:
1882+
ggg: 4
1883+
YAML;
1884+
$expected = (object) array(
1885+
'aaa' => (object) array(
1886+
'bbb' => 1,
1887+
),
1888+
'ccc' => (object) array(
1889+
'ddd' => 2,
1890+
'bbb' => 1,
1891+
),
1892+
'eee' => (object) array(
1893+
'fff' => 3,
1894+
'ggg' => 4,
1895+
),
1896+
);
1897+
1898+
$this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
1899+
}
18681900
}
18691901

18701902
class B

0 commit comments

Comments
 (0)