Skip to content

Commit 8990ac6

Browse files
committed
bug #11677 [YAML] resolve variables in inlined YAML (xabbuh)
This PR was merged into the 2.3 branch. Discussion ---------- [YAML] resolve variables in inlined YAML | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #11665 | License | MIT | Doc PR | #11569 does not resolve variables in inline YAML. Commits ------- 45a5863 [YAML] resolve variables in inlined YAML
2 parents 7510d06 + 45a5863 commit 8990ac6

File tree

4 files changed

+104
-24
lines changed

4 files changed

+104
-24
lines changed

src/Symfony/Component/Yaml/Inline.php

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,13 @@ class Inline
3232
* @param string $value A YAML string
3333
* @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
3434
* @param bool $objectSupport true if object support is enabled, false otherwise
35+
* @param array $references Mapping of variable names to values
3536
*
3637
* @return array A PHP array representing the YAML string
3738
*
3839
* @throws ParseException
3940
*/
40-
public static function parse($value, $exceptionOnInvalidType = false, $objectSupport = false)
41+
public static function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $references = array())
4142
{
4243
self::$exceptionOnInvalidType = $exceptionOnInvalidType;
4344
self::$objectSupport = $objectSupport;
@@ -56,15 +57,15 @@ public static function parse($value, $exceptionOnInvalidType = false, $objectSup
5657
$i = 0;
5758
switch ($value[0]) {
5859
case '[':
59-
$result = self::parseSequence($value, $i);
60+
$result = self::parseSequence($value, $i, $references);
6061
++$i;
6162
break;
6263
case '{':
63-
$result = self::parseMapping($value, $i);
64+
$result = self::parseMapping($value, $i, $references);
6465
++$i;
6566
break;
6667
default:
67-
$result = self::parseScalar($value, null, array('"', "'"), $i);
68+
$result = self::parseScalar($value, null, array('"', "'"), $i, true, $references);
6869
}
6970

7071
// some comments are allowed at the end
@@ -184,14 +185,15 @@ private static function dumpArray($value, $exceptionOnInvalidType, $objectSuppor
184185
* @param scalar $scalar
185186
* @param string $delimiters
186187
* @param array $stringDelimiters
187-
* @param int &$i
188-
* @param bool $evaluate
188+
* @param int &$i
189+
* @param bool $evaluate
190+
* @param array $references
189191
*
190192
* @return string A YAML string
191193
*
192194
* @throws ParseException When malformed inline YAML string is parsed
193195
*/
194-
public static function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true)
196+
public static function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true, $references = array())
195197
{
196198
if (in_array($scalar[$i], $stringDelimiters)) {
197199
// quoted scalar
@@ -221,7 +223,7 @@ public static function parseScalar($scalar, $delimiters = null, $stringDelimiter
221223
}
222224

223225
if ($evaluate) {
224-
$output = self::evaluateScalar($output);
226+
$output = self::evaluateScalar($output, $references);
225227
}
226228
}
227229

@@ -262,13 +264,14 @@ private static function parseQuotedScalar($scalar, &$i)
262264
* Parses a sequence to a YAML string.
263265
*
264266
* @param string $sequence
265-
* @param int &$i
267+
* @param int &$i
268+
* @param array $references
266269
*
267270
* @return string A YAML string
268271
*
269272
* @throws ParseException When malformed inline YAML string is parsed
270273
*/
271-
private static function parseSequence($sequence, &$i = 0)
274+
private static function parseSequence($sequence, &$i = 0, $references = array())
272275
{
273276
$output = array();
274277
$len = strlen($sequence);
@@ -279,11 +282,11 @@ private static function parseSequence($sequence, &$i = 0)
279282
switch ($sequence[$i]) {
280283
case '[':
281284
// nested sequence
282-
$output[] = self::parseSequence($sequence, $i);
285+
$output[] = self::parseSequence($sequence, $i, $references);
283286
break;
284287
case '{':
285288
// nested mapping
286-
$output[] = self::parseMapping($sequence, $i);
289+
$output[] = self::parseMapping($sequence, $i, $references);
287290
break;
288291
case ']':
289292
return $output;
@@ -292,12 +295,14 @@ private static function parseSequence($sequence, &$i = 0)
292295
break;
293296
default:
294297
$isQuoted = in_array($sequence[$i], array('"', "'"));
295-
$value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i);
298+
$value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i, true, $references);
296299

297-
if (!$isQuoted && false !== strpos($value, ': ')) {
300+
// the value can be an array if a reference has been resolved to an array var
301+
if (!is_array($value) && !$isQuoted && false !== strpos($value, ': ')) {
298302
// embedded mapping?
299303
try {
300-
$value = self::parseMapping('{'.$value.'}');
304+
$pos = 0;
305+
$value = self::parseMapping('{'.$value.'}', $pos, $references);
301306
} catch (\InvalidArgumentException $e) {
302307
// no, it's not
303308
}
@@ -318,13 +323,14 @@ private static function parseSequence($sequence, &$i = 0)
318323
* Parses a mapping to a YAML string.
319324
*
320325
* @param string $mapping
321-
* @param int &$i
326+
* @param int &$i
327+
* @param array $references
322328
*
323329
* @return string A YAML string
324330
*
325331
* @throws ParseException When malformed inline YAML string is parsed
326332
*/
327-
private static function parseMapping($mapping, &$i = 0)
333+
private static function parseMapping($mapping, &$i = 0, $references = array())
328334
{
329335
$output = array();
330336
$len = strlen($mapping);
@@ -350,19 +356,19 @@ private static function parseMapping($mapping, &$i = 0)
350356
switch ($mapping[$i]) {
351357
case '[':
352358
// nested sequence
353-
$output[$key] = self::parseSequence($mapping, $i);
359+
$output[$key] = self::parseSequence($mapping, $i, $references);
354360
$done = true;
355361
break;
356362
case '{':
357363
// nested mapping
358-
$output[$key] = self::parseMapping($mapping, $i);
364+
$output[$key] = self::parseMapping($mapping, $i, $references);
359365
$done = true;
360366
break;
361367
case ':':
362368
case ' ':
363369
break;
364370
default:
365-
$output[$key] = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i);
371+
$output[$key] = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i, true, $references);
366372
$done = true;
367373
--$i;
368374
}
@@ -382,15 +388,31 @@ private static function parseMapping($mapping, &$i = 0)
382388
* Evaluates scalars and replaces magic values.
383389
*
384390
* @param string $scalar
391+
* @param array $references
385392
*
386393
* @return string A YAML string
387394
*
388395
* @throws ParseException when object parsing support was disabled and the parser detected a PHP object
389396
*/
390-
private static function evaluateScalar($scalar)
397+
private static function evaluateScalar($scalar, $references = array())
391398
{
392399
$scalar = trim($scalar);
393400
$scalarLower = strtolower($scalar);
401+
402+
if (0 === strpos($scalar, '*')) {
403+
if (false !== $pos = strpos($scalar, '#')) {
404+
$value = substr($scalar, 1, $pos - 2);
405+
} else {
406+
$value = substr($scalar, 1);
407+
}
408+
409+
if (!array_key_exists($value, $references)) {
410+
throw new ParseException(sprintf('Reference "%s" does not exist.', $value));
411+
}
412+
413+
return $references[$value];
414+
}
415+
394416
switch (true) {
395417
case 'null' === $scalarLower:
396418
case '' === $scalar:

src/Symfony/Component/Yaml/Parser.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
121121
$context = 'mapping';
122122

123123
// force correct settings
124-
Inline::parse(null, $exceptionOnInvalidType, $objectSupport);
124+
Inline::parse(null, $exceptionOnInvalidType, $objectSupport, $this->refs);
125125
try {
126126
$key = Inline::parseScalar($values['key']);
127127
} catch (ParseException $e) {
@@ -197,7 +197,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
197197
$lineCount = count($this->lines);
198198
if (1 === $lineCount || (2 === $lineCount && empty($this->lines[1]))) {
199199
try {
200-
$value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport);
200+
$value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport, $this->refs);
201201
} catch (ParseException $e) {
202202
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
203203
$e->setSnippet($this->currentLine);
@@ -404,7 +404,7 @@ private function parseValue($value, $exceptionOnInvalidType, $objectSupport)
404404
}
405405

406406
try {
407-
return Inline::parse($value, $exceptionOnInvalidType, $objectSupport);
407+
return Inline::parse($value, $exceptionOnInvalidType, $objectSupport, $this->refs);
408408
} catch (ParseException $e) {
409409
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
410410
$e->setSnippet($this->currentLine);

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,38 @@ public function testParseScalarWithCorrectlyQuotedStringShouldReturnString()
115115
$this->assertSame($expect, Inline::parseScalar($value));
116116
}
117117

118+
/**
119+
* @dataProvider getDataForParseReferences
120+
*/
121+
public function testParseReferences($yaml, $expected)
122+
{
123+
$this->assertSame($expected, Inline::parse($yaml, false, false, array('var' => 'var-value')));
124+
}
125+
126+
public function getDataForParseReferences()
127+
{
128+
return array(
129+
'scalar' => array('*var', 'var-value'),
130+
'list' => array('[ *var ]', array('var-value')),
131+
'list-in-list' => array('[[ *var ]]', array(array('var-value'))),
132+
'map-in-list' => array('[ { key: *var } ]', array(array('key' => 'var-value'))),
133+
'embedded-mapping-in-list' => array('[ key: *var ]', array(array('key' => 'var-value'))),
134+
'map' => array('{ key: *var }', array('key' => 'var-value')),
135+
'list-in-map' => array('{ key: [*var] }', array('key' => array('var-value'))),
136+
'map-in-map' => array('{ foo: { bar: *var } }', array('foo' => array('bar' => 'var-value'))),
137+
);
138+
}
139+
140+
public function testParseMapReferenceInSequence()
141+
{
142+
$foo = array(
143+
'a' => 'Steve',
144+
'b' => 'Clark',
145+
'c' => 'Brian',
146+
);
147+
$this->assertSame(array($foo), Inline::parse('[*foo]', false, false, array('foo' => $foo)));
148+
}
149+
118150
protected function getTestsForParse()
119151
{
120152
return array(

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,32 @@ public function testNestedFoldedStringBlockWithComments()
602602
</body>
603603
604604
footer # comment3
605+
EOF
606+
));
607+
}
608+
609+
public function testReferenceResolvingInInlineStrings()
610+
{
611+
$this->assertEquals(array(
612+
'var' => 'var-value',
613+
'scalar' => 'var-value',
614+
'list' => array('var-value'),
615+
'list_in_list' => array(array('var-value')),
616+
'map_in_list' => array(array('key' => 'var-value')),
617+
'embedded_mapping' => array(array('key' => 'var-value')),
618+
'map' => array('key' => 'var-value'),
619+
'list_in_map' => array('key' => array('var-value')),
620+
'map_in_map' => array('foo' => array('bar' => 'var-value')),
621+
), Yaml::parse(<<<EOF
622+
var: &var var-value
623+
scalar: *var
624+
list: [ *var ]
625+
list_in_list: [[ *var ]]
626+
map_in_list: [ { key: *var } ]
627+
embedded_mapping: [ key: *var ]
628+
map: { key: *var }
629+
list_in_map: { key: [*var] }
630+
map_in_map: { foo: { bar: *var } }
605631
EOF
606632
));
607633
}

0 commit comments

Comments
 (0)