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