[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 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 $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 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
|
* @return array A PHP array representing the YAML string
|
||||||
*
|
*
|
||||||
* @throws ParseException
|
* @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::$exceptionOnInvalidType = $exceptionOnInvalidType;
|
||||||
self::$objectSupport = $objectSupport;
|
self::$objectSupport = $objectSupport;
|
||||||
@ -56,15 +57,15 @@ class Inline
|
|||||||
$i = 0;
|
$i = 0;
|
||||||
switch ($value[0]) {
|
switch ($value[0]) {
|
||||||
case '[':
|
case '[':
|
||||||
$result = self::parseSequence($value, $i);
|
$result = self::parseSequence($value, $i, $references);
|
||||||
++$i;
|
++$i;
|
||||||
break;
|
break;
|
||||||
case '{':
|
case '{':
|
||||||
$result = self::parseMapping($value, $i);
|
$result = self::parseMapping($value, $i, $references);
|
||||||
++$i;
|
++$i;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$result = self::parseScalar($value, null, array('"', "'"), $i);
|
$result = self::parseScalar($value, null, array('"', "'"), $i, true, $references);
|
||||||
}
|
}
|
||||||
|
|
||||||
// some comments are allowed at the end
|
// some comments are allowed at the end
|
||||||
@ -186,12 +187,13 @@ class Inline
|
|||||||
* @param array $stringDelimiters
|
* @param array $stringDelimiters
|
||||||
* @param int &$i
|
* @param int &$i
|
||||||
* @param bool $evaluate
|
* @param bool $evaluate
|
||||||
|
* @param array $references
|
||||||
*
|
*
|
||||||
* @return string A YAML string
|
* @return string A YAML string
|
||||||
*
|
*
|
||||||
* @throws ParseException When malformed inline YAML string is parsed
|
* @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)) {
|
if (in_array($scalar[$i], $stringDelimiters)) {
|
||||||
// quoted scalar
|
// quoted scalar
|
||||||
@ -221,7 +223,7 @@ class Inline
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($evaluate) {
|
if ($evaluate) {
|
||||||
$output = self::evaluateScalar($output);
|
$output = self::evaluateScalar($output, $references);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,12 +265,13 @@ class Inline
|
|||||||
*
|
*
|
||||||
* @param string $sequence
|
* @param string $sequence
|
||||||
* @param int &$i
|
* @param int &$i
|
||||||
|
* @param array $references
|
||||||
*
|
*
|
||||||
* @return string A YAML string
|
* @return string A YAML string
|
||||||
*
|
*
|
||||||
* @throws ParseException When malformed inline YAML string is parsed
|
* @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();
|
$output = array();
|
||||||
$len = strlen($sequence);
|
$len = strlen($sequence);
|
||||||
@ -279,11 +282,11 @@ class Inline
|
|||||||
switch ($sequence[$i]) {
|
switch ($sequence[$i]) {
|
||||||
case '[':
|
case '[':
|
||||||
// nested sequence
|
// nested sequence
|
||||||
$output[] = self::parseSequence($sequence, $i);
|
$output[] = self::parseSequence($sequence, $i, $references);
|
||||||
break;
|
break;
|
||||||
case '{':
|
case '{':
|
||||||
// nested mapping
|
// nested mapping
|
||||||
$output[] = self::parseMapping($sequence, $i);
|
$output[] = self::parseMapping($sequence, $i, $references);
|
||||||
break;
|
break;
|
||||||
case ']':
|
case ']':
|
||||||
return $output;
|
return $output;
|
||||||
@ -292,12 +295,14 @@ class Inline
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$isQuoted = in_array($sequence[$i], array('"', "'"));
|
$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?
|
// embedded mapping?
|
||||||
try {
|
try {
|
||||||
$value = self::parseMapping('{'.$value.'}');
|
$pos = 0;
|
||||||
|
$value = self::parseMapping('{'.$value.'}', $pos, $references);
|
||||||
} catch (\InvalidArgumentException $e) {
|
} catch (\InvalidArgumentException $e) {
|
||||||
// no, it's not
|
// no, it's not
|
||||||
}
|
}
|
||||||
@ -319,12 +324,13 @@ class Inline
|
|||||||
*
|
*
|
||||||
* @param string $mapping
|
* @param string $mapping
|
||||||
* @param int &$i
|
* @param int &$i
|
||||||
|
* @param array $references
|
||||||
*
|
*
|
||||||
* @return string A YAML string
|
* @return string A YAML string
|
||||||
*
|
*
|
||||||
* @throws ParseException When malformed inline YAML string is parsed
|
* @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();
|
$output = array();
|
||||||
$len = strlen($mapping);
|
$len = strlen($mapping);
|
||||||
@ -350,19 +356,19 @@ class Inline
|
|||||||
switch ($mapping[$i]) {
|
switch ($mapping[$i]) {
|
||||||
case '[':
|
case '[':
|
||||||
// nested sequence
|
// nested sequence
|
||||||
$output[$key] = self::parseSequence($mapping, $i);
|
$output[$key] = self::parseSequence($mapping, $i, $references);
|
||||||
$done = true;
|
$done = true;
|
||||||
break;
|
break;
|
||||||
case '{':
|
case '{':
|
||||||
// nested mapping
|
// nested mapping
|
||||||
$output[$key] = self::parseMapping($mapping, $i);
|
$output[$key] = self::parseMapping($mapping, $i, $references);
|
||||||
$done = true;
|
$done = true;
|
||||||
break;
|
break;
|
||||||
case ':':
|
case ':':
|
||||||
case ' ':
|
case ' ':
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$output[$key] = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i);
|
$output[$key] = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i, true, $references);
|
||||||
$done = true;
|
$done = true;
|
||||||
--$i;
|
--$i;
|
||||||
}
|
}
|
||||||
@ -382,15 +388,31 @@ class Inline
|
|||||||
* Evaluates scalars and replaces magic values.
|
* Evaluates scalars and replaces magic values.
|
||||||
*
|
*
|
||||||
* @param string $scalar
|
* @param string $scalar
|
||||||
|
* @param array $references
|
||||||
*
|
*
|
||||||
* @return string A YAML string
|
* @return string A YAML string
|
||||||
*
|
*
|
||||||
* @throws ParseException when object parsing support was disabled and the parser detected a PHP object
|
* @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);
|
$scalar = trim($scalar);
|
||||||
$scalarLower = strtolower($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) {
|
switch (true) {
|
||||||
case 'null' === $scalarLower:
|
case 'null' === $scalarLower:
|
||||||
case '' === $scalar:
|
case '' === $scalar:
|
||||||
|
@ -121,7 +121,7 @@ class Parser
|
|||||||
$context = 'mapping';
|
$context = 'mapping';
|
||||||
|
|
||||||
// force correct settings
|
// force correct settings
|
||||||
Inline::parse(null, $exceptionOnInvalidType, $objectSupport);
|
Inline::parse(null, $exceptionOnInvalidType, $objectSupport, $this->refs);
|
||||||
try {
|
try {
|
||||||
$key = Inline::parseScalar($values['key']);
|
$key = Inline::parseScalar($values['key']);
|
||||||
} catch (ParseException $e) {
|
} catch (ParseException $e) {
|
||||||
@ -197,7 +197,7 @@ class Parser
|
|||||||
$lineCount = count($this->lines);
|
$lineCount = count($this->lines);
|
||||||
if (1 === $lineCount || (2 === $lineCount && empty($this->lines[1]))) {
|
if (1 === $lineCount || (2 === $lineCount && empty($this->lines[1]))) {
|
||||||
try {
|
try {
|
||||||
$value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport);
|
$value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport, $this->refs);
|
||||||
} catch (ParseException $e) {
|
} catch (ParseException $e) {
|
||||||
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
|
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
|
||||||
$e->setSnippet($this->currentLine);
|
$e->setSnippet($this->currentLine);
|
||||||
@ -404,7 +404,7 @@ class Parser
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return Inline::parse($value, $exceptionOnInvalidType, $objectSupport);
|
return Inline::parse($value, $exceptionOnInvalidType, $objectSupport, $this->refs);
|
||||||
} catch (ParseException $e) {
|
} catch (ParseException $e) {
|
||||||
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
|
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
|
||||||
$e->setSnippet($this->currentLine);
|
$e->setSnippet($this->currentLine);
|
||||||
|
@ -115,6 +115,38 @@ class InlineTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertSame($expect, Inline::parseScalar($value));
|
$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()
|
protected function getTestsForParse()
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
|
@ -602,6 +602,32 @@ EOT
|
|||||||
</body>
|
</body>
|
||||||
|
|
||||||
footer # comment3
|
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
|
EOF
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user