4 * This file is part of the symfony package.
5 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
12 * sfYamlInline implements a YAML parser/dumper for the YAML inline syntax.
16 * @author Fabien Potencier <fabien.potencier@symfony-project.com>
17 * @version SVN: $Id: sfYamlInline.class.php 16177 2009-03-11 08:32:48Z fabien $
22 * Convert a YAML string to a PHP array.
24 * @param string $value A YAML string
26 * @return array A PHP array representing the YAML string
28 static public function load($value)
30 $value = trim($value);
32 if (0 == strlen($value))
40 return self
::parseSequence($value);
42 return self
::parseMapping($value);
44 return self
::parseScalar($value);
49 * Dumps a given PHP variable to a YAML string.
51 * @param mixed $value The PHP variable to convert
53 * @return string The YAML string representing the PHP array
55 static public function dump($value)
57 $trueValues = '1.1' == sfYaml
::getSpecVersion() ?
array('true', 'on', '+', 'yes', 'y') : array('true');
58 $falseValues = '1.1' == sfYaml
::getSpecVersion() ?
array('false', 'off', '-', 'no', 'n') : array('false');
62 case is_resource($value):
63 throw new InvalidArgumentException('Unable to dump PHP resources in a YAML file.');
64 case is_object($value):
65 return '!!php/object:'.serialize($value);
66 case is_array($value):
67 return self
::dumpArray($value);
72 case false === $value:
74 case ctype_digit($value):
75 return is_string($value) ?
"'$value'" : (int) $value;
76 case is_numeric($value):
77 return is_infinite($value) ?
str_ireplace('INF', '.Inf', strval($value)) : (is_string($value) ?
"'$value'" : $value);
78 case false !== strpos($value, "\n"):
79 return sprintf('"%s"', str_replace(array('"', "\n", "\r"), array('\\"', '\n', '\r'), $value));
80 case preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \#] | \A[ - ? | < > = ! % @ ]/x', $value):
81 return sprintf("'%s'", str_replace('\'', '\'\'', $value));
84 case preg_match(self
::getTimestampRegex(), $value):
86 case in_array(strtolower($value), $trueValues):
88 case in_array(strtolower($value), $falseValues):
90 case in_array(strtolower($value), array('null', '~')):
98 * Dumps a PHP array to a YAML string.
100 * @param array $value The PHP array to dump
102 * @return string The YAML string representing the PHP array
104 static protected function dumpArray($value)
107 $keys = array_keys($value);
109 (1 == count($keys) && '0' == $keys[0])
111 (count($keys) > 1 && array_reduce($keys, create_function('$v,$w', 'return (integer) $v + $w;'), 0) == count($keys) * (count($keys) - 1) / 2))
114 foreach ($value as $val)
116 $output[] = self
::dump($val);
119 return sprintf('[%s]', implode(', ', $output));
124 foreach ($value as $key => $val)
126 $output[] = sprintf('%s: %s', self
::dump($key), self
::dump($val));
129 return sprintf('{ %s }', implode(', ', $output));
133 * Parses a scalar to a YAML string.
135 * @param scalar $scalar
136 * @param string $delimiters
137 * @param array $stringDelimiter
139 * @param boolean $evaluate
141 * @return string A YAML string
143 static public function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true)
145 if (in_array($scalar[$i], $stringDelimiters))
148 $output = self
::parseQuotedScalar($scalar, $i);
150 // skip next delimiter
158 $output = substr($scalar, $i);
159 $i +
= strlen($output);
162 if (false !== $strpos = strpos($output, ' #'))
164 $output = rtrim(substr($output, 0, $strpos));
167 else if (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match))
170 $i +
= strlen($output);
174 throw new InvalidArgumentException(sprintf('Malformed inline YAML string (%s).', $scalar));
177 $output = $evaluate ? self
::evaluateScalar($output) : $output;
184 * Parses a quoted scalar to YAML.
186 * @param string $scalar
189 * @return string A YAML string
191 static protected function parseQuotedScalar($scalar, &$i)
193 $delimiter = $scalar[$i];
196 $len = strlen($scalar);
197 $escaped = '"' == $delimiter ?
'\\"' : "''";
201 if (isset($scalar[$i +
1]) && $escaped == $scalar[$i].$scalar[$i +
1])
203 $buffer .= $delimiter;
206 else if ($delimiter == $scalar[$i])
212 $buffer .= $scalar[$i];
218 if ('"' == $delimiter)
220 // evaluate the string
221 $buffer = str_replace(array('\\n', '\\r'), array("\n", "\r"), $buffer);
222 if (strpos($buffer,'\\x')!==false){
223 $buffer = preg_replace_callback(',\\\\x([0-9a-f]+),', create_function('$m', 'return chr(hexdec($m[1]));'), $buffer);
231 * Parses a sequence to a YAML string.
233 * @param string $sequence
236 * @return string A YAML string
238 static protected function parseSequence($sequence, &$i = 0)
241 $len = strlen($sequence);
247 switch ($sequence[$i])
251 $output[] = self
::parseSequence($sequence, $i);
255 $output[] = self
::parseMapping($sequence, $i);
263 $isQuoted = in_array($sequence[$i], array('"', "'"));
264 $value = self
::parseScalar($sequence, array(',', ']'), array('"', "'"), $i);
266 if (!$isQuoted && false !== strpos($value, ': '))
271 $value = self
::parseMapping('{'.$value.'}');
273 catch (InvalidArgumentException
$e)
287 throw new InvalidArgumentException(sprintf('Malformed inline YAML string %s', $sequence));
291 * Parses a mapping to a YAML string.
293 * @param string $mapping
296 * @return string A YAML string
298 static protected function parseMapping($mapping, &$i = 0)
301 $len = strlen($mapping);
304 // {foo: bar, bar:foo, ...}
307 switch ($mapping[$i])
318 $key = self
::parseScalar($mapping, array(':', ' '), array('"', "'"), $i, false);
324 switch ($mapping[$i])
328 $output[$key] = self
::parseSequence($mapping, $i);
333 $output[$key] = self
::parseMapping($mapping, $i);
340 $output[$key] = self
::parseScalar($mapping, array(',', '}'), array('"', "'"), $i);
354 throw new InvalidArgumentException(sprintf('Malformed inline YAML string %s', $mapping));
358 * Evaluates scalars and replaces magic values.
360 * @param string $scalar
362 * @return string A YAML string
364 static protected function evaluateScalar($scalar)
366 $scalar = trim($scalar);
368 $trueValues = '1.1' == sfYaml
::getSpecVersion() ?
array('true', 'on', '+', 'yes', 'y') : array('true');
369 $falseValues = '1.1' == sfYaml
::getSpecVersion() ?
array('false', 'off', '-', 'no', 'n') : array('false');
373 case 'null' == strtolower($scalar):
377 case 0 === strpos($scalar, '!str'):
378 return (string) substr($scalar, 5);
379 case 0 === strpos($scalar, '! '):
380 return intval(self
::parseScalar(substr($scalar, 2)));
381 case 0 === strpos($scalar, '!!php/object:'):
382 return unserialize(substr($scalar, 13));
383 case ctype_digit($scalar):
385 $cast = intval($scalar);
386 return '0' == $scalar[0] ?
octdec($scalar) : (((string) $raw == (string) $cast) ?
$cast : $raw);
387 case in_array(strtolower($scalar), $trueValues):
389 case in_array(strtolower($scalar), $falseValues):
391 case is_numeric($scalar):
392 return '0x' == $scalar[0].$scalar[1] ?
hexdec($scalar) : floatval($scalar);
393 case 0 == strcasecmp($scalar, '.inf'):
394 case 0 == strcasecmp($scalar, '.NaN'):
396 case 0 == strcasecmp($scalar, '-.inf'):
398 case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar):
399 return floatval(str_replace(',', '', $scalar));
400 case preg_match(self
::getTimestampRegex(), $scalar):
401 return strtotime($scalar);
403 return (string) $scalar;
407 static protected function getTimestampRegex()
411 (?P<year>[0-9][0-9][0-9][0-9])
412 -(?P<month>[0-9][0-9]?)
413 -(?P<day>[0-9][0-9]?)
415 (?P<hour>[0-9][0-9]?)
416 :(?P<minute>[0-9][0-9])
417 :(?P<second>[0-9][0-9])
418 (?:\.(?P<fraction>[0-9]*))?
419 (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
420 (?::(?P<tz_minute>[0-9][0-9]))?))?)?