Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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
require_once dirname(__FILE__).'/sfYaml.php';
12
 
13
/**
14
 * sfYamlInline implements a YAML parser/dumper for the YAML inline syntax.
15
 *
16
 * @package    symfony
17
 * @subpackage yaml
18
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
19
 * @version    SVN: $Id: sfYamlInline.class.php 16177 2009-03-11 08:32:48Z fabien $
20
 */
21
class sfYamlInline
22
{
23
  const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')';
24
 
25
  /**
26
   * Convert a YAML string to a PHP array.
27
   *
28
   * @param string $value A YAML string
29
   *
30
   * @return array A PHP array representing the YAML string
31
   */
32
  static public function load($value)
33
  {
34
    $value = trim($value);
35
 
36
    if (0 == strlen($value))
37
    {
38
      return '';
39
    }
40
 
41
    if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2)
42
    {
43
      $mbEncoding = mb_internal_encoding();
44
      mb_internal_encoding('ASCII');
45
    }
46
 
47
    switch ($value[0])
48
    {
49
      case '[':
50
        $result = self::parseSequence($value);
51
        break;
52
      case '{':
53
        $result = self::parseMapping($value);
54
        break;
55
      default:
56
        $result = self::parseScalar($value);
57
    }
58
 
59
    if (isset($mbEncoding))
60
    {
61
      mb_internal_encoding($mbEncoding);
62
    }
63
 
64
    return $result;
65
  }
66
 
67
  /**
68
   * Dumps a given PHP variable to a YAML string.
69
   *
70
   * @param mixed $value The PHP variable to convert
71
   *
72
   * @return string The YAML string representing the PHP array
73
   */
74
  static public function dump($value)
75
  {
76
    if ('1.1' === sfYaml::getSpecVersion())
77
    {
78
      $trueValues = array('true', 'on', '+', 'yes', 'y');
79
      $falseValues = array('false', 'off', '-', 'no', 'n');
80
    }
81
    else
82
    {
83
      $trueValues = array('true');
84
      $falseValues = array('false');
85
    }
86
 
87
    switch (true)
88
    {
89
      case is_resource($value):
90
        throw new InvalidArgumentException('Unable to dump PHP resources in a YAML file.');
91
      case is_object($value):
92
        return '!!php/object:'.serialize($value);
93
      case is_array($value):
94
        return self::dumpArray($value);
95
      case null === $value:
96
        return 'null';
97
      case true === $value:
98
        return 'true';
99
      case false === $value:
100
        return 'false';
101
      case ctype_digit($value):
102
        return is_string($value) ? "'$value'" : (int) $value;
103
      case is_numeric($value):
104
        return is_infinite($value) ? str_ireplace('INF', '.Inf', strval($value)) : (is_string($value) ? "'$value'" : $value);
105
      case false !== strpos($value, "\n") || false !== strpos($value, "\r"):
106
        return sprintf('"%s"', str_replace(array('"', "\n", "\r"), array('\\"', '\n', '\r'), $value));
107
      case preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ - ? | < > = ! % @ ` ]/x', $value):
108
        return sprintf("'%s'", str_replace('\'', '\'\'', $value));
109
      case '' == $value:
110
        return "''";
111
      case preg_match(self::getTimestampRegex(), $value):
112
        return "'$value'";
113
      case in_array(strtolower($value), $trueValues):
114
        return "'$value'";
115
      case in_array(strtolower($value), $falseValues):
116
        return "'$value'";
117
      case in_array(strtolower($value), array('null', '~')):
118
        return "'$value'";
119
      default:
120
        return $value;
121
    }
122
  }
123
 
124
  /**
125
   * Dumps a PHP array to a YAML string.
126
   *
127
   * @param array $value The PHP array to dump
128
   *
129
   * @return string The YAML string representing the PHP array
130
   */
131
  static protected function dumpArray($value)
132
  {
133
    // array
134
    $keys = array_keys($value);
135
    if (
136
      (1 == count($keys) && '0' == $keys[0])
137
      ||
138
      (count($keys) > 1 && array_reduce($keys, create_function('$v,$w', 'return (integer) $v + $w;'), 0) == count($keys) * (count($keys) - 1) / 2))
139
    {
140
      $output = array();
141
      foreach ($value as $val)
142
      {
143
        $output[] = self::dump($val);
144
      }
145
 
146
      return sprintf('[%s]', implode(', ', $output));
147
    }
148
 
149
    // mapping
150
    $output = array();
151
    foreach ($value as $key => $val)
152
    {
153
      $output[] = sprintf('%s: %s', self::dump($key), self::dump($val));
154
    }
155
 
156
    return sprintf('{ %s }', implode(', ', $output));
157
  }
158
 
159
  /**
160
   * Parses a scalar to a YAML string.
161
   *
162
   * @param scalar  $scalar
163
   * @param string  $delimiters
164
   * @param array   $stringDelimiter
165
   * @param integer $i
166
   * @param boolean $evaluate
167
   *
168
   * @return string A YAML string
169
   */
170
  static public function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true)
171
  {
172
    if (in_array($scalar[$i], $stringDelimiters))
173
    {
174
      // quoted scalar
175
      $output = self::parseQuotedScalar($scalar, $i);
176
    }
177
    else
178
    {
179
      // "normal" string
180
      if (!$delimiters)
181
      {
182
        $output = substr($scalar, $i);
183
        $i += strlen($output);
184
 
185
        // remove comments
186
        if (false !== $strpos = strpos($output, ' #'))
187
        {
188
          $output = rtrim(substr($output, 0, $strpos));
189
        }
190
      }
191
      else if (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match))
192
      {
193
        $output = $match[1];
194
        $i += strlen($output);
195
      }
196
      else
197
      {
198
        throw new InvalidArgumentException(sprintf('Malformed inline YAML string (%s).', $scalar));
199
      }
200
 
201
      $output = $evaluate ? self::evaluateScalar($output) : $output;
202
    }
203
 
204
    return $output;
205
  }
206
 
207
  /**
208
   * Parses a quoted scalar to YAML.
209
   *
210
   * @param string  $scalar
211
   * @param integer $i
212
   *
213
   * @return string A YAML string
214
   */
215
  static protected function parseQuotedScalar($scalar, &$i)
216
  {
217
    if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match))
218
    {
219
      throw new InvalidArgumentException(sprintf('Malformed inline YAML string (%s).', substr($scalar, $i)));
220
    }
221
 
222
    $output = substr($match[0], 1, strlen($match[0]) - 2);
223
 
224
    if ('"' == $scalar[$i])
225
    {
226
      // evaluate the string
227
      $output = str_replace(array('\\"', '\\n', '\\r'), array('"', "\n", "\r"), $output);
228
    }
229
    else
230
    {
231
      // unescape '
232
      $output = str_replace('\'\'', '\'', $output);
233
    }
234
 
235
    $i += strlen($match[0]);
236
 
237
    return $output;
238
  }
239
 
240
  /**
241
   * Parses a sequence to a YAML string.
242
   *
243
   * @param string  $sequence
244
   * @param integer $i
245
   *
246
   * @return string A YAML string
247
   */
248
  static protected function parseSequence($sequence, &$i = 0)
249
  {
250
    $output = array();
251
    $len = strlen($sequence);
252
    $i += 1;
253
 
254
    // [foo, bar, ...]
255
    while ($i < $len)
256
    {
257
      switch ($sequence[$i])
258
      {
259
        case '[':
260
          // nested sequence
261
          $output[] = self::parseSequence($sequence, $i);
262
          break;
263
        case '{':
264
          // nested mapping
265
          $output[] = self::parseMapping($sequence, $i);
266
          break;
267
        case ']':
268
          return $output;
269
        case ',':
270
        case ' ':
271
          break;
272
        default:
273
          $isQuoted = in_array($sequence[$i], array('"', "'"));
274
          $value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i);
275
 
276
          if (!$isQuoted && false !== strpos($value, ': '))
277
          {
278
            // embedded mapping?
279
            try
280
            {
281
              $value = self::parseMapping('{'.$value.'}');
282
            }
283
            catch (InvalidArgumentException $e)
284
            {
285
              // no, it's not
286
            }
287
          }
288
 
289
          $output[] = $value;
290
 
291
          --$i;
292
      }
293
 
294
      ++$i;
295
    }
296
 
297
    throw new InvalidArgumentException(sprintf('Malformed inline YAML string %s', $sequence));
298
  }
299
 
300
  /**
301
   * Parses a mapping to a YAML string.
302
   *
303
   * @param string  $mapping
304
   * @param integer $i
305
   *
306
   * @return string A YAML string
307
   */
308
  static protected function parseMapping($mapping, &$i = 0)
309
  {
310
    $output = array();
311
    $len = strlen($mapping);
312
    $i += 1;
313
 
314
    // {foo: bar, bar:foo, ...}
315
    while ($i < $len)
316
    {
317
      switch ($mapping[$i])
318
      {
319
        case ' ':
320
        case ',':
321
          ++$i;
322
          continue 2;
323
        case '}':
324
          return $output;
325
      }
326
 
327
      // key
328
      $key = self::parseScalar($mapping, array(':', ' '), array('"', "'"), $i, false);
329
 
330
      // value
331
      $done = false;
332
      while ($i < $len)
333
      {
334
        switch ($mapping[$i])
335
        {
336
          case '[':
337
            // nested sequence
338
            $output[$key] = self::parseSequence($mapping, $i);
339
            $done = true;
340
            break;
341
          case '{':
342
            // nested mapping
343
            $output[$key] = self::parseMapping($mapping, $i);
344
            $done = true;
345
            break;
346
          case ':':
347
          case ' ':
348
            break;
349
          default:
350
            $output[$key] = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i);
351
            $done = true;
352
            --$i;
353
        }
354
 
355
        ++$i;
356
 
357
        if ($done)
358
        {
359
          continue 2;
360
        }
361
      }
362
    }
363
 
364
    throw new InvalidArgumentException(sprintf('Malformed inline YAML string %s', $mapping));
365
  }
366
 
367
  /**
368
   * Evaluates scalars and replaces magic values.
369
   *
370
   * @param string $scalar
371
   *
372
   * @return string A YAML string
373
   */
374
  static protected function evaluateScalar($scalar)
375
  {
376
    $scalar = trim($scalar);
377
 
378
    if ('1.1' === sfYaml::getSpecVersion())
379
    {
380
      $trueValues = array('true', 'on', '+', 'yes', 'y');
381
      $falseValues = array('false', 'off', '-', 'no', 'n');
382
    }
383
    else
384
    {
385
      $trueValues = array('true');
386
      $falseValues = array('false');
387
    }
388
 
389
    switch (true)
390
    {
391
      case 'null' == strtolower($scalar):
392
      case '' == $scalar:
393
      case '~' == $scalar:
394
        return null;
395
      case 0 === strpos($scalar, '!str'):
396
        return (string) substr($scalar, 5);
397
      case 0 === strpos($scalar, '! '):
398
        return intval(self::parseScalar(substr($scalar, 2)));
399
      case 0 === strpos($scalar, '!!php/object:'):
400
        return unserialize(substr($scalar, 13));
401
      case ctype_digit($scalar):
402
        $raw = $scalar;
403
        $cast = intval($scalar);
404
        return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
405
      case in_array(strtolower($scalar), $trueValues):
406
        return true;
407
      case in_array(strtolower($scalar), $falseValues):
408
        return false;
409
      case is_numeric($scalar):
410
        return '0x' == $scalar[0].$scalar[1] ? hexdec($scalar) : floatval($scalar);
411
      case 0 == strcasecmp($scalar, '.inf'):
412
      case 0 == strcasecmp($scalar, '.NaN'):
413
        return -log(0);
414
      case 0 == strcasecmp($scalar, '-.inf'):
415
        return log(0);
416
      case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar):
417
        return floatval(str_replace(',', '', $scalar));
418
      case preg_match(self::getTimestampRegex(), $scalar):
419
        return strtotime($scalar);
420
      default:
421
        return (string) $scalar;
422
    }
423
  }
424
 
425
  static protected function getTimestampRegex()
426
  {
427
    return <<<EOF
428
    ~^
429
    (?P<year>[0-9][0-9][0-9][0-9])
430
    -(?P<month>[0-9][0-9]?)
431
    -(?P<day>[0-9][0-9]?)
432
    (?:(?:[Tt]|[ \t]+)
433
    (?P<hour>[0-9][0-9]?)
434
    :(?P<minute>[0-9][0-9])
435
    :(?P<second>[0-9][0-9])
436
    (?:\.(?P<fraction>[0-9]*))?
437
    (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
438
    (?::(?P<tz_minute>[0-9][0-9]))?))?)?
439
    $~x
440
EOF;
441
  }
442
}