Пример #1
0
 /**
  * Parses a YAML string to a PHP value.
  *
  * @param string $value A YAML string
  *
  * @return mixed  A PHP value
  *
  * @throws ParseException If the YAML is not valid
  */
 public function parse($value)
 {
     $this->currentLineNb = -1;
     $this->currentLine = '';
     $this->lines = explode("\n", $this->cleanup($value));
     if (function_exists('mb_detect_encoding') && false === mb_detect_encoding($value, 'UTF-8', true)) {
         throw new ParseException('The YAML value does not appear to be valid UTF-8.');
     }
     if (function_exists('mb_internal_encoding') && (int) ini_get('mbstring.func_overload') & 2) {
         $mbEncoding = mb_internal_encoding();
         mb_internal_encoding('UTF-8');
     }
     $data = array();
     $context = null;
     while ($this->moveToNextLine()) {
         if ($this->isCurrentLineEmpty()) {
             continue;
         }
         // tab?
         if ("\t" === $this->currentLine[0]) {
             throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
         }
         $isRef = $isInPlace = $isProcessed = false;
         if (preg_match('#^\\-((?P<leadspaces>\\s+)(?P<value>.+?))?\\s*$#u', $this->currentLine, $values)) {
             if ($context && 'mapping' == $context) {
                 throw new ParseException('You cannot define a sequence item when in a mapping');
             }
             $context = 'sequence';
             if (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
                 $isRef = $matches['ref'];
                 $values['value'] = $matches['value'];
             }
             // array
             if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
                 $c = $this->getRealCurrentLineNb() + 1;
                 $parser = new Parser($c);
                 $parser->refs =& $this->refs;
                 $data[] = $parser->parse($this->getNextEmbedBlock());
             } else {
                 if (isset($values['leadspaces']) && ' ' == $values['leadspaces'] && preg_match('#^(?P<key>' . Inline::REGEX_QUOTED_STRING . '|[^ \'"\\{\\[].*?) *\\:(\\s+(?P<value>.+?))?\\s*$#u', $values['value'], $matches)) {
                     // this is a compact notation element, add to next block and parse
                     $c = $this->getRealCurrentLineNb();
                     $parser = new Parser($c);
                     $parser->refs =& $this->refs;
                     $block = $values['value'];
                     if (!$this->isNextLineIndented()) {
                         $block .= "\n" . $this->getNextEmbedBlock($this->getCurrentLineIndentation() + 2);
                     }
                     $data[] = $parser->parse($block);
                 } else {
                     $data[] = $this->parseValue($values['value']);
                 }
             }
         } elseif (preg_match('#^(?P<key>' . Inline::REGEX_QUOTED_STRING . '|[^ \'"\\[\\{].*?) *\\:(\\s+(?P<value>.+?))?\\s*$#u', $this->currentLine, $values)) {
             if ($context && 'sequence' == $context) {
                 throw new ParseException('You cannot define a mapping item when in a sequence');
             }
             $context = 'mapping';
             try {
                 $key = Inline::parseScalar($values['key']);
             } catch (ParseException $e) {
                 $e->setParsedLine($this->getRealCurrentLineNb() + 1);
                 $e->setSnippet($this->currentLine);
                 throw $e;
             }
             if ('<<' === $key) {
                 if (isset($values['value']) && 0 === strpos($values['value'], '*')) {
                     $isInPlace = substr($values['value'], 1);
                     if (!array_key_exists($isInPlace, $this->refs)) {
                         throw new ParseException(sprintf('Reference "%s" does not exist.', $isInPlace), $this->getRealCurrentLineNb() + 1, $this->currentLine);
                     }
                 } else {
                     if (isset($values['value']) && $values['value'] !== '') {
                         $value = $values['value'];
                     } else {
                         $value = $this->getNextEmbedBlock();
                     }
                     $c = $this->getRealCurrentLineNb() + 1;
                     $parser = new Parser($c);
                     $parser->refs =& $this->refs;
                     $parsed = $parser->parse($value);
                     $merged = array();
                     if (!is_array($parsed)) {
                         throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
                     } elseif (isset($parsed[0])) {
                         // Numeric array, merge individual elements
                         foreach (array_reverse($parsed) as $parsedItem) {
                             if (!is_array($parsedItem)) {
                                 throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem);
                             }
                             $merged = array_merge($parsedItem, $merged);
                         }
                     } else {
                         // Associative array, merge
                         $merged = array_merge($merged, $parsed);
                     }
                     $isProcessed = $merged;
                 }
             } elseif (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
                 $isRef = $matches['ref'];
                 $values['value'] = $matches['value'];
             }
             if ($isProcessed) {
                 // Merge keys
                 $data = $isProcessed;
                 // hash
             } elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
                 // if next line is less indented or equal, then it means that the current value is null
                 if ($this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) {
                     $data[$key] = null;
                 } else {
                     $c = $this->getRealCurrentLineNb() + 1;
                     $parser = new Parser($c);
                     $parser->refs =& $this->refs;
                     $data[$key] = $parser->parse($this->getNextEmbedBlock());
                 }
             } else {
                 if ($isInPlace) {
                     $data = $this->refs[$isInPlace];
                 } else {
                     $data[$key] = $this->parseValue($values['value']);
                 }
             }
         } else {
             // 1-liner followed by newline
             if (2 == count($this->lines) && empty($this->lines[1])) {
                 try {
                     $value = Inline::parse($this->lines[0]);
                 } catch (ParseException $e) {
                     $e->setParsedLine($this->getRealCurrentLineNb() + 1);
                     $e->setSnippet($this->currentLine);
                     throw $e;
                 }
                 if (is_array($value)) {
                     $first = reset($value);
                     if (is_string($first) && 0 === strpos($first, '*')) {
                         $data = array();
                         foreach ($value as $alias) {
                             $data[] = $this->refs[substr($alias, 1)];
                         }
                         $value = $data;
                     }
                 }
                 if (isset($mbEncoding)) {
                     mb_internal_encoding($mbEncoding);
                 }
                 return $value;
             }
             switch (preg_last_error()) {
                 case PREG_INTERNAL_ERROR:
                     $error = 'Internal PCRE error.';
                     break;
                 case PREG_BACKTRACK_LIMIT_ERROR:
                     $error = 'pcre.backtrack_limit reached.';
                     break;
                 case PREG_RECURSION_LIMIT_ERROR:
                     $error = 'pcre.recursion_limit reached.';
                     break;
                 case PREG_BAD_UTF8_ERROR:
                     $error = 'Malformed UTF-8 data.';
                     break;
                 case PREG_BAD_UTF8_OFFSET_ERROR:
                     $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.';
                     break;
                 default:
                     $error = 'Unable to parse.';
             }
             throw new ParseException($error, $this->getRealCurrentLineNb() + 1, $this->currentLine);
         }
         if ($isRef) {
             $this->refs[$isRef] = end($data);
         }
     }
     if (isset($mbEncoding)) {
         mb_internal_encoding($mbEncoding);
     }
     return empty($data) ? null : $data;
 }
Пример #2
0
 /**
  * Parses a YAML string to a PHP value.
  *
  * @param string  $value                  A YAML string
  * @param bool    $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
  * @param bool    $objectSupport          true if object support is enabled, false otherwise
  *
  * @return mixed  A PHP value
  *
  * @throws ParseException If the YAML is not valid
  */
 public function parse($value, $exceptionOnInvalidType = false, $objectSupport = false)
 {
     $this->currentLineNb = -1;
     $this->currentLine = '';
     $this->lines = explode("\n", $this->cleanup($value));
     if (!preg_match('//u', $value)) {
         throw new ParseException('The YAML value does not appear to be valid UTF-8.');
     }
     if (function_exists('mb_internal_encoding') && (int) ini_get('mbstring.func_overload') & 2) {
         $mbEncoding = mb_internal_encoding();
         mb_internal_encoding('UTF-8');
     }
     $data = array();
     $context = null;
     $allowOverwrite = false;
     while ($this->moveToNextLine()) {
         if ($this->isCurrentLineEmpty()) {
             continue;
         }
         // tab?
         if ("\t" === $this->currentLine[0]) {
             throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
         }
         $isRef = $mergeNode = false;
         if (preg_match('#^\\-((?P<leadspaces>\\s+)(?P<value>.+?))?\\s*$#u', $this->currentLine, $values)) {
             if ($context && 'mapping' == $context) {
                 throw new ParseException('You cannot define a sequence item when in a mapping');
             }
             $context = 'sequence';
             if (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
                 $isRef = $matches['ref'];
                 $values['value'] = $matches['value'];
             }
             // array
             if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
                 $c = $this->getRealCurrentLineNb() + 1;
                 $parser = new Parser($c);
                 $parser->refs =& $this->refs;
                 $data[] = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport);
             } else {
                 if (isset($values['leadspaces']) && ' ' == $values['leadspaces'] && preg_match('#^(?P<key>' . Inline::REGEX_QUOTED_STRING . '|[^ \'"\\{\\[].*?) *\\:(\\s+(?P<value>.+?))?\\s*$#u', $values['value'], $matches)) {
                     // this is a compact notation element, add to next block and parse
                     $c = $this->getRealCurrentLineNb();
                     $parser = new Parser($c);
                     $parser->refs =& $this->refs;
                     $block = $values['value'];
                     if ($this->isNextLineIndented()) {
                         $block .= "\n" . $this->getNextEmbedBlock($this->getCurrentLineIndentation() + 2);
                     }
                     $data[] = $parser->parse($block, $exceptionOnInvalidType, $objectSupport);
                 } else {
                     $data[] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport);
                 }
             }
         } elseif (preg_match('#^(?P<key>' . Inline::REGEX_QUOTED_STRING . '|[^ \'"\\[\\{].*?) *\\:(\\s+(?P<value>.+?))?\\s*$#u', $this->currentLine, $values) && (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'")))) {
             if ($context && 'sequence' == $context) {
                 throw new ParseException('You cannot define a mapping item when in a sequence');
             }
             $context = 'mapping';
             // force correct settings
             Inline::parse(null, $exceptionOnInvalidType, $objectSupport, $this->refs);
             try {
                 $key = Inline::parseScalar($values['key']);
             } catch (ParseException $e) {
                 $e->setParsedLine($this->getRealCurrentLineNb() + 1);
                 $e->setSnippet($this->currentLine);
                 throw $e;
             }
             if ('<<' === $key) {
                 $mergeNode = true;
                 $allowOverwrite = true;
                 if (isset($values['value']) && 0 === strpos($values['value'], '*')) {
                     $refName = substr($values['value'], 1);
                     if (!array_key_exists($refName, $this->refs)) {
                         throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine);
                     }
                     $refValue = $this->refs[$refName];
                     if (!is_array($refValue)) {
                         throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
                     }
                     foreach ($refValue as $key => $value) {
                         if (!isset($data[$key])) {
                             $data[$key] = $value;
                         }
                     }
                 } else {
                     if (isset($values['value']) && $values['value'] !== '') {
                         $value = $values['value'];
                     } else {
                         $value = $this->getNextEmbedBlock();
                     }
                     $c = $this->getRealCurrentLineNb() + 1;
                     $parser = new Parser($c);
                     $parser->refs =& $this->refs;
                     $parsed = $parser->parse($value, $exceptionOnInvalidType, $objectSupport);
                     if (!is_array($parsed)) {
                         throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
                     }
                     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 (!is_array($parsedItem)) {
                                 throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem);
                             }
                             foreach ($parsedItem as $key => $value) {
                                 if (!isset($data[$key])) {
                                     $data[$key] = $value;
                                 }
                             }
                         }
                     } 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.
                         foreach ($parsed as $key => $value) {
                             if (!isset($data[$key])) {
                                 $data[$key] = $value;
                             }
                         }
                     }
                 }
             } elseif (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
                 $isRef = $matches['ref'];
                 $values['value'] = $matches['value'];
             }
             if ($mergeNode) {
                 // Merge keys
             } elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
                 // 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])) {
                         $data[$key] = null;
                     }
                 } else {
                     $c = $this->getRealCurrentLineNb() + 1;
                     $parser = new Parser($c);
                     $parser->refs =& $this->refs;
                     $value = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport);
                     // 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 {
                 $value = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport);
                 // 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 {
             // 1-liner optionally followed by newline
             $lineCount = count($this->lines);
             if (1 === $lineCount || 2 === $lineCount && empty($this->lines[1])) {
                 try {
                     $value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport, $this->refs);
                 } catch (ParseException $e) {
                     $e->setParsedLine($this->getRealCurrentLineNb() + 1);
                     $e->setSnippet($this->currentLine);
                     throw $e;
                 }
                 if (is_array($value)) {
                     $first = reset($value);
                     if (is_string($first) && 0 === strpos($first, '*')) {
                         $data = array();
                         foreach ($value as $alias) {
                             $data[] = $this->refs[substr($alias, 1)];
                         }
                         $value = $data;
                     }
                 }
                 if (isset($mbEncoding)) {
                     mb_internal_encoding($mbEncoding);
                 }
                 return $value;
             }
             switch (preg_last_error()) {
                 case PREG_INTERNAL_ERROR:
                     $error = 'Internal PCRE error.';
                     break;
                 case PREG_BACKTRACK_LIMIT_ERROR:
                     $error = 'pcre.backtrack_limit reached.';
                     break;
                 case PREG_RECURSION_LIMIT_ERROR:
                     $error = 'pcre.recursion_limit reached.';
                     break;
                 case PREG_BAD_UTF8_ERROR:
                     $error = 'Malformed UTF-8 data.';
                     break;
                 case PREG_BAD_UTF8_OFFSET_ERROR:
                     $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.';
                     break;
                 default:
                     $error = 'Unable to parse.';
             }
             throw new ParseException($error, $this->getRealCurrentLineNb() + 1, $this->currentLine);
         }
         if ($isRef) {
             $this->refs[$isRef] = end($data);
         }
     }
     if (isset($mbEncoding)) {
         mb_internal_encoding($mbEncoding);
     }
     return empty($data) ? null : $data;
 }
Пример #3
0
 /**
  * Parses a YAML string to a PHP value.
  *
  * @param string $value A YAML string
  * @param int    $flags A bit field of PARSE_* constants to customize the YAML parser behavior
  *
  * @return mixed A PHP value
  *
  * @throws ParseException If the YAML is not valid
  */
 public function parse($value, $flags = 0)
 {
     if (is_bool($flags)) {
         @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
         if ($flags) {
             $flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE;
         } else {
             $flags = 0;
         }
     }
     if (func_num_args() >= 3) {
         @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED);
         if (func_get_arg(2)) {
             $flags |= Yaml::PARSE_OBJECT;
         }
     }
     if (func_num_args() >= 4) {
         @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED);
         if (func_get_arg(3)) {
             $flags |= Yaml::PARSE_OBJECT_FOR_MAP;
         }
     }
     if (!preg_match('//u', $value)) {
         throw new ParseException('The YAML value does not appear to be valid UTF-8.');
     }
     $this->currentLineNb = -1;
     $this->currentLine = '';
     $value = $this->cleanup($value);
     $this->lines = explode("\n", $value);
     if (null === $this->totalNumberOfLines) {
         $this->totalNumberOfLines = count($this->lines);
     }
     if (2 & (int) ini_get('mbstring.func_overload')) {
         $mbEncoding = mb_internal_encoding();
         mb_internal_encoding('UTF-8');
     }
     $data = array();
     $context = null;
     $allowOverwrite = false;
     while ($this->moveToNextLine()) {
         if ($this->isCurrentLineEmpty()) {
             continue;
         }
         // tab?
         if ("\t" === $this->currentLine[0]) {
             throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
         }
         $isRef = $mergeNode = false;
         if (preg_match('#^\\-((?P<leadspaces>\\s+)(?P<value>.+?))?\\s*$#u', $this->currentLine, $values)) {
             if ($context && 'mapping' == $context) {
                 throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine);
             }
             $context = 'sequence';
             if (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
                 $isRef = $matches['ref'];
                 $values['value'] = $matches['value'];
             }
             // array
             if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
                 $data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags);
             } else {
                 if (isset($values['leadspaces']) && preg_match('#^(?P<key>' . Inline::REGEX_QUOTED_STRING . '|[^ \'"\\{\\[].*?) *\\:(\\s+(?P<value>.+?))?\\s*$#u', $values['value'], $matches)) {
                     // this is a compact notation element, add to next block and parse
                     $block = $values['value'];
                     if ($this->isNextLineIndented()) {
                         $block .= "\n" . $this->getNextEmbedBlock($this->getCurrentLineIndentation() + strlen($values['leadspaces']) + 1);
                     }
                     $data[] = $this->parseBlock($this->getRealCurrentLineNb(), $block, $flags);
                 } else {
                     $data[] = $this->parseValue($values['value'], $flags, $context);
                 }
             }
             if ($isRef) {
                 $this->refs[$isRef] = end($data);
             }
         } elseif (preg_match('#^(?P<key>' . Inline::REGEX_QUOTED_STRING . '|[^ \'"\\[\\{].*?) *\\:(\\s+(?P<value>.+?))?\\s*$#u', $this->currentLine, $values) && (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'")))) {
             if ($context && 'sequence' == $context) {
                 throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine);
             }
             $context = 'mapping';
             // force correct settings
             Inline::parse(null, $flags, $this->refs);
             try {
                 Inline::$parsedLineNumber = $this->getRealCurrentLineNb();
                 $key = Inline::parseScalar($values['key']);
             } catch (ParseException $e) {
                 $e->setParsedLine($this->getRealCurrentLineNb() + 1);
                 $e->setSnippet($this->currentLine);
                 throw $e;
             }
             // Convert float keys to strings, to avoid being converted to integers by PHP
             if (is_float($key)) {
                 $key = (string) $key;
             }
             if ('<<' === $key) {
                 $mergeNode = true;
                 $allowOverwrite = true;
                 if (isset($values['value']) && 0 === strpos($values['value'], '*')) {
                     $refName = substr($values['value'], 1);
                     if (!array_key_exists($refName, $this->refs)) {
                         throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine);
                     }
                     $refValue = $this->refs[$refName];
                     if (!is_array($refValue)) {
                         throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
                     }
                     foreach ($refValue as $key => $value) {
                         if (!isset($data[$key])) {
                             $data[$key] = $value;
                         }
                     }
                 } else {
                     if (isset($values['value']) && $values['value'] !== '') {
                         $value = $values['value'];
                     } else {
                         $value = $this->getNextEmbedBlock();
                     }
                     $parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $flags);
                     if (!is_array($parsed)) {
                         throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
                     }
                     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 (!is_array($parsedItem)) {
                                 throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem);
                             }
                             foreach ($parsedItem as $key => $value) {
                                 if (!isset($data[$key])) {
                                     $data[$key] = $value;
                                 }
                             }
                         }
                     } 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.
                         foreach ($parsed as $key => $value) {
                             if (!isset($data[$key])) {
                                 $data[$key] = $value;
                             }
                         }
                     }
                 }
             } elseif (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
                 $isRef = $matches['ref'];
                 $values['value'] = $matches['value'];
             }
             if ($mergeNode) {
                 // Merge keys
             } elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
                 // 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])) {
                         $data[$key] = null;
                     } else {
                         @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \\Symfony\\Component\\Yaml\\Exception\\ParseException in 4.0.', $key, $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED);
                     }
                 } 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);
                     // 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 {
                         @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \\Symfony\\Component\\Yaml\\Exception\\ParseException in 4.0.', $key, $realCurrentLineNbKey + 1), E_USER_DEPRECATED);
                     }
                 }
             } else {
                 $value = $this->parseValue($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 {
                     @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \\Symfony\\Component\\Yaml\\Exception\\ParseException in 4.0.', $key, $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED);
                 }
             }
             if ($isRef) {
                 $this->refs[$isRef] = $data[$key];
             }
         } else {
             // multiple documents are not supported
             if ('---' === $this->currentLine) {
                 throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine);
             }
             // 1-liner optionally followed by newline(s)
             if (is_string($value) && $this->lines[0] === trim($value)) {
                 try {
                     Inline::$parsedLineNumber = $this->getRealCurrentLineNb();
                     $value = Inline::parse($this->lines[0], $flags, $this->refs);
                 } catch (ParseException $e) {
                     $e->setParsedLine($this->getRealCurrentLineNb() + 1);
                     $e->setSnippet($this->currentLine);
                     throw $e;
                 }
                 if (isset($mbEncoding)) {
                     mb_internal_encoding($mbEncoding);
                 }
                 return $value;
             }
             switch (preg_last_error()) {
                 case PREG_INTERNAL_ERROR:
                     $error = 'Internal PCRE error.';
                     break;
                 case PREG_BACKTRACK_LIMIT_ERROR:
                     $error = 'pcre.backtrack_limit reached.';
                     break;
                 case PREG_RECURSION_LIMIT_ERROR:
                     $error = 'pcre.recursion_limit reached.';
                     break;
                 case PREG_BAD_UTF8_ERROR:
                     $error = 'Malformed UTF-8 data.';
                     break;
                 case PREG_BAD_UTF8_OFFSET_ERROR:
                     $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.';
                     break;
                 default:
                     $error = 'Unable to parse.';
             }
             throw new ParseException($error, $this->getRealCurrentLineNb() + 1, $this->currentLine);
         }
     }
     if (isset($mbEncoding)) {
         mb_internal_encoding($mbEncoding);
     }
     if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && !is_object($data) && 'mapping' === $context) {
         $object = new \stdClass();
         foreach ($data as $key => $value) {
             $object->{$key} = $value;
         }
         $data = $object;
     }
     return empty($data) ? null : $data;
 }
 public function testParseScalarWithCorrectlyQuotedStringShouldReturnString()
 {
     $value = "'don''t do somthin'' like that'";
     $expect = "don't do somthin' like that";
     $this->assertSame($expect, Inline::parseScalar($value));
 }
Пример #5
0
public function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false)
{
if (!preg_match('//u', $value)) {
throw new ParseException('The YAML value does not appear to be valid UTF-8.');
}
$this->currentLineNb = -1;
$this->currentLine = '';
$value = $this->cleanup($value);
$this->lines = explode("\n", $value);

if (2  & (int) ini_get('mbstring.func_overload')) {
$mbEncoding = mb_internal_encoding();
mb_internal_encoding('UTF-8');
}

$data = array();
$context = null;
$allowOverwrite = false;
while ($this->moveToNextLine()) {
if ($this->isCurrentLineEmpty()) {
continue;
}


 if ("\t" === $this->currentLine[0]) {
throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
}

$isRef = $mergeNode = false;
if (preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+?))?\s*$#u', $this->currentLine, $values)) {
if ($context && 'mapping' == $context) {
throw new ParseException('You cannot define a sequence item when in a mapping');
}
$context = 'sequence';

if (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
$isRef = $matches['ref'];
$values['value'] = $matches['value'];
}


 if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
$c = $this->getRealCurrentLineNb() + 1;
$parser = new self($c);
$parser->refs = &$this->refs;
$data[] = $parser->parse($this->getNextEmbedBlock(null, true), $exceptionOnInvalidType, $objectSupport, $objectForMap);
} else {
if (isset($values['leadspaces'])
&& preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $values['value'], $matches)
) {

 $c = $this->getRealCurrentLineNb();
$parser = new self($c);
$parser->refs = &$this->refs;

$block = $values['value'];
if ($this->isNextLineIndented()) {
$block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + strlen($values['leadspaces']) + 1);
}

$data[] = $parser->parse($block, $exceptionOnInvalidType, $objectSupport, $objectForMap);
} else {
$data[] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport, $objectForMap, $context);
}
}
if ($isRef) {
$this->refs[$isRef] = end($data);
}
} elseif (preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $this->currentLine, $values) && (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'")))) {
if ($context && 'sequence' == $context) {
throw new ParseException('You cannot define a mapping item when in a sequence');
}
$context = 'mapping';


 Inline::parse(null, $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs);
try {
$key = Inline::parseScalar($values['key']);
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);

throw $e;
}


 if (is_float($key)) {
$key = (string) $key;
}

if ('<<' === $key) {
$mergeNode = true;
$allowOverwrite = true;
if (isset($values['value']) && 0 === strpos($values['value'], '*')) {
$refName = substr($values['value'], 1);
if (!array_key_exists($refName, $this->refs)) {
throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine);
}

$refValue = $this->refs[$refName];

if (!is_array($refValue)) {
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
}

foreach ($refValue as $key => $value) {
if (!isset($data[$key])) {
$data[$key] = $value;
}
}
} else {
if (isset($values['value']) && $values['value'] !== '') {
$value = $values['value'];
} else {
$value = $this->getNextEmbedBlock();
}
$c = $this->getRealCurrentLineNb() + 1;
$parser = new self($c);
$parser->refs = &$this->refs;
$parsed = $parser->parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap);

if (!is_array($parsed)) {
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
}

if (isset($parsed[0])) {

 
 
 foreach ($parsed as $parsedItem) {
if (!is_array($parsedItem)) {
throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem);
}

foreach ($parsedItem as $key => $value) {
if (!isset($data[$key])) {
$data[$key] = $value;
}
}
}
} else {

 
 foreach ($parsed as $key => $value) {
if (!isset($data[$key])) {
$data[$key] = $value;
}
}
}
}
} elseif (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
$isRef = $matches['ref'];
$values['value'] = $matches['value'];
}

if ($mergeNode) {

 } elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {

 
 if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) {

 
 if ($allowOverwrite || !isset($data[$key])) {
$data[$key] = null;
}
} else {
$c = $this->getRealCurrentLineNb() + 1;
$parser = new self($c);
$parser->refs = &$this->refs;
$value = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport, $objectForMap);

 
 if ($allowOverwrite || !isset($data[$key])) {
$data[$key] = $value;
}
}
} else {
$value = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport, $objectForMap, $context);

 
 if ($allowOverwrite || !isset($data[$key])) {
$data[$key] = $value;
}
}
if ($isRef) {
$this->refs[$isRef] = $data[$key];
}
} else {

 if ('---' === $this->currentLine) {
throw new ParseException('Multiple documents are not supported.');
}


 if (is_string($value) && $this->lines[0] === trim($value)) {
try {
$value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs);
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);

throw $e;
}

if (is_array($value)) {
$first = reset($value);
if (is_string($first) && 0 === strpos($first, '*')) {
$data = array();
foreach ($value as $alias) {
$data[] = $this->refs[substr($alias, 1)];
}
$value = $data;
}
}

if (isset($mbEncoding)) {
mb_internal_encoding($mbEncoding);
}

return $value;
}

switch (preg_last_error()) {
case PREG_INTERNAL_ERROR:
$error = 'Internal PCRE error.';
break;
case PREG_BACKTRACK_LIMIT_ERROR:
$error = 'pcre.backtrack_limit reached.';
break;
case PREG_RECURSION_LIMIT_ERROR:
$error = 'pcre.recursion_limit reached.';
break;
case PREG_BAD_UTF8_ERROR:
$error = 'Malformed UTF-8 data.';
break;
case PREG_BAD_UTF8_OFFSET_ERROR:
$error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.';
break;
default:
$error = 'Unable to parse.';
}

throw new ParseException($error, $this->getRealCurrentLineNb() + 1, $this->currentLine);
}
}

if (isset($mbEncoding)) {
mb_internal_encoding($mbEncoding);
}

if ($objectForMap && !is_object($data)) {
$data = (object) $data;
}

return empty($data) ? null : $data;
}