This commit is contained in:
2025-05-12 14:25:25 +02:00
parent ab2db755ef
commit 9e378ca2b7
2719 changed files with 46505 additions and 60181 deletions

View File

@@ -42,19 +42,19 @@ class Parser
/**
* Parses a YAML file into a PHP value.
*
* @param string $filename The path to the YAML file to be parsed
* @param int $flags A bit field of Yaml::PARSE_* constants to customize the YAML parser behavior
* @param string $filename The path to the YAML file to be parsed
* @param int-mask-of<Yaml::PARSE_*> $flags A bit field of Yaml::PARSE_* constants to customize the YAML parser behavior
*
* @throws ParseException If the file could not be read or the YAML is not valid
*/
public function parseFile(string $filename, int $flags = 0): mixed
{
if (!is_file($filename)) {
throw new ParseException(sprintf('File "%s" does not exist.', $filename));
throw new ParseException(\sprintf('File "%s" does not exist.', $filename));
}
if (!is_readable($filename)) {
throw new ParseException(sprintf('File "%s" cannot be read.', $filename));
throw new ParseException(\sprintf('File "%s" cannot be read.', $filename));
}
$this->filename = $filename;
@@ -69,8 +69,8 @@ class Parser
/**
* Parses a YAML string to a PHP value.
*
* @param string $value A YAML string
* @param int $flags A bit field of Yaml::PARSE_* constants to customize the YAML parser behavior
* @param string $value A YAML string
* @param int-mask-of<Yaml::PARSE_*> $flags A bit field of Yaml::PARSE_* constants to customize the YAML parser behavior
*
* @throws ParseException If the YAML is not valid
*/
@@ -230,10 +230,10 @@ class Parser
$refName = substr(rtrim($values['value']), 1);
if (!\array_key_exists($refName, $this->refs)) {
if (false !== $pos = array_search($refName, $this->refsBeingParsed, true)) {
throw new ParseException(sprintf('Circular reference [%s] detected for reference "%s".', implode(', ', array_merge(\array_slice($this->refsBeingParsed, $pos), [$refName])), $refName), $this->currentLineNb + 1, $this->currentLine, $this->filename);
throw new ParseException(\sprintf('Circular reference [%s] detected for reference "%s".', implode(', ', array_merge(\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);
}
$refValue = $this->refs[$refName];
@@ -300,13 +300,17 @@ class Parser
// Spec: Keys MUST be unique; first one wins.
// But overwriting is allowed when a merge node is used in current block.
if ($allowOverwrite || !isset($data[$key])) {
if (!$allowOverwrite && \array_key_exists($key, $data)) {
trigger_deprecation('symfony/yaml', '7.2', 'Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated and will throw a ParseException in 8.0.', $key, $this->getRealCurrentLineNb() + 1);
}
if (null !== $subTag) {
$data[$key] = new TaggedValue($subTag, '');
} else {
$data[$key] = null;
}
} else {
throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), $this->getRealCurrentLineNb() + 1, $this->currentLine);
throw new ParseException(\sprintf('Duplicate key "%s" detected.', $key), $this->getRealCurrentLineNb() + 1, $this->currentLine);
}
} else {
// remember the parsed line number here in case we need it to provide some contexts in error messages below
@@ -321,6 +325,10 @@ class Parser
$data += $value;
} elseif ($allowOverwrite || !isset($data[$key])) {
if (!$allowOverwrite && \array_key_exists($key, $data)) {
trigger_deprecation('symfony/yaml', '7.2', 'Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated and will throw a ParseException in 8.0.', $key, $this->getRealCurrentLineNb() + 1);
}
// Spec: Keys MUST be unique; first one wins.
// But overwriting is allowed when a merge node is used in current block.
if (null !== $subTag) {
@@ -329,7 +337,7 @@ class Parser
$data[$key] = $value;
}
} else {
throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), $realCurrentLineNbKey + 1, $this->currentLine);
throw new ParseException(\sprintf('Duplicate key "%s" detected.', $key), $realCurrentLineNbKey + 1, $this->currentLine);
}
}
} else {
@@ -337,9 +345,13 @@ class Parser
// Spec: Keys MUST be unique; first one wins.
// But overwriting is allowed when a merge node is used in current block.
if ($allowOverwrite || !isset($data[$key])) {
if (!$allowOverwrite && \array_key_exists($key, $data)) {
trigger_deprecation('symfony/yaml', '7.2', 'Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated and will throw a ParseException in 8.0.', $key, $this->getRealCurrentLineNb() + 1);
}
$data[$key] = $value;
} else {
throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), $this->getRealCurrentLineNb() + 1, $this->currentLine);
throw new ParseException(\sprintf('Duplicate key "%s" detected.', $key), $this->getRealCurrentLineNb() + 1, $this->currentLine);
}
}
if ($isRef) {
@@ -494,7 +506,7 @@ class Parser
$data = $object;
}
return empty($data) ? null : $data;
return $data ?: null;
}
private function parseBlock(int $offset, string $yaml, int $flags): mixed
@@ -556,7 +568,7 @@ class Parser
*
* @throws ParseException When indentation problem are detected
*/
private function getNextEmbedBlock(int $indentation = null, bool $inSequence = false): string
private function getNextEmbedBlock(?int $indentation = null, bool $inSequence = false): string
{
$oldLineIndentation = $this->getCurrentLineIndentation();
@@ -633,12 +645,12 @@ class Parser
}
if ($this->isCurrentLineBlank()) {
$data[] = substr($this->currentLine, $newIndent);
$data[] = substr($this->currentLine, $newIndent ?? 0);
continue;
}
if ($indent >= $newIndent) {
$data[] = substr($this->currentLine, $newIndent);
$data[] = substr($this->currentLine, $newIndent ?? 0);
} elseif ($this->isCurrentLineComment()) {
$data[] = $this->currentLine;
} elseif (0 == $indent) {
@@ -706,10 +718,10 @@ class Parser
if (!\array_key_exists($value, $this->refs)) {
if (false !== $pos = array_search($value, $this->refsBeingParsed, true)) {
throw new ParseException(sprintf('Circular reference [%s] detected for reference "%s".', implode(', ', array_merge(\array_slice($this->refsBeingParsed, $pos), [$value])), $value), $this->currentLineNb + 1, $this->currentLine, $this->filename);
throw new ParseException(\sprintf('Circular reference [%s] detected for reference "%s".', implode(', ', array_merge(\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);
}
return $this->refs[$value];
@@ -749,7 +761,7 @@ class Parser
$parsedValue = Inline::parse($this->lexInlineQuotedString($cursor), $flags, $this->refs);
if (isset($this->currentLine[$cursor]) && preg_replace('/\s*(#.*)?$/A', '', substr($this->currentLine, $cursor))) {
throw new ParseException(sprintf('Unexpected characters near "%s".', substr($this->currentLine, $cursor)));
throw new ParseException(\sprintf('Unexpected characters near "%s".', substr($this->currentLine, $cursor)));
}
return $parsedValue;
@@ -834,7 +846,7 @@ class Parser
}
if ($indentation > 0) {
$pattern = sprintf('/^ {%d}(.*)$/', $indentation);
$pattern = \sprintf('/^ {%d}(.*)$/', $indentation);
while (
$notEOF && (
@@ -862,7 +874,7 @@ class Parser
if ($notEOF) {
$blockLines[] = '';
$this->moveToPreviousLine();
} elseif (!$notEOF && !$this->isCurrentLineLastLineInDocument()) {
} elseif (!$this->isCurrentLineLastLineInDocument()) {
$blockLines[] = '';
}
@@ -1038,7 +1050,7 @@ class Parser
*
* @internal
*/
public static function preg_match(string $pattern, string $subject, array &$matches = null, int $flags = 0, int $offset = 0): int
public static function preg_match(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int
{
if (false === $ret = preg_match($pattern, $subject, $matches, $flags, $offset)) {
throw new ParseException(preg_last_error_msg());
@@ -1076,14 +1088,14 @@ class Parser
// Built-in tags
if ($tag && '!' === $tag[0]) {
throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag), $this->getRealCurrentLineNb() + 1, $value, $this->filename);
throw new ParseException(\sprintf('The built-in tag "!%s" is not implemented.', $tag), $this->getRealCurrentLineNb() + 1, $value, $this->filename);
}
if (Yaml::PARSE_CUSTOM_TAGS & $flags) {
return $tag;
}
throw new ParseException(sprintf('Tags support is not enabled. You must use the flag "Yaml::PARSE_CUSTOM_TAGS" to use "%s".', $matches['tag']), $this->getRealCurrentLineNb() + 1, $value, $this->filename);
throw new ParseException(\sprintf('Tags support is not enabled. You must use the flag "Yaml::PARSE_CUSTOM_TAGS" to use "%s".', $matches['tag']), $this->getRealCurrentLineNb() + 1, $value, $this->filename);
}
private function lexInlineQuotedString(int &$cursor = 0): string
@@ -1153,7 +1165,18 @@ class Parser
private function lexUnquotedString(int &$cursor): string
{
$offset = $cursor;
$cursor += strcspn($this->currentLine, '[]{},: ', $cursor);
while ($cursor < strlen($this->currentLine)) {
if (in_array($this->currentLine[$cursor], ['[', ']', '{', '}', ',', ':'], true)) {
break;
}
if (\in_array($this->currentLine[$cursor], [' ', "\t"], true) && '#' === ($this->currentLine[$cursor + 1] ?? '')) {
break;
}
++$cursor;
}
if ($cursor === $offset) {
throw new ParseException('Malformed unquoted YAML string.');
@@ -1162,17 +1185,17 @@ class Parser
return substr($this->currentLine, $offset, $cursor - $offset);
}
private function lexInlineMapping(int &$cursor = 0): string
private function lexInlineMapping(int &$cursor = 0, bool $consumeUntilEol = true): string
{
return $this->lexInlineStructure($cursor, '}');
return $this->lexInlineStructure($cursor, '}', $consumeUntilEol);
}
private function lexInlineSequence(int &$cursor = 0): string
private function lexInlineSequence(int &$cursor = 0, bool $consumeUntilEol = true): string
{
return $this->lexInlineStructure($cursor, ']');
return $this->lexInlineStructure($cursor, ']', $consumeUntilEol);
}
private function lexInlineStructure(int &$cursor, string $closingTag): string
private function lexInlineStructure(int &$cursor, string $closingTag, bool $consumeUntilEol = true): string
{
$value = $this->currentLine[$cursor];
++$cursor;
@@ -1192,15 +1215,19 @@ class Parser
++$cursor;
break;
case '{':
$value .= $this->lexInlineMapping($cursor);
$value .= $this->lexInlineMapping($cursor, false);
break;
case '[':
$value .= $this->lexInlineSequence($cursor);
$value .= $this->lexInlineSequence($cursor, false);
break;
case $closingTag:
$value .= $this->currentLine[$cursor];
++$cursor;
if ($consumeUntilEol && isset($this->currentLine[$cursor]) && ($whitespaces = strspn($this->currentLine, ' ', $cursor) + $cursor) < strlen($this->currentLine) && '#' !== $this->currentLine[$whitespaces]) {
throw new ParseException(sprintf('Unexpected token "%s".', trim(substr($this->currentLine, $cursor))));
}
return $value;
case '#':
break 2;
@@ -1226,7 +1253,7 @@ class Parser
$whitespacesConsumed = 0;
do {
$whitespaceOnlyTokenLength = strspn($this->currentLine, ' ', $cursor);
$whitespaceOnlyTokenLength = strspn($this->currentLine, " \t", $cursor);
$whitespacesConsumed += $whitespaceOnlyTokenLength;
$cursor += $whitespaceOnlyTokenLength;