8000 [Yaml] Fail properly when the !php/const tag is used in a mapping key by fancyweb · Pull Request #35208 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Yaml] Fail properly when the !php/const tag is used in a mapping k 8000 ey #35208

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions src/Symfony/Component/Yaml/Inline.php
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,11 @@ private static function dumpArray($value, $flags)
*/
public static function parseScalar($scalar, $flags = 0, $delimiters = null, &$i = 0, $evaluate = true, $references = [], $legacyOmittedKeySupport = false)
{
if (\in_array($scalar[$i], ['"', "'"])) {
if ('' === $scalar = (string) $scalar) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This cast can be removed when merged in upper branches since the parameter is typed, so when false is passed it is converted to ''.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docblock states that $scalar is a string. Can we detect the cases where we pass something else here and terminate there instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I did first was to cast to string before calling this method, to stay compliant with the method contract, but see #35208 (comment).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have to call the method at all if the scalar is not a string?

return '';
}

if (isset($scalar[$i]) && \in_array($scalar[$i], ['"', "'"])) {
// quoted scalar
$output = self::parseQuotedScalar($scalar, $i);

Expand Down Expand Up @@ -513,7 +517,7 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = []
}

if (!$isKeyQuoted) {
$evaluatedKey = self::evaluateScalar($key, $flags, $references);
$evaluatedKey = self::evaluateScalar($key, $flags, $references, true);

if ('' !== $key && $evaluatedKey !== $key && !\is_string($evaluatedKey) && !\is_int($evaluatedKey)) {
@trigger_error(self::getDeprecationMessage('Implicit casting of incompatible mapping keys to strings is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead.'), E_USER_DEPRECATED);
Expand Down Expand Up @@ -611,12 +615,13 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = []
* @param string $scalar
* @param int $flags
* @param array $references
* @param bool $isMappingKey
*
* @return mixed The evaluated YAML string
*
* @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($scalar, $flags, $references = [])
private static function evaluateScalar($scalar, $flags, $references = [], $isMappingKey = false)
{
$scalar = trim($scalar);
$scalarLower = strtolower($scalar);
Expand Down Expand Up @@ -712,6 +717,10 @@ private static function evaluateScalar($scalar, $flags, $references = [])
return null;
case 0 === strpos($scalar, '!php/const'):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The legacy tag does not need to be taken into account since they finish with ":", the mapping key is always the new tag.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this works with the legacy tag, this means to me that having an error during parsing when using the new tag is a bug and we should see how to fix that instead of changing the error message.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't work with the legacy tag. If you use the legacy tag as a key, eg !php/const:MY_CONST: "value", then the parsed key is !php/const (the new one) because : is the key / value separator. So it's impossible to be a in a $isMappingKey case and in a legacy tag that ends with :.

if (self::$constantSupport) {
if ($isMappingKey) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One edge case is if the constant '' is defined (define('', 'foo')). In this case it currently works but the resulting key value is the tag (!php/const), not the evaluated value. So I guess we don't care of breaking this case.

throw new ParseException('The !php/const tag is not supported in a mapping key.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}

$i = 0;
if (\defined($const = self::parseScalar(substr($scalar, 11), 0, null, $i, false))) {
return \constant($const);
Expand Down
25 changes: 25 additions & 0 deletions src/Symfony/Component/Yaml/Tests/InlineTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -797,4 +797,29 @@ public function getTestsForOctalNumbers()
'negative octal number' => [-28, '-034'],
];
}

public function testParseScalarDoesNotFailWithAnEmptyString()
{
$this->assertSame('', Inline::parseScalar(''));
}

/**
* @dataProvider phpConstTagInAMappingKeyThrowsProvider
*/
public function testPhpConstTagInAMappingKeyThrows($extra)
{
$this->expectException(ParseException::class);
$this->expectExceptionMessage('The !php/const tag is not supported in a mapping key at line 1 (near "!php/const").');

Inline::parse(sprintf('{!php/const%s: foo}', $extra), Yaml::PARSE_CONSTANT);
}

public function phpConstTagInAMappingKeyThrowsProvider()
{
return [
[''],
[' '],
[' MyConst'],
];
}
}
0