Deprecated: Constant E_STRICT is deprecated in /home/normanv/www/annuairepro/vendor/symfony/error-handler/ErrorHandler.php on line 58

Deprecated: Constant E_STRICT is deprecated in /home/normanv/www/annuairepro/vendor/symfony/error-handler/ErrorHandler.php on line 76
Symfony Profiler

vendor/symfony/yaml/Parser.php line 97

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\Yaml;
  11. use Symfony\Component\Yaml\Exception\ParseException;
  12. use Symfony\Component\Yaml\Tag\TaggedValue;
  13. /**
  14.  * Parser parses YAML strings to convert them to PHP arrays.
  15.  *
  16.  * @author Fabien Potencier <fabien@symfony.com>
  17.  *
  18.  * @final
  19.  */
  20. class Parser
  21. {
  22.     public const TAG_PATTERN '(?P<tag>![\w!.\/:-]+)';
  23.     public const BLOCK_SCALAR_HEADER_PATTERN '(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments> +#.*)?';
  24.     public const REFERENCE_PATTERN '#^&(?P<ref>[^ ]++) *+(?P<value>.*)#u';
  25.     private $filename;
  26.     private $offset 0;
  27.     private $numberOfParsedLines 0;
  28.     private $totalNumberOfLines;
  29.     private $lines = [];
  30.     private $currentLineNb = -1;
  31.     private $currentLine '';
  32.     private $refs = [];
  33.     private $skippedLineNumbers = [];
  34.     private $locallySkippedLineNumbers = [];
  35.     private $refsBeingParsed = [];
  36.     /**
  37.      * Parses a YAML file into a PHP value.
  38.      *
  39.      * @param string $filename The path to the YAML file to be parsed
  40.      * @param int    $flags    A bit field of Yaml::PARSE_* constants to customize the YAML parser behavior
  41.      *
  42.      * @return mixed
  43.      *
  44.      * @throws ParseException If the file could not be read or the YAML is not valid
  45.      */
  46.     public function parseFile(string $filenameint $flags 0)
  47.     {
  48.         if (!is_file($filename)) {
  49.             throw new ParseException(sprintf('File "%s" does not exist.'$filename));
  50.         }
  51.         if (!is_readable($filename)) {
  52.             throw new ParseException(sprintf('File "%s" cannot be read.'$filename));
  53.         }
  54.         $this->filename $filename;
  55.         try {
  56.             return $this->parse(file_get_contents($filename), $flags);
  57.         } finally {
  58.             $this->filename null;
  59.         }
  60.     }
  61.     /**
  62.      * Parses a YAML string to a PHP value.
  63.      *
  64.      * @param string $value A YAML string
  65.      * @param int    $flags A bit field of Yaml::PARSE_* constants to customize the YAML parser behavior
  66.      *
  67.      * @return mixed
  68.      *
  69.      * @throws ParseException If the YAML is not valid
  70.      */
  71.     public function parse(string $valueint $flags 0)
  72.     {
  73.         if (false === preg_match('//u'$value)) {
  74.             throw new ParseException('The YAML value does not appear to be valid UTF-8.', -1null$this->filename);
  75.         }
  76.         $this->refs = [];
  77.         $mbEncoding null;
  78.         if (/* MB_OVERLOAD_STRING */ & (int) \ini_get('mbstring.func_overload')) {
  79.             $mbEncoding mb_internal_encoding();
  80.             mb_internal_encoding('UTF-8');
  81.         }
  82.         try {
  83.             $data $this->doParse($value$flags);
  84.         } finally {
  85.             if (null !== $mbEncoding) {
  86.                 mb_internal_encoding($mbEncoding);
  87.             }
  88.             $this->refsBeingParsed = [];
  89.             $this->offset 0;
  90.             $this->lines = [];
  91.             $this->currentLine '';
  92.             $this->numberOfParsedLines 0;
  93.             $this->refs = [];
  94.             $this->skippedLineNumbers = [];
  95.             $this->locallySkippedLineNumbers = [];
  96.             $this->totalNumberOfLines null;
  97.         }
  98.         return $data;
  99.     }
  100.     private function doParse(string $valueint $flags)
  101.     {
  102.         $this->currentLineNb = -1;
  103.         $this->currentLine '';
  104.         $value $this->cleanup($value);
  105.         $this->lines explode("\n"$value);
  106.         $this->numberOfParsedLines \count($this->lines);
  107.         $this->locallySkippedLineNumbers = [];
  108.         if (null === $this->totalNumberOfLines) {
  109.             $this->totalNumberOfLines $this->numberOfParsedLines;
  110.         }
  111.         if (!$this->moveToNextLine()) {
  112.             return null;
  113.         }
  114.         $data = [];
  115.         $context null;
  116.         $allowOverwrite false;
  117.         while ($this->isCurrentLineEmpty()) {
  118.             if (!$this->moveToNextLine()) {
  119.                 return null;
  120.             }
  121.         }
  122.         // Resolves the tag and returns if end of the document
  123.         if (null !== ($tag $this->getLineTag($this->currentLine$flagsfalse)) && !$this->moveToNextLine()) {
  124.             return new TaggedValue($tag'');
  125.         }
  126.         do {
  127.             if ($this->isCurrentLineEmpty()) {
  128.                 continue;
  129.             }
  130.             // tab?
  131.             if ("\t" === $this->currentLine[0]) {
  132.                 throw new ParseException('A YAML file cannot contain tabs as indentation.'$this->getRealCurrentLineNb() + 1$this->currentLine$this->filename);
  133.             }
  134.             Inline::initialize($flags$this->getRealCurrentLineNb(), $this->filename);
  135.             $isRef $mergeNode false;
  136.             if ('-' === $this->currentLine[0] && self::preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+))?$#u'rtrim($this->currentLine), $values)) {
  137.                 if ($context && 'mapping' == $context) {
  138.                     throw new ParseException('You cannot define a sequence item when in a mapping.'$this->getRealCurrentLineNb() + 1$this->currentLine$this->filename);
  139.                 }
  140.                 $context 'sequence';
  141.                 if (isset($values['value']) && '&' === $values['value'][0] && self::preg_match(self::REFERENCE_PATTERN$values['value'], $matches)) {
  142.                     $isRef $matches['ref'];
  143.                     $this->refsBeingParsed[] = $isRef;
  144.                     $values['value'] = $matches['value'];
  145.                 }
  146.                 if (isset($values['value'][1]) && '?' === $values['value'][0] && ' ' === $values['value'][1]) {
  147.                     throw new ParseException('Complex mappings are not supported.'$this->getRealCurrentLineNb() + 1$this->currentLine);
  148.                 }
  149.                 // array
  150.                 if (isset($values['value']) && === strpos(ltrim($values['value'], ' '), '-')) {
  151.                     // Inline first child
  152.                     $currentLineNumber $this->getRealCurrentLineNb();
  153.                     $sequenceIndentation \strlen($values['leadspaces']) + 1;
  154.                     $sequenceYaml substr($this->currentLine$sequenceIndentation);
  155.                     $sequenceYaml .= "\n".$this->getNextEmbedBlock($sequenceIndentationtrue);
  156.                     $data[] = $this->parseBlock($currentLineNumberrtrim($sequenceYaml), $flags);
  157.                 } elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || === strpos(ltrim($values['value'], ' '), '#')) {
  158.                     $data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1$this->getNextEmbedBlock(nulltrue) ?? ''$flags);
  159.                 } elseif (null !== $subTag $this->getLineTag(ltrim($values['value'], ' '), $flags)) {
  160.                     $data[] = new TaggedValue(
  161.                         $subTag,
  162.                         $this->parseBlock($this->getRealCurrentLineNb() + 1$this->getNextEmbedBlock(nulltrue), $flags)
  163.                     );
  164.                 } else {
  165.                     if (
  166.                         isset($values['leadspaces'])
  167.                         && (
  168.                             '!' === $values['value'][0]
  169.                             || self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u'$this->trimTag($values['value']), $matches)
  170.                         )
  171.                     ) {
  172.                         $block $values['value'];
  173.                         if ($this->isNextLineIndented() || isset($matches['value']) && '>-' === $matches['value']) {
  174.                             $block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + \strlen($values['leadspaces']) + 1);
  175.                         }
  176.                         $data[] = $this->parseBlock($this->getRealCurrentLineNb(), $block$flags);
  177.                     } else {
  178.                         $data[] = $this->parseValue($values['value'], $flags$context);
  179.                     }
  180.                 }
  181.                 if ($isRef) {
  182.                     $this->refs[$isRef] = end($data);
  183.                     array_pop($this->refsBeingParsed);
  184.                 }
  185.             } elseif (
  186.                 self::preg_match('#^(?P<key>(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?[^ \'"\[\{!].*?)) *\:(( |\t)++(?P<value>.+))?$#u'rtrim($this->currentLine), $values)
  187.                 && (false === strpos($values['key'], ' #') || \in_array($values['key'][0], ['"'"'"]))
  188.             ) {
  189.                 if ($context && 'sequence' == $context) {
  190.                     throw new ParseException('You cannot define a mapping item when in a sequence.'$this->currentLineNb 1$this->currentLine$this->filename);
  191.                 }
  192.                 $context 'mapping';
  193.                 try {
  194.                     $key Inline::parseScalar($values['key']);
  195.                 } catch (ParseException $e) {
  196.                     $e->setParsedLine($this->getRealCurrentLineNb() + 1);
  197.                     $e->setSnippet($this->currentLine);
  198.                     throw $e;
  199.                 }
  200.                 if (!\is_string($key) && !\is_int($key)) {
  201.                     throw new ParseException((is_numeric($key) ? 'Numeric' 'Non-string').' keys are not supported. Quote your evaluable mapping keys instead.'$this->getRealCurrentLineNb() + 1$this->currentLine);
  202.                 }
  203.                 // Convert float keys to strings, to avoid being converted to integers by PHP
  204.                 if (\is_float($key)) {
  205.                     $key = (string) $key;
  206.                 }
  207.                 if ('<<' === $key && (!isset($values['value']) || '&' !== $values['value'][0] || !self::preg_match('#^&(?P<ref>[^ ]+)#u'$values['value'], $refMatches))) {
  208.                     $mergeNode true;
  209.                     $allowOverwrite true;
  210.                     if (isset($values['value'][0]) && '*' === $values['value'][0]) {
  211.                         $refName substr(rtrim($values['value']), 1);
  212.                         if (!\array_key_exists($refName$this->refs)) {
  213.                             if (false !== $pos array_search($refName$this->refsBeingParsedtrue)) {
  214.                                 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);
  215.                             }
  216.                             throw new ParseException(sprintf('Reference "%s" does not exist.'$refName), $this->getRealCurrentLineNb() + 1$this->currentLine$this->filename);
  217.                         }
  218.                         $refValue $this->refs[$refName];
  219.                         if (Yaml::PARSE_OBJECT_FOR_MAP $flags && $refValue instanceof \stdClass) {
  220.                             $refValue = (array) $refValue;
  221.                         }
  222.                         if (!\is_array($refValue)) {
  223.                             throw new ParseException('YAML merge keys used with a scalar value instead of an array.'$this->getRealCurrentLineNb() + 1$this->currentLine$this->filename);
  224.                         }
  225.                         $data += $refValue// array union
  226.                     } else {
  227.                         if (isset($values['value']) && '' !== $values['value']) {
  228.                             $value $values['value'];
  229.                         } else {
  230.                             $value $this->getNextEmbedBlock();
  231.                         }
  232.                         $parsed $this->parseBlock($this->getRealCurrentLineNb() + 1$value$flags);
  233.                         if (Yaml::PARSE_OBJECT_FOR_MAP $flags && $parsed instanceof \stdClass) {
  234.                             $parsed = (array) $parsed;
  235.                         }
  236.                         if (!\is_array($parsed)) {
  237.                             throw new ParseException('YAML merge keys used with a scalar value instead of an array.'$this->getRealCurrentLineNb() + 1$this->currentLine$this->filename);
  238.                         }
  239.                         if (isset($parsed[0])) {
  240.                             // If the value associated with the merge key is a sequence, then this sequence is expected to contain mapping nodes
  241.                             // and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier
  242.                             // in the sequence override keys specified in later mapping nodes.
  243.                             foreach ($parsed as $parsedItem) {
  244.                                 if (Yaml::PARSE_OBJECT_FOR_MAP $flags && $parsedItem instanceof \stdClass) {
  245.                                     $parsedItem = (array) $parsedItem;
  246.                                 }
  247.                                 if (!\is_array($parsedItem)) {
  248.                                     throw new ParseException('Merge items must be arrays.'$this->getRealCurrentLineNb() + 1$parsedItem$this->filename);
  249.                                 }
  250.                                 $data += $parsedItem// array union
  251.                             }
  252.                         } else {
  253.                             // If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the
  254.                             // current mapping, unless the key already exists in it.
  255.                             $data += $parsed// array union
  256.                         }
  257.                     }
  258.                 } elseif ('<<' !== $key && isset($values['value']) && '&' === $values['value'][0] && self::preg_match(self::REFERENCE_PATTERN$values['value'], $matches)) {
  259.                     $isRef $matches['ref'];
  260.                     $this->refsBeingParsed[] = $isRef;
  261.                     $values['value'] = $matches['value'];
  262.                 }
  263.                 $subTag null;
  264.                 if ($mergeNode) {
  265.                     // Merge keys
  266.                 } elseif (!isset($values['value']) || '' === $values['value'] || === strpos($values['value'], '#') || (null !== $subTag $this->getLineTag($values['value'], $flags)) || '<<' === $key) {
  267.                     // hash
  268.                     // if next line is less indented or equal, then it means that the current value is null
  269.                     if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) {
  270.                         // Spec: Keys MUST be unique; first one wins.
  271.                         // But overwriting is allowed when a merge node is used in current block.
  272.                         if ($allowOverwrite || !isset($data[$key])) {
  273.                             if (null !== $subTag) {
  274.                                 $data[$key] = new TaggedValue($subTag'');
  275.                             } else {
  276.                                 $data[$key] = null;
  277.                             }
  278.                         } else {
  279.                             throw new ParseException(sprintf('Duplicate key "%s" detected.'$key), $this->getRealCurrentLineNb() + 1$this->currentLine);
  280.                         }
  281.                     } else {
  282.                         // remember the parsed line number here in case we need it to provide some contexts in error messages below
  283.                         $realCurrentLineNbKey $this->getRealCurrentLineNb();
  284.                         $value $this->parseBlock($this->getRealCurrentLineNb() + 1$this->getNextEmbedBlock(), $flags);
  285.                         if ('<<' === $key) {
  286.                             $this->refs[$refMatches['ref']] = $value;
  287.                             if (Yaml::PARSE_OBJECT_FOR_MAP $flags && $value instanceof \stdClass) {
  288.                                 $value = (array) $value;
  289.                             }
  290.                             $data += $value;
  291.                         } elseif ($allowOverwrite || !isset($data[$key])) {
  292.                             // Spec: Keys MUST be unique; first one wins.
  293.                             // But overwriting is allowed when a merge node is used in current block.
  294.                             if (null !== $subTag) {
  295.                                 $data[$key] = new TaggedValue($subTag$value);
  296.                             } else {
  297.                                 $data[$key] = $value;
  298.                             }
  299.                         } else {
  300.                             throw new ParseException(sprintf('Duplicate key "%s" detected.'$key), $realCurrentLineNbKey 1$this->currentLine);
  301.                         }
  302.                     }
  303.                 } else {
  304.                     $value $this->parseValue(rtrim($values['value']), $flags$context);
  305.                     // Spec: Keys MUST be unique; first one wins.
  306.                     // But overwriting is allowed when a merge node is used in current block.
  307.                     if ($allowOverwrite || !isset($data[$key])) {
  308.                         $data[$key] = $value;
  309.                     } else {
  310.                         throw new ParseException(sprintf('Duplicate key "%s" detected.'$key), $this->getRealCurrentLineNb() + 1$this->currentLine);
  311.                     }
  312.                 }
  313.                 if ($isRef) {
  314.                     $this->refs[$isRef] = $data[$key];
  315.                     array_pop($this->refsBeingParsed);
  316.                 }
  317.             } elseif ('"' === $this->currentLine[0] || "'" === $this->currentLine[0]) {
  318.                 if (null !== $context) {
  319.                     throw new ParseException('Unable to parse.'$this->getRealCurrentLineNb() + 1$this->currentLine$this->filename);
  320.                 }
  321.                 try {
  322.                     return Inline::parse($this->lexInlineQuotedString(), $flags$this->refs);
  323.                 } catch (ParseException $e) {
  324.                     $e->setParsedLine($this->getRealCurrentLineNb() + 1);
  325.                     $e->setSnippet($this->currentLine);
  326.                     throw $e;
  327.                 }
  328.             } elseif ('{' === $this->currentLine[0]) {
  329.                 if (null !== $context) {
  330.                     throw new ParseException('Unable to parse.'$this->getRealCurrentLineNb() + 1$this->currentLine$this->filename);
  331.                 }
  332.                 try {
  333.                     $parsedMapping Inline::parse($this->lexInlineMapping(), $flags$this->refs);
  334.                     while ($this->moveToNextLine()) {
  335.                         if (!$this->isCurrentLineEmpty()) {
  336.                             throw new ParseException('Unable to parse.'$this->getRealCurrentLineNb() + 1$this->currentLine$this->filename);
  337.                         }
  338.                     }
  339.                     return $parsedMapping;
  340.                 } catch (ParseException $e) {
  341.                     $e->setParsedLine($this->getRealCurrentLineNb() + 1);
  342.                     $e->setSnippet($this->currentLine);
  343.                     throw $e;
  344.                 }
  345.             } elseif ('[' === $this->currentLine[0]) {
  346.                 if (null !== $context) {
  347.                     throw new ParseException('Unable to parse.'$this->getRealCurrentLineNb() + 1$this->currentLine$this->filename);
  348.                 }
  349.                 try {
  350.                     $parsedSequence Inline::parse($this->lexInlineSequence(), $flags$this->refs);
  351.                     while ($this->moveToNextLine()) {
  352.                         if (!$this->isCurrentLineEmpty()) {
  353.                             throw new ParseException('Unable to parse.'$this->getRealCurrentLineNb() + 1$this->currentLine$this->filename);
  354.                         }
  355.                     }
  356.                     return $parsedSequence;
  357.                 } catch (ParseException $e) {
  358.                     $e->setParsedLine($this->getRealCurrentLineNb() + 1);
  359.                     $e->setSnippet($this->currentLine);
  360.                     throw $e;
  361.                 }
  362.             } else {
  363.                 // multiple documents are not supported
  364.                 if ('---' === $this->currentLine) {
  365.                     throw new ParseException('Multiple documents are not supported.'$this->currentLineNb 1$this->currentLine$this->filename);
  366.                 }
  367.                 if ($deprecatedUsage = (isset($this->currentLine[1]) && '?' === $this->currentLine[0] && ' ' === $this->currentLine[1])) {
  368.                     throw new ParseException('Complex mappings are not supported.'$this->getRealCurrentLineNb() + 1$this->currentLine);
  369.                 }
  370.                 // 1-liner optionally followed by newline(s)
  371.                 if (\is_string($value) && $this->lines[0] === trim($value)) {
  372.                     try {
  373.                         $value Inline::parse($this->lines[0], $flags$this->refs);
  374.                     } catch (ParseException $e) {
  375.                         $e->setParsedLine($this->getRealCurrentLineNb() + 1);
  376.                         $e->setSnippet($this->currentLine);
  377.                         throw $e;
  378.                     }
  379.                     return $value;
  380.                 }
  381.                 // try to parse the value as a multi-line string as a last resort
  382.                 if (=== $this->currentLineNb) {
  383.                     $previousLineWasNewline false;
  384.                     $previousLineWasTerminatedWithBackslash false;
  385.                     $value '';
  386.                     foreach ($this->lines as $line) {
  387.                         $trimmedLine trim($line);
  388.                         if ('#' === ($trimmedLine[0] ?? '')) {
  389.                             continue;
  390.                         }
  391.                         // If the indentation is not consistent at offset 0, it is to be considered as a ParseError
  392.                         if (=== $this->offset && !$deprecatedUsage && isset($line[0]) && ' ' === $line[0]) {
  393.                             throw new ParseException('Unable to parse.'$this->getRealCurrentLineNb() + 1$this->currentLine$this->filename);
  394.                         }
  395.                         if (false !== strpos($line': ')) {
  396.                             throw new ParseException('Mapping values are not allowed in multi-line blocks.'$this->getRealCurrentLineNb() + 1$this->currentLine$this->filename);
  397.                         }
  398.                         if ('' === $trimmedLine) {
  399.                             $value .= "\n";
  400.                         } elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) {
  401.                             $value .= ' ';
  402.                         }
  403.                         if ('' !== $trimmedLine && '\\' === substr($line, -1)) {
  404.                             $value .= ltrim(substr($line0, -1));
  405.                         } elseif ('' !== $trimmedLine) {
  406.                             $value .= $trimmedLine;
  407.                         }
  408.                         if ('' === $trimmedLine) {
  409.                             $previousLineWasNewline true;
  410.                             $previousLineWasTerminatedWithBackslash false;
  411.                         } elseif ('\\' === substr($line, -1)) {
  412.                             $previousLineWasNewline false;
  413.                             $previousLineWasTerminatedWithBackslash true;
  414.                         } else {
  415.                             $previousLineWasNewline false;
  416.                             $previousLineWasTerminatedWithBackslash false;
  417.                         }
  418.                     }
  419.                     try {
  420.                         return Inline::parse(trim($value));
  421.                     } catch (ParseException $e) {
  422.                         // fall-through to the ParseException thrown below
  423.                     }
  424.                 }
  425.                 throw new ParseException('Unable to parse.'$this->getRealCurrentLineNb() + 1$this->currentLine$this->filename);
  426.             }
  427.         } while ($this->moveToNextLine());
  428.         if (null !== $tag) {
  429.             $data = new TaggedValue($tag$data);
  430.         }
  431.         if (Yaml::PARSE_OBJECT_FOR_MAP $flags && 'mapping' === $context && !\is_object($data)) {
  432.             $object = new \stdClass();
  433.             foreach ($data as $key => $value) {
  434.                 $object->$key $value;
  435.             }
  436.             $data $object;
  437.         }
  438.         return empty($data) ? null $data;
  439.     }
  440.     private function parseBlock(int $offsetstring $yamlint $flags)
  441.     {
  442.         $skippedLineNumbers $this->skippedLineNumbers;
  443.         foreach ($this->locallySkippedLineNumbers as $lineNumber) {
  444.             if ($lineNumber $offset) {
  445.                 continue;
  446.             }
  447.             $skippedLineNumbers[] = $lineNumber;
  448.         }
  449.         $parser = new self();
  450.         $parser->offset $offset;
  451.         $parser->totalNumberOfLines $this->totalNumberOfLines;
  452.         $parser->skippedLineNumbers $skippedLineNumbers;
  453.         $parser->refs = &$this->refs;
  454.         $parser->refsBeingParsed $this->refsBeingParsed;
  455.         return $parser->doParse($yaml$flags);
  456.     }
  457.     /**
  458.      * Returns the current line number (takes the offset into account).
  459.      *
  460.      * @internal
  461.      */
  462.     public function getRealCurrentLineNb(): int
  463.     {
  464.         $realCurrentLineNumber $this->currentLineNb $this->offset;
  465.         foreach ($this->skippedLineNumbers as $skippedLineNumber) {
  466.             if ($skippedLineNumber $realCurrentLineNumber) {
  467.                 break;
  468.             }
  469.             ++$realCurrentLineNumber;
  470.         }
  471.         return $realCurrentLineNumber;
  472.     }
  473.     /**
  474.      * Returns the current line indentation.
  475.      */
  476.     private function getCurrentLineIndentation(): int
  477.     {
  478.         if (' ' !== ($this->currentLine[0] ?? '')) {
  479.             return 0;
  480.         }
  481.         return \strlen($this->currentLine) - \strlen(ltrim($this->currentLine' '));
  482.     }
  483.     /**
  484.      * Returns the next embed block of YAML.
  485.      *
  486.      * @param int|null $indentation The indent level at which the block is to be read, or null for default
  487.      * @param bool     $inSequence  True if the enclosing data structure is a sequence
  488.      *
  489.      * @throws ParseException When indentation problem are detected
  490.      */
  491.     private function getNextEmbedBlock(int $indentation nullbool $inSequence false): string
  492.     {
  493.         $oldLineIndentation $this->getCurrentLineIndentation();
  494.         if (!$this->moveToNextLine()) {
  495.             return '';
  496.         }
  497.         if (null === $indentation) {
  498.             $newIndent null;
  499.             $movements 0;
  500.             do {
  501.                 $EOF false;
  502.                 // empty and comment-like lines do not influence the indentation depth
  503.                 if ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()) {
  504.                     $EOF = !$this->moveToNextLine();
  505.                     if (!$EOF) {
  506.                         ++$movements;
  507.                     }
  508.                 } else {
  509.                     $newIndent $this->getCurrentLineIndentation();
  510.                 }
  511.             } while (!$EOF && null === $newIndent);
  512.             for ($i 0$i $movements; ++$i) {
  513.                 $this->moveToPreviousLine();
  514.             }
  515.             $unindentedEmbedBlock $this->isStringUnIndentedCollectionItem();
  516.             if (!$this->isCurrentLineEmpty() && === $newIndent && !$unindentedEmbedBlock) {
  517.                 throw new ParseException('Indentation problem.'$this->getRealCurrentLineNb() + 1$this->currentLine$this->filename);
  518.             }
  519.         } else {
  520.             $newIndent $indentation;
  521.         }
  522.         $data = [];
  523.         if ($this->getCurrentLineIndentation() >= $newIndent) {
  524.             $data[] = substr($this->currentLine$newIndent ?? 0);
  525.         } elseif ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()) {
  526.             $data[] = $this->currentLine;
  527.         } else {
  528.             $this->moveToPreviousLine();
  529.             return '';
  530.         }
  531.         if ($inSequence && $oldLineIndentation === $newIndent && isset($data[0][0]) && '-' === $data[0][0]) {
  532.             // the previous line contained a dash but no item content, this line is a sequence item with the same indentation
  533.             // and therefore no nested list or mapping
  534.             $this->moveToPreviousLine();
  535.             return '';
  536.         }
  537.         $isItUnindentedCollection $this->isStringUnIndentedCollectionItem();
  538.         $isItComment $this->isCurrentLineComment();
  539.         while ($this->moveToNextLine()) {
  540.             if ($isItComment && !$isItUnindentedCollection) {
  541.                 $isItUnindentedCollection $this->isStringUnIndentedCollectionItem();
  542.                 $isItComment $this->isCurrentLineComment();
  543.             }
  544.             $indent $this->getCurrentLineIndentation();
  545.             if ($isItUnindentedCollection && !$this->isCurrentLineEmpty() && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) {
  546.                 $this->moveToPreviousLine();
  547.                 break;
  548.             }
  549.             if ($this->isCurrentLineBlank()) {
  550.                 $data[] = substr($this->currentLine$newIndent);
  551.                 continue;
  552.             }
  553.             if ($indent >= $newIndent) {
  554.                 $data[] = substr($this->currentLine$newIndent);
  555.             } elseif ($this->isCurrentLineComment()) {
  556.                 $data[] = $this->currentLine;
  557.             } elseif (== $indent) {
  558.                 $this->moveToPreviousLine();
  559.                 break;
  560.             } else {
  561.                 throw new ParseException('Indentation problem.'$this->getRealCurrentLineNb() + 1$this->currentLine$this->filename);
  562.             }
  563.         }
  564.         return implode("\n"$data);
  565.     }
  566.     private function hasMoreLines(): bool
  567.     {
  568.         return (\count($this->lines) - 1) > $this->currentLineNb;
  569.     }
  570.     /**
  571.      * Moves the parser to the next line.
  572.      */
  573.     private function moveToNextLine(): bool
  574.     {
  575.         if ($this->currentLineNb >= $this->numberOfParsedLines 1) {
  576.             return false;
  577.         }
  578.         $this->currentLine $this->lines[++$this->currentLineNb];
  579.         return true;
  580.     }
  581.     /**
  582.      * Moves the parser to the previous line.
  583.      */
  584.     private function moveToPreviousLine(): bool
  585.     {
  586.         if ($this->currentLineNb 1) {
  587.             return false;
  588.         }
  589.         $this->currentLine $this->lines[--$this->currentLineNb];
  590.         return true;
  591.     }
  592.     /**
  593.      * Parses a YAML value.
  594.      *
  595.      * @param string $value   A YAML value
  596.      * @param int    $flags   A bit field of Yaml::PARSE_* constants to customize the YAML parser behavior
  597.      * @param string $context The parser context (either sequence or mapping)
  598.      *
  599.      * @return mixed
  600.      *
  601.      * @throws ParseException When reference does not exist
  602.      */
  603.     private function parseValue(string $valueint $flagsstring $context)
  604.     {
  605.         if (=== strpos($value'*')) {
  606.             if (false !== $pos strpos($value'#')) {
  607.                 $value substr($value1$pos 2);
  608.             } else {
  609.                 $value substr($value1);
  610.             }
  611.             if (!\array_key_exists($value$this->refs)) {
  612.                 if (false !== $pos array_search($value$this->refsBeingParsedtrue)) {
  613.                     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);
  614.                 }
  615.                 throw new ParseException(sprintf('Reference "%s" does not exist.'$value), $this->currentLineNb 1$this->currentLine$this->filename);
  616.             }
  617.             return $this->refs[$value];
  618.         }
  619.         if (\in_array($value[0], ['!''|''>'], true) && self::preg_match('/^(?:'.self::TAG_PATTERN.' +)?'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/'$value$matches)) {
  620.             $modifiers $matches['modifiers'] ?? '';
  621.             $data $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#'''$modifiers), abs((int) $modifiers));
  622.             if ('' !== $matches['tag'] && '!' !== $matches['tag']) {
  623.                 if ('!!binary' === $matches['tag']) {
  624.                     return Inline::evaluateBinaryScalar($data);
  625.                 }
  626.                 return new TaggedValue(substr($matches['tag'], 1), $data);
  627.             }
  628.             return $data;
  629.         }
  630.         try {
  631.             if ('' !== $value && '{' === $value[0]) {
  632.                 $cursor \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value));
  633.                 return Inline::parse($this->lexInlineMapping($cursor), $flags$this->refs);
  634.             } elseif ('' !== $value && '[' === $value[0]) {
  635.                 $cursor \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value));
  636.                 return Inline::parse($this->lexInlineSequence($cursor), $flags$this->refs);
  637.             }
  638.             switch ($value[0] ?? '') {
  639.                 case '"':
  640.                 case "'":
  641.                     $cursor \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value));
  642.                     $parsedValue Inline::parse($this->lexInlineQuotedString($cursor), $flags$this->refs);
  643.                     if (isset($this->currentLine[$cursor]) && preg_replace('/\s*(#.*)?$/A'''substr($this->currentLine$cursor))) {
  644.                         throw new ParseException(sprintf('Unexpected characters near "%s".'substr($this->currentLine$cursor)));
  645.                     }
  646.                     return $parsedValue;
  647.                 default:
  648.                     $lines = [];
  649.                     while ($this->moveToNextLine()) {
  650.                         // unquoted strings end before the first unindented line
  651.                         if (=== $this->getCurrentLineIndentation()) {
  652.                             $this->moveToPreviousLine();
  653.                             break;
  654.                         }
  655.                         $lines[] = trim($this->currentLine);
  656.                     }
  657.                     for ($i 0$linesCount \count($lines), $previousLineBlank false$i $linesCount; ++$i) {
  658.                         if ('' === $lines[$i]) {
  659.                             $value .= "\n";
  660.                             $previousLineBlank true;
  661.                         } elseif ($previousLineBlank) {
  662.                             $value .= $lines[$i];
  663.                             $previousLineBlank false;
  664.                         } else {
  665.                             $value .= ' '.$lines[$i];
  666.                             $previousLineBlank false;
  667.                         }
  668.                     }
  669.                     Inline::$parsedLineNumber $this->getRealCurrentLineNb();
  670.                     $parsedValue Inline::parse($value$flags$this->refs);
  671.                     if ('mapping' === $context && \is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue': ')) {
  672.                         throw new ParseException('A colon cannot be used in an unquoted mapping value.'$this->getRealCurrentLineNb() + 1$value$this->filename);
  673.                     }
  674.                     return $parsedValue;
  675.             }
  676.         } catch (ParseException $e) {
  677.             $e->setParsedLine($this->getRealCurrentLineNb() + 1);
  678.             $e->setSnippet($this->currentLine);
  679.             throw $e;
  680.         }
  681.     }
  682.     /**
  683.      * Parses a block scalar.
  684.      *
  685.      * @param string $style       The style indicator that was used to begin this block scalar (| or >)
  686.      * @param string $chomping    The chomping indicator that was used to begin this block scalar (+ or -)
  687.      * @param int    $indentation The indentation indicator that was used to begin this block scalar
  688.      */
  689.     private function parseBlockScalar(string $stylestring $chomping ''int $indentation 0): string
  690.     {
  691.         $notEOF $this->moveToNextLine();
  692.         if (!$notEOF) {
  693.             return '';
  694.         }
  695.         $isCurrentLineBlank $this->isCurrentLineBlank();
  696.         $blockLines = [];
  697.         // leading blank lines are consumed before determining indentation
  698.         while ($notEOF && $isCurrentLineBlank) {
  699.             // newline only if not EOF
  700.             if ($notEOF $this->moveToNextLine()) {
  701.                 $blockLines[] = '';
  702.                 $isCurrentLineBlank $this->isCurrentLineBlank();
  703.             }
  704.         }
  705.         // determine indentation if not specified
  706.         if (=== $indentation) {
  707.             $currentLineLength \strlen($this->currentLine);
  708.             for ($i 0$i $currentLineLength && ' ' === $this->currentLine[$i]; ++$i) {
  709.                 ++$indentation;
  710.             }
  711.         }
  712.         if ($indentation 0) {
  713.             $pattern sprintf('/^ {%d}(.*)$/'$indentation);
  714.             while (
  715.                 $notEOF && (
  716.                     $isCurrentLineBlank ||
  717.                     self::preg_match($pattern$this->currentLine$matches)
  718.                 )
  719.             ) {
  720.                 if ($isCurrentLineBlank && \strlen($this->currentLine) > $indentation) {
  721.                     $blockLines[] = substr($this->currentLine$indentation);
  722.                 } elseif ($isCurrentLineBlank) {
  723.                     $blockLines[] = '';
  724.                 } else {
  725.                     $blockLines[] = $matches[1];
  726.                 }
  727.                 // newline only if not EOF
  728.                 if ($notEOF $this->moveToNextLine()) {
  729.                     $isCurrentLineBlank $this->isCurrentLineBlank();
  730.                 }
  731.             }
  732.         } elseif ($notEOF) {
  733.             $blockLines[] = '';
  734.         }
  735.         if ($notEOF) {
  736.             $blockLines[] = '';
  737.             $this->moveToPreviousLine();
  738.         } elseif (!$notEOF && !$this->isCurrentLineLastLineInDocument()) {
  739.             $blockLines[] = '';
  740.         }
  741.         // folded style
  742.         if ('>' === $style) {
  743.             $text '';
  744.             $previousLineIndented false;
  745.             $previousLineBlank false;
  746.             for ($i 0$blockLinesCount \count($blockLines); $i $blockLinesCount; ++$i) {
  747.                 if ('' === $blockLines[$i]) {
  748.                     $text .= "\n";
  749.                     $previousLineIndented false;
  750.                     $previousLineBlank true;
  751.                 } elseif (' ' === $blockLines[$i][0]) {
  752.                     $text .= "\n".$blockLines[$i];
  753.                     $previousLineIndented true;
  754.                     $previousLineBlank false;
  755.                 } elseif ($previousLineIndented) {
  756.                     $text .= "\n".$blockLines[$i];
  757.                     $previousLineIndented false;
  758.                     $previousLineBlank false;
  759.                 } elseif ($previousLineBlank || === $i) {
  760.                     $text .= $blockLines[$i];
  761.                     $previousLineIndented false;
  762.                     $previousLineBlank false;
  763.                 } else {
  764.                     $text .= ' '.$blockLines[$i];
  765.                     $previousLineIndented false;
  766.                     $previousLineBlank false;
  767.                 }
  768.             }
  769.         } else {
  770.             $text implode("\n"$blockLines);
  771.         }
  772.         // deal with trailing newlines
  773.         if ('' === $chomping) {
  774.             $text preg_replace('/\n+$/'"\n"$text);
  775.         } elseif ('-' === $chomping) {
  776.             $text preg_replace('/\n+$/'''$text);
  777.         }
  778.         return $text;
  779.     }
  780.     /**
  781.      * Returns true if the next line is indented.
  782.      */
  783.     private function isNextLineIndented(): bool
  784.     {
  785.         $currentIndentation $this->getCurrentLineIndentation();
  786.         $movements 0;
  787.         do {
  788.             $EOF = !$this->moveToNextLine();
  789.             if (!$EOF) {
  790.                 ++$movements;
  791.             }
  792.         } while (!$EOF && ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()));
  793.         if ($EOF) {
  794.             for ($i 0$i $movements; ++$i) {
  795.                 $this->moveToPreviousLine();
  796.             }
  797.             return false;
  798.         }
  799.         $ret $this->getCurrentLineIndentation() > $currentIndentation;
  800.         for ($i 0$i $movements; ++$i) {
  801.             $this->moveToPreviousLine();
  802.         }
  803.         return $ret;
  804.     }
  805.     /**
  806.      * Returns true if the current line is blank or if it is a comment line.
  807.      */
  808.     private function isCurrentLineEmpty(): bool
  809.     {
  810.         return $this->isCurrentLineBlank() || $this->isCurrentLineComment();
  811.     }
  812.     /**
  813.      * Returns true if the current line is blank.
  814.      */
  815.     private function isCurrentLineBlank(): bool
  816.     {
  817.         return '' === $this->currentLine || '' === trim($this->currentLine' ');
  818.     }
  819.     /**
  820.      * Returns true if the current line is a comment line.
  821.      */
  822.     private function isCurrentLineComment(): bool
  823.     {
  824.         // checking explicitly the first char of the trim is faster than loops or strpos
  825.         $ltrimmedLine '' !== $this->currentLine && ' ' === $this->currentLine[0] ? ltrim($this->currentLine' ') : $this->currentLine;
  826.         return '' !== $ltrimmedLine && '#' === $ltrimmedLine[0];
  827.     }
  828.     private function isCurrentLineLastLineInDocument(): bool
  829.     {
  830.         return ($this->offset $this->currentLineNb) >= ($this->totalNumberOfLines 1);
  831.     }
  832.     /**
  833.      * Cleanups a YAML string to be parsed.
  834.      *
  835.      * @param string $value The input YAML string
  836.      */
  837.     private function cleanup(string $value): string
  838.     {
  839.         $value str_replace(["\r\n""\r"], "\n"$value);
  840.         // strip YAML header
  841.         $count 0;
  842.         $value preg_replace('#^\%YAML[: ][\d\.]+.*\n#u'''$value, -1$count);
  843.         $this->offset += $count;
  844.         // remove leading comments
  845.         $trimmedValue preg_replace('#^(\#.*?\n)+#s'''$value, -1$count);
  846.         if (=== $count) {
  847.             // items have been removed, update the offset
  848.             $this->offset += substr_count($value"\n") - substr_count($trimmedValue"\n");
  849.             $value $trimmedValue;
  850.         }
  851.         // remove start of the document marker (---)
  852.         $trimmedValue preg_replace('#^\-\-\-.*?\n#s'''$value, -1$count);
  853.         if (=== $count) {
  854.             // items have been removed, update the offset
  855.             $this->offset += substr_count($value"\n") - substr_count($trimmedValue"\n");
  856.             $value $trimmedValue;
  857.             // remove end of the document marker (...)
  858.             $value preg_replace('#\.\.\.\s*$#'''$value);
  859.         }
  860.         return $value;
  861.     }
  862.     /**
  863.      * Returns true if the next line starts unindented collection.
  864.      */
  865.     private function isNextLineUnIndentedCollection(): bool
  866.     {
  867.         $currentIndentation $this->getCurrentLineIndentation();
  868.         $movements 0;
  869.         do {
  870.             $EOF = !$this->moveToNextLine();
  871.             if (!$EOF) {
  872.                 ++$movements;
  873.             }
  874.         } while (!$EOF && ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()));
  875.         if ($EOF) {
  876.             return false;
  877.         }
  878.         $ret $this->getCurrentLineIndentation() === $currentIndentation && $this->isStringUnIndentedCollectionItem();
  879.         for ($i 0$i $movements; ++$i) {
  880.             $this->moveToPreviousLine();
  881.         }
  882.         return $ret;
  883.     }
  884.     /**
  885.      * Returns true if the string is un-indented collection item.
  886.      */
  887.     private function isStringUnIndentedCollectionItem(): bool
  888.     {
  889.         return '-' === rtrim($this->currentLine) || === strpos($this->currentLine'- ');
  890.     }
  891.     /**
  892.      * A local wrapper for "preg_match" which will throw a ParseException if there
  893.      * is an internal error in the PCRE engine.
  894.      *
  895.      * This avoids us needing to check for "false" every time PCRE is used
  896.      * in the YAML engine
  897.      *
  898.      * @throws ParseException on a PCRE internal error
  899.      *
  900.      * @see preg_last_error()
  901.      *
  902.      * @internal
  903.      */
  904.     public static function preg_match(string $patternstring $subject, array &$matches nullint $flags 0int $offset 0): int
  905.     {
  906.         if (false === $ret preg_match($pattern$subject$matches$flags$offset)) {
  907.             switch (preg_last_error()) {
  908.                 case \PREG_INTERNAL_ERROR:
  909.                     $error 'Internal PCRE error.';
  910.                     break;
  911.                 case \PREG_BACKTRACK_LIMIT_ERROR:
  912.                     $error 'pcre.backtrack_limit reached.';
  913.                     break;
  914.                 case \PREG_RECURSION_LIMIT_ERROR:
  915.                     $error 'pcre.recursion_limit reached.';
  916.                     break;
  917.                 case \PREG_BAD_UTF8_ERROR:
  918.                     $error 'Malformed UTF-8 data.';
  919.                     break;
  920.                 case \PREG_BAD_UTF8_OFFSET_ERROR:
  921.                     $error 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.';
  922.                     break;
  923.                 default:
  924.                     $error 'Error.';
  925.             }
  926.             throw new ParseException($error);
  927.         }
  928.         return $ret;
  929.     }
  930.     /**
  931.      * Trim the tag on top of the value.
  932.      *
  933.      * Prevent values such as "!foo {quz: bar}" to be considered as
  934.      * a mapping block.
  935.      */
  936.     private function trimTag(string $value): string
  937.     {
  938.         if ('!' === $value[0]) {
  939.             return ltrim(substr($value1strcspn($value" \r\n"1)), ' ');
  940.         }
  941.         return $value;
  942.     }
  943.     private function getLineTag(string $valueint $flagsbool $nextLineCheck true): ?string
  944.     {
  945.         if ('' === $value || '!' !== $value[0] || !== self::preg_match('/^'.self::TAG_PATTERN.' *( +#.*)?$/'$value$matches)) {
  946.             return null;
  947.         }
  948.         if ($nextLineCheck && !$this->isNextLineIndented()) {
  949.             return null;
  950.         }
  951.         $tag substr($matches['tag'], 1);
  952.         // Built-in tags
  953.         if ($tag && '!' === $tag[0]) {
  954.             throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.'$tag), $this->getRealCurrentLineNb() + 1$value$this->filename);
  955.         }
  956.         if (Yaml::PARSE_CUSTOM_TAGS $flags) {
  957.             return $tag;
  958.         }
  959.         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);
  960.     }
  961.     private function lexInlineQuotedString(int &$cursor 0): string
  962.     {
  963.         $quotation $this->currentLine[$cursor];
  964.         $value $quotation;
  965.         ++$cursor;
  966.         $previousLineWasNewline true;
  967.         $previousLineWasTerminatedWithBackslash false;
  968.         $lineNumber 0;
  969.         do {
  970.             if (++$lineNumber 1) {
  971.                 $cursor += strspn($this->currentLine' '$cursor);
  972.             }
  973.             if ($this->isCurrentLineBlank()) {
  974.                 $value .= "\n";
  975.             } elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) {
  976.                 $value .= ' ';
  977.             }
  978.             for (; \strlen($this->currentLine) > $cursor; ++$cursor) {
  979.                 switch ($this->currentLine[$cursor]) {
  980.                     case '\\':
  981.                         if ("'" === $quotation) {
  982.                             $value .= '\\';
  983.                         } elseif (isset($this->currentLine[++$cursor])) {
  984.                             $value .= '\\'.$this->currentLine[$cursor];
  985.                         }
  986.                         break;
  987.                     case $quotation:
  988.                         ++$cursor;
  989.                         if ("'" === $quotation && isset($this->currentLine[$cursor]) && "'" === $this->currentLine[$cursor]) {
  990.                             $value .= "''";
  991.                             break;
  992.                         }
  993.                         return $value.$quotation;
  994.                     default:
  995.                         $value .= $this->currentLine[$cursor];
  996.                 }
  997.             }
  998.             if ($this->isCurrentLineBlank()) {
  999.                 $previousLineWasNewline true;
  1000.                 $previousLineWasTerminatedWithBackslash false;
  1001.             } elseif ('\\' === $this->currentLine[-1]) {
  1002.                 $previousLineWasNewline false;
  1003.                 $previousLineWasTerminatedWithBackslash true;
  1004.             } else {
  1005.                 $previousLineWasNewline false;
  1006.                 $previousLineWasTerminatedWithBackslash false;
  1007.             }
  1008.             if ($this->hasMoreLines()) {
  1009.                 $cursor 0;
  1010.             }
  1011.         } while ($this->moveToNextLine());
  1012.         throw new ParseException('Malformed inline YAML string.');
  1013.     }
  1014.     private function lexUnquotedString(int &$cursor): string
  1015.     {
  1016.         $offset $cursor;
  1017.         $cursor += strcspn($this->currentLine'[]{},: '$cursor);
  1018.         if ($cursor === $offset) {
  1019.             throw new ParseException('Malformed unquoted YAML string.');
  1020.         }
  1021.         return substr($this->currentLine$offset$cursor $offset);
  1022.     }
  1023.     private function lexInlineMapping(int &$cursor 0): string
  1024.     {
  1025.         return $this->lexInlineStructure($cursor'}');
  1026.     }
  1027.     private function lexInlineSequence(int &$cursor 0): string
  1028.     {
  1029.         return $this->lexInlineStructure($cursor']');
  1030.     }
  1031.     private function lexInlineStructure(int &$cursorstring $closingTag): string
  1032.     {
  1033.         $value $this->currentLine[$cursor];
  1034.         ++$cursor;
  1035.         do {
  1036.             $this->consumeWhitespaces($cursor);
  1037.             while (isset($this->currentLine[$cursor])) {
  1038.                 switch ($this->currentLine[$cursor]) {
  1039.                     case '"':
  1040.                     case "'":
  1041.                         $value .= $this->lexInlineQuotedString($cursor);
  1042.                         break;
  1043.                     case ':':
  1044.                     case ',':
  1045.                         $value .= $this->currentLine[$cursor];
  1046.                         ++$cursor;
  1047.                         break;
  1048.                     case '{':
  1049.                         $value .= $this->lexInlineMapping($cursor);
  1050.                         break;
  1051.                     case '[':
  1052.                         $value .= $this->lexInlineSequence($cursor);
  1053.                         break;
  1054.                     case $closingTag:
  1055.                         $value .= $this->currentLine[$cursor];
  1056.                         ++$cursor;
  1057.                         return $value;
  1058.                     case '#':
  1059.                         break 2;
  1060.                     default:
  1061.                         $value .= $this->lexUnquotedString($cursor);
  1062.                 }
  1063.                 if ($this->consumeWhitespaces($cursor)) {
  1064.                     $value .= ' ';
  1065.                 }
  1066.             }
  1067.             if ($this->hasMoreLines()) {
  1068.                 $cursor 0;
  1069.             }
  1070.         } while ($this->moveToNextLine());
  1071.         throw new ParseException('Malformed inline YAML string.');
  1072.     }
  1073.     private function consumeWhitespaces(int &$cursor): bool
  1074.     {
  1075.         $whitespacesConsumed 0;
  1076.         do {
  1077.             $whitespaceOnlyTokenLength strspn($this->currentLine' '$cursor);
  1078.             $whitespacesConsumed += $whitespaceOnlyTokenLength;
  1079.             $cursor += $whitespaceOnlyTokenLength;
  1080.             if (isset($this->currentLine[$cursor])) {
  1081.                 return $whitespacesConsumed;
  1082.             }
  1083.             if ($this->hasMoreLines()) {
  1084.                 $cursor 0;
  1085.             }
  1086.         } while ($this->moveToNextLine());
  1087.         return $whitespacesConsumed;
  1088.     }
  1089. }