[[^ ]+)#u', $values['value'], $refMatches))) {
+ $mergeNode = true;
+ $allowOverwrite = true;
+ if (isset($values['value'][0]) && '*' === $values['value'][0]) {
+ $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('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
+ }
+
+ $refValue = $this->refs[$refName];
+
+ if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $refValue instanceof \stdClass) {
+ $refValue = (array) $refValue;
+ }
+
+ if (!\is_array($refValue)) {
+ throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
+ }
+
+ $data += $refValue; // array union
+ } else {
+ if (isset($values['value']) && '' !== $values['value']) {
+ $value = $values['value'];
+ } else {
+ $value = $this->getNextEmbedBlock();
+ }
+ $parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $flags);
+
+ if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $parsed instanceof \stdClass) {
+ $parsed = (array) $parsed;
+ }
+
+ if (!\is_array($parsed)) {
+ throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
+ }
+
+ if (isset($parsed[0])) {
+ // If the value associated with the merge key is a sequence, then this sequence is expected to contain mapping nodes
+ // and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier
+ // in the sequence override keys specified in later mapping nodes.
+ foreach ($parsed as $parsedItem) {
+ if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $parsedItem instanceof \stdClass) {
+ $parsedItem = (array) $parsedItem;
+ }
+
+ if (!\is_array($parsedItem)) {
+ throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem, $this->filename);
+ }
+
+ $data += $parsedItem; // array union
+ }
+ } else {
+ // If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the
+ // current mapping, unless the key already exists in it.
+ $data += $parsed; // array union
+ }
+ }
+ } elseif ('<<' !== $key && isset($values['value']) && '&' === $values['value'][0] && self::preg_match(self::REFERENCE_PATTERN, $values['value'], $matches)) {
+ $isRef = $matches['ref'];
+ $this->refsBeingParsed[] = $isRef;
+ $values['value'] = $matches['value'];
+ }
+
+ $subTag = null;
+ if ($mergeNode) {
+ // Merge keys
+ } elseif (!isset($values['value']) || '' === $values['value'] || str_starts_with($values['value'], '#') || (null !== $subTag = $this->getLineTag($values['value'], $flags)) || '<<' === $key) {
+ // hash
+ // if next line is less indented or equal, then it means that the current value is null
+ if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) {
+ // 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 (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);
+ }
+ } else {
+ // remember the parsed line number here in case we need it to provide some contexts in error messages below
+ $realCurrentLineNbKey = $this->getRealCurrentLineNb();
+ $value = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(), $flags);
+ if ('<<' === $key) {
+ $this->refs[$refMatches['ref']] = $value;
+
+ if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $value instanceof \stdClass) {
+ $value = (array) $value;
+ }
+
+ $data += $value;
+ } elseif ($allowOverwrite || !isset($data[$key])) {
+ // Spec: Keys MUST be unique; first one wins.
+ // But overwriting is allowed when a merge node is used in current block.
+ if (null !== $subTag) {
+ $data[$key] = new TaggedValue($subTag, $value);
+ } else {
+ $data[$key] = $value;
+ }
+ } else {
+ throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), $realCurrentLineNbKey + 1, $this->currentLine);
+ }
+ }
+ } else {
+ $value = $this->parseValue(rtrim($values['value']), $flags, $context);
+ // 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])) {
+ $data[$key] = $value;
+ } else {
+ throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), $this->getRealCurrentLineNb() + 1, $this->currentLine);
+ }
+ }
+ if ($isRef) {
+ $this->refs[$isRef] = $data[$key];
+ array_pop($this->refsBeingParsed);
+ }
+ } elseif ('"' === $this->currentLine[0] || "'" === $this->currentLine[0]) {
+ if (null !== $context) {
+ throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
+ }
+
+ try {
+ return Inline::parse($this->lexInlineQuotedString(), $flags, $this->refs);
+ } catch (ParseException $e) {
+ $e->setParsedLine($this->getRealCurrentLineNb() + 1);
+ $e->setSnippet($this->currentLine);
+
+ throw $e;
+ }
+ } elseif ('{' === $this->currentLine[0]) {
+ if (null !== $context) {
+ throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
+ }
+
+ try {
+ $parsedMapping = Inline::parse($this->lexInlineMapping(), $flags, $this->refs);
+
+ while ($this->moveToNextLine()) {
+ if (!$this->isCurrentLineEmpty()) {
+ throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
+ }
+ }
+
+ return $parsedMapping;
+ } catch (ParseException $e) {
+ $e->setParsedLine($this->getRealCurrentLineNb() + 1);
+ $e->setSnippet($this->currentLine);
+
+ throw $e;
+ }
+ } elseif ('[' === $this->currentLine[0]) {
+ if (null !== $context) {
+ throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
+ }
+
+ try {
+ $parsedSequence = Inline::parse($this->lexInlineSequence(), $flags, $this->refs);
+
+ while ($this->moveToNextLine()) {
+ if (!$this->isCurrentLineEmpty()) {
+ throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
+ }
+ }
+
+ return $parsedSequence;
+ } catch (ParseException $e) {
+ $e->setParsedLine($this->getRealCurrentLineNb() + 1);
+ $e->setSnippet($this->currentLine);
+
+ throw $e;
+ }
+ } else {
+ // multiple documents are not supported
+ if ('---' === $this->currentLine) {
+ throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine, $this->filename);
+ }
+
+ if ($deprecatedUsage = (isset($this->currentLine[1]) && '?' === $this->currentLine[0] && ' ' === $this->currentLine[1])) {
+ throw new ParseException('Complex mappings are not supported.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
+ }
+
+ // 1-liner optionally followed by newline(s)
+ if (\is_string($value) && $this->lines[0] === trim($value)) {
+ try {
+ $value = Inline::parse($this->lines[0], $flags, $this->refs);
+ } catch (ParseException $e) {
+ $e->setParsedLine($this->getRealCurrentLineNb() + 1);
+ $e->setSnippet($this->currentLine);
+
+ throw $e;
+ }
+
+ return $value;
+ }
+
+ // try to parse the value as a multi-line string as a last resort
+ if (0 === $this->currentLineNb) {
+ $previousLineWasNewline = false;
+ $previousLineWasTerminatedWithBackslash = false;
+ $value = '';
+
+ foreach ($this->lines as $line) {
+ $trimmedLine = trim($line);
+ if ('#' === ($trimmedLine[0] ?? '')) {
+ continue;
+ }
+ // If the indentation is not consistent at offset 0, it is to be considered as a ParseError
+ if (0 === $this->offset && !$deprecatedUsage && isset($line[0]) && ' ' === $line[0]) {
+ throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
+ }
+
+ if (str_contains($line, ': ')) {
+ throw new ParseException('Mapping values are not allowed in multi-line blocks.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
+ }
+
+ if ('' === $trimmedLine) {
+ $value .= "\n";
+ } elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) {
+ $value .= ' ';
+ }
+
+ if ('' !== $trimmedLine && str_ends_with($line, '\\')) {
+ $value .= ltrim(substr($line, 0, -1));
+ } elseif ('' !== $trimmedLine) {
+ $value .= $trimmedLine;
+ }
+
+ if ('' === $trimmedLine) {
+ $previousLineWasNewline = true;
+ $previousLineWasTerminatedWithBackslash = false;
+ } elseif (str_ends_with($line, '\\')) {
+ $previousLineWasNewline = false;
+ $previousLineWasTerminatedWithBackslash = true;
+ } else {
+ $previousLineWasNewline = false;
+ $previousLineWasTerminatedWithBackslash = false;
+ }
+ }
+
+ try {
+ return Inline::parse(trim($value));
+ } catch (ParseException) {
+ // fall-through to the ParseException thrown below
+ }
+ }
+
+ throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
+ }
+ } while ($this->moveToNextLine());
+
+ if (null !== $tag) {
+ $data = new TaggedValue($tag, $data);
+ }
+
+ if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && 'mapping' === $context && !\is_object($data)) {
+ $object = new \stdClass();
+
+ foreach ($data as $key => $value) {
+ $object->$key = $value;
+ }
+
+ $data = $object;
+ }
+
+ return empty($data) ? null : $data;
+ }
+
+ private function parseBlock(int $offset, string $yaml, int $flags)
+ {
+ $skippedLineNumbers = $this->skippedLineNumbers;
+
+ foreach ($this->locallySkippedLineNumbers as $lineNumber) {
+ if ($lineNumber < $offset) {
+ continue;
+ }
+
+ $skippedLineNumbers[] = $lineNumber;
+ }
+
+ $parser = new self();
+ $parser->offset = $offset;
+ $parser->totalNumberOfLines = $this->totalNumberOfLines;
+ $parser->skippedLineNumbers = $skippedLineNumbers;
+ $parser->refs = &$this->refs;
+ $parser->refsBeingParsed = $this->refsBeingParsed;
+
+ return $parser->doParse($yaml, $flags);
+ }
+
+ /**
+ * Returns the current line number (takes the offset into account).
+ *
+ * @internal
+ */
+ public function getRealCurrentLineNb(): int
+ {
+ $realCurrentLineNumber = $this->currentLineNb + $this->offset;
+
+ foreach ($this->skippedLineNumbers as $skippedLineNumber) {
+ if ($skippedLineNumber > $realCurrentLineNumber) {
+ break;
+ }
+
+ ++$realCurrentLineNumber;
+ }
+
+ return $realCurrentLineNumber;
+ }
+
+ private function getCurrentLineIndentation(): int
+ {
+ if (' ' !== ($this->currentLine[0] ?? '')) {
+ return 0;
+ }
+
+ return \strlen($this->currentLine) - \strlen(ltrim($this->currentLine, ' '));
+ }
+
+ /**
+ * Returns the next embed block of YAML.
+ *
+ * @param int|null $indentation The indent level at which the block is to be read, or null for default
+ * @param bool $inSequence True if the enclosing data structure is a sequence
+ *
+ * @throws ParseException When indentation problem are detected
+ */
+ private function getNextEmbedBlock(int $indentation = null, bool $inSequence = false): string
+ {
+ $oldLineIndentation = $this->getCurrentLineIndentation();
+
+ if (!$this->moveToNextLine()) {
+ return '';
+ }
+
+ if (null === $indentation) {
+ $newIndent = null;
+ $movements = 0;
+
+ do {
+ $EOF = false;
+
+ // empty and comment-like lines do not influence the indentation depth
+ if ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()) {
+ $EOF = !$this->moveToNextLine();
+
+ if (!$EOF) {
+ ++$movements;
+ }
+ } else {
+ $newIndent = $this->getCurrentLineIndentation();
+ }
+ } while (!$EOF && null === $newIndent);
+
+ for ($i = 0; $i < $movements; ++$i) {
+ $this->moveToPreviousLine();
+ }
+
+ $unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem();
+
+ if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) {
+ throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
+ }
+ } else {
+ $newIndent = $indentation;
+ }
+
+ $data = [];
+
+ if ($this->getCurrentLineIndentation() >= $newIndent) {
+ $data[] = substr($this->currentLine, $newIndent ?? 0);
+ } elseif ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()) {
+ $data[] = $this->currentLine;
+ } else {
+ $this->moveToPreviousLine();
+
+ return '';
+ }
+
+ if ($inSequence && $oldLineIndentation === $newIndent && isset($data[0][0]) && '-' === $data[0][0]) {
+ // the previous line contained a dash but no item content, this line is a sequence item with the same indentation
+ // and therefore no nested list or mapping
+ $this->moveToPreviousLine();
+
+ return '';
+ }
+
+ $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem();
+ $isItComment = $this->isCurrentLineComment();
+
+ while ($this->moveToNextLine()) {
+ if ($isItComment && !$isItUnindentedCollection) {
+ $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem();
+ $isItComment = $this->isCurrentLineComment();
+ }
+
+ $indent = $this->getCurrentLineIndentation();
+
+ if ($isItUnindentedCollection && !$this->isCurrentLineEmpty() && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) {
+ $this->moveToPreviousLine();
+ break;
+ }
+
+ if ($this->isCurrentLineBlank()) {
+ $data[] = substr($this->currentLine, $newIndent);
+ continue;
+ }
+
+ if ($indent >= $newIndent) {
+ $data[] = substr($this->currentLine, $newIndent);
+ } elseif ($this->isCurrentLineComment()) {
+ $data[] = $this->currentLine;
+ } elseif (0 == $indent) {
+ $this->moveToPreviousLine();
+
+ break;
+ } else {
+ throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
+ }
+ }
+
+ return implode("\n", $data);
+ }
+
+ private function hasMoreLines(): bool
+ {
+ return (\count($this->lines) - 1) > $this->currentLineNb;
+ }
+
+ /**
+ * Moves the parser to the next line.
+ */
+ private function moveToNextLine(): bool
+ {
+ if ($this->currentLineNb >= $this->numberOfParsedLines - 1) {
+ return false;
+ }
+
+ $this->currentLine = $this->lines[++$this->currentLineNb];
+
+ return true;
+ }
+
+ /**
+ * Moves the parser to the previous line.
+ */
+ private function moveToPreviousLine(): bool
+ {
+ if ($this->currentLineNb < 1) {
+ return false;
+ }
+
+ $this->currentLine = $this->lines[--$this->currentLineNb];
+
+ return true;
+ }
+
+ /**
+ * Parses a YAML value.
+ *
+ * @param string $value A YAML value
+ * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
+ * @param string $context The parser context (either sequence or mapping)
+ *
+ * @throws ParseException When reference does not exist
+ */
+ private function parseValue(string $value, int $flags, string $context): mixed
+ {
+ if (str_starts_with($value, '*')) {
+ if (false !== $pos = strpos($value, '#')) {
+ $value = substr($value, 1, $pos - 2);
+ } else {
+ $value = substr($value, 1);
+ }
+
+ 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('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine, $this->filename);
+ }
+
+ return $this->refs[$value];
+ }
+
+ if (\in_array($value[0], ['!', '|', '>'], true) && self::preg_match('/^(?:'.self::TAG_PATTERN.' +)?'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) {
+ $modifiers = $matches['modifiers'] ?? '';
+
+ $data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), abs((int) $modifiers));
+
+ if ('' !== $matches['tag'] && '!' !== $matches['tag']) {
+ if ('!!binary' === $matches['tag']) {
+ return Inline::evaluateBinaryScalar($data);
+ }
+
+ return new TaggedValue(substr($matches['tag'], 1), $data);
+ }
+
+ return $data;
+ }
+
+ try {
+ if ('' !== $value && '{' === $value[0]) {
+ $cursor = \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value));
+
+ return Inline::parse($this->lexInlineMapping($cursor), $flags, $this->refs);
+ } elseif ('' !== $value && '[' === $value[0]) {
+ $cursor = \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value));
+
+ return Inline::parse($this->lexInlineSequence($cursor), $flags, $this->refs);
+ }
+
+ switch ($value[0] ?? '') {
+ case '"':
+ case "'":
+ $cursor = \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value));
+ $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)));
+ }
+
+ return $parsedValue;
+ default:
+ $lines = [];
+
+ while ($this->moveToNextLine()) {
+ // unquoted strings end before the first unindented line
+ if (0 === $this->getCurrentLineIndentation()) {
+ $this->moveToPreviousLine();
+
+ break;
+ }
+
+ $lines[] = trim($this->currentLine);
+ }
+
+ for ($i = 0, $linesCount = \count($lines), $previousLineBlank = false; $i < $linesCount; ++$i) {
+ if ('' === $lines[$i]) {
+ $value .= "\n";
+ $previousLineBlank = true;
+ } elseif ($previousLineBlank) {
+ $value .= $lines[$i];
+ $previousLineBlank = false;
+ } else {
+ $value .= ' '.$lines[$i];
+ $previousLineBlank = false;
+ }
+ }
+
+ Inline::$parsedLineNumber = $this->getRealCurrentLineNb();
+
+ $parsedValue = Inline::parse($value, $flags, $this->refs);
+
+ if ('mapping' === $context && \is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && str_contains($parsedValue, ': ')) {
+ throw new ParseException('A colon cannot be used in an unquoted mapping value.', $this->getRealCurrentLineNb() + 1, $value, $this->filename);
+ }
+
+ return $parsedValue;
+ }
+ } catch (ParseException $e) {
+ $e->setParsedLine($this->getRealCurrentLineNb() + 1);
+ $e->setSnippet($this->currentLine);
+
+ throw $e;
+ }
+ }
+
+ /**
+ * Parses a block scalar.
+ *
+ * @param string $style The style indicator that was used to begin this block scalar (| or >)
+ * @param string $chomping The chomping indicator that was used to begin this block scalar (+ or -)
+ * @param int $indentation The indentation indicator that was used to begin this block scalar
+ */
+ private function parseBlockScalar(string $style, string $chomping = '', int $indentation = 0): string
+ {
+ $notEOF = $this->moveToNextLine();
+ if (!$notEOF) {
+ return '';
+ }
+
+ $isCurrentLineBlank = $this->isCurrentLineBlank();
+ $blockLines = [];
+
+ // leading blank lines are consumed before determining indentation
+ while ($notEOF && $isCurrentLineBlank) {
+ // newline only if not EOF
+ if ($notEOF = $this->moveToNextLine()) {
+ $blockLines[] = '';
+ $isCurrentLineBlank = $this->isCurrentLineBlank();
+ }
+ }
+
+ // determine indentation if not specified
+ if (0 === $indentation) {
+ $currentLineLength = \strlen($this->currentLine);
+
+ for ($i = 0; $i < $currentLineLength && ' ' === $this->currentLine[$i]; ++$i) {
+ ++$indentation;
+ }
+ }
+
+ if ($indentation > 0) {
+ $pattern = sprintf('/^ {%d}(.*)$/', $indentation);
+
+ while (
+ $notEOF && (
+ $isCurrentLineBlank ||
+ self::preg_match($pattern, $this->currentLine, $matches)
+ )
+ ) {
+ if ($isCurrentLineBlank && \strlen($this->currentLine) > $indentation) {
+ $blockLines[] = substr($this->currentLine, $indentation);
+ } elseif ($isCurrentLineBlank) {
+ $blockLines[] = '';
+ } else {
+ $blockLines[] = $matches[1];
+ }
+
+ // newline only if not EOF
+ if ($notEOF = $this->moveToNextLine()) {
+ $isCurrentLineBlank = $this->isCurrentLineBlank();
+ }
+ }
+ } elseif ($notEOF) {
+ $blockLines[] = '';
+ }
+
+ if ($notEOF) {
+ $blockLines[] = '';
+ $this->moveToPreviousLine();
+ } elseif (!$notEOF && !$this->isCurrentLineLastLineInDocument()) {
+ $blockLines[] = '';
+ }
+
+ // folded style
+ if ('>' === $style) {
+ $text = '';
+ $previousLineIndented = false;
+ $previousLineBlank = false;
+
+ for ($i = 0, $blockLinesCount = \count($blockLines); $i < $blockLinesCount; ++$i) {
+ if ('' === $blockLines[$i]) {
+ $text .= "\n";
+ $previousLineIndented = false;
+ $previousLineBlank = true;
+ } elseif (' ' === $blockLines[$i][0]) {
+ $text .= "\n".$blockLines[$i];
+ $previousLineIndented = true;
+ $previousLineBlank = false;
+ } elseif ($previousLineIndented) {
+ $text .= "\n".$blockLines[$i];
+ $previousLineIndented = false;
+ $previousLineBlank = false;
+ } elseif ($previousLineBlank || 0 === $i) {
+ $text .= $blockLines[$i];
+ $previousLineIndented = false;
+ $previousLineBlank = false;
+ } else {
+ $text .= ' '.$blockLines[$i];
+ $previousLineIndented = false;
+ $previousLineBlank = false;
+ }
+ }
+ } else {
+ $text = implode("\n", $blockLines);
+ }
+
+ // deal with trailing newlines
+ if ('' === $chomping) {
+ $text = preg_replace('/\n+$/', "\n", $text);
+ } elseif ('-' === $chomping) {
+ $text = preg_replace('/\n+$/', '', $text);
+ }
+
+ return $text;
+ }
+
+ /**
+ * Returns true if the next line is indented.
+ */
+ private function isNextLineIndented(): bool
+ {
+ $currentIndentation = $this->getCurrentLineIndentation();
+ $movements = 0;
+
+ do {
+ $EOF = !$this->moveToNextLine();
+
+ if (!$EOF) {
+ ++$movements;
+ }
+ } while (!$EOF && ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()));
+
+ if ($EOF) {
+ return false;
+ }
+
+ $ret = $this->getCurrentLineIndentation() > $currentIndentation;
+
+ for ($i = 0; $i < $movements; ++$i) {
+ $this->moveToPreviousLine();
+ }
+
+ return $ret;
+ }
+
+ private function isCurrentLineEmpty(): bool
+ {
+ return $this->isCurrentLineBlank() || $this->isCurrentLineComment();
+ }
+
+ private function isCurrentLineBlank(): bool
+ {
+ return '' === $this->currentLine || '' === trim($this->currentLine, ' ');
+ }
+
+ private function isCurrentLineComment(): bool
+ {
+ // checking explicitly the first char of the trim is faster than loops or strpos
+ $ltrimmedLine = '' !== $this->currentLine && ' ' === $this->currentLine[0] ? ltrim($this->currentLine, ' ') : $this->currentLine;
+
+ return '' !== $ltrimmedLine && '#' === $ltrimmedLine[0];
+ }
+
+ private function isCurrentLineLastLineInDocument(): bool
+ {
+ return ($this->offset + $this->currentLineNb) >= ($this->totalNumberOfLines - 1);
+ }
+
+ private function cleanup(string $value): string
+ {
+ $value = str_replace(["\r\n", "\r"], "\n", $value);
+
+ // strip YAML header
+ $count = 0;
+ $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#u', '', $value, -1, $count);
+ $this->offset += $count;
+
+ // remove leading comments
+ $trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count);
+ if (1 === $count) {
+ // items have been removed, update the offset
+ $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n");
+ $value = $trimmedValue;
+ }
+
+ // remove start of the document marker (---)
+ $trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count);
+ if (1 === $count) {
+ // items have been removed, update the offset
+ $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n");
+ $value = $trimmedValue;
+
+ // remove end of the document marker (...)
+ $value = preg_replace('#\.\.\.\s*$#', '', $value);
+ }
+
+ return $value;
+ }
+
+ private function isNextLineUnIndentedCollection(): bool
+ {
+ $currentIndentation = $this->getCurrentLineIndentation();
+ $movements = 0;
+
+ do {
+ $EOF = !$this->moveToNextLine();
+
+ if (!$EOF) {
+ ++$movements;
+ }
+ } while (!$EOF && ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()));
+
+ if ($EOF) {
+ return false;
+ }
+
+ $ret = $this->getCurrentLineIndentation() === $currentIndentation && $this->isStringUnIndentedCollectionItem();
+
+ for ($i = 0; $i < $movements; ++$i) {
+ $this->moveToPreviousLine();
+ }
+
+ return $ret;
+ }
+
+ private function isStringUnIndentedCollectionItem(): bool
+ {
+ return '-' === rtrim($this->currentLine) || str_starts_with($this->currentLine, '- ');
+ }
+
+ /**
+ * A local wrapper for "preg_match" which will throw a ParseException if there
+ * is an internal error in the PCRE engine.
+ *
+ * This avoids us needing to check for "false" every time PCRE is used
+ * in the YAML engine
+ *
+ * @throws ParseException on a PCRE internal error
+ *
+ * @see preg_last_error()
+ *
+ * @internal
+ */
+ 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)) {
+ $error = match (preg_last_error()) {
+ \PREG_INTERNAL_ERROR => 'Internal PCRE error.',
+ \PREG_BACKTRACK_LIMIT_ERROR => 'pcre.backtrack_limit reached.',
+ \PREG_RECURSION_LIMIT_ERROR => 'pcre.recursion_limit reached.',
+ \PREG_BAD_UTF8_ERROR => 'Malformed UTF-8 data.',
+ \PREG_BAD_UTF8_OFFSET_ERROR => 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.',
+ default => 'Error.',
+ };
+
+ throw new ParseException($error);
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Trim the tag on top of the value.
+ *
+ * Prevent values such as "!foo {quz: bar}" to be considered as
+ * a mapping block.
+ */
+ private function trimTag(string $value): string
+ {
+ if ('!' === $value[0]) {
+ return ltrim(substr($value, 1, strcspn($value, " \r\n", 1)), ' ');
+ }
+
+ return $value;
+ }
+
+ private function getLineTag(string $value, int $flags, bool $nextLineCheck = true): ?string
+ {
+ if ('' === $value || '!' !== $value[0] || 1 !== self::preg_match('/^'.self::TAG_PATTERN.' *( +#.*)?$/', $value, $matches)) {
+ return null;
+ }
+
+ if ($nextLineCheck && !$this->isNextLineIndented()) {
+ return null;
+ }
+
+ $tag = substr($matches['tag'], 1);
+
+ // 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);
+ }
+
+ 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);
+ }
+
+ private function lexInlineQuotedString(int &$cursor = 0): string
+ {
+ $quotation = $this->currentLine[$cursor];
+ $value = $quotation;
+ ++$cursor;
+
+ $previousLineWasNewline = true;
+ $previousLineWasTerminatedWithBackslash = false;
+ $lineNumber = 0;
+
+ do {
+ if (++$lineNumber > 1) {
+ $cursor += strspn($this->currentLine, ' ', $cursor);
+ }
+
+ if ($this->isCurrentLineBlank()) {
+ $value .= "\n";
+ } elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) {
+ $value .= ' ';
+ }
+
+ for (; \strlen($this->currentLine) > $cursor; ++$cursor) {
+ switch ($this->currentLine[$cursor]) {
+ case '\\':
+ if ("'" === $quotation) {
+ $value .= '\\';
+ } elseif (isset($this->currentLine[++$cursor])) {
+ $value .= '\\'.$this->currentLine[$cursor];
+ }
+
+ break;
+ case $quotation:
+ ++$cursor;
+
+ if ("'" === $quotation && isset($this->currentLine[$cursor]) && "'" === $this->currentLine[$cursor]) {
+ $value .= "''";
+ break;
+ }
+
+ return $value.$quotation;
+ default:
+ $value .= $this->currentLine[$cursor];
+ }
+ }
+
+ if ($this->isCurrentLineBlank()) {
+ $previousLineWasNewline = true;
+ $previousLineWasTerminatedWithBackslash = false;
+ } elseif ('\\' === $this->currentLine[-1]) {
+ $previousLineWasNewline = false;
+ $previousLineWasTerminatedWithBackslash = true;
+ } else {
+ $previousLineWasNewline = false;
+ $previousLineWasTerminatedWithBackslash = false;
+ }
+
+ if ($this->hasMoreLines()) {
+ $cursor = 0;
+ }
+ } while ($this->moveToNextLine());
+
+ throw new ParseException('Malformed inline YAML string.');
+ }
+
+ private function lexUnquotedString(int &$cursor): string
+ {
+ $offset = $cursor;
+ $cursor += strcspn($this->currentLine, '[]{},: ', $cursor);
+
+ if ($cursor === $offset) {
+ throw new ParseException('Malformed unquoted YAML string.');
+ }
+
+ return substr($this->currentLine, $offset, $cursor - $offset);
+ }
+
+ private function lexInlineMapping(int &$cursor = 0): string
+ {
+ return $this->lexInlineStructure($cursor, '}');
+ }
+
+ private function lexInlineSequence(int &$cursor = 0): string
+ {
+ return $this->lexInlineStructure($cursor, ']');
+ }
+
+ private function lexInlineStructure(int &$cursor, string $closingTag): string
+ {
+ $value = $this->currentLine[$cursor];
+ ++$cursor;
+
+ do {
+ $this->consumeWhitespaces($cursor);
+
+ while (isset($this->currentLine[$cursor])) {
+ switch ($this->currentLine[$cursor]) {
+ case '"':
+ case "'":
+ $value .= $this->lexInlineQuotedString($cursor);
+ break;
+ case ':':
+ case ',':
+ $value .= $this->currentLine[$cursor];
+ ++$cursor;
+ break;
+ case '{':
+ $value .= $this->lexInlineMapping($cursor);
+ break;
+ case '[':
+ $value .= $this->lexInlineSequence($cursor);
+ break;
+ case $closingTag:
+ $value .= $this->currentLine[$cursor];
+ ++$cursor;
+
+ return $value;
+ case '#':
+ break 2;
+ default:
+ $value .= $this->lexUnquotedString($cursor);
+ }
+
+ if ($this->consumeWhitespaces($cursor)) {
+ $value .= ' ';
+ }
+ }
+
+ if ($this->hasMoreLines()) {
+ $cursor = 0;
+ }
+ } while ($this->moveToNextLine());
+
+ throw new ParseException('Malformed inline YAML string.');
+ }
+
+ private function consumeWhitespaces(int &$cursor): bool
+ {
+ $whitespacesConsumed = 0;
+
+ do {
+ $whitespaceOnlyTokenLength = strspn($this->currentLine, ' ', $cursor);
+ $whitespacesConsumed += $whitespaceOnlyTokenLength;
+ $cursor += $whitespaceOnlyTokenLength;
+
+ if (isset($this->currentLine[$cursor])) {
+ return 0 < $whitespacesConsumed;
+ }
+
+ if ($this->hasMoreLines()) {
+ $cursor = 0;
+ }
+ } while ($this->moveToNextLine());
+
+ return 0 < $whitespacesConsumed;
+ }
+}
diff --git a/src/ncc/ThirdParty/Symfony/Yaml/README.md b/src/ncc/ThirdParty/Symfony/Yaml/README.md
new file mode 100644
index 0000000..ac25024
--- /dev/null
+++ b/src/ncc/ThirdParty/Symfony/Yaml/README.md
@@ -0,0 +1,13 @@
+Yaml Component
+==============
+
+The Yaml component loads and dumps YAML files.
+
+Resources
+---------
+
+ * [Documentation](https://symfony.com/doc/current/components/yaml.html)
+ * [Contributing](https://symfony.com/doc/current/contributing/index.html)
+ * [Report issues](https://github.com/symfony/symfony/issues) and
+ [send Pull Requests](https://github.com/symfony/symfony/pulls)
+ in the [main Symfony repository](https://github.com/symfony/symfony)
diff --git a/src/ncc/ThirdParty/Symfony/Yaml/Tag/TaggedValue.php b/src/ncc/ThirdParty/Symfony/Yaml/Tag/TaggedValue.php
new file mode 100644
index 0000000..73b313e
--- /dev/null
+++ b/src/ncc/ThirdParty/Symfony/Yaml/Tag/TaggedValue.php
@@ -0,0 +1,38 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace ncc\ThirdParty\Symfony\Yaml\Tag;
+
+/**
+ * @author Nicolas Grekas ]
+ * @author Guilhem N.
+ */
+final class TaggedValue
+{
+ private string $tag;
+ private mixed $value;
+
+ public function __construct(string $tag, mixed $value)
+ {
+ $this->tag = $tag;
+ $this->value = $value;
+ }
+
+ public function getTag(): string
+ {
+ return $this->tag;
+ }
+
+ public function getValue()
+ {
+ return $this->value;
+ }
+}
diff --git a/src/ncc/ThirdParty/Symfony/Yaml/Unescaper.php b/src/ncc/ThirdParty/Symfony/Yaml/Unescaper.php
new file mode 100644
index 0000000..c3d9ca1
--- /dev/null
+++ b/src/ncc/ThirdParty/Symfony/Yaml/Unescaper.php
@@ -0,0 +1,110 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace ncc\ThirdParty\Symfony\Yaml;
+
+use ncc\ThirdParty\Symfony\Yaml\Exception\ParseException;
+
+/**
+ * Unescaper encapsulates unescaping rules for single and double-quoted
+ * YAML strings.
+ *
+ * @author Matthew Lewinski
+ *
+ * @internal
+ */
+class Unescaper
+{
+ /**
+ * Regex fragment that matches an escaped character in a double quoted string.
+ */
+ public const REGEX_ESCAPED_CHARACTER = '\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)';
+
+ /**
+ * Unescapes a single quoted string.
+ *
+ * @param string $value A single quoted string
+ */
+ public function unescapeSingleQuotedString(string $value): string
+ {
+ return str_replace('\'\'', '\'', $value);
+ }
+
+ /**
+ * Unescapes a double quoted string.
+ *
+ * @param string $value A double quoted string
+ */
+ public function unescapeDoubleQuotedString(string $value): string
+ {
+ $callback = function ($match) {
+ return $this->unescapeCharacter($match[0]);
+ };
+
+ // evaluate the string
+ return preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u', $callback, $value);
+ }
+
+ /**
+ * Unescapes a character that was found in a double-quoted string.
+ *
+ * @param string $value An escaped character
+ */
+ private function unescapeCharacter(string $value): string
+ {
+ return match ($value[1]) {
+ '0' => "\x0",
+ 'a' => "\x7",
+ 'b' => "\x8",
+ 't' => "\t",
+ "\t" => "\t",
+ 'n' => "\n",
+ 'v' => "\xB",
+ 'f' => "\xC",
+ 'r' => "\r",
+ 'e' => "\x1B",
+ ' ' => ' ',
+ '"' => '"',
+ '/' => '/',
+ '\\' => '\\',
+ // U+0085 NEXT LINE
+ 'N' => "\xC2\x85",
+ // U+00A0 NO-BREAK SPACE
+ '_' => "\xC2\xA0",
+ // U+2028 LINE SEPARATOR
+ 'L' => "\xE2\x80\xA8",
+ // U+2029 PARAGRAPH SEPARATOR
+ 'P' => "\xE2\x80\xA9",
+ 'x' => self::utf8chr(hexdec(substr($value, 2, 2))),
+ 'u' => self::utf8chr(hexdec(substr($value, 2, 4))),
+ 'U' => self::utf8chr(hexdec(substr($value, 2, 8))),
+ default => throw new ParseException(sprintf('Found unknown escape character "%s".', $value)),
+ };
+ }
+
+ /**
+ * Get the UTF-8 character for the given code point.
+ */
+ private static function utf8chr(int $c): string
+ {
+ if (0x80 > $c %= 0x200000) {
+ return \chr($c);
+ }
+ if (0x800 > $c) {
+ return \chr(0xC0 | $c >> 6).\chr(0x80 | $c & 0x3F);
+ }
+ if (0x10000 > $c) {
+ return \chr(0xE0 | $c >> 12).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F);
+ }
+
+ return \chr(0xF0 | $c >> 18).\chr(0x80 | $c >> 12 & 0x3F).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F);
+ }
+}
diff --git a/src/ncc/ThirdParty/Symfony/Yaml/VERSION b/src/ncc/ThirdParty/Symfony/Yaml/VERSION
new file mode 100644
index 0000000..d7ff925
--- /dev/null
+++ b/src/ncc/ThirdParty/Symfony/Yaml/VERSION
@@ -0,0 +1 @@
+6.1.3
\ No newline at end of file
diff --git a/src/ncc/ThirdParty/Symfony/Yaml/Yaml.php b/src/ncc/ThirdParty/Symfony/Yaml/Yaml.php
new file mode 100644
index 0000000..7c9b14e
--- /dev/null
+++ b/src/ncc/ThirdParty/Symfony/Yaml/Yaml.php
@@ -0,0 +1,96 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace ncc\ThirdParty\Symfony\Yaml;
+
+use ncc\ThirdParty\Symfony\Yaml\Exception\ParseException;
+
+/**
+ * Yaml offers convenience methods to load and dump YAML.
+ *
+ * @author Fabien Potencier
+ *
+ * @final
+ */
+class Yaml
+{
+ public const DUMP_OBJECT = 1;
+ public const PARSE_EXCEPTION_ON_INVALID_TYPE = 2;
+ public const PARSE_OBJECT = 4;
+ public const PARSE_OBJECT_FOR_MAP = 8;
+ public const DUMP_EXCEPTION_ON_INVALID_TYPE = 16;
+ public const PARSE_DATETIME = 32;
+ public const DUMP_OBJECT_AS_MAP = 64;
+ public const DUMP_MULTI_LINE_LITERAL_BLOCK = 128;
+ public const PARSE_CONSTANT = 256;
+ public const PARSE_CUSTOM_TAGS = 512;
+ public const DUMP_EMPTY_ARRAY_AS_SEQUENCE = 1024;
+ public const DUMP_NULL_AS_TILDE = 2048;
+
+ /**
+ * Parses a YAML file into a PHP value.
+ *
+ * Usage:
+ *
+ * $array = Yaml::parseFile('config.yml');
+ * print_r($array);
+ *
+ * @param string $filename The path to the YAML file to be parsed
+ * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
+ *
+ * @throws ParseException If the file could not be read or the YAML is not valid
+ */
+ public static function parseFile(string $filename, int $flags = 0): mixed
+ {
+ $yaml = new Parser();
+
+ return $yaml->parseFile($filename, $flags);
+ }
+
+ /**
+ * Parses YAML into a PHP value.
+ *
+ * Usage:
+ *
+ * $array = Yaml::parse(file_get_contents('config.yml'));
+ * print_r($array);
+ *
+ *
+ * @param string $input A string containing YAML
+ * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
+ *
+ * @throws ParseException If the YAML is not valid
+ */
+ public static function parse(string $input, int $flags = 0): mixed
+ {
+ $yaml = new Parser();
+
+ return $yaml->parse($input, $flags);
+ }
+
+ /**
+ * Dumps a PHP value to a YAML string.
+ *
+ * The dump method, when supplied with an array, will do its best
+ * to convert the array into friendly YAML.
+ *
+ * @param mixed $input The PHP value
+ * @param int $inline The level where you switch to inline YAML
+ * @param int $indent The amount of spaces to use for indentation of nested nodes
+ * @param int $flags A bit field of DUMP_* constants to customize the dumped YAML string
+ */
+ public static function dump(mixed $input, int $inline = 2, int $indent = 4, int $flags = 0): string
+ {
+ $yaml = new Dumper($indent);
+
+ return $yaml->dump($input, $inline, 0, $flags);
+ }
+}
diff --git a/src/ncc/ThirdParty/austinhyde/IniParser/IniParser.php b/src/ncc/ThirdParty/austinhyde/IniParser/IniParser.php
deleted file mode 100644
index 6511c8a..0000000
--- a/src/ncc/ThirdParty/austinhyde/IniParser/IniParser.php
+++ /dev/null
@@ -1,282 +0,0 @@
-
- */
-class IniParser
-{
-
- /**
- * Filename of our .ini file.
- * @var string
- */
- protected $file;
-
- /**
- * Enable/disable property nesting feature
- * @var boolean
- */
- public $property_nesting = true;
-
- /**
- * Use ArrayObject to allow array work as object (true) or use native arrays (false)
- * @var boolean
- */
- public $use_array_object = true;
-
- /**
- * Include original sections (pre-inherit names) on the final output
- * @var boolean
- */
- public $include_original_sections = false;
-
- /**
- * Disable array literal parsing
- */
- const NO_PARSE = 0;
-
- /**
- * Parse simple arrays using regex (ex: [a,b,c,...])
- */
- const PARSE_SIMPLE = 1;
-
- /**
- * Parse array literals using JSON, allowing advanced features like
- * dictionaries, array nesting, etc.
- */
- const PARSE_JSON = 2;
-
- /**
- * Array literals parse mode
- * @var int
- */
- public $array_literals_behavior = self::PARSE_SIMPLE;
-
- /**
- * @param string $file
- *
- * @return IniParser
- */
- public function __construct($file = null)
- {
- if ($file !== null) {
- $this->setFile($file);
- }
- }
-
- /**
- * Parses an INI file
- *
- * @param string $file
- * @return array
- */
- public function parse($file = null)
- {
- if ($file !== null) {
- $this->setFile($file);
- }
- if (empty($this->file)) {
- throw new LogicException("Need a file to parse.");
- }
-
- $simple_parsed = parse_ini_file($this->file, true);
- $inheritance_parsed = $this->parseSections($simple_parsed);
- return $this->parseKeys($inheritance_parsed);
- }
-
- /**
- * Parses a string with INI contents
- *
- * @param string $src
- *
- * @return array
- */
- public function process($src)
- {
- $simple_parsed = parse_ini_string($src, true);
- $inheritance_parsed = $this->parseSections($simple_parsed);
- return $this->parseKeys($inheritance_parsed);
- }
-
- /**
- * @param string $file
- *
- * @return IniParser
- * @throws InvalidArgumentException
- */
- public function setFile($file)
- {
- if (!file_exists($file) || !is_readable($file)) {
- throw new InvalidArgumentException("The file '{$file}' cannot be opened.");
- }
- $this->file = $file;
- return $this;
- }
-
- /**
- * Parse sections and inheritance.
- * @param array $simple_parsed
- * @return array Parsed sections
- */
- private function parseSections(array $simple_parsed)
- {
- // do an initial pass to gather section names
- $sections = array();
- $globals = array();
- foreach ($simple_parsed as $k => $v) {
- if (is_array($v)) {
- // $k is a section name
- $sections[$k] = $v;
- } else {
- $globals[$k] = $v;
- }
- }
-
- // now for each section, see if it uses inheritance
- $output_sections = array();
- foreach ($sections as $k => $v) {
- $sects = array_map('trim', array_reverse(explode(':', $k)));
- $root = array_pop($sects);
- $arr = $v;
- foreach ($sects as $s) {
- if ($s === '^') {
- $arr = array_merge($globals, $arr);
- } elseif (array_key_exists($s, $output_sections)) {
- $arr = array_merge($output_sections[$s], $arr);
- } elseif (array_key_exists($s, $sections)) {
- $arr = array_merge($sections[$s], $arr);
- } else {
- throw new UnexpectedValueException("IniParser: In file '{$this->file}', section '{$root}': Cannot inherit from unknown section '{$s}'");
- }
- }
-
- if ($this->include_original_sections) {
- $output_sections[$k] = $v;
- }
- $output_sections[$root] = $arr;
- }
-
-
- return $globals + $output_sections;
- }
-
- /**
- * @param array $arr
- *
- * @return array
- */
- private function parseKeys(array $arr)
- {
- $output = $this->getArrayValue();
- $append_regex = '/\s*\+\s*$/';
- foreach ($arr as $k => $v) {
- if (is_array($v) && FALSE === strpos($k, '.')) {
- // this element represents a section; recursively parse the value
- $output[$k] = $this->parseKeys($v);
- } else {
- // if the key ends in a +, it means we should append to the previous value, if applicable
- $append = false;
- if (preg_match($append_regex, $k)) {
- $k = preg_replace($append_regex, '', $k);
- $append = true;
- }
-
- // transform "a.b.c = x" into $output[a][b][c] = x
- $current = &$output;
-
- $path = $this->property_nesting ? explode('.', $k) : array($k);
- while (($current_key = array_shift($path)) !== null) {
- if ('string' === gettype($current)) {
- $current = array($current);
- }
-
- if (!array_key_exists($current_key, $current)) {
- if (!empty($path)) {
- $current[$current_key] = $this->getArrayValue();
- } else {
- $current[$current_key] = null;
- }
- }
- $current = &$current[$current_key];
- }
-
- // parse value
- $value = $v;
- if (!is_array($v)) {
- $value = $this->parseValue($v);
- }
-
- if ($append && $current !== null) {
- if (is_array($value)) {
- if (!is_array($current)) {
- throw new LogicException("Cannot append array to inherited value '{$k}'");
- }
- $value = array_merge($current, $value);
- } else {
- $value = $current . $value;
- }
- }
-
- $current = $value;
- }
- }
-
- return $output;
- }
-
- /**
- * Parses and formats the value in a key-value pair
- *
- * @param string $value
- *
- * @return mixed
- */
- protected function parseValue($value)
- {
- switch ($this->array_literals_behavior) {
- case self::PARSE_JSON:
- if (in_array(substr($value, 0, 1), array('[', '{')) && in_array(substr($value, -1), array(']', '}'))) {
- if (defined('JSON_BIGINT_AS_STRING')) {
- $output = json_decode($value, true, 512, JSON_BIGINT_AS_STRING);
- } else {
- $output = json_decode($value, true);
- }
-
- if ($output !== NULL) {
- return $output;
- }
- }
- // fallthrough
- // try regex parser for simple estructures not JSON-compatible (ex: colors = [blue, green, red])
- case self::PARSE_SIMPLE:
- // if the value looks like [a,b,c,...], interpret as array
- if (preg_match('/^\[\s*.*?(?:\s*,\s*.*?)*\s*\]$/', trim($value))) {
- return array_map('trim', explode(',', trim(trim($value), '[]')));
- }
- break;
- }
- return $value;
- }
-
- protected function getArrayValue($array = array())
- {
- if ($this->use_array_object) {
- return new ArrayObject($array, ArrayObject::ARRAY_AS_PROPS);
- } else {
- return $array;
- }
- }
-
-}
diff --git a/src/ncc/ThirdParty/austinhyde/IniParser/README.markdown b/src/ncc/ThirdParty/austinhyde/IniParser/README.markdown
deleted file mode 100644
index ecbb4bd..0000000
--- a/src/ncc/ThirdParty/austinhyde/IniParser/README.markdown
+++ /dev/null
@@ -1,247 +0,0 @@
-# IniParser
-
-[](http://travis-ci.org/austinhyde/IniParser)
-
-IniParser is a simple parser for complex INI files, providing a number of extra syntactic features to the built-in INI parsing functions, including section inheritance, property nesting, and array literals.
-
-**IMPORTANT:** IniParser should be considered beta-quality, and there may still be bugs. Feel free to open an issue or submit a pull request, and I'll take a look at it!
-
-## Installing by [Composer](https://getcomposer.org)
-
-Set your `composer.json` file to have :
-
-```json
-{
- "require": {
- "austinhyde/iniparser": "dev-master"
- }
-}
-```
-
-Then install the dependencies :
-
-```shell
-composer install
-```
-
-## An Example
-
-Standard INI files look like this:
-
- key = value
- another_key = another value
-
- [section_name]
- a_sub_key = yet another value
-
-And when parsed with PHP's built-in `parse_ini_string()` or `parse_ini_file()`, looks like
-
-```php
-array(
- 'key' => 'value',
- 'another_key' => 'another value',
- 'section_name' => array(
- 'a_sub_key' => 'yet another value'
- )
-)
-```
-
-This is great when you just want a simple configuration file, but here is a super-charged INI file that you might find in the wild:
-
- environment = testing
-
- [testing]
- debug = true
- database.connection = "mysql:host=127.0.0.1"
- database.name = test
- database.username =
- database.password =
- secrets = [1,2,3]
-
- [staging : testing]
- database.name = stage
- database.username = staging
- database.password = 12345
-
- [production : staging]
- debug = false;
- database.name = production
- database.username = root
-
-And when parsed with IniParser:
-
- $parser = new \IniParser('sample.ini');
- $config = $parser->parse();
-
-You get the following structure:
-
-```php
-array(
- 'environment' => 'testing',
- 'testing' => array(
- 'debug' => '1',
- 'database' => array(
- 'connection' => 'mysql:host=127.0.0.1',
- 'name' => 'test',
- 'username' => '',
- 'password' => ''
- ),
- 'secrets' => array('1','2','3')
- ),
- 'staging' => array(
- 'debug' => '1',
- 'database' => array(
- 'connection' => 'mysql:host=127.0.0.1',
- 'name' => 'stage',
- 'username' => 'staging',
- 'password' => '12345'
- ),
- 'secrets' => array('1','2','3')
- ),
- 'production' => array(
- 'debug' => '',
- 'database' => array(
- 'connection' => 'mysql:host=127.0.0.1',
- 'name' => 'production',
- 'username' => 'root',
- 'password' => '12345'
- ),
- 'secrets' => array('1','2','3')
- )
-)
-```
-
-## Supported Features
-
-### Array Literals
-
-You can directly create arrays using the syntax `[a, b, c]` on the right hand side of an assignment. For example:
-
- colors = [blue, green, red]
-
-**NOTE:** At the moment, quoted strings inside array literals have undefined behavior.
-
-### Dictionaries and complex structures
-
-Besides arrays, you can create dictionaries and more complex structures using JSON syntax. For example, you can use:
-
- people = '{
- "boss": {
- "name": "John",
- "age": 42
- },
- "staff": [
- {
- "name": "Mark",
- "age": 35
- },
- {
- "name": "Bill",
- "age": 44
- }
- ]
- }'
-
-This turns into an array like:
-
-```php
-array(
- 'boss' => array(
- 'name' => 'John',
- 'age' => 42
- ),
- 'staff' => array(
- array (
- 'name' => 'Mark',
- 'age' => 35,
- ),
- array (
- 'name' => 'Bill',
- 'age' => 44,
- ),
- ),
-)
-```
-
-**NOTE:** Remember to wrap the JSON strings in single quotes for a correct analysis. The JSON names must be enclosed in double quotes and trailing commas are not allowed.
-
-### Property Nesting
-
-IniParser allows you to treat properties as associative arrays:
-
- person.age = 42
- person.name.first = John
- person.name.last = Doe
-
-This turns into an array like:
-
-```php
-array (
- 'person' => array (
- 'age' => 42,
- 'name' => array (
- 'first' => 'John',
- 'last' => 'Doe'
- )
- )
-)
-```
-
-### Section Inheritance
-
-Keeping to the DRY principle, IniParser allows you to "inherit" from other sections (similar to OOP inheritance), meaning you don't have to continually re-define the same properties over and over again. As you can see in the example above, "production" inherits from "staging", which in turn inherits from "testing".
-
-You can even inherit from multiple parents, as in `[child : p1 : p2 : p3]`. The properties of each parent are merged into the child from left to right, so that the properties in `p1` are overridden by those in `p2`, then by `p3`, then by those in `child` on top of that.
-
-During the inheritance process, if a key ends in a `+`, the merge behavior changes from overwriting the parent value to prepending the parent value (or appending the child value - same thing). So the example file
-
- [parent]
- arr = [a,b,c]
- val = foo
-
- [child : parent]
- arr += [x,y,z]
- val += bar
-
-would be parsed into the following:
-
-```php
-array(
- 'parent' => array(
- 'arr' => array('a','b','c'),
- 'val' => 'foo'
- ),
- 'child' => array(
- 'arr' => array('a','b','c','x','y','z'),
- 'val' => 'foobar'
- )
-)
-```
-
-*If you can think of a more useful operation than concatenation for non-array types, please open an issue*
-
-Finally, it is possible to inherit from the special `^` section, representing the top-level or global properties:
-
- foo = bar
-
- [sect : ^]
-
-Parses to:
-
-```php
-array (
- 'foo' => 'bar',
- 'sect' => array (
- 'foo' => 'bar'
- )
-)
-```
-
-### ArrayObject
-
-As an added bonus, IniParser also allows you to access the values OO-style:
-
-```php
-echo $config->production->database->connection; // output: mysql:host=127.0.0.1
-echo $config->staging->debug; // output: 1
-```
diff --git a/src/ncc/ThirdParty/austinhyde/IniParser/VERSION b/src/ncc/ThirdParty/austinhyde/IniParser/VERSION
deleted file mode 100644
index afaf360..0000000
--- a/src/ncc/ThirdParty/austinhyde/IniParser/VERSION
+++ /dev/null
@@ -1 +0,0 @@
-1.0.0
\ No newline at end of file
diff --git a/src/ncc/autoload.php b/src/ncc/autoload.php
index a1d00d1..2bb52d7 100644
--- a/src/ncc/autoload.php
+++ b/src/ncc/autoload.php
@@ -21,6 +21,7 @@
$third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Process' . DIRECTORY_SEPARATOR . 'autoload_spl.php',
$third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Uid' . DIRECTORY_SEPARATOR . 'autoload_spl.php',
$third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Filesystem' . DIRECTORY_SEPARATOR . 'autoload_spl.php',
+ $third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Yaml' . DIRECTORY_SEPARATOR . 'autoload_spl.php',
];
foreach($target_files as $file)
diff --git a/src/ncc/version.json b/src/ncc/version.json
index faf0ca7..55d3da6 100644
--- a/src/ncc/version.json
+++ b/src/ncc/version.json
@@ -28,8 +28,8 @@
"package_name": "Filesystem"
},
{
- "vendor": "austinhyde",
- "package_name": "IniParser"
+ "vendor": "Symfony",
+ "package_name": "Yaml"
}
],
"update_source": null