bug #40923 [Yaml] expose references detected in inline notation structures (xabbuh)
This PR was merged into the 4.4 branch.
Discussion
----------
[Yaml] expose references detected in inline notation structures
| Q | A
| ------------- | ---
| Branch? | 4.4
| Bug fix? | yes
| New feature? | no
| Deprecations? | no
| Tickets | Fix #40823
| License | MIT
| Doc PR |
Commits
-------
6d7d3fb520
expose references detected in inline notation structures
This commit is contained in:
commit
061ac77773
@ -58,7 +58,7 @@ class Inline
|
|||||||
*
|
*
|
||||||
* @throws ParseException
|
* @throws ParseException
|
||||||
*/
|
*/
|
||||||
public static function parse(string $value = null, int $flags = 0, array $references = [])
|
public static function parse(string $value = null, int $flags = 0, array &$references = [])
|
||||||
{
|
{
|
||||||
self::initialize($flags);
|
self::initialize($flags);
|
||||||
|
|
||||||
@ -267,7 +267,7 @@ class Inline
|
|||||||
*
|
*
|
||||||
* @throws ParseException When malformed inline YAML string is parsed
|
* @throws ParseException When malformed inline YAML string is parsed
|
||||||
*/
|
*/
|
||||||
public static function parseScalar(string $scalar, int $flags = 0, array $delimiters = null, int &$i = 0, bool $evaluate = true, array $references = [])
|
public static function parseScalar(string $scalar, int $flags = 0, array $delimiters = null, int &$i = 0, bool $evaluate = true, array &$references = [])
|
||||||
{
|
{
|
||||||
if (\in_array($scalar[$i], ['"', "'"])) {
|
if (\in_array($scalar[$i], ['"', "'"])) {
|
||||||
// quoted scalar
|
// quoted scalar
|
||||||
@ -343,7 +343,7 @@ class Inline
|
|||||||
*
|
*
|
||||||
* @throws ParseException When malformed inline YAML string is parsed
|
* @throws ParseException When malformed inline YAML string is parsed
|
||||||
*/
|
*/
|
||||||
private static function parseSequence(string $sequence, int $flags, int &$i = 0, array $references = []): array
|
private static function parseSequence(string $sequence, int $flags, int &$i = 0, array &$references = []): array
|
||||||
{
|
{
|
||||||
$output = [];
|
$output = [];
|
||||||
$len = \strlen($sequence);
|
$len = \strlen($sequence);
|
||||||
@ -385,6 +385,11 @@ class Inline
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (\is_string($value) && '' !== $value && '&' === $value[0] && Parser::preg_match(Parser::REFERENCE_PATTERN, $value, $matches)) {
|
||||||
|
$references[$matches['ref']] = $matches['value'];
|
||||||
|
$value = $matches['value'];
|
||||||
|
}
|
||||||
|
|
||||||
--$i;
|
--$i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,7 +412,7 @@ class Inline
|
|||||||
*
|
*
|
||||||
* @throws ParseException When malformed inline YAML string is parsed
|
* @throws ParseException When malformed inline YAML string is parsed
|
||||||
*/
|
*/
|
||||||
private static function parseMapping(string $mapping, int $flags, int &$i = 0, array $references = [])
|
private static function parseMapping(string $mapping, int $flags, int &$i = 0, array &$references = [])
|
||||||
{
|
{
|
||||||
$output = [];
|
$output = [];
|
||||||
$len = \strlen($mapping);
|
$len = \strlen($mapping);
|
||||||
@ -433,14 +438,14 @@ class Inline
|
|||||||
// key
|
// key
|
||||||
$offsetBeforeKeyParsing = $i;
|
$offsetBeforeKeyParsing = $i;
|
||||||
$isKeyQuoted = \in_array($mapping[$i], ['"', "'"], true);
|
$isKeyQuoted = \in_array($mapping[$i], ['"', "'"], true);
|
||||||
$key = self::parseScalar($mapping, $flags, [':', ' '], $i, false, []);
|
$key = self::parseScalar($mapping, $flags, [':', ' '], $i, false);
|
||||||
|
|
||||||
if ($offsetBeforeKeyParsing === $i) {
|
if ($offsetBeforeKeyParsing === $i) {
|
||||||
throw new ParseException('Missing mapping key.', self::$parsedLineNumber + 1, $mapping);
|
throw new ParseException('Missing mapping key.', self::$parsedLineNumber + 1, $mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('!php/const' === $key) {
|
if ('!php/const' === $key) {
|
||||||
$key .= ' '.self::parseScalar($mapping, $flags, [':'], $i, false, []);
|
$key .= ' '.self::parseScalar($mapping, $flags, [':'], $i, false);
|
||||||
$key = self::evaluateScalar($key, $flags);
|
$key = self::evaluateScalar($key, $flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -522,6 +527,11 @@ class Inline
|
|||||||
if ('<<' === $key) {
|
if ('<<' === $key) {
|
||||||
$output += $value;
|
$output += $value;
|
||||||
} elseif ($allowOverwrite || !isset($output[$key])) {
|
} elseif ($allowOverwrite || !isset($output[$key])) {
|
||||||
|
if (\is_string($value) && '' !== $value && '&' === $value[0] && Parser::preg_match(Parser::REFERENCE_PATTERN, $value, $matches)) {
|
||||||
|
$references[$matches['ref']] = $matches['value'];
|
||||||
|
$value = $matches['value'];
|
||||||
|
}
|
||||||
|
|
||||||
if (null !== $tag) {
|
if (null !== $tag) {
|
||||||
$output[$key] = new TaggedValue($tag, $value);
|
$output[$key] = new TaggedValue($tag, $value);
|
||||||
} else {
|
} else {
|
||||||
@ -548,7 +558,7 @@ class Inline
|
|||||||
*
|
*
|
||||||
* @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved
|
* @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved
|
||||||
*/
|
*/
|
||||||
private static function evaluateScalar(string $scalar, int $flags, array $references = [])
|
private static function evaluateScalar(string $scalar, int $flags, array &$references = [])
|
||||||
{
|
{
|
||||||
$scalar = trim($scalar);
|
$scalar = trim($scalar);
|
||||||
$scalarLower = strtolower($scalar);
|
$scalarLower = strtolower($scalar);
|
||||||
|
@ -25,6 +25,7 @@ class Parser
|
|||||||
{
|
{
|
||||||
public const TAG_PATTERN = '(?P<tag>![\w!.\/:-]+)';
|
public const TAG_PATTERN = '(?P<tag>![\w!.\/:-]+)';
|
||||||
public const BLOCK_SCALAR_HEADER_PATTERN = '(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments> +#.*)?';
|
public const BLOCK_SCALAR_HEADER_PATTERN = '(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments> +#.*)?';
|
||||||
|
public const REFERENCE_PATTERN = '#^&(?P<ref>[^ ]++) *+(?P<value>.*)#u';
|
||||||
|
|
||||||
private $filename;
|
private $filename;
|
||||||
private $offset = 0;
|
private $offset = 0;
|
||||||
@ -158,7 +159,7 @@ class Parser
|
|||||||
}
|
}
|
||||||
$context = 'sequence';
|
$context = 'sequence';
|
||||||
|
|
||||||
if (isset($values['value']) && '&' === $values['value'][0] && self::preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
|
if (isset($values['value']) && '&' === $values['value'][0] && self::preg_match(self::REFERENCE_PATTERN, $values['value'], $matches)) {
|
||||||
$isRef = $matches['ref'];
|
$isRef = $matches['ref'];
|
||||||
$this->refsBeingParsed[] = $isRef;
|
$this->refsBeingParsed[] = $isRef;
|
||||||
$values['value'] = $matches['value'];
|
$values['value'] = $matches['value'];
|
||||||
@ -296,7 +297,7 @@ class Parser
|
|||||||
$data += $parsed; // array union
|
$data += $parsed; // array union
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif ('<<' !== $key && isset($values['value']) && '&' === $values['value'][0] && self::preg_match('#^&(?P<ref>[^ ]++) *+(?P<value>.*)#u', $values['value'], $matches)) {
|
} elseif ('<<' !== $key && isset($values['value']) && '&' === $values['value'][0] && self::preg_match(self::REFERENCE_PATTERN, $values['value'], $matches)) {
|
||||||
$isRef = $matches['ref'];
|
$isRef = $matches['ref'];
|
||||||
$this->refsBeingParsed[] = $isRef;
|
$this->refsBeingParsed[] = $isRef;
|
||||||
$values['value'] = $matches['value'];
|
$values['value'] = $matches['value'];
|
||||||
|
@ -187,7 +187,8 @@ class InlineTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testParseReferences($yaml, $expected)
|
public function testParseReferences($yaml, $expected)
|
||||||
{
|
{
|
||||||
$this->assertSame($expected, Inline::parse($yaml, 0, ['var' => 'var-value']));
|
$references = ['var' => 'var-value'];
|
||||||
|
$this->assertSame($expected, Inline::parse($yaml, 0, $references));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDataForParseReferences()
|
public function getDataForParseReferences()
|
||||||
@ -211,7 +212,8 @@ class InlineTest extends TestCase
|
|||||||
'b' => 'Clark',
|
'b' => 'Clark',
|
||||||
'c' => 'Brian',
|
'c' => 'Brian',
|
||||||
];
|
];
|
||||||
$this->assertSame([$foo], Inline::parse('[*foo]', 0, ['foo' => $foo]));
|
$references = ['foo' => $foo];
|
||||||
|
$this->assertSame([$foo], Inline::parse('[*foo]', 0, $references));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testParseUnquotedAsterisk()
|
public function testParseUnquotedAsterisk()
|
||||||
|
@ -1073,6 +1073,10 @@ EOF
|
|||||||
'map' => ['key' => 'var-value'],
|
'map' => ['key' => 'var-value'],
|
||||||
'list_in_map' => ['key' => ['var-value']],
|
'list_in_map' => ['key' => ['var-value']],
|
||||||
'map_in_map' => ['foo' => ['bar' => 'var-value']],
|
'map_in_map' => ['foo' => ['bar' => 'var-value']],
|
||||||
|
'foo' => ['bar' => 'baz'],
|
||||||
|
'bar' => ['foo' => 'baz'],
|
||||||
|
'baz' => ['foo'],
|
||||||
|
'foobar' => ['foo'],
|
||||||
], Yaml::parse(<<<'EOF'
|
], Yaml::parse(<<<'EOF'
|
||||||
var: &var var-value
|
var: &var var-value
|
||||||
scalar: *var
|
scalar: *var
|
||||||
@ -1083,6 +1087,10 @@ embedded_mapping: [ key: *var ]
|
|||||||
map: { key: *var }
|
map: { key: *var }
|
||||||
list_in_map: { key: [*var] }
|
list_in_map: { key: [*var] }
|
||||||
map_in_map: { foo: { bar: *var } }
|
map_in_map: { foo: { bar: *var } }
|
||||||
|
foo: { bar: &baz baz }
|
||||||
|
bar: { foo: *baz }
|
||||||
|
baz: [ &foo foo ]
|
||||||
|
foobar: [ *foo ]
|
||||||
EOF
|
EOF
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user