7c9d4e03b5fd3538734dd9e727eca6205c600094
[yaffs-website] / vendor / symfony / yaml / Inline.php
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\DumpException;
15 use Symfony\Component\Yaml\Exception\ParseException;
16 use Symfony\Component\Yaml\Tag\TaggedValue;
17
18 /**
19  * Inline implements a YAML parser/dumper for the YAML inline syntax.
20  *
21  * @author Fabien Potencier <fabien@symfony.com>
22  *
23  * @internal
24  */
25 class Inline
26 {
27     const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')';
28
29     public static $parsedLineNumber = -1;
30     public static $parsedFilename;
31
32     private static $exceptionOnInvalidType = false;
33     private static $objectSupport = false;
34     private static $objectForMap = false;
35     private static $constantSupport = false;
36
37     /**
38      * @param int         $flags
39      * @param int|null    $parsedLineNumber
40      * @param string|null $parsedFilename
41      */
42     public static function initialize($flags, $parsedLineNumber = null, $parsedFilename = null)
43     {
44         self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags);
45         self::$objectSupport = (bool) (Yaml::PARSE_OBJECT & $flags);
46         self::$objectForMap = (bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags);
47         self::$constantSupport = (bool) (Yaml::PARSE_CONSTANT & $flags);
48         self::$parsedFilename = $parsedFilename;
49
50         if (null !== $parsedLineNumber) {
51             self::$parsedLineNumber = $parsedLineNumber;
52         }
53     }
54
55     /**
56      * Converts a YAML string to a PHP value.
57      *
58      * @param string $value      A YAML string
59      * @param int    $flags      A bit field of PARSE_* constants to customize the YAML parser behavior
60      * @param array  $references Mapping of variable names to values
61      *
62      * @return mixed A PHP value
63      *
64      * @throws ParseException
65      */
66     public static function parse($value, $flags = 0, $references = array())
67     {
68         if (\is_bool($flags)) {
69             @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
70
71             if ($flags) {
72                 $flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE;
73             } else {
74                 $flags = 0;
75             }
76         }
77
78         if (\func_num_args() >= 3 && !\is_array($references)) {
79             @trigger_error('Passing a boolean flag to toggle object support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED);
80
81             if ($references) {
82                 $flags |= Yaml::PARSE_OBJECT;
83             }
84
85             if (\func_num_args() >= 4) {
86                 @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED);
87
88                 if (func_get_arg(3)) {
89                     $flags |= Yaml::PARSE_OBJECT_FOR_MAP;
90                 }
91             }
92
93             if (\func_num_args() >= 5) {
94                 $references = func_get_arg(4);
95             } else {
96                 $references = array();
97             }
98         }
99
100         self::initialize($flags);
101
102         $value = trim($value);
103
104         if ('' === $value) {
105             return '';
106         }
107
108         if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) {
109             $mbEncoding = mb_internal_encoding();
110             mb_internal_encoding('ASCII');
111         }
112
113         $i = 0;
114         $tag = self::parseTag($value, $i, $flags);
115         switch ($value[$i]) {
116             case '[':
117                 $result = self::parseSequence($value, $flags, $i, $references);
118                 ++$i;
119                 break;
120             case '{':
121                 $result = self::parseMapping($value, $flags, $i, $references);
122                 ++$i;
123                 break;
124             default:
125                 $result = self::parseScalar($value, $flags, null, $i, null === $tag, $references);
126         }
127
128         if (null !== $tag) {
129             return new TaggedValue($tag, $result);
130         }
131
132         // some comments are allowed at the end
133         if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) {
134             throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
135         }
136
137         if (isset($mbEncoding)) {
138             mb_internal_encoding($mbEncoding);
139         }
140
141         return $result;
142     }
143
144     /**
145      * Dumps a given PHP variable to a YAML string.
146      *
147      * @param mixed $value The PHP variable to convert
148      * @param int   $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
149      *
150      * @return string The YAML string representing the PHP value
151      *
152      * @throws DumpException When trying to dump PHP resource
153      */
154     public static function dump($value, $flags = 0)
155     {
156         if (\is_bool($flags)) {
157             @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
158
159             if ($flags) {
160                 $flags = Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE;
161             } else {
162                 $flags = 0;
163             }
164         }
165
166         if (\func_num_args() >= 3) {
167             @trigger_error('Passing a boolean flag to toggle object support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::DUMP_OBJECT flag instead.', E_USER_DEPRECATED);
168
169             if (func_get_arg(2)) {
170                 $flags |= Yaml::DUMP_OBJECT;
171             }
172         }
173
174         switch (true) {
175             case \is_resource($value):
176                 if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) {
177                     throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value)));
178                 }
179
180                 return 'null';
181             case $value instanceof \DateTimeInterface:
182                 return $value->format('c');
183             case \is_object($value):
184                 if ($value instanceof TaggedValue) {
185                     return '!'.$value->getTag().' '.self::dump($value->getValue(), $flags);
186                 }
187
188                 if (Yaml::DUMP_OBJECT & $flags) {
189                     return '!php/object '.self::dump(serialize($value));
190                 }
191
192                 if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) {
193                     return self::dumpArray($value, $flags & ~Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE);
194                 }
195
196                 if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) {
197                     throw new DumpException('Object support when dumping a YAML file has been disabled.');
198                 }
199
200                 return 'null';
201             case \is_array($value):
202                 return self::dumpArray($value, $flags);
203             case null === $value:
204                 return 'null';
205             case true === $value:
206                 return 'true';
207             case false === $value:
208                 return 'false';
209             case ctype_digit($value):
210                 return \is_string($value) ? "'$value'" : (int) $value;
211             case is_numeric($value):
212                 $locale = setlocale(LC_NUMERIC, 0);
213                 if (false !== $locale) {
214                     setlocale(LC_NUMERIC, 'C');
215                 }
216                 if (\is_float($value)) {
217                     $repr = (string) $value;
218                     if (is_infinite($value)) {
219                         $repr = str_ireplace('INF', '.Inf', $repr);
220                     } elseif (floor($value) == $value && $repr == $value) {
221                         // Preserve float data type since storing a whole number will result in integer value.
222                         $repr = '!!float '.$repr;
223                     }
224                 } else {
225                     $repr = \is_string($value) ? "'$value'" : (string) $value;
226                 }
227                 if (false !== $locale) {
228                     setlocale(LC_NUMERIC, $locale);
229                 }
230
231                 return $repr;
232             case '' == $value:
233                 return "''";
234             case self::isBinaryString($value):
235                 return '!!binary '.base64_encode($value);
236             case Escaper::requiresDoubleQuoting($value):
237                 return Escaper::escapeWithDoubleQuotes($value);
238             case Escaper::requiresSingleQuoting($value):
239             case Parser::preg_match('{^[0-9]+[_0-9]*$}', $value):
240             case Parser::preg_match(self::getHexRegex(), $value):
241             case Parser::preg_match(self::getTimestampRegex(), $value):
242                 return Escaper::escapeWithSingleQuotes($value);
243             default:
244                 return $value;
245         }
246     }
247
248     /**
249      * Check if given array is hash or just normal indexed array.
250      *
251      * @internal
252      *
253      * @param array|\ArrayObject|\stdClass $value The PHP array or array-like object to check
254      *
255      * @return bool true if value is hash array, false otherwise
256      */
257     public static function isHash($value)
258     {
259         if ($value instanceof \stdClass || $value instanceof \ArrayObject) {
260             return true;
261         }
262
263         $expectedKey = 0;
264
265         foreach ($value as $key => $val) {
266             if ($key !== $expectedKey++) {
267                 return true;
268             }
269         }
270
271         return false;
272     }
273
274     /**
275      * Dumps a PHP array to a YAML string.
276      *
277      * @param array $value The PHP array to dump
278      * @param int   $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
279      *
280      * @return string The YAML string representing the PHP array
281      */
282     private static function dumpArray($value, $flags)
283     {
284         // array
285         if (($value || Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE & $flags) && !self::isHash($value)) {
286             $output = array();
287             foreach ($value as $val) {
288                 $output[] = self::dump($val, $flags);
289             }
290
291             return sprintf('[%s]', implode(', ', $output));
292         }
293
294         // hash
295         $output = array();
296         foreach ($value as $key => $val) {
297             $output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags));
298         }
299
300         return sprintf('{ %s }', implode(', ', $output));
301     }
302
303     /**
304      * Parses a YAML scalar.
305      *
306      * @param string   $scalar
307      * @param int      $flags
308      * @param string[] $delimiters
309      * @param int      &$i
310      * @param bool     $evaluate
311      * @param array    $references
312      *
313      * @return string
314      *
315      * @throws ParseException When malformed inline YAML string is parsed
316      *
317      * @internal
318      */
319     public static function parseScalar($scalar, $flags = 0, $delimiters = null, &$i = 0, $evaluate = true, $references = array(), $legacyOmittedKeySupport = false)
320     {
321         if (\in_array($scalar[$i], array('"', "'"))) {
322             // quoted scalar
323             $output = self::parseQuotedScalar($scalar, $i);
324
325             if (null !== $delimiters) {
326                 $tmp = ltrim(substr($scalar, $i), ' ');
327                 if ('' === $tmp) {
328                     throw new ParseException(sprintf('Unexpected end of line, expected one of "%s".', implode('', $delimiters)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
329                 }
330                 if (!\in_array($tmp[0], $delimiters)) {
331                     throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
332                 }
333             }
334         } else {
335             // "normal" string
336             if (!$delimiters) {
337                 $output = substr($scalar, $i);
338                 $i += \strlen($output);
339
340                 // remove comments
341                 if (Parser::preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) {
342                     $output = substr($output, 0, $match[0][1]);
343                 }
344             } elseif (Parser::preg_match('/^(.'.($legacyOmittedKeySupport ? '+' : '*').'?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
345                 $output = $match[1];
346                 $i += \strlen($output);
347             } else {
348                 throw new ParseException(sprintf('Malformed inline YAML string: %s.', $scalar), self::$parsedLineNumber + 1, null, self::$parsedFilename);
349             }
350
351             // a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >)
352             if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0])) {
353                 throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]), self::$parsedLineNumber + 1, $output, self::$parsedFilename);
354             }
355
356             if ($output && '%' === $output[0]) {
357                 @trigger_error(self::getDeprecationMessage(sprintf('Not quoting the scalar "%s" starting with the "%%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.', $output)), E_USER_DEPRECATED);
358             }
359
360             if ($evaluate) {
361                 $output = self::evaluateScalar($output, $flags, $references);
362             }
363         }
364
365         return $output;
366     }
367
368     /**
369      * Parses a YAML quoted scalar.
370      *
371      * @param string $scalar
372      * @param int    &$i
373      *
374      * @return string
375      *
376      * @throws ParseException When malformed inline YAML string is parsed
377      */
378     private static function parseQuotedScalar($scalar, &$i)
379     {
380         if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
381             throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
382         }
383
384         $output = substr($match[0], 1, \strlen($match[0]) - 2);
385
386         $unescaper = new Unescaper();
387         if ('"' == $scalar[$i]) {
388             $output = $unescaper->unescapeDoubleQuotedString($output);
389         } else {
390             $output = $unescaper->unescapeSingleQuotedString($output);
391         }
392
393         $i += \strlen($match[0]);
394
395         return $output;
396     }
397
398     /**
399      * Parses a YAML sequence.
400      *
401      * @param string $sequence
402      * @param int    $flags
403      * @param int    &$i
404      * @param array  $references
405      *
406      * @return array
407      *
408      * @throws ParseException When malformed inline YAML string is parsed
409      */
410     private static function parseSequence($sequence, $flags, &$i = 0, $references = array())
411     {
412         $output = array();
413         $len = \strlen($sequence);
414         ++$i;
415
416         // [foo, bar, ...]
417         while ($i < $len) {
418             if (']' === $sequence[$i]) {
419                 return $output;
420             }
421             if (',' === $sequence[$i] || ' ' === $sequence[$i]) {
422                 ++$i;
423
424                 continue;
425             }
426
427             $tag = self::parseTag($sequence, $i, $flags);
428             switch ($sequence[$i]) {
429                 case '[':
430                     // nested sequence
431                     $value = self::parseSequence($sequence, $flags, $i, $references);
432                     break;
433                 case '{':
434                     // nested mapping
435                     $value = self::parseMapping($sequence, $flags, $i, $references);
436                     break;
437                 default:
438                     $isQuoted = \in_array($sequence[$i], array('"', "'"));
439                     $value = self::parseScalar($sequence, $flags, array(',', ']'), $i, null === $tag, $references);
440
441                     // the value can be an array if a reference has been resolved to an array var
442                     if (\is_string($value) && !$isQuoted && false !== strpos($value, ': ')) {
443                         // embedded mapping?
444                         try {
445                             $pos = 0;
446                             $value = self::parseMapping('{'.$value.'}', $flags, $pos, $references);
447                         } catch (\InvalidArgumentException $e) {
448                             // no, it's not
449                         }
450                     }
451
452                     --$i;
453             }
454
455             if (null !== $tag) {
456                 $value = new TaggedValue($tag, $value);
457             }
458
459             $output[] = $value;
460
461             ++$i;
462         }
463
464         throw new ParseException(sprintf('Malformed inline YAML string: %s.', $sequence), self::$parsedLineNumber + 1, null, self::$parsedFilename);
465     }
466
467     /**
468      * Parses a YAML mapping.
469      *
470      * @param string $mapping
471      * @param int    $flags
472      * @param int    &$i
473      * @param array  $references
474      *
475      * @return array|\stdClass
476      *
477      * @throws ParseException When malformed inline YAML string is parsed
478      */
479     private static function parseMapping($mapping, $flags, &$i = 0, $references = array())
480     {
481         $output = array();
482         $len = \strlen($mapping);
483         ++$i;
484         $allowOverwrite = false;
485
486         // {foo: bar, bar:foo, ...}
487         while ($i < $len) {
488             switch ($mapping[$i]) {
489                 case ' ':
490                 case ',':
491                     ++$i;
492                     continue 2;
493                 case '}':
494                     if (self::$objectForMap) {
495                         return (object) $output;
496                     }
497
498                     return $output;
499             }
500
501             // key
502             $isKeyQuoted = \in_array($mapping[$i], array('"', "'"), true);
503             $key = self::parseScalar($mapping, $flags, array(':', ' '), $i, false, array(), true);
504
505             if (':' !== $key && false === $i = strpos($mapping, ':', $i)) {
506                 break;
507             }
508
509             if (':' === $key) {
510                 @trigger_error(self::getDeprecationMessage('Omitting the key of a mapping is deprecated and will throw a ParseException in 4.0.'), E_USER_DEPRECATED);
511             }
512
513             if (!$isKeyQuoted) {
514                 $evaluatedKey = self::evaluateScalar($key, $flags, $references);
515
516                 if ('' !== $key && $evaluatedKey !== $key && !\is_string($evaluatedKey) && !\is_int($evaluatedKey)) {
517                     @trigger_error(self::getDeprecationMessage('Implicit casting of incompatible mapping keys to strings is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead.'), E_USER_DEPRECATED);
518                 }
519             }
520
521             if (':' !== $key && !$isKeyQuoted && (!isset($mapping[$i + 1]) || !\in_array($mapping[$i + 1], array(' ', ',', '[', ']', '{', '}'), true))) {
522                 @trigger_error(self::getDeprecationMessage('Using a colon after an unquoted mapping key that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}") is deprecated since Symfony 3.2 and will throw a ParseException in 4.0.'), E_USER_DEPRECATED);
523             }
524
525             if ('<<' === $key) {
526                 $allowOverwrite = true;
527             }
528
529             while ($i < $len) {
530                 if (':' === $mapping[$i] || ' ' === $mapping[$i]) {
531                     ++$i;
532
533                     continue;
534                 }
535
536                 $tag = self::parseTag($mapping, $i, $flags);
537                 switch ($mapping[$i]) {
538                     case '[':
539                         // nested sequence
540                         $value = self::parseSequence($mapping, $flags, $i, $references);
541                         // Spec: Keys MUST be unique; first one wins.
542                         // Parser cannot abort this mapping earlier, since lines
543                         // are processed sequentially.
544                         // But overwriting is allowed when a merge node is used in current block.
545                         if ('<<' === $key) {
546                             foreach ($value as $parsedValue) {
547                                 $output += $parsedValue;
548                             }
549                         } elseif ($allowOverwrite || !isset($output[$key])) {
550                             if (null !== $tag) {
551                                 $output[$key] = new TaggedValue($tag, $value);
552                             } else {
553                                 $output[$key] = $value;
554                             }
555                         } elseif (isset($output[$key])) {
556                             @trigger_error(self::getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED);
557                         }
558                         break;
559                     case '{':
560                         // nested mapping
561                         $value = self::parseMapping($mapping, $flags, $i, $references);
562                         // Spec: Keys MUST be unique; first one wins.
563                         // Parser cannot abort this mapping earlier, since lines
564                         // are processed sequentially.
565                         // But overwriting is allowed when a merge node is used in current block.
566                         if ('<<' === $key) {
567                             $output += $value;
568                         } elseif ($allowOverwrite || !isset($output[$key])) {
569                             if (null !== $tag) {
570                                 $output[$key] = new TaggedValue($tag, $value);
571                             } else {
572                                 $output[$key] = $value;
573                             }
574                         } elseif (isset($output[$key])) {
575                             @trigger_error(self::getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED);
576                         }
577                         break;
578                     default:
579                         $value = self::parseScalar($mapping, $flags, array(',', '}'), $i, null === $tag, $references);
580                         // Spec: Keys MUST be unique; first one wins.
581                         // Parser cannot abort this mapping earlier, since lines
582                         // are processed sequentially.
583                         // But overwriting is allowed when a merge node is used in current block.
584                         if ('<<' === $key) {
585                             $output += $value;
586                         } elseif ($allowOverwrite || !isset($output[$key])) {
587                             if (null !== $tag) {
588                                 $output[$key] = new TaggedValue($tag, $value);
589                             } else {
590                                 $output[$key] = $value;
591                             }
592                         } elseif (isset($output[$key])) {
593                             @trigger_error(self::getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED);
594                         }
595                         --$i;
596                 }
597                 ++$i;
598
599                 continue 2;
600             }
601         }
602
603         throw new ParseException(sprintf('Malformed inline YAML string: %s.', $mapping), self::$parsedLineNumber + 1, null, self::$parsedFilename);
604     }
605
606     /**
607      * Evaluates scalars and replaces magic values.
608      *
609      * @param string $scalar
610      * @param int    $flags
611      * @param array  $references
612      *
613      * @return mixed The evaluated YAML string
614      *
615      * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved
616      */
617     private static function evaluateScalar($scalar, $flags, $references = array())
618     {
619         $scalar = trim($scalar);
620         $scalarLower = strtolower($scalar);
621
622         if (0 === strpos($scalar, '*')) {
623             if (false !== $pos = strpos($scalar, '#')) {
624                 $value = substr($scalar, 1, $pos - 2);
625             } else {
626                 $value = substr($scalar, 1);
627             }
628
629             // an unquoted *
630             if (false === $value || '' === $value) {
631                 throw new ParseException('A reference must contain at least one character.', self::$parsedLineNumber + 1, $value, self::$parsedFilename);
632             }
633
634             if (!array_key_exists($value, $references)) {
635                 throw new ParseException(sprintf('Reference "%s" does not exist.', $value), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
636             }
637
638             return $references[$value];
639         }
640
641         switch (true) {
642             case 'null' === $scalarLower:
643             case '' === $scalar:
644             case '~' === $scalar:
645                 return;
646             case 'true' === $scalarLower:
647                 return true;
648             case 'false' === $scalarLower:
649                 return false;
650             case '!' === $scalar[0]:
651                 switch (true) {
652                     case 0 === strpos($scalar, '!str'):
653                         @trigger_error(self::getDeprecationMessage('Support for the !str tag is deprecated since Symfony 3.4. Use the !!str tag instead.'), E_USER_DEPRECATED);
654
655                         return (string) substr($scalar, 5);
656                     case 0 === strpos($scalar, '!!str '):
657                         return (string) substr($scalar, 6);
658                     case 0 === strpos($scalar, '! '):
659                         @trigger_error(self::getDeprecationMessage('Using the non-specific tag "!" is deprecated since Symfony 3.4 as its behavior will change in 4.0. It will force non-evaluating your values in 4.0. Use plain integers or !!float instead.'), E_USER_DEPRECATED);
660
661                         return (int) self::parseScalar(substr($scalar, 2), $flags);
662                     case 0 === strpos($scalar, '!php/object:'):
663                         if (self::$objectSupport) {
664                             @trigger_error(self::getDeprecationMessage('The !php/object: tag to indicate dumped PHP objects is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/object (without the colon) tag instead.'), E_USER_DEPRECATED);
665
666                             return unserialize(substr($scalar, 12));
667                         }
668
669                         if (self::$exceptionOnInvalidType) {
670                             throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
671                         }
672
673                         return;
674                     case 0 === strpos($scalar, '!!php/object:'):
675                         if (self::$objectSupport) {
676                             @trigger_error(self::getDeprecationMessage('The !!php/object: tag to indicate dumped PHP objects is deprecated since Symfony 3.1 and will be removed in 4.0. Use the !php/object (without the colon) tag instead.'), E_USER_DEPRECATED);
677
678                             return unserialize(substr($scalar, 13));
679                         }
680
681                         if (self::$exceptionOnInvalidType) {
682                             throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
683                         }
684
685                         return;
686                     case 0 === strpos($scalar, '!php/object'):
687                         if (self::$objectSupport) {
688                             return unserialize(self::parseScalar(substr($scalar, 12)));
689                         }
690
691                         if (self::$exceptionOnInvalidType) {
692                             throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
693                         }
694
695                         return;
696                     case 0 === strpos($scalar, '!php/const:'):
697                         if (self::$constantSupport) {
698                             @trigger_error(self::getDeprecationMessage('The !php/const: tag to indicate dumped PHP constants is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/const (without the colon) tag instead.'), E_USER_DEPRECATED);
699
700                             if (\defined($const = substr($scalar, 11))) {
701                                 return \constant($const);
702                             }
703
704                             throw new ParseException(sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
705                         }
706                         if (self::$exceptionOnInvalidType) {
707                             throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Have you forgotten to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
708                         }
709
710                         return;
711                     case 0 === strpos($scalar, '!php/const'):
712                         if (self::$constantSupport) {
713                             $i = 0;
714                             if (\defined($const = self::parseScalar(substr($scalar, 11), 0, null, $i, false))) {
715                                 return \constant($const);
716                             }
717
718                             throw new ParseException(sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
719                         }
720                         if (self::$exceptionOnInvalidType) {
721                             throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Have you forgotten to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
722                         }
723
724                         return;
725                     case 0 === strpos($scalar, '!!float '):
726                         return (float) substr($scalar, 8);
727                     case 0 === strpos($scalar, '!!binary '):
728                         return self::evaluateBinaryScalar(substr($scalar, 9));
729                     default:
730                         @trigger_error(self::getDeprecationMessage(sprintf('Using the unquoted scalar value "%s" is deprecated since Symfony 3.3 and will be considered as a tagged value in 4.0. You must quote it.', $scalar)), E_USER_DEPRECATED);
731                 }
732
733             // Optimize for returning strings.
734             // no break
735             case '+' === $scalar[0] || '-' === $scalar[0] || '.' === $scalar[0] || is_numeric($scalar[0]):
736                 switch (true) {
737                     case Parser::preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar):
738                         $scalar = str_replace('_', '', (string) $scalar);
739                         // omitting the break / return as integers are handled in the next case
740                         // no break
741                     case ctype_digit($scalar):
742                         $raw = $scalar;
743                         $cast = (int) $scalar;
744
745                         return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
746                     case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)):
747                         $raw = $scalar;
748                         $cast = (int) $scalar;
749
750                         return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw);
751                     case is_numeric($scalar):
752                     case Parser::preg_match(self::getHexRegex(), $scalar):
753                         $scalar = str_replace('_', '', $scalar);
754
755                         return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar;
756                     case '.inf' === $scalarLower:
757                     case '.nan' === $scalarLower:
758                         return -log(0);
759                     case '-.inf' === $scalarLower:
760                         return log(0);
761                     case Parser::preg_match('/^(-|\+)?[0-9][0-9,]*(\.[0-9_]+)?$/', $scalar):
762                     case Parser::preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar):
763                         if (false !== strpos($scalar, ',')) {
764                             @trigger_error(self::getDeprecationMessage('Using the comma as a group separator for floats is deprecated since Symfony 3.2 and will be removed in 4.0.'), E_USER_DEPRECATED);
765                         }
766
767                         return (float) str_replace(array(',', '_'), '', $scalar);
768                     case Parser::preg_match(self::getTimestampRegex(), $scalar):
769                         if (Yaml::PARSE_DATETIME & $flags) {
770                             // When no timezone is provided in the parsed date, YAML spec says we must assume UTC.
771                             return new \DateTime($scalar, new \DateTimeZone('UTC'));
772                         }
773
774                         $timeZone = date_default_timezone_get();
775                         date_default_timezone_set('UTC');
776                         $time = strtotime($scalar);
777                         date_default_timezone_set($timeZone);
778
779                         return $time;
780                 }
781         }
782
783         return (string) $scalar;
784     }
785
786     /**
787      * @param string $value
788      * @param int    &$i
789      * @param int    $flags
790      *
791      * @return string|null
792      */
793     private static function parseTag($value, &$i, $flags)
794     {
795         if ('!' !== $value[$i]) {
796             return;
797         }
798
799         $tagLength = strcspn($value, " \t\n", $i + 1);
800         $tag = substr($value, $i + 1, $tagLength);
801
802         $nextOffset = $i + $tagLength + 1;
803         $nextOffset += strspn($value, ' ', $nextOffset);
804
805         // Is followed by a scalar
806         if ((!isset($value[$nextOffset]) || !\in_array($value[$nextOffset], array('[', '{'), true)) && 'tagged' !== $tag) {
807             // Manage non-whitelisted scalars in {@link self::evaluateScalar()}
808             return;
809         }
810
811         // Built-in tags
812         if ($tag && '!' === $tag[0]) {
813             throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
814         }
815
816         if (Yaml::PARSE_CUSTOM_TAGS & $flags) {
817             $i = $nextOffset;
818
819             return $tag;
820         }
821
822         throw new ParseException(sprintf('Tags support is not enabled. Enable the `Yaml::PARSE_CUSTOM_TAGS` flag to use "!%s".', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
823     }
824
825     /**
826      * @param string $scalar
827      *
828      * @return string
829      *
830      * @internal
831      */
832     public static function evaluateBinaryScalar($scalar)
833     {
834         $parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar));
835
836         if (0 !== (\strlen($parsedBinaryData) % 4)) {
837             throw new ParseException(sprintf('The normalized base64 encoded data (data without whitespace characters) length must be a multiple of four (%d bytes given).', \strlen($parsedBinaryData)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
838         }
839
840         if (!Parser::preg_match('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) {
841             throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
842         }
843
844         return base64_decode($parsedBinaryData, true);
845     }
846
847     private static function isBinaryString($value)
848     {
849         return !preg_match('//u', $value) || preg_match('/[^\x00\x07-\x0d\x1B\x20-\xff]/', $value);
850     }
851
852     /**
853      * Gets a regex that matches a YAML date.
854      *
855      * @return string The regular expression
856      *
857      * @see http://www.yaml.org/spec/1.2/spec.html#id2761573
858      */
859     private static function getTimestampRegex()
860     {
861         return <<<EOF
862         ~^
863         (?P<year>[0-9][0-9][0-9][0-9])
864         -(?P<month>[0-9][0-9]?)
865         -(?P<day>[0-9][0-9]?)
866         (?:(?:[Tt]|[ \t]+)
867         (?P<hour>[0-9][0-9]?)
868         :(?P<minute>[0-9][0-9])
869         :(?P<second>[0-9][0-9])
870         (?:\.(?P<fraction>[0-9]*))?
871         (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
872         (?::(?P<tz_minute>[0-9][0-9]))?))?)?
873         $~x
874 EOF;
875     }
876
877     /**
878      * Gets a regex that matches a YAML number in hexadecimal notation.
879      *
880      * @return string
881      */
882     private static function getHexRegex()
883     {
884         return '~^0x[0-9a-f_]++$~i';
885     }
886
887     private static function getDeprecationMessage($message)
888     {
889         $message = rtrim($message, '.');
890
891         if (null !== self::$parsedFilename) {
892             $message .= ' in '.self::$parsedFilename;
893         }
894
895         if (-1 !== self::$parsedLineNumber) {
896             $message .= ' on line '.(self::$parsedLineNumber + 1);
897         }
898
899         return $message.'.';
900     }
901 }