Esempio n. 1
0
 /**
  * Parses YAML into a PHP array.
  *
  * The parse method, when supplied with a YAML stream (string or file),
  * will do its best to convert YAML in a file into a PHP array.
  *
  *  Usage:
  *  <code>
  *   $array = Yaml::parse('config.yml');
  *   print_r($array);
  *  </code>
  *
  * As this method accepts both plain strings and file names as an input,
  * you must validate the input before calling this method. Passing a file
  * as an input is a deprecated feature and will be removed in 3.0.
  *
  * @param string  $input                  Path to a YAML file or a string containing YAML
  * @param Boolean $exceptionOnInvalidType True if an exception must be thrown on invalid types false otherwise
  * @param Boolean $objectSupport          True if object support is enabled, false otherwise
  *
  * @return array The YAML converted to a PHP array
  *
  * @throws ParseException If the YAML is not valid
  *
  * @api
  */
 public static function parse($input, $exceptionOnInvalidType = false, $objectSupport = false)
 {
     // if input is a file, process it
     $file = '';
     if (strpos($input, "\n") === false && is_file($input)) {
         if (false === is_readable($input)) {
             throw new ParseException(sprintf('Unable to parse "%s" as the file is not readable.', $input));
         }
         $file = $input;
         $input = file_get_contents($file);
     }
     $yaml = new Parser();
     try {
         return $yaml->parse($input, $exceptionOnInvalidType, $objectSupport);
     } catch (ParseException $e) {
         if ($file) {
             $e->setParsedFile($file);
         }
         throw $e;
     }
 }
Esempio n. 2
0
 /**
  * Parses a YAML string to a PHP value.
  *
  * @param string  $value                  A YAML string
  * @param Boolean $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
  * @param Boolean $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 (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(), $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'], ' #')) {
             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);
             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, $exceptionOnInvalidType, $objectSupport);
                     $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(), $exceptionOnInvalidType, $objectSupport);
                 }
             } else {
                 if ($isInPlace) {
                     $data = $this->refs[$isInPlace];
                 } else {
                     $data[$key] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport);
                 }
             }
         } 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);
                 } 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;
 }