[Yaml] detect circular references
This commit is contained in:
parent
9e84e0ff98
commit
b7487f476b
@ -35,6 +35,7 @@ class Parser
|
|||||||
private $refs = array();
|
private $refs = array();
|
||||||
private $skippedLineNumbers = array();
|
private $skippedLineNumbers = array();
|
||||||
private $locallySkippedLineNumbers = array();
|
private $locallySkippedLineNumbers = array();
|
||||||
|
private $refsBeingParsed = array();
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
@ -212,6 +213,7 @@ class Parser
|
|||||||
|
|
||||||
if (isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
|
if (isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
|
||||||
$isRef = $matches['ref'];
|
$isRef = $matches['ref'];
|
||||||
|
$this->refsBeingParsed[] = $isRef;
|
||||||
$values['value'] = $matches['value'];
|
$values['value'] = $matches['value'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,6 +246,7 @@ class Parser
|
|||||||
}
|
}
|
||||||
if ($isRef) {
|
if ($isRef) {
|
||||||
$this->refs[$isRef] = end($data);
|
$this->refs[$isRef] = end($data);
|
||||||
|
array_pop($this->refsBeingParsed);
|
||||||
}
|
}
|
||||||
} elseif (
|
} elseif (
|
||||||
self::preg_match('#^(?P<key>(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?[^ \'"\[\{!].*?)) *\:(\s++(?P<value>.+))?$#u', rtrim($this->currentLine), $values)
|
self::preg_match('#^(?P<key>(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?[^ \'"\[\{!].*?)) *\:(\s++(?P<value>.+))?$#u', rtrim($this->currentLine), $values)
|
||||||
@ -287,6 +290,10 @@ class Parser
|
|||||||
if (isset($values['value'][0]) && '*' === $values['value'][0]) {
|
if (isset($values['value'][0]) && '*' === $values['value'][0]) {
|
||||||
$refName = substr(rtrim($values['value']), 1);
|
$refName = substr(rtrim($values['value']), 1);
|
||||||
if (!array_key_exists($refName, $this->refs)) {
|
if (!array_key_exists($refName, $this->refs)) {
|
||||||
|
if (false !== $pos = array_search($refName, $this->refsBeingParsed, true)) {
|
||||||
|
throw new ParseException(sprintf('Circular reference [%s, %s] detected for reference "%s".', implode(', ', \array_slice($this->refsBeingParsed, $pos)), $refName, $refName), $this->currentLineNb + 1, $this->currentLine, $this->filename);
|
||||||
|
}
|
||||||
|
|
||||||
throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
|
throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,6 +347,7 @@ class Parser
|
|||||||
}
|
}
|
||||||
} elseif ('<<' !== $key && isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]++) *+(?P<value>.*)#u', $values['value'], $matches)) {
|
} elseif ('<<' !== $key && isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]++) *+(?P<value>.*)#u', $values['value'], $matches)) {
|
||||||
$isRef = $matches['ref'];
|
$isRef = $matches['ref'];
|
||||||
|
$this->refsBeingParsed[] = $isRef;
|
||||||
$values['value'] = $matches['value'];
|
$values['value'] = $matches['value'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,6 +403,7 @@ class Parser
|
|||||||
}
|
}
|
||||||
if ($isRef) {
|
if ($isRef) {
|
||||||
$this->refs[$isRef] = $data[$key];
|
$this->refs[$isRef] = $data[$key];
|
||||||
|
array_pop($this->refsBeingParsed);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// multiple documents are not supported
|
// multiple documents are not supported
|
||||||
@ -500,6 +509,7 @@ class Parser
|
|||||||
$parser->totalNumberOfLines = $this->totalNumberOfLines;
|
$parser->totalNumberOfLines = $this->totalNumberOfLines;
|
||||||
$parser->skippedLineNumbers = $skippedLineNumbers;
|
$parser->skippedLineNumbers = $skippedLineNumbers;
|
||||||
$parser->refs = &$this->refs;
|
$parser->refs = &$this->refs;
|
||||||
|
$parser->refsBeingParsed = $this->refsBeingParsed;
|
||||||
|
|
||||||
return $parser->doParse($yaml, $flags);
|
return $parser->doParse($yaml, $flags);
|
||||||
}
|
}
|
||||||
@ -689,6 +699,10 @@ class Parser
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!array_key_exists($value, $this->refs)) {
|
if (!array_key_exists($value, $this->refs)) {
|
||||||
|
if (false !== $pos = array_search($value, $this->refsBeingParsed, true)) {
|
||||||
|
throw new ParseException(sprintf('Circular reference [%s, %s] detected for reference "%s".', implode(', ', \array_slice($this->refsBeingParsed, $pos)), $value, $value), $this->currentLineNb + 1, $this->currentLine, $this->filename);
|
||||||
|
}
|
||||||
|
|
||||||
throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine, $this->filename);
|
throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine, $this->filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2177,6 +2177,48 @@ EOE;
|
|||||||
$this->parser->parse($yaml);
|
$this->parser->parse($yaml);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider circularReferenceProvider
|
||||||
|
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
|
||||||
|
* @expectedExceptionMessage Circular reference [foo, bar, foo] detected
|
||||||
|
*/
|
||||||
|
public function testDetectCircularReferences($yaml)
|
||||||
|
{
|
||||||
|
$this->parser->parse($yaml, Yaml::PARSE_CUSTOM_TAGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function circularReferenceProvider()
|
||||||
|
{
|
||||||
|
$tests = array();
|
||||||
|
|
||||||
|
$yaml = <<<YAML
|
||||||
|
foo:
|
||||||
|
- &foo
|
||||||
|
- &bar
|
||||||
|
bar: foobar
|
||||||
|
baz: *foo
|
||||||
|
YAML;
|
||||||
|
$tests['sequence'] = array($yaml);
|
||||||
|
|
||||||
|
$yaml = <<<YAML
|
||||||
|
foo: &foo
|
||||||
|
bar: &bar
|
||||||
|
foobar: baz
|
||||||
|
baz: *foo
|
||||||
|
YAML;
|
||||||
|
$tests['mapping'] = array($yaml);
|
||||||
|
|
||||||
|
$yaml = <<<YAML
|
||||||
|
foo: &foo
|
||||||
|
bar: &bar
|
||||||
|
foobar: baz
|
||||||
|
<<: *foo
|
||||||
|
YAML;
|
||||||
|
$tests['mapping with merge key'] = array($yaml);
|
||||||
|
|
||||||
|
return $tests;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider indentedMappingData
|
* @dataProvider indentedMappingData
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user