/**
* Parses a YAML string to a PHP value.
*
* @param string A YAML string
*
* @return mixed A PHP value
*/
public function parse($value)
{
$this->value = $this->cleanup($value);
$this->currentLineNb = -1;
$this->currentLine = '';
$this->lines = explode("\n", $this->value);
$data = array();
while ($this->moveToNextLine()) {
if ($this->isCurrentLineEmpty()) {
continue;
}
// tab?
if (preg_match('#^\\t+#', $this->currentLine)) {
throw new InvalidArgumentException(sprintf('A YAML file cannot contain tabs as indentation at line %d (%s).', $this->getRealCurrentLineNb() + 1, $this->currentLine));
}
$isRef = $isInPlace = $isProcessed = false;
if (preg_match('#^\\-(\\s+(?P<value>.+?))?\\s*$#', $this->currentLine, $values)) {
if (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#', $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 YamlParser($c);
$parser->refs =& $this->refs;
$data[] = $parser->parse($this->getNextEmbedBlock());
} else {
if (preg_match('/^([^ ]+)\\: +({.*?)$/', $values['value'], $matches)) {
$data[] = array($matches[1] => YamlInline::load($matches[2]));
} else {
$data[] = $this->parseValue($values['value']);
}
}
} else {
if (preg_match('#^(?P<key>[^ ].*?) *\\:(\\s+(?P<value>.+?))?\\s*$#', $this->currentLine, $values)) {
$key = YamlInline::parseScalar($values['key']);
if ('<<' === $key) {
if (isset($values['value']) && '*' === substr($values['value'], 0, 1)) {
$isInPlace = substr($values['value'], 1);
if (!array_key_exists($isInPlace, $this->refs)) {
throw new InvalidArgumentException(sprintf('Reference "%s" does not exist at line %s (%s).', $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 YamlParser($c);
$parser->refs =& $this->refs;
$parsed = $parser->parse($value);
$merged = array();
if (!is_array($parsed)) {
throw new InvalidArgumentException(sprintf("YAML merge keys used with a scalar value instead of an array at line %s (%s)", $this->getRealCurrentLineNb() + 1, $this->currentLine));
} else {
if (isset($parsed[0])) {
// Numeric array, merge individual elements
foreach (array_reverse($parsed) as $parsedItem) {
if (!is_array($parsedItem)) {
throw new InvalidArgumentException(sprintf("Merge items must be arrays at line %s (%s).", $this->getRealCurrentLineNb() + 1, $parsedItem));
}
$merged = array_merge($parsedItem, $merged);
}
} else {
// Associative array, merge
$merged = array_merge($merge, $parsed);
}
}
$isProcessed = $merged;
}
} else {
if (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#', $values['value'], $matches)) {
$isRef = $matches['ref'];
$values['value'] = $matches['value'];
}
}
if ($isProcessed) {
// Merge keys
$data = $isProcessed;
} else {
if (!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()) {
$data[$key] = null;
} else {
$c = $this->getRealCurrentLineNb() + 1;
$parser = new YamlParser($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 {
// one liner?
if (1 == count(explode("\n", rtrim($this->value, "\n")))) {
$value = YamlInline::load($this->lines[0]);
if (is_array($value)) {
$first = reset($value);
if ('*' === substr($first, 0, 1)) {
$data = array();
foreach ($value as $alias) {
$data[] = $this->refs[substr($alias, 1)];
}
$value = $data;
}
}
return $value;
}
throw new InvalidArgumentException(sprintf('Unable to parse line %d (%s).', $this->getRealCurrentLineNb() + 1, $this->currentLine));
}
}
if ($isRef) {
$this->refs[$isRef] = end($data);
}
}
return empty($data) ? null : $data;
}