/**
  * Test method for scalar with correct quoted string
  *
  * @return void
  */
 public function testParseScalarWithCorrectlyQuotedStringShouldReturnString()
 {
     $value = "'don''t do somthin'' like that'";
     $expect = "don't do somthin' like that";
     $this->assertSame($expect, Inline::parseScalar($value));
 }
Beispiel #2
0
 /**
  * Parses a YAML string to a PHP value
  *
  * @param string  $value                  A YAML string
  *
  * @throws ParseException If the YAML is not valid
  * @return mixed  A PHP value
  */
 public function parse($value)
 {
     $this->currentLineNb = -1;
     $this->currentLine = "";
     $value = $this->cleanup($value);
     $this->lines = explode("\n", $value);
     $data = [];
     $context = null;
     while ($this->moveToNextLine()) {
         if ($this->isCurrentLineEmpty()) {
             continue;
         }
         // tab?
         if ($this->currentLine[0] === "\t") {
             throw new ParseException("A YAML file cannot contain tabs as indentation.", $this->getRealCurrentLineNb() + 1, $this->currentLine);
         }
         $isRef = false;
         $isInPlace = false;
         $isProcessed = false;
         if (preg_match("#^\\-((?P<leadspaces>\\s+)(?P<value>.+?))?\\s*\$#u", $this->currentLine, $values)) {
             if ($context && $context === "mapping") {
                 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"], " ") === "" || strpos(ltrim($values["value"], " "), "#") === 0) {
                 $c = $this->getRealCurrentLineNb() + 1;
                 $parser = new static($c);
                 $parser->refs =& $this->refs;
                 $data[] = $parser->parse($this->getNextEmbedBlock(null, true));
             } 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
                     $c = $this->getRealCurrentLineNb();
                     $parser = new static($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);
                 } else {
                     $data[] = $this->parseValue($values["value"], $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) && (strpos($values["key"], " #") === false || in_array($values["key"][0], ["\"", "'"]))) {
             if ($context && $context === "sequence") {
                 throw new ParseException("You cannot define a mapping item when in a sequence");
             }
             $context = "mapping";
             // force correct settings
             Inline::parse(null, $this->refs);
             try {
                 $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 === "<<") {
                 if (isset($values["value"]) && strpos($values["value"], "*") === 0) {
                     $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 static($c);
                     $parser->refs =& $this->refs;
                     $parsed = $parser->parse($value);
                     $merged = [];
                     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;
             } elseif (!isset($values["value"]) || trim($values["value"], " ") === "" || strpos(ltrim($values["value"], " "), "#") === 0) {
                 // 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 static($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"], $context);
                 }
             }
             if ($isRef) {
                 $this->refs[$isRef] = $data[$key];
             }
         } else {
             // multiple documents are not supported
             if ($this->currentLine === "---") {
                 throw new ParseException("Multiple documents are not supported.");
             }
             // 1-liner optionally followed by newline(s)
             if (is_string($value) && $this->lines[0] === trim($value)) {
                 try {
                     $value = Inline::parse($this->lines[0], $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) && strpos($first, "*") === 0) {
                         $data = [];
                         foreach ($value as $alias) {
                             $data[] = $this->refs[substr($alias, 1)];
                         }
                         $value = $data;
                     }
                 }
                 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 (count($data) === 0) {
         return null;
     }
     return $data;
 }