[PLUGINS] +set de base
[lhc/web/www.git] / www / plugins / yaml / sfyaml / sfYamlInline.php
1 <?php
2
3 /*
4 * This file is part of the symfony package.
5 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10
11 /**
12 * sfYamlInline implements a YAML parser/dumper for the YAML inline syntax.
13 *
14 * @package symfony
15 * @subpackage yaml
16 * @author Fabien Potencier <fabien.potencier@symfony-project.com>
17 * @version SVN: $Id: sfYamlInline.class.php 16177 2009-03-11 08:32:48Z fabien $
18 */
19 class sfYamlInline
20 {
21 /**
22 * Convert a YAML string to a PHP array.
23 *
24 * @param string $value A YAML string
25 *
26 * @return array A PHP array representing the YAML string
27 */
28 static public function load($value)
29 {
30 $value = trim($value);
31
32 if (0 == strlen($value))
33 {
34 return '';
35 }
36
37 switch ($value[0])
38 {
39 case '[':
40 return self::parseSequence($value);
41 case '{':
42 return self::parseMapping($value);
43 default:
44 return self::parseScalar($value);
45 }
46 }
47
48 /**
49 * Dumps a given PHP variable to a YAML string.
50 *
51 * @param mixed $value The PHP variable to convert
52 *
53 * @return string The YAML string representing the PHP array
54 */
55 static public function dump($value)
56 {
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');
59
60 switch (true)
61 {
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);
68 case null === $value:
69 return 'null';
70 case true === $value:
71 return 'true';
72 case false === $value:
73 return 'false';
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));
82 case '' == $value:
83 return "''";
84 case preg_match(self::getTimestampRegex(), $value):
85 return "'$value'";
86 case in_array(strtolower($value), $trueValues):
87 return "'$value'";
88 case in_array(strtolower($value), $falseValues):
89 return "'$value'";
90 case in_array(strtolower($value), array('null', '~')):
91 return "'$value'";
92 default:
93 return $value;
94 }
95 }
96
97 /**
98 * Dumps a PHP array to a YAML string.
99 *
100 * @param array $value The PHP array to dump
101 *
102 * @return string The YAML string representing the PHP array
103 */
104 static protected function dumpArray($value)
105 {
106 // array
107 $keys = array_keys($value);
108 if (
109 (1 == count($keys) && '0' == $keys[0])
110 ||
111 (count($keys) > 1 && array_reduce($keys, create_function('$v,$w', 'return (integer) $v + $w;'), 0) == count($keys) * (count($keys) - 1) / 2))
112 {
113 $output = array();
114 foreach ($value as $val)
115 {
116 $output[] = self::dump($val);
117 }
118
119 return sprintf('[%s]', implode(', ', $output));
120 }
121
122 // mapping
123 $output = array();
124 foreach ($value as $key => $val)
125 {
126 $output[] = sprintf('%s: %s', self::dump($key), self::dump($val));
127 }
128
129 return sprintf('{ %s }', implode(', ', $output));
130 }
131
132 /**
133 * Parses a scalar to a YAML string.
134 *
135 * @param scalar $scalar
136 * @param string $delimiters
137 * @param array $stringDelimiter
138 * @param integer $i
139 * @param boolean $evaluate
140 *
141 * @return string A YAML string
142 */
143 static public function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true)
144 {
145 if (in_array($scalar[$i], $stringDelimiters))
146 {
147 // quoted scalar
148 $output = self::parseQuotedScalar($scalar, $i);
149
150 // skip next delimiter
151 ++$i;
152 }
153 else
154 {
155 // "normal" string
156 if (!$delimiters)
157 {
158 $output = substr($scalar, $i);
159 $i += strlen($output);
160
161 // remove comments
162 if (false !== $strpos = strpos($output, ' #'))
163 {
164 $output = rtrim(substr($output, 0, $strpos));
165 }
166 }
167 else if (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match))
168 {
169 $output = $match[1];
170 $i += strlen($output);
171 }
172 else
173 {
174 throw new InvalidArgumentException(sprintf('Malformed inline YAML string (%s).', $scalar));
175 }
176
177 $output = $evaluate ? self::evaluateScalar($output) : $output;
178 }
179
180 return $output;
181 }
182
183 /**
184 * Parses a quoted scalar to YAML.
185 *
186 * @param string $scalar
187 * @param integer $i
188 *
189 * @return string A YAML string
190 */
191 static protected function parseQuotedScalar($scalar, &$i)
192 {
193 $delimiter = $scalar[$i];
194 ++$i;
195 $buffer = '';
196 $len = strlen($scalar);
197 $escaped = '"' == $delimiter ? '\\"' : "''";
198
199 while ($i < $len)
200 {
201 if (isset($scalar[$i + 1]) && $escaped == $scalar[$i].$scalar[$i + 1])
202 {
203 $buffer .= $delimiter;
204 ++$i;
205 }
206 else if ($delimiter == $scalar[$i])
207 {
208 break;
209 }
210 else
211 {
212 $buffer .= $scalar[$i];
213 }
214
215 ++$i;
216 }
217
218 if ('"' == $delimiter)
219 {
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);
224 }
225 }
226
227 return $buffer;
228 }
229
230 /**
231 * Parses a sequence to a YAML string.
232 *
233 * @param string $sequence
234 * @param integer $i
235 *
236 * @return string A YAML string
237 */
238 static protected function parseSequence($sequence, &$i = 0)
239 {
240 $output = array();
241 $len = strlen($sequence);
242 $i += 1;
243
244 // [foo, bar, ...]
245 while ($i < $len)
246 {
247 switch ($sequence[$i])
248 {
249 case '[':
250 // nested sequence
251 $output[] = self::parseSequence($sequence, $i);
252 break;
253 case '{':
254 // nested mapping
255 $output[] = self::parseMapping($sequence, $i);
256 break;
257 case ']':
258 return $output;
259 case ',':
260 case ' ':
261 break;
262 default:
263 $isQuoted = in_array($sequence[$i], array('"', "'"));
264 $value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i);
265
266 if (!$isQuoted && false !== strpos($value, ': '))
267 {
268 // embedded mapping?
269 try
270 {
271 $value = self::parseMapping('{'.$value.'}');
272 }
273 catch (InvalidArgumentException $e)
274 {
275 // no, it's not
276 }
277 }
278
279 $output[] = $value;
280
281 --$i;
282 }
283
284 ++$i;
285 }
286
287 throw new InvalidArgumentException(sprintf('Malformed inline YAML string %s', $sequence));
288 }
289
290 /**
291 * Parses a mapping to a YAML string.
292 *
293 * @param string $mapping
294 * @param integer $i
295 *
296 * @return string A YAML string
297 */
298 static protected function parseMapping($mapping, &$i = 0)
299 {
300 $output = array();
301 $len = strlen($mapping);
302 $i += 1;
303
304 // {foo: bar, bar:foo, ...}
305 while ($i < $len)
306 {
307 switch ($mapping[$i])
308 {
309 case ' ':
310 case ',':
311 ++$i;
312 continue 2;
313 case '}':
314 return $output;
315 }
316
317 // key
318 $key = self::parseScalar($mapping, array(':', ' '), array('"', "'"), $i, false);
319
320 // value
321 $done = false;
322 while ($i < $len)
323 {
324 switch ($mapping[$i])
325 {
326 case '[':
327 // nested sequence
328 $output[$key] = self::parseSequence($mapping, $i);
329 $done = true;
330 break;
331 case '{':
332 // nested mapping
333 $output[$key] = self::parseMapping($mapping, $i);
334 $done = true;
335 break;
336 case ':':
337 case ' ':
338 break;
339 default:
340 $output[$key] = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i);
341 $done = true;
342 --$i;
343 }
344
345 ++$i;
346
347 if ($done)
348 {
349 continue 2;
350 }
351 }
352 }
353
354 throw new InvalidArgumentException(sprintf('Malformed inline YAML string %s', $mapping));
355 }
356
357 /**
358 * Evaluates scalars and replaces magic values.
359 *
360 * @param string $scalar
361 *
362 * @return string A YAML string
363 */
364 static protected function evaluateScalar($scalar)
365 {
366 $scalar = trim($scalar);
367
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');
370
371 switch (true)
372 {
373 case 'null' == strtolower($scalar):
374 case '' == $scalar:
375 case '~' == $scalar:
376 return null;
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):
384 $raw = $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):
388 return true;
389 case in_array(strtolower($scalar), $falseValues):
390 return false;
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'):
395 return -log(0);
396 case 0 == strcasecmp($scalar, '-.inf'):
397 return log(0);
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);
402 default:
403 return (string) $scalar;
404 }
405 }
406
407 static protected function getTimestampRegex()
408 {
409 return <<<EOF
410 ~^
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]?)
414 (?:(?:[Tt]|[ \t]+)
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]))?))?)?
421 $~x
422 EOF;
423 }
424 }