~tcuthbert/wordpress/openstack-objectstorage

« back to all changes in this revision

Viewing changes to vendor/symfony/yaml/Symfony/Component/Yaml/Inline.php

  • Committer: Jacek Nykis
  • Date: 2015-02-11 15:35:31 UTC
  • Revision ID: jacek.nykis@canonical.com-20150211153531-hmy6zi0ov2qfkl0b
Initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
 
 
3
/*
 
4
 * This file is part of the Symfony package.
 
5
 *
 
6
 * (c) Fabien Potencier <fabien@symfony.com>
 
7
 *
 
8
 * For the full copyright and license information, please view the LICENSE
 
9
 * file that was distributed with this source code.
 
10
 */
 
11
 
 
12
namespace Symfony\Component\Yaml;
 
13
 
 
14
use Symfony\Component\Yaml\Exception\ParseException;
 
15
use Symfony\Component\Yaml\Exception\DumpException;
 
16
 
 
17
/**
 
18
 * Inline implements a YAML parser/dumper for the YAML inline syntax.
 
19
 *
 
20
 * @author Fabien Potencier <fabien@symfony.com>
 
21
 */
 
22
class Inline
 
23
{
 
24
    const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')';
 
25
 
 
26
    private static $exceptionOnInvalidType = false;
 
27
    private static $objectSupport = false;
 
28
    private static $objectForMap = false;
 
29
 
 
30
    /**
 
31
     * Converts a YAML string to a PHP array.
 
32
     *
 
33
     * @param string $value                  A YAML string
 
34
     * @param bool   $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
 
35
     * @param bool   $objectSupport          true if object support is enabled, false otherwise
 
36
     * @param bool   $objectForMap           true if maps should return a stdClass instead of array()
 
37
     * @param array  $references             Mapping of variable names to values
 
38
     *
 
39
     * @return array A PHP array representing the YAML string
 
40
     *
 
41
     * @throws ParseException
 
42
     */
 
43
    public static function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false, $references = array())
 
44
    {
 
45
        self::$exceptionOnInvalidType = $exceptionOnInvalidType;
 
46
        self::$objectSupport = $objectSupport;
 
47
        self::$objectForMap = $objectForMap;
 
48
 
 
49
        $value = trim($value);
 
50
 
 
51
        if (0 == strlen($value)) {
 
52
            return '';
 
53
        }
 
54
 
 
55
        if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
 
56
            $mbEncoding = mb_internal_encoding();
 
57
            mb_internal_encoding('ASCII');
 
58
        }
 
59
 
 
60
        $i = 0;
 
61
        switch ($value[0]) {
 
62
            case '[':
 
63
                $result = self::parseSequence($value, $i, $references);
 
64
                ++$i;
 
65
                break;
 
66
            case '{':
 
67
                $result = self::parseMapping($value, $i, $references);
 
68
                ++$i;
 
69
                break;
 
70
            default:
 
71
                $result = self::parseScalar($value, null, array('"', "'"), $i, true, $references);
 
72
        }
 
73
 
 
74
        // some comments are allowed at the end
 
75
        if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) {
 
76
            throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)));
 
77
        }
 
78
 
 
79
        if (isset($mbEncoding)) {
 
80
            mb_internal_encoding($mbEncoding);
 
81
        }
 
82
 
 
83
        return $result;
 
84
    }
 
85
 
 
86
    /**
 
87
     * Dumps a given PHP variable to a YAML string.
 
88
     *
 
89
     * @param mixed $value                  The PHP variable to convert
 
90
     * @param bool  $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
 
91
     * @param bool  $objectSupport          true if object support is enabled, false otherwise
 
92
     *
 
93
     * @return string The YAML string representing the PHP array
 
94
     *
 
95
     * @throws DumpException When trying to dump PHP resource
 
96
     */
 
97
    public static function dump($value, $exceptionOnInvalidType = false, $objectSupport = false)
 
98
    {
 
99
        switch (true) {
 
100
            case is_resource($value):
 
101
                if ($exceptionOnInvalidType) {
 
102
                    throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value)));
 
103
                }
 
104
 
 
105
                return 'null';
 
106
            case is_object($value):
 
107
                if ($objectSupport) {
 
108
                    return '!!php/object:'.serialize($value);
 
109
                }
 
110
 
 
111
                if ($exceptionOnInvalidType) {
 
112
                    throw new DumpException('Object support when dumping a YAML file has been disabled.');
 
113
                }
 
114
 
 
115
                return 'null';
 
116
            case is_array($value):
 
117
                return self::dumpArray($value, $exceptionOnInvalidType, $objectSupport);
 
118
            case null === $value:
 
119
                return 'null';
 
120
            case true === $value:
 
121
                return 'true';
 
122
            case false === $value:
 
123
                return 'false';
 
124
            case ctype_digit($value):
 
125
                return is_string($value) ? "'$value'" : (int) $value;
 
126
            case is_numeric($value):
 
127
                $locale = setlocale(LC_NUMERIC, 0);
 
128
                if (false !== $locale) {
 
129
                    setlocale(LC_NUMERIC, 'C');
 
130
                }
 
131
                if (is_float($value)) {
 
132
                    $repr = strval($value);
 
133
                    if (is_infinite($value)) {
 
134
                        $repr = str_ireplace('INF', '.Inf', $repr);
 
135
                    } elseif (floor($value) == $value && $repr == $value) {
 
136
                        // Preserve float data type since storing a whole number will result in integer value.
 
137
                        $repr = '!!float '.$repr;
 
138
                    }
 
139
                } else {
 
140
                    $repr = is_string($value) ? "'$value'" : strval($value);
 
141
                }
 
142
                if (false !== $locale) {
 
143
                    setlocale(LC_NUMERIC, $locale);
 
144
                }
 
145
 
 
146
                return $repr;
 
147
            case '' == $value:
 
148
                return "''";
 
149
            case Escaper::requiresDoubleQuoting($value):
 
150
                return Escaper::escapeWithDoubleQuotes($value);
 
151
            case Escaper::requiresSingleQuoting($value):
 
152
            case preg_match(self::getTimestampRegex(), $value):
 
153
                return Escaper::escapeWithSingleQuotes($value);
 
154
            default:
 
155
                return $value;
 
156
        }
 
157
    }
 
158
 
 
159
    /**
 
160
     * Dumps a PHP array to a YAML string.
 
161
     *
 
162
     * @param array $value                  The PHP array to dump
 
163
     * @param bool  $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
 
164
     * @param bool  $objectSupport          true if object support is enabled, false otherwise
 
165
     *
 
166
     * @return string The YAML string representing the PHP array
 
167
     */
 
168
    private static function dumpArray($value, $exceptionOnInvalidType, $objectSupport)
 
169
    {
 
170
        // array
 
171
        $keys = array_keys($value);
 
172
        if ((1 == count($keys) && '0' == $keys[0])
 
173
            || (count($keys) > 1 && array_reduce($keys, function ($v, $w) { return (int) $v + $w; }, 0) == count($keys) * (count($keys) - 1) / 2)
 
174
        ) {
 
175
            $output = array();
 
176
            foreach ($value as $val) {
 
177
                $output[] = self::dump($val, $exceptionOnInvalidType, $objectSupport);
 
178
            }
 
179
 
 
180
            return sprintf('[%s]', implode(', ', $output));
 
181
        }
 
182
 
 
183
        // mapping
 
184
        $output = array();
 
185
        foreach ($value as $key => $val) {
 
186
            $output[] = sprintf('%s: %s', self::dump($key, $exceptionOnInvalidType, $objectSupport), self::dump($val, $exceptionOnInvalidType, $objectSupport));
 
187
        }
 
188
 
 
189
        return sprintf('{ %s }', implode(', ', $output));
 
190
    }
 
191
 
 
192
    /**
 
193
     * Parses a scalar to a YAML string.
 
194
     *
 
195
     * @param string $scalar
 
196
     * @param string $delimiters
 
197
     * @param array  $stringDelimiters
 
198
     * @param int    &$i
 
199
     * @param bool   $evaluate
 
200
     * @param array  $references
 
201
     *
 
202
     * @return string A YAML string
 
203
     *
 
204
     * @throws ParseException When malformed inline YAML string is parsed
 
205
     */
 
206
    public static function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true, $references = array())
 
207
    {
 
208
        if (in_array($scalar[$i], $stringDelimiters)) {
 
209
            // quoted scalar
 
210
            $output = self::parseQuotedScalar($scalar, $i);
 
211
 
 
212
            if (null !== $delimiters) {
 
213
                $tmp = ltrim(substr($scalar, $i), ' ');
 
214
                if (!in_array($tmp[0], $delimiters)) {
 
215
                    throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)));
 
216
                }
 
217
            }
 
218
        } else {
 
219
            // "normal" string
 
220
            if (!$delimiters) {
 
221
                $output = substr($scalar, $i);
 
222
                $i += strlen($output);
 
223
 
 
224
                // remove comments
 
225
                if (false !== $strpos = strpos($output, ' #')) {
 
226
                    $output = rtrim(substr($output, 0, $strpos));
 
227
                }
 
228
            } elseif (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
 
229
                $output = $match[1];
 
230
                $i += strlen($output);
 
231
            } else {
 
232
                throw new ParseException(sprintf('Malformed inline YAML string (%s).', $scalar));
 
233
            }
 
234
 
 
235
            if ($evaluate) {
 
236
                $output = self::evaluateScalar($output, $references);
 
237
            }
 
238
        }
 
239
 
 
240
        return $output;
 
241
    }
 
242
 
 
243
    /**
 
244
     * Parses a quoted scalar to YAML.
 
245
     *
 
246
     * @param string $scalar
 
247
     * @param int    &$i
 
248
     *
 
249
     * @return string A YAML string
 
250
     *
 
251
     * @throws ParseException When malformed inline YAML string is parsed
 
252
     */
 
253
    private static function parseQuotedScalar($scalar, &$i)
 
254
    {
 
255
        if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
 
256
            throw new ParseException(sprintf('Malformed inline YAML string (%s).', substr($scalar, $i)));
 
257
        }
 
258
 
 
259
        $output = substr($match[0], 1, strlen($match[0]) - 2);
 
260
 
 
261
        $unescaper = new Unescaper();
 
262
        if ('"' == $scalar[$i]) {
 
263
            $output = $unescaper->unescapeDoubleQuotedString($output);
 
264
        } else {
 
265
            $output = $unescaper->unescapeSingleQuotedString($output);
 
266
        }
 
267
 
 
268
        $i += strlen($match[0]);
 
269
 
 
270
        return $output;
 
271
    }
 
272
 
 
273
    /**
 
274
     * Parses a sequence to a YAML string.
 
275
     *
 
276
     * @param string $sequence
 
277
     * @param int    &$i
 
278
     * @param array  $references
 
279
     *
 
280
     * @return string A YAML string
 
281
     *
 
282
     * @throws ParseException When malformed inline YAML string is parsed
 
283
     */
 
284
    private static function parseSequence($sequence, &$i = 0, $references = array())
 
285
    {
 
286
        $output = array();
 
287
        $len = strlen($sequence);
 
288
        $i += 1;
 
289
 
 
290
        // [foo, bar, ...]
 
291
        while ($i < $len) {
 
292
            switch ($sequence[$i]) {
 
293
                case '[':
 
294
                    // nested sequence
 
295
                    $output[] = self::parseSequence($sequence, $i, $references);
 
296
                    break;
 
297
                case '{':
 
298
                    // nested mapping
 
299
                    $output[] = self::parseMapping($sequence, $i, $references);
 
300
                    break;
 
301
                case ']':
 
302
                    return $output;
 
303
                case ',':
 
304
                case ' ':
 
305
                    break;
 
306
                default:
 
307
                    $isQuoted = in_array($sequence[$i], array('"', "'"));
 
308
                    $value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i, true, $references);
 
309
 
 
310
                    // the value can be an array if a reference has been resolved to an array var
 
311
                    if (!is_array($value) && !$isQuoted && false !== strpos($value, ': ')) {
 
312
                        // embedded mapping?
 
313
                        try {
 
314
                            $pos = 0;
 
315
                            $value = self::parseMapping('{'.$value.'}', $pos, $references);
 
316
                        } catch (\InvalidArgumentException $e) {
 
317
                            // no, it's not
 
318
                        }
 
319
                    }
 
320
 
 
321
                    $output[] = $value;
 
322
 
 
323
                    --$i;
 
324
            }
 
325
 
 
326
            ++$i;
 
327
        }
 
328
 
 
329
        throw new ParseException(sprintf('Malformed inline YAML string %s', $sequence));
 
330
    }
 
331
 
 
332
    /**
 
333
     * Parses a mapping to a YAML string.
 
334
     *
 
335
     * @param string $mapping
 
336
     * @param int    &$i
 
337
     * @param array  $references
 
338
     *
 
339
     * @return string A YAML string
 
340
     *
 
341
     * @throws ParseException When malformed inline YAML string is parsed
 
342
     */
 
343
    private static function parseMapping($mapping, &$i = 0, $references = array())
 
344
    {
 
345
        $output = array();
 
346
        $len = strlen($mapping);
 
347
        $i += 1;
 
348
 
 
349
        // {foo: bar, bar:foo, ...}
 
350
        while ($i < $len) {
 
351
            switch ($mapping[$i]) {
 
352
                case ' ':
 
353
                case ',':
 
354
                    ++$i;
 
355
                    continue 2;
 
356
                case '}':
 
357
                    if (self::$objectForMap) {
 
358
                        return (object) $output;
 
359
                    }
 
360
 
 
361
                    return $output;
 
362
            }
 
363
 
 
364
            // key
 
365
            $key = self::parseScalar($mapping, array(':', ' '), array('"', "'"), $i, false);
 
366
 
 
367
            // value
 
368
            $done = false;
 
369
 
 
370
            while ($i < $len) {
 
371
                switch ($mapping[$i]) {
 
372
                    case '[':
 
373
                        // nested sequence
 
374
                        $value = self::parseSequence($mapping, $i, $references);
 
375
                        // Spec: Keys MUST be unique; first one wins.
 
376
                        // Parser cannot abort this mapping earlier, since lines
 
377
                        // are processed sequentially.
 
378
                        if (!isset($output[$key])) {
 
379
                            $output[$key] = $value;
 
380
                        }
 
381
                        $done = true;
 
382
                        break;
 
383
                    case '{':
 
384
                        // nested mapping
 
385
                        $value = self::parseMapping($mapping, $i, $references);
 
386
                        // Spec: Keys MUST be unique; first one wins.
 
387
                        // Parser cannot abort this mapping earlier, since lines
 
388
                        // are processed sequentially.
 
389
                        if (!isset($output[$key])) {
 
390
                            $output[$key] = $value;
 
391
                        }
 
392
                        $done = true;
 
393
                        break;
 
394
                    case ':':
 
395
                    case ' ':
 
396
                        break;
 
397
                    default:
 
398
                        $value = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i, true, $references);
 
399
                        // Spec: Keys MUST be unique; first one wins.
 
400
                        // Parser cannot abort this mapping earlier, since lines
 
401
                        // are processed sequentially.
 
402
                        if (!isset($output[$key])) {
 
403
                            $output[$key] = $value;
 
404
                        }
 
405
                        $done = true;
 
406
                        --$i;
 
407
                }
 
408
 
 
409
                ++$i;
 
410
 
 
411
                if ($done) {
 
412
                    continue 2;
 
413
                }
 
414
            }
 
415
        }
 
416
 
 
417
        throw new ParseException(sprintf('Malformed inline YAML string %s', $mapping));
 
418
    }
 
419
 
 
420
    /**
 
421
     * Evaluates scalars and replaces magic values.
 
422
     *
 
423
     * @param string $scalar
 
424
     * @param array  $references
 
425
     *
 
426
     * @return string A YAML string
 
427
     *
 
428
     * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved
 
429
     */
 
430
    private static function evaluateScalar($scalar, $references = array())
 
431
    {
 
432
        $scalar = trim($scalar);
 
433
        $scalarLower = strtolower($scalar);
 
434
 
 
435
        if (0 === strpos($scalar, '*')) {
 
436
            if (false !== $pos = strpos($scalar, '#')) {
 
437
                $value = substr($scalar, 1, $pos - 2);
 
438
            } else {
 
439
                $value = substr($scalar, 1);
 
440
            }
 
441
 
 
442
            // an unquoted *
 
443
            if (false === $value || '' === $value) {
 
444
                throw new ParseException('A reference must contain at least one character.');
 
445
            }
 
446
 
 
447
            if (!array_key_exists($value, $references)) {
 
448
                throw new ParseException(sprintf('Reference "%s" does not exist.', $value));
 
449
            }
 
450
 
 
451
            return $references[$value];
 
452
        }
 
453
 
 
454
        switch (true) {
 
455
            case 'null' === $scalarLower:
 
456
            case '' === $scalar:
 
457
            case '~' === $scalar:
 
458
                return;
 
459
            case 'true' === $scalarLower:
 
460
                return true;
 
461
            case 'false' === $scalarLower:
 
462
                return false;
 
463
            // Optimise for returning strings.
 
464
            case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || $scalar[0] === '!' || is_numeric($scalar[0]):
 
465
                switch (true) {
 
466
                    case 0 === strpos($scalar, '!str'):
 
467
                        return (string) substr($scalar, 5);
 
468
                    case 0 === strpos($scalar, '! '):
 
469
                        return intval(self::parseScalar(substr($scalar, 2)));
 
470
                    case 0 === strpos($scalar, '!!php/object:'):
 
471
                        if (self::$objectSupport) {
 
472
                            return unserialize(substr($scalar, 13));
 
473
                        }
 
474
 
 
475
                        if (self::$exceptionOnInvalidType) {
 
476
                            throw new ParseException('Object support when parsing a YAML file has been disabled.');
 
477
                        }
 
478
 
 
479
                        return;
 
480
                    case 0 === strpos($scalar, '!!float '):
 
481
                        return (float) substr($scalar, 8);
 
482
                    case ctype_digit($scalar):
 
483
                        $raw = $scalar;
 
484
                        $cast = intval($scalar);
 
485
 
 
486
                        return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
 
487
                    case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)):
 
488
                        $raw = $scalar;
 
489
                        $cast = intval($scalar);
 
490
 
 
491
                        return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
 
492
                    case is_numeric($scalar):
 
493
                        return '0x' == $scalar[0].$scalar[1] ? hexdec($scalar) : floatval($scalar);
 
494
                    case '.inf' === $scalarLower:
 
495
                    case '.nan' === $scalarLower:
 
496
                        return -log(0);
 
497
                    case '-.inf' === $scalarLower:
 
498
                        return log(0);
 
499
                    case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar):
 
500
                        return floatval(str_replace(',', '', $scalar));
 
501
                    case preg_match(self::getTimestampRegex(), $scalar):
 
502
                        return strtotime($scalar);
 
503
                }
 
504
            default:
 
505
                return (string) $scalar;
 
506
        }
 
507
    }
 
508
 
 
509
    /**
 
510
     * Gets a regex that matches a YAML date.
 
511
     *
 
512
     * @return string The regular expression
 
513
     *
 
514
     * @see http://www.yaml.org/spec/1.2/spec.html#id2761573
 
515
     */
 
516
    private static function getTimestampRegex()
 
517
    {
 
518
        return <<<EOF
 
519
        ~^
 
520
        (?P<year>[0-9][0-9][0-9][0-9])
 
521
        -(?P<month>[0-9][0-9]?)
 
522
        -(?P<day>[0-9][0-9]?)
 
523
        (?:(?:[Tt]|[ \t]+)
 
524
        (?P<hour>[0-9][0-9]?)
 
525
        :(?P<minute>[0-9][0-9])
 
526
        :(?P<second>[0-9][0-9])
 
527
        (?:\.(?P<fraction>[0-9]*))?
 
528
        (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
 
529
        (?::(?P<tz_minute>[0-9][0-9]))?))?)?
 
530
        $~x
 
531
EOF;
 
532
    }
 
533
}