Skip to content

Commit 464aaf3

Browse files
committed
parse merge keys with PARSE_OBJECT_FOR_MAP flag
1 parent 3f4c47c commit 464aaf3

File tree

2 files changed

+56
-7
lines changed

2 files changed

+56
-7
lines changed

src/Symfony/Component/Yaml/Parser.php

+19-7
Original file line numberDiff line numberDiff line change
@@ -249,27 +249,35 @@ 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 ((bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags) && $refValue instanceof \stdClass) {
261+
$refValue = (array) $refValue;
262+
}
263+
260264
if (!is_array($refValue)) {
261265
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
262266
}
263267

264268
$data += $refValue; // array union
265269
} else {
266-
if (isset($values['value']) && $values['value'] !== '') {
270+
if (isset($values['value']) && '' !== $values['value']) {
267271
$value = $values['value'];
268272
} else {
269273
$value = $this->getNextEmbedBlock();
270274
}
271275
$parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $flags);
272276

277+
if ((bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags) && $parsed instanceof \stdClass) {
278+
$parsed = (array) $parsed;
279+
}
280+
273281
if (!is_array($parsed)) {
274282
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
275283
}
@@ -279,6 +287,10 @@ private function doParse($value, $flags)
279287
// and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier
280288
// in the sequence override keys specified in later mapping nodes.
281289
foreach ($parsed as $parsedItem) {
290+
if ((bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags) && $parsedItem instanceof \stdClass) {
291+
$parsedItem = (array) $parsedItem;
292+
}
293+
282294
if (!is_array($parsedItem)) {
283295
throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem);
284296
}
@@ -544,7 +556,7 @@ private function getNextEmbedBlock($indentation = null, $inSequence = false)
544556
$indent = $this->getCurrentLineIndentation();
545557

546558
// terminate all block scalars that are more indented than the current line
547-
if (!empty($blockScalarIndentations) && $indent < $previousLineIndentation && trim($this->currentLine) !== '') {
559+
if (!empty($blockScalarIndentations) && $indent < $previousLineIndentation && '' !== trim($this->currentLine)) {
548560
foreach ($blockScalarIndentations as $key => $blockScalarIndentation) {
549561
if ($blockScalarIndentation >= $indent) {
550562
unset($blockScalarIndentations[$key]);
@@ -680,7 +692,7 @@ private function parseValue($value, $flags, $context)
680692

681693
while ($this->moveToNextLine()) {
682694
// unquoted strings end before the first unindented line
683-
if (null === $quotation && $this->getCurrentLineIndentation() === 0) {
695+
if (null === $quotation && 0 === $this->getCurrentLineIndentation()) {
684696
$this->moveToPreviousLine();
685697

686698
break;
@@ -876,7 +888,7 @@ private function isCurrentLineComment()
876888
//checking explicitly the first char of the trim is faster than loops or strpos
877889
$ltrimmedLine = ltrim($this->currentLine, ' ');
878890

879-
return '' !== $ltrimmedLine && $ltrimmedLine[0] === '#';
891+
return '' !== $ltrimmedLine && '#' === $ltrimmedLine[0];
880892
}
881893

882894
private function isCurrentLineLastLineInDocument()
@@ -902,15 +914,15 @@ private function cleanup($value)
902914

903915
// remove leading comments
904916
$trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count);
905-
if ($count == 1) {
917+
if (1 === $count) {
906918
// items have been removed, update the offset
907919
$this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n");
908920
$value = $trimmedValue;
909921
}
910922

911923
// remove start of the document marker (---)
912924
$trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count);
913-
if ($count == 1) {
925+
if (1 === $count) {
914926
// items have been removed, update the offset
915927
$this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n");
916928
$value = $trimmedValue;

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

+37
Original file line numberDiff line numberDiff line change
@@ -1865,6 +1865,43 @@ 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+
foo: &FOO
1873+
bar: 1
1874+
bar: &BAR
1875+
baz: 2
1876+
<<: *FOO
1877+
baz:
1878+
baz_foo: 3
1879+
<<:
1880+
baz_bar: 4
1881+
foobar:
1882+
bar: ~
1883+
<<: [*FOO, *BAR]
1884+
YAML;
1885+
$expected = (object) array(
1886+
'foo' => (object) array(
1887+
'bar' => 1,
1888+
),
1889+
'bar' => (object) array(
1890+
'baz' => 2,
1891+
'bar' => 1,
1892+
),
1893+
'baz' => (object) array(
1894+
'baz_foo' => 3,
1895+
'baz_bar' => 4,
1896+
),
1897+
'foobar' => (object) array(
1898+
'bar' => null,
1899+
'baz' => 2,
1900+
),
1901+
);
1902+
1903+
$this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
1904+
}
18681905
}
18691906

18701907
class B

0 commit comments

Comments
 (0)