Completed
Pull Request — master (#583)
by Richard
11:35
created

Inline::parseMapping()   D

Complexity

Conditions 23
Paths 51

Size

Total Lines 92
Code Lines 53

Duplication

Lines 30
Ratio 32.61 %

Importance

Changes 0
Metric Value
cc 23
eloc 53
nc 51
nop 3
dl 30
loc 92
rs 4.6303
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <[email protected]>
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 <[email protected]>
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 value.
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 mixed A PHP value
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 ('' === $value) {
52
            return '';
53
        }
54
55
        if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) {
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 value
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) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing \Symfony\Component\Yaml\...:getHexRegex(), $value) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing \Symfony\Component\Yaml\...mestampRegex(), $value) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
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 = (string) $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'" : (string) $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):
0 ignored issues
show
Bug introduced by
It seems like $value defined by parameter $value on line 97 can also be of type boolean or resource; however, Symfony\Component\Yaml\E...requiresDoubleQuoting() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
150
                return Escaper::escapeWithDoubleQuotes($value);
0 ignored issues
show
Bug introduced by
It seems like $value defined by parameter $value on line 97 can also be of type boolean or resource; however, Symfony\Component\Yaml\E...scapeWithDoubleQuotes() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
151
            case Escaper::requiresSingleQuoting($value):
0 ignored issues
show
Bug introduced by
It seems like $value defined by parameter $value on line 97 can also be of type boolean or resource; however, Symfony\Component\Yaml\E...requiresSingleQuoting() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
152
            case Parser::preg_match(self::getHexRegex(), $value):
153
            case Parser::preg_match(self::getTimestampRegex(), $value):
154
                return Escaper::escapeWithSingleQuotes($value);
0 ignored issues
show
Bug introduced by
It seems like $value defined by parameter $value on line 97 can also be of type boolean or resource; however, Symfony\Component\Yaml\E...scapeWithSingleQuotes() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
155
            default:
156
                return $value;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $value; (string|boolean|resource) is incompatible with the return type documented by Symfony\Component\Yaml\Inline::dump of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
157
        }
158
    }
159
160
    /**
161
     * Check if given array is hash or just normal indexed array.
162
     *
163
     * @internal
164
     *
165
     * @param array $value The PHP array to check
166
     *
167
     * @return bool true if value is hash array, false otherwise
168
     */
169
    public static function isHash(array $value)
170
    {
171
        $expectedKey = 0;
172
173
        foreach ($value as $key => $val) {
174
            if ($key !== $expectedKey++) {
175
                return true;
176
            }
177
        }
178
179
        return false;
180
    }
181
182
    /**
183
     * Dumps a PHP array to a YAML string.
184
     *
185
     * @param array $value                  The PHP array to dump
186
     * @param bool  $exceptionOnInvalidType True if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
187
     * @param bool  $objectSupport          True if object support is enabled, false otherwise
188
     *
189
     * @return string The YAML string representing the PHP array
190
     */
191
    private static function dumpArray($value, $exceptionOnInvalidType, $objectSupport)
192
    {
193
        // array
194
        if ($value && !self::isHash($value)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $value of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
195
            $output = array();
196
            foreach ($value as $val) {
197
                $output[] = self::dump($val, $exceptionOnInvalidType, $objectSupport);
198
            }
199
200
            return sprintf('[%s]', implode(', ', $output));
201
        }
202
203
        // hash
204
        $output = array();
205
        foreach ($value as $key => $val) {
206
            $output[] = sprintf('%s: %s', self::dump($key, $exceptionOnInvalidType, $objectSupport), self::dump($val, $exceptionOnInvalidType, $objectSupport));
207
        }
208
209
        return sprintf('{ %s }', implode(', ', $output));
210
    }
211
212
    /**
213
     * Parses a YAML scalar.
214
     *
215
     * @param string   $scalar
216
     * @param string[] $delimiters
217
     * @param string[] $stringDelimiters
218
     * @param int      &$i
219
     * @param bool     $evaluate
220
     * @param array    $references
221
     *
222
     * @return string
223
     *
224
     * @throws ParseException When malformed inline YAML string is parsed
225
     *
226
     * @internal
227
     */
228
    public static function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true, $references = array())
229
    {
230
        if (in_array($scalar[$i], $stringDelimiters)) {
231
            // quoted scalar
232
            $output = self::parseQuotedScalar($scalar, $i);
233
234
            if (null !== $delimiters) {
235
                $tmp = ltrim(substr($scalar, $i), ' ');
236
                if ('' === $tmp) {
237
                    throw new ParseException(sprintf('Unexpected end of line, expected one of "%s".', implode($delimiters)));
238
                }
239
                if (!in_array($tmp[0], $delimiters)) {
240
                    throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)));
241
                }
242
            }
243
        } else {
244
            // "normal" string
245
            if (!$delimiters) {
246
                $output = substr($scalar, $i);
247
                $i += strlen($output);
248
249
                // remove comments
250
                if (Parser::preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) {
251
                    $output = substr($output, 0, $match[0][1]);
252
                }
253
            } elseif (Parser::preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
254
                $output = $match[1];
255
                $i += strlen($output);
256
            } else {
257
                throw new ParseException(sprintf('Malformed inline YAML string: %s.', $scalar));
258
            }
259
260
            // a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >)
261
            if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0])) {
262
                @trigger_error(sprintf('Not quoting the scalar "%s" starting with "%s" is deprecated since Symfony 2.8 and will throw a ParseException in 3.0.', $output, $output[0]), E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
263
264
                // to be thrown in 3.0
265
                // throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]));
266
            }
267
268
            if ($evaluate) {
269
                $output = self::evaluateScalar($output, $references);
270
            }
271
        }
272
273
        return $output;
274
    }
275
276
    /**
277
     * Parses a YAML quoted scalar.
278
     *
279
     * @param string $scalar
280
     * @param int    &$i
281
     *
282
     * @return string
283
     *
284
     * @throws ParseException When malformed inline YAML string is parsed
285
     */
286
    private static function parseQuotedScalar($scalar, &$i)
287
    {
288
        if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
289
            throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i)));
290
        }
291
292
        $output = substr($match[0], 1, strlen($match[0]) - 2);
293
294
        $unescaper = new Unescaper();
295
        if ('"' == $scalar[$i]) {
296
            $output = $unescaper->unescapeDoubleQuotedString($output);
297
        } else {
298
            $output = $unescaper->unescapeSingleQuotedString($output);
299
        }
300
301
        $i += strlen($match[0]);
302
303
        return $output;
304
    }
305
306
    /**
307
     * Parses a YAML sequence.
308
     *
309
     * @param string $sequence
310
     * @param int    &$i
311
     * @param array  $references
312
     *
313
     * @return array
314
     *
315
     * @throws ParseException When malformed inline YAML string is parsed
316
     */
317
    private static function parseSequence($sequence, &$i = 0, $references = array())
318
    {
319
        $output = array();
320
        $len = strlen($sequence);
321
        ++$i;
322
323
        // [foo, bar, ...]
324
        while ($i < $len) {
325
            switch ($sequence[$i]) {
326
                case '[':
327
                    // nested sequence
328
                    $output[] = self::parseSequence($sequence, $i, $references);
329
                    break;
330
                case '{':
331
                    // nested mapping
332
                    $output[] = self::parseMapping($sequence, $i, $references);
333
                    break;
334
                case ']':
335
                    return $output;
336
                case ',':
337
                case ' ':
338
                    break;
339
                default:
340
                    $isQuoted = in_array($sequence[$i], array('"', "'"));
341
                    $value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i, true, $references);
342
343
                    // the value can be an array if a reference has been resolved to an array var
344
                    if (!is_array($value) && !$isQuoted && false !== strpos($value, ': ')) {
345
                        // embedded mapping?
346
                        try {
347
                            $pos = 0;
348
                            $value = self::parseMapping('{'.$value.'}', $pos, $references);
349
                        } catch (\InvalidArgumentException $e) {
350
                            // no, it's not
351
                        }
352
                    }
353
354
                    $output[] = $value;
355
356
                    --$i;
357
            }
358
359
            ++$i;
360
        }
361
362
        throw new ParseException(sprintf('Malformed inline YAML string: %s.', $sequence));
363
    }
364
365
    /**
366
     * Parses a YAML mapping.
367
     *
368
     * @param string $mapping
369
     * @param int    &$i
370
     * @param array  $references
371
     *
372
     * @return array|\stdClass
373
     *
374
     * @throws ParseException When malformed inline YAML string is parsed
375
     */
376
    private static function parseMapping($mapping, &$i = 0, $references = array())
377
    {
378
        $output = array();
379
        $len = strlen($mapping);
380
        ++$i;
381
        $allowOverwrite = false;
382
383
        // {foo: bar, bar:foo, ...}
384
        while ($i < $len) {
385
            switch ($mapping[$i]) {
386
                case ' ':
387
                case ',':
388
                    ++$i;
389
                    continue 2;
390
                case '}':
391
                    if (self::$objectForMap) {
392
                        return (object) $output;
393
                    }
394
395
                    return $output;
396
            }
397
398
            // key
399
            $key = self::parseScalar($mapping, array(':', ' '), array('"', "'"), $i, false);
400
401
            if ('<<' === $key) {
402
                $allowOverwrite = true;
403
            }
404
405
            // value
406
            $done = false;
407
408
            while ($i < $len) {
409
                switch ($mapping[$i]) {
410 View Code Duplication
                    case '[':
411
                        // nested sequence
412
                        $value = self::parseSequence($mapping, $i, $references);
413
                        // Spec: Keys MUST be unique; first one wins.
414
                        // Parser cannot abort this mapping earlier, since lines
415
                        // are processed sequentially.
416
                        // But overwriting is allowed when a merge node is used in current block.
417
                        if ('<<' === $key) {
418
                            foreach ($value as $parsedValue) {
419
                                $output += $parsedValue;
420
                            }
421
                        } elseif ($allowOverwrite || !isset($output[$key])) {
422
                            $output[$key] = $value;
423
                        }
424
                        $done = true;
425
                        break;
426 View Code Duplication
                    case '{':
427
                        // nested mapping
428
                        $value = self::parseMapping($mapping, $i, $references);
429
                        // Spec: Keys MUST be unique; first one wins.
430
                        // Parser cannot abort this mapping earlier, since lines
431
                        // are processed sequentially.
432
                        // But overwriting is allowed when a merge node is used in current block.
433
                        if ('<<' === $key) {
434
                            $output += $value;
435
                        } elseif ($allowOverwrite || !isset($output[$key])) {
436
                            $output[$key] = $value;
437
                        }
438
                        $done = true;
439
                        break;
440
                    case ':':
441
                    case ' ':
442
                        break;
443
                    default:
444
                        $value = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i, true, $references);
445
                        // Spec: Keys MUST be unique; first one wins.
446
                        // Parser cannot abort this mapping earlier, since lines
447
                        // are processed sequentially.
448
                        // But overwriting is allowed when a merge node is used in current block.
449
                        if ('<<' === $key) {
450
                            $output += $value;
451
                        } elseif ($allowOverwrite || !isset($output[$key])) {
452
                            $output[$key] = $value;
453
                        }
454
                        $done = true;
455
                        --$i;
456
                }
457
458
                ++$i;
459
460
                if ($done) {
461
                    continue 2;
462
                }
463
            }
464
        }
465
466
        throw new ParseException(sprintf('Malformed inline YAML string: %s.', $mapping));
467
    }
468
469
    /**
470
     * Evaluates scalars and replaces magic values.
471
     *
472
     * @param string $scalar
473
     * @param array  $references
474
     *
475
     * @return mixed The evaluated YAML string
476
     *
477
     * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved
478
     */
479
    private static function evaluateScalar($scalar, $references = array())
480
    {
481
        $scalar = trim($scalar);
482
        $scalarLower = strtolower($scalar);
483
484
        if (0 === strpos($scalar, '*')) {
485 View Code Duplication
            if (false !== $pos = strpos($scalar, '#')) {
486
                $value = substr($scalar, 1, $pos - 2);
487
            } else {
488
                $value = substr($scalar, 1);
489
            }
490
491
            // an unquoted *
492
            if (false === $value || '' === $value) {
493
                throw new ParseException('A reference must contain at least one character.');
494
            }
495
496
            if (!array_key_exists($value, $references)) {
497
                throw new ParseException(sprintf('Reference "%s" does not exist.', $value));
498
            }
499
500
            return $references[$value];
501
        }
502
503
        switch (true) {
504
            case 'null' === $scalarLower:
505
            case '' === $scalar:
506
            case '~' === $scalar:
507
                return;
508
            case 'true' === $scalarLower:
509
                return true;
510
            case 'false' === $scalarLower:
511
                return false;
512
            // Optimise for returning strings.
513
            case '+' === $scalar[0] || '-' === $scalar[0] || '.' === $scalar[0] || '!' === $scalar[0] || is_numeric($scalar[0]):
514
                switch (true) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing \Symfony\Component\Yaml\...getHexRegex(), $scalar) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing \Symfony\Component\Yaml\...\.[0-9]+)?$/', $scalar) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing \Symfony\Component\Yaml\...estampRegex(), $scalar) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
515
                    case 0 === strpos($scalar, '!str'):
516
                        return (string) substr($scalar, 5);
517
                    case 0 === strpos($scalar, '! '):
518
                        return (int) self::parseScalar(substr($scalar, 2));
519 View Code Duplication
                    case 0 === strpos($scalar, '!php/object:'):
520
                        if (self::$objectSupport) {
521
                            return unserialize(substr($scalar, 12));
522
                        }
523
524
                        if (self::$exceptionOnInvalidType) {
525
                            throw new ParseException('Object support when parsing a YAML file has been disabled.');
526
                        }
527
528
                        return;
529 View Code Duplication
                    case 0 === strpos($scalar, '!!php/object:'):
530
                        if (self::$objectSupport) {
531
                            return unserialize(substr($scalar, 13));
532
                        }
533
534
                        if (self::$exceptionOnInvalidType) {
535
                            throw new ParseException('Object support when parsing a YAML file has been disabled.');
536
                        }
537
538
                        return;
539
                    case 0 === strpos($scalar, '!!float '):
540
                        return (float) substr($scalar, 8);
541
                    case ctype_digit($scalar):
542
                        $raw = $scalar;
543
                        $cast = (int) $scalar;
544
545
                        return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
546
                    case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)):
547
                        $raw = $scalar;
548
                        $cast = (int) $scalar;
549
550
                        return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw);
551
                    case is_numeric($scalar):
552
                    case Parser::preg_match(self::getHexRegex(), $scalar):
553
                        return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar;
554
                    case '.inf' === $scalarLower:
555
                    case '.nan' === $scalarLower:
556
                        return -log(0);
557
                    case '-.inf' === $scalarLower:
558
                        return log(0);
559
                    case Parser::preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar):
560
                        return (float) str_replace(',', '', $scalar);
561
                    case Parser::preg_match(self::getTimestampRegex(), $scalar):
562
                        $timeZone = date_default_timezone_get();
563
                        date_default_timezone_set('UTC');
564
                        $time = strtotime($scalar);
565
                        date_default_timezone_set($timeZone);
566
567
                        return $time;
568
                }
569
                // no break
570
            default:
571
                return (string) $scalar;
572
        }
573
    }
574
575
    /**
576
     * Gets a regex that matches a YAML date.
577
     *
578
     * @return string The regular expression
579
     *
580
     * @see http://www.yaml.org/spec/1.2/spec.html#id2761573
581
     */
582
    private static function getTimestampRegex()
583
    {
584
        return <<<EOF
585
        ~^
586
        (?P<year>[0-9][0-9][0-9][0-9])
587
        -(?P<month>[0-9][0-9]?)
588
        -(?P<day>[0-9][0-9]?)
589
        (?:(?:[Tt]|[ \t]+)
590
        (?P<hour>[0-9][0-9]?)
591
        :(?P<minute>[0-9][0-9])
592
        :(?P<second>[0-9][0-9])
593
        (?:\.(?P<fraction>[0-9]*))?
594
        (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
595
        (?::(?P<tz_minute>[0-9][0-9]))?))?)?
596
        $~x
597
EOF;
598
    }
599
600
    /**
601
     * Gets a regex that matches a YAML number in hexadecimal notation.
602
     *
603
     * @return string
604
     */
605
    private static function getHexRegex()
606
    {
607
        return '~^0x[0-9a-f]++$~i';
608
    }
609
}
610