[YAML] resolve variables in inlined YAML
This commit is contained in:
parent
65862c9947
commit
45a5863508
|
@ -32,12 +32,13 @@ class Inline
|
|||
* @param string $value A YAML string
|
||||
* @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
|
||||
* @param bool $objectSupport true if object support is enabled, false otherwise
|
||||
* @param array $references Mapping of variable names to values
|
||||
*
|
||||
* @return array A PHP array representing the YAML string
|
||||
*
|
||||
* @throws ParseException
|
||||
*/
|
||||
public static function parse($value, $exceptionOnInvalidType = false, $objectSupport = false)
|
||||
public static function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $references = array())
|
||||
{
|
||||
self::$exceptionOnInvalidType = $exceptionOnInvalidType;
|
||||
self::$objectSupport = $objectSupport;
|
||||
|
@ -56,15 +57,15 @@ class Inline
|
|||
$i = 0;
|
||||
switch ($value[0]) {
|
||||
case '[':
|
||||
$result = self::parseSequence($value, $i);
|
||||
$result = self::parseSequence($value, $i, $references);
|
||||
++$i;
|
||||
break;
|
||||
case '{':
|
||||
$result = self::parseMapping($value, $i);
|
||||
$result = self::parseMapping($value, $i, $references);
|
||||
++$i;
|
||||
break;
|
||||
default:
|
||||
$result = self::parseScalar($value, null, array('"', "'"), $i);
|
||||
$result = self::parseScalar($value, null, array('"', "'"), $i, true, $references);
|
||||
}
|
||||
|
||||
// some comments are allowed at the end
|
||||
|
@ -184,14 +185,15 @@ class Inline
|
|||
* @param scalar $scalar
|
||||
* @param string $delimiters
|
||||
* @param array $stringDelimiters
|
||||
* @param int &$i
|
||||
* @param bool $evaluate
|
||||
* @param int &$i
|
||||
* @param bool $evaluate
|
||||
* @param array $references
|
||||
*
|
||||
* @return string A YAML string
|
||||
*
|
||||
* @throws ParseException When malformed inline YAML string is parsed
|
||||
*/
|
||||
public static function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true)
|
||||
public static function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true, $references = array())
|
||||
{
|
||||
if (in_array($scalar[$i], $stringDelimiters)) {
|
||||
// quoted scalar
|
||||
|
@ -221,7 +223,7 @@ class Inline
|
|||
}
|
||||
|
||||
if ($evaluate) {
|
||||
$output = self::evaluateScalar($output);
|
||||
$output = self::evaluateScalar($output, $references);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,13 +264,14 @@ class Inline
|
|||
* Parses a sequence to a YAML string.
|
||||
*
|
||||
* @param string $sequence
|
||||
* @param int &$i
|
||||
* @param int &$i
|
||||
* @param array $references
|
||||
*
|
||||
* @return string A YAML string
|
||||
*
|
||||
* @throws ParseException When malformed inline YAML string is parsed
|
||||
*/
|
||||
private static function parseSequence($sequence, &$i = 0)
|
||||
private static function parseSequence($sequence, &$i = 0, $references = array())
|
||||
{
|
||||
$output = array();
|
||||
$len = strlen($sequence);
|
||||
|
@ -279,11 +282,11 @@ class Inline
|
|||
switch ($sequence[$i]) {
|
||||
case '[':
|
||||
// nested sequence
|
||||
$output[] = self::parseSequence($sequence, $i);
|
||||
$output[] = self::parseSequence($sequence, $i, $references);
|
||||
break;
|
||||
case '{':
|
||||
// nested mapping
|
||||
$output[] = self::parseMapping($sequence, $i);
|
||||
$output[] = self::parseMapping($sequence, $i, $references);
|
||||
break;
|
||||
case ']':
|
||||
return $output;
|
||||
|
@ -292,12 +295,14 @@ class Inline
|
|||
break;
|
||||
default:
|
||||
$isQuoted = in_array($sequence[$i], array('"', "'"));
|
||||
$value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i);
|
||||
$value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i, true, $references);
|
||||
|
||||
if (!$isQuoted && false !== strpos($value, ': ')) {
|
||||
// the value can be an array if a reference has been resolved to an array var
|
||||
if (!is_array($value) && !$isQuoted && false !== strpos($value, ': ')) {
|
||||
// embedded mapping?
|
||||
try {
|
||||
$value = self::parseMapping('{'.$value.'}');
|
||||
$pos = 0;
|
||||
$value = self::parseMapping('{'.$value.'}', $pos, $references);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
// no, it's not
|
||||
}
|
||||
|
@ -318,13 +323,14 @@ class Inline
|
|||
* Parses a mapping to a YAML string.
|
||||
*
|
||||
* @param string $mapping
|
||||
* @param int &$i
|
||||
* @param int &$i
|
||||
* @param array $references
|
||||
*
|
||||
* @return string A YAML string
|
||||
*
|
||||
* @throws ParseException When malformed inline YAML string is parsed
|
||||
*/
|
||||
private static function parseMapping($mapping, &$i = 0)
|
||||
private static function parseMapping($mapping, &$i = 0, $references = array())
|
||||
{
|
||||
$output = array();
|
||||
$len = strlen($mapping);
|
||||
|
@ -350,19 +356,19 @@ class Inline
|
|||
switch ($mapping[$i]) {
|
||||
case '[':
|
||||
// nested sequence
|
||||
$output[$key] = self::parseSequence($mapping, $i);
|
||||
$output[$key] = self::parseSequence($mapping, $i, $references);
|
||||
$done = true;
|
||||
break;
|
||||
case '{':
|
||||
// nested mapping
|
||||
$output[$key] = self::parseMapping($mapping, $i);
|
||||
$output[$key] = self::parseMapping($mapping, $i, $references);
|
||||
$done = true;
|
||||
break;
|
||||
case ':':
|
||||
case ' ':
|
||||
break;
|
||||
default:
|
||||
$output[$key] = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i);
|
||||
$output[$key] = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i, true, $references);
|
||||
$done = true;
|
||||
--$i;
|
||||
}
|
||||
|
@ -382,15 +388,31 @@ class Inline
|
|||
* Evaluates scalars and replaces magic values.
|
||||
*
|
||||
* @param string $scalar
|
||||
* @param array $references
|
||||
*
|
||||
* @return string A YAML string
|
||||
*
|
||||
* @throws ParseException when object parsing support was disabled and the parser detected a PHP object
|
||||
*/
|
||||
private static function evaluateScalar($scalar)
|
||||
private static function evaluateScalar($scalar, $references = array())
|
||||
{
|
||||
$scalar = trim($scalar);
|
||||
$scalarLower = strtolower($scalar);
|
||||
|
||||
if (0 === strpos($scalar, '*')) {
|
||||
if (false !== $pos = strpos($scalar, '#')) {
|
||||
$value = substr($scalar, 1, $pos - 2);
|
||||
} else {
|
||||
$value = substr($scalar, 1);
|
||||
}
|
||||
|
||||
if (!array_key_exists($value, $references)) {
|
||||
throw new ParseException(sprintf('Reference "%s" does not exist.', $value));
|
||||
}
|
||||
|
||||
return $references[$value];
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case 'null' === $scalarLower:
|
||||
case '' === $scalar:
|
||||
|
|
|
@ -121,7 +121,7 @@ class Parser
|
|||
$context = 'mapping';
|
||||
|
||||
// force correct settings
|
||||
Inline::parse(null, $exceptionOnInvalidType, $objectSupport);
|
||||
Inline::parse(null, $exceptionOnInvalidType, $objectSupport, $this->refs);
|
||||
try {
|
||||
$key = Inline::parseScalar($values['key']);
|
||||
} catch (ParseException $e) {
|
||||
|
@ -197,7 +197,7 @@ class Parser
|
|||
$lineCount = count($this->lines);
|
||||
if (1 === $lineCount || (2 === $lineCount && empty($this->lines[1]))) {
|
||||
try {
|
||||
$value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport);
|
||||
$value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport, $this->refs);
|
||||
} catch (ParseException $e) {
|
||||
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
|
||||
$e->setSnippet($this->currentLine);
|
||||
|
@ -404,7 +404,7 @@ class Parser
|
|||
}
|
||||
|
||||
try {
|
||||
return Inline::parse($value, $exceptionOnInvalidType, $objectSupport);
|
||||
return Inline::parse($value, $exceptionOnInvalidType, $objectSupport, $this->refs);
|
||||
} catch (ParseException $e) {
|
||||
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
|
||||
$e->setSnippet($this->currentLine);
|
||||
|
|
|
@ -115,6 +115,38 @@ class InlineTest extends \PHPUnit_Framework_TestCase
|
|||
$this->assertSame($expect, Inline::parseScalar($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getDataForParseReferences
|
||||
*/
|
||||
public function testParseReferences($yaml, $expected)
|
||||
{
|
||||
$this->assertSame($expected, Inline::parse($yaml, false, false, array('var' => 'var-value')));
|
||||
}
|
||||
|
||||
public function getDataForParseReferences()
|
||||
{
|
||||
return array(
|
||||
'scalar' => array('*var', 'var-value'),
|
||||
'list' => array('[ *var ]', array('var-value')),
|
||||
'list-in-list' => array('[[ *var ]]', array(array('var-value'))),
|
||||
'map-in-list' => array('[ { key: *var } ]', array(array('key' => 'var-value'))),
|
||||
'embedded-mapping-in-list' => array('[ key: *var ]', array(array('key' => 'var-value'))),
|
||||
'map' => array('{ key: *var }', array('key' => 'var-value')),
|
||||
'list-in-map' => array('{ key: [*var] }', array('key' => array('var-value'))),
|
||||
'map-in-map' => array('{ foo: { bar: *var } }', array('foo' => array('bar' => 'var-value'))),
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseMapReferenceInSequence()
|
||||
{
|
||||
$foo = array(
|
||||
'a' => 'Steve',
|
||||
'b' => 'Clark',
|
||||
'c' => 'Brian',
|
||||
);
|
||||
$this->assertSame(array($foo), Inline::parse('[*foo]', false, false, array('foo' => $foo)));
|
||||
}
|
||||
|
||||
protected function getTestsForParse()
|
||||
{
|
||||
return array(
|
||||
|
|
|
@ -602,6 +602,32 @@ EOT
|
|||
</body>
|
||||
|
||||
footer # comment3
|
||||
EOF
|
||||
));
|
||||
}
|
||||
|
||||
public function testReferenceResolvingInInlineStrings()
|
||||
{
|
||||
$this->assertEquals(array(
|
||||
'var' => 'var-value',
|
||||
'scalar' => 'var-value',
|
||||
'list' => array('var-value'),
|
||||
'list_in_list' => array(array('var-value')),
|
||||
'map_in_list' => array(array('key' => 'var-value')),
|
||||
'embedded_mapping' => array(array('key' => 'var-value')),
|
||||
'map' => array('key' => 'var-value'),
|
||||
'list_in_map' => array('key' => array('var-value')),
|
||||
'map_in_map' => array('foo' => array('bar' => 'var-value')),
|
||||
), Yaml::parse(<<<EOF
|
||||
var: &var var-value
|
||||
scalar: *var
|
||||
list: [ *var ]
|
||||
list_in_list: [[ *var ]]
|
||||
map_in_list: [ { key: *var } ]
|
||||
embedded_mapping: [ key: *var ]
|
||||
map: { key: *var }
|
||||
list_in_map: { key: [*var] }
|
||||
map_in_map: { foo: { bar: *var } }
|
||||
EOF
|
||||
));
|
||||
}
|
||||
|
|
Reference in New Issue