ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
Inline.php
Go to the documentation of this file.
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 
16 
24 class Inline
25 {
26  const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')';
27 
28  private static $exceptionOnInvalidType = false;
29  private static $objectSupport = false;
30  private static $objectForMap = false;
31 
43  public static function parse($value, $flags = 0, $references = array())
44  {
45  if (is_bool($flags)) {
46  @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);
47 
48  if ($flags) {
50  } else {
51  $flags = 0;
52  }
53  }
54 
55  if (func_num_args() >= 3 && !is_array($references)) {
56  @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);
57 
58  if ($references) {
59  $flags |= Yaml::PARSE_OBJECT;
60  }
61 
62  if (func_num_args() >= 4) {
63  @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);
64 
65  if (func_get_arg(3)) {
67  }
68  }
69 
70  if (func_num_args() >= 5) {
71  $references = func_get_arg(4);
72  } else {
73  $references = array();
74  }
75  }
76 
77  self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags);
78  self::$objectSupport = (bool) (Yaml::PARSE_OBJECT & $flags);
79  self::$objectForMap = (bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags);
80 
81  $value = trim($value);
82 
83  if ('' === $value) {
84  return '';
85  }
86 
87  if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) {
88  $mbEncoding = mb_internal_encoding();
89  mb_internal_encoding('ASCII');
90  }
91 
92  $i = 0;
93  switch ($value[0]) {
94  case '[':
95  $result = self::parseSequence($value, $flags, $i, $references);
96  ++$i;
97  break;
98  case '{':
99  $result = self::parseMapping($value, $flags, $i, $references);
100  ++$i;
101  break;
102  default:
103  $result = self::parseScalar($value, $flags, null, array('"', "'"), $i, true, $references);
104  }
105 
106  // some comments are allowed at the end
107  if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) {
108  throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)));
109  }
110 
111  if (isset($mbEncoding)) {
112  mb_internal_encoding($mbEncoding);
113  }
114 
115  return $result;
116  }
117 
128  public static function dump($value, $flags = 0)
129  {
130  if (is_bool($flags)) {
131  @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);
132 
133  if ($flags) {
135  } else {
136  $flags = 0;
137  }
138  }
139 
140  if (func_num_args() >= 3) {
141  @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);
142 
143  if (func_get_arg(2)) {
144  $flags |= Yaml::DUMP_OBJECT;
145  }
146  }
147 
148  switch (true) {
149  case is_resource($value):
151  throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value)));
152  }
153 
154  return 'null';
155  case $value instanceof \DateTimeInterface:
156  return $value->format('c');
157  case is_object($value):
158  if (Yaml::DUMP_OBJECT & $flags) {
159  return '!php/object:'.serialize($value);
160  }
161 
162  if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) {
163  return self::dumpArray((array) $value, $flags);
164  }
165 
167  throw new DumpException('Object support when dumping a YAML file has been disabled.');
168  }
169 
170  return 'null';
171  case is_array($value):
172  return self::dumpArray($value, $flags);
173  case null === $value:
174  return 'null';
175  case true === $value:
176  return 'true';
177  case false === $value:
178  return 'false';
179  case ctype_digit($value):
180  return is_string($value) ? "'$value'" : (int) $value;
181  case is_numeric($value):
182  $locale = setlocale(LC_NUMERIC, 0);
183  if (false !== $locale) {
184  setlocale(LC_NUMERIC, 'C');
185  }
186  if (is_float($value)) {
187  $repr = (string) $value;
188  if (is_infinite($value)) {
189  $repr = str_ireplace('INF', '.Inf', $repr);
190  } elseif (floor($value) == $value && $repr == $value) {
191  // Preserve float data type since storing a whole number will result in integer value.
192  $repr = '!!float '.$repr;
193  }
194  } else {
195  $repr = is_string($value) ? "'$value'" : (string) $value;
196  }
197  if (false !== $locale) {
198  setlocale(LC_NUMERIC, $locale);
199  }
200 
201  return $repr;
202  case '' == $value:
203  return "''";
204  case self::isBinaryString($value):
205  return '!!binary '.base64_encode($value);
206  case Escaper::requiresDoubleQuoting($value):
207  return Escaper::escapeWithDoubleQuotes($value);
208  case Escaper::requiresSingleQuoting($value):
209  case preg_match(self::getHexRegex(), $value):
210  case preg_match(self::getTimestampRegex(), $value):
211  return Escaper::escapeWithSingleQuotes($value);
212  default:
213  return $value;
214  }
215  }
216 
226  public static function isHash(array $value)
227  {
228  $expectedKey = 0;
229 
230  foreach ($value as $key => $val) {
231  if ($key !== $expectedKey++) {
232  return true;
233  }
234  }
235 
236  return false;
237  }
238 
247  private static function dumpArray($value, $flags)
248  {
249  // array
250  if ($value && !self::isHash($value)) {
251  $output = array();
252  foreach ($value as $val) {
253  $output[] = self::dump($val, $flags);
254  }
255 
256  return sprintf('[%s]', implode(', ', $output));
257  }
258 
259  // hash
260  $output = array();
261  foreach ($value as $key => $val) {
262  $output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags));
263  }
264 
265  return sprintf('{ %s }', implode(', ', $output));
266  }
267 
285  public static function parseScalar($scalar, $flags = 0, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true, $references = array())
286  {
287  if (in_array($scalar[$i], $stringDelimiters)) {
288  // quoted scalar
289  $output = self::parseQuotedScalar($scalar, $i);
290 
291  if (null !== $delimiters) {
292  $tmp = ltrim(substr($scalar, $i), ' ');
293  if (!in_array($tmp[0], $delimiters)) {
294  throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)));
295  }
296  }
297  } else {
298  // "normal" string
299  if (!$delimiters) {
300  $output = substr($scalar, $i);
301  $i += strlen($output);
302 
303  // remove comments
304  if (preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) {
305  $output = substr($output, 0, $match[0][1]);
306  }
307  } elseif (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
308  $output = $match[1];
309  $i += strlen($output);
310  } else {
311  throw new ParseException(sprintf('Malformed inline YAML string (%s).', $scalar));
312  }
313 
314  // a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >)
315  if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0])) {
316  throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]));
317  }
318 
319  if ($output && '%' === $output[0]) {
320  @trigger_error('Not quoting a scalar starting with the "%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.', E_USER_DEPRECATED);
321  }
322 
323  if ($evaluate) {
324  $output = self::evaluateScalar($output, $flags, $references);
325  }
326  }
327 
328  return $output;
329  }
330 
341  private static function parseQuotedScalar($scalar, &$i)
342  {
343  if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
344  throw new ParseException(sprintf('Malformed inline YAML string (%s).', substr($scalar, $i)));
345  }
346 
347  $output = substr($match[0], 1, strlen($match[0]) - 2);
348 
349  $unescaper = new Unescaper();
350  if ('"' == $scalar[$i]) {
351  $output = $unescaper->unescapeDoubleQuotedString($output);
352  } else {
353  $output = $unescaper->unescapeSingleQuotedString($output);
354  }
355 
356  $i += strlen($match[0]);
357 
358  return $output;
359  }
360 
373  private static function parseSequence($sequence, $flags, &$i = 0, $references = array())
374  {
375  $output = array();
376  $len = strlen($sequence);
377  ++$i;
378 
379  // [foo, bar, ...]
380  while ($i < $len) {
381  switch ($sequence[$i]) {
382  case '[':
383  // nested sequence
384  $output[] = self::parseSequence($sequence, $flags, $i, $references);
385  break;
386  case '{':
387  // nested mapping
388  $output[] = self::parseMapping($sequence, $flags, $i, $references);
389  break;
390  case ']':
391  return $output;
392  case ',':
393  case ' ':
394  break;
395  default:
396  $isQuoted = in_array($sequence[$i], array('"', "'"));
397  $value = self::parseScalar($sequence, $flags, array(',', ']'), array('"', "'"), $i, true, $references);
398 
399  // the value can be an array if a reference has been resolved to an array var
400  if (is_string($value) && !$isQuoted && false !== strpos($value, ': ')) {
401  // embedded mapping?
402  try {
403  $pos = 0;
404  $value = self::parseMapping('{'.$value.'}', $flags, $pos, $references);
405  } catch (\InvalidArgumentException $e) {
406  // no, it's not
407  }
408  }
409 
410  $output[] = $value;
411 
412  --$i;
413  }
414 
415  ++$i;
416  }
417 
418  throw new ParseException(sprintf('Malformed inline YAML string %s', $sequence));
419  }
420 
433  private static function parseMapping($mapping, $flags, &$i = 0, $references = array())
434  {
435  $output = array();
436  $len = strlen($mapping);
437  ++$i;
438 
439  // {foo: bar, bar:foo, ...}
440  while ($i < $len) {
441  switch ($mapping[$i]) {
442  case ' ':
443  case ',':
444  ++$i;
445  continue 2;
446  case '}':
447  if (self::$objectForMap) {
448  return (object) $output;
449  }
450 
451  return $output;
452  }
453 
454  // key
455  $key = self::parseScalar($mapping, $flags, array(':', ' '), array('"', "'"), $i, false);
456 
457  // value
458  $done = false;
459 
460  while ($i < $len) {
461  switch ($mapping[$i]) {
462  case '[':
463  // nested sequence
464  $value = self::parseSequence($mapping, $flags, $i, $references);
465  // Spec: Keys MUST be unique; first one wins.
466  // Parser cannot abort this mapping earlier, since lines
467  // are processed sequentially.
468  if (!isset($output[$key])) {
469  $output[$key] = $value;
470  }
471  $done = true;
472  break;
473  case '{':
474  // nested mapping
475  $value = self::parseMapping($mapping, $flags, $i, $references);
476  // Spec: Keys MUST be unique; first one wins.
477  // Parser cannot abort this mapping earlier, since lines
478  // are processed sequentially.
479  if (!isset($output[$key])) {
480  $output[$key] = $value;
481  }
482  $done = true;
483  break;
484  case ':':
485  case ' ':
486  break;
487  default:
488  $value = self::parseScalar($mapping, $flags, array(',', '}'), array('"', "'"), $i, true, $references);
489  // Spec: Keys MUST be unique; first one wins.
490  // Parser cannot abort this mapping earlier, since lines
491  // are processed sequentially.
492  if (!isset($output[$key])) {
493  $output[$key] = $value;
494  }
495  $done = true;
496  --$i;
497  }
498 
499  ++$i;
500 
501  if ($done) {
502  continue 2;
503  }
504  }
505  }
506 
507  throw new ParseException(sprintf('Malformed inline YAML string %s', $mapping));
508  }
509 
521  private static function evaluateScalar($scalar, $flags, $references = array())
522  {
523  $scalar = trim($scalar);
524  $scalarLower = strtolower($scalar);
525 
526  if (0 === strpos($scalar, '*')) {
527  if (false !== $pos = strpos($scalar, '#')) {
528  $value = substr($scalar, 1, $pos - 2);
529  } else {
530  $value = substr($scalar, 1);
531  }
532 
533  // an unquoted *
534  if (false === $value || '' === $value) {
535  throw new ParseException('A reference must contain at least one character.');
536  }
537 
538  if (!array_key_exists($value, $references)) {
539  throw new ParseException(sprintf('Reference "%s" does not exist.', $value));
540  }
541 
542  return $references[$value];
543  }
544 
545  switch (true) {
546  case 'null' === $scalarLower:
547  case '' === $scalar:
548  case '~' === $scalar:
549  return;
550  case 'true' === $scalarLower:
551  return true;
552  case 'false' === $scalarLower:
553  return false;
554  // Optimise for returning strings.
555  case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || $scalar[0] === '!' || is_numeric($scalar[0]):
556  switch (true) {
557  case 0 === strpos($scalar, '!str'):
558  return (string) substr($scalar, 5);
559  case 0 === strpos($scalar, '! '):
560  return (int) self::parseScalar(substr($scalar, 2), $flags);
561  case 0 === strpos($scalar, '!php/object:'):
562  if (self::$objectSupport) {
563  return unserialize(substr($scalar, 12));
564  }
565 
566  if (self::$exceptionOnInvalidType) {
567  throw new ParseException('Object support when parsing a YAML file has been disabled.');
568  }
569 
570  return;
571  case 0 === strpos($scalar, '!!php/object:'):
572  if (self::$objectSupport) {
573  @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);
574 
575  return unserialize(substr($scalar, 13));
576  }
577 
578  if (self::$exceptionOnInvalidType) {
579  throw new ParseException('Object support when parsing a YAML file has been disabled.');
580  }
581 
582  return;
583  case 0 === strpos($scalar, '!!float '):
584  return (float) substr($scalar, 8);
585  case ctype_digit($scalar):
586  $raw = $scalar;
587  $cast = (int) $scalar;
588 
589  return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
590  case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)):
591  $raw = $scalar;
592  $cast = (int) $scalar;
593 
594  return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw);
595  case is_numeric($scalar):
596  case preg_match(self::getHexRegex(), $scalar):
597  return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar;
598  case '.inf' === $scalarLower:
599  case '.nan' === $scalarLower:
600  return -log(0);
601  case '-.inf' === $scalarLower:
602  return log(0);
603  case 0 === strpos($scalar, '!!binary '):
604  return self::evaluateBinaryScalar(substr($scalar, 9));
605  case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar):
606  return (float) str_replace(',', '', $scalar);
607  case preg_match(self::getTimestampRegex(), $scalar):
608  if (Yaml::PARSE_DATETIME & $flags) {
609  return new \DateTime($scalar, new \DateTimeZone('UTC'));
610  }
611 
612  $timeZone = date_default_timezone_get();
613  date_default_timezone_set('UTC');
614  $time = strtotime($scalar);
615  date_default_timezone_set($timeZone);
616 
617  return $time;
618  }
619  default:
620  return (string) $scalar;
621  }
622  }
623 
631  public static function evaluateBinaryScalar($scalar)
632  {
633  $parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar));
634 
635  if (0 !== (strlen($parsedBinaryData) % 4)) {
636  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)));
637  }
638 
639  if (!preg_match('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) {
640  throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData));
641  }
642 
643  return base64_decode($parsedBinaryData, true);
644  }
645 
646  private static function isBinaryString($value)
647  {
648  return !preg_match('//u', $value) || preg_match('/[^\x09-\x0d\x20-\xff]/', $value);
649  }
650 
658  private static function getTimestampRegex()
659  {
660  return <<<EOF
661  ~^
662  (?P<year>[0-9][0-9][0-9][0-9])
663  -(?P<month>[0-9][0-9]?)
664  -(?P<day>[0-9][0-9]?)
665  (?:(?:[Tt]|[ \t]+)
666  (?P<hour>[0-9][0-9]?)
667  :(?P<minute>[0-9][0-9])
668  :(?P<second>[0-9][0-9])
669  (?:\.(?P<fraction>[0-9]*))?
670  (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
671  (?::(?P<tz_minute>[0-9][0-9]))?))?)?
672  $~x
673 EOF;
674  }
675 
681  private static function getHexRegex()
682  {
683  return '~^0x[0-9a-f]++$~i';
684  }
685 }
static evaluateBinaryScalar($scalar)
Definition: Inline.php:631
static requiresDoubleQuoting($value)
Determines if a PHP value would require double quoting in YAML.
Definition: Escaper.php:51
const PARSE_EXCEPTION_ON_INVALID_TYPE
Definition: Yaml.php:24
static evaluateScalar($scalar, $flags, $references=array())
Evaluates scalars and replaces magic values.
Definition: Inline.php:521
$result
static parseQuotedScalar($scalar, &$i)
Parses a quoted scalar to YAML.
Definition: Inline.php:341
Add rich text string
The name of the decorator.
static parseSequence($sequence, $flags, &$i=0, $references=array())
Parses a sequence to a YAML string.
Definition: Inline.php:373
static isHash(array $value)
Check if given array is hash or just normal indexed array.
Definition: Inline.php:226
Inline implements a YAML parser/dumper for the YAML inline syntax.
Definition: Inline.php:24
if(!is_dir( $entity_dir)) exit("Fatal Error ([A-Za-z0-9]+)\+" &#(? foreach( $entity_files as $file) $output
static dump($value, $flags=0)
Dumps a given PHP variable to a YAML string.
Definition: Inline.php:128
Unescaper encapsulates unescaping rules for single and double-quoted YAML strings.
Definition: Unescaper.php:24
Exception class thrown when an error occurs during dumping.
static getHexRegex()
Gets a regex that matches a YAML number in hexadecimal notation.
Definition: Inline.php:681
static parseScalar($scalar, $flags=0, $delimiters=null, $stringDelimiters=array('"', "'"), &$i = 0, $evaluate = true, $references = array())
Parses a scalar to a YAML string.
Definition: Inline.php:285
Create styles array
The data for the language used.
static escapeWithDoubleQuotes($value)
Escapes and surrounds a PHP value with double quotes.
Definition: Escaper.php:63
static getTimestampRegex()
Gets a regex that matches a YAML date.
Definition: Inline.php:658
static escapeWithSingleQuotes($value)
Escapes and surrounds a PHP value with single quotes.
Definition: Escaper.php:95
static parseMapping($mapping, $flags, &$i=0, $references=array())
Parses a mapping to a YAML string.
Definition: Inline.php:433
static parse($value, $flags=0, $references=array())
Converts a YAML string to a PHP array.
Definition: Inline.php:43
Exception class thrown when an error occurs during parsing.
static isBinaryString($value)
Definition: Inline.php:646
static dumpArray($value, $flags)
Dumps a PHP array to a YAML string.
Definition: Inline.php:247
const DUMP_EXCEPTION_ON_INVALID_TYPE
Definition: Yaml.php:27
static requiresSingleQuoting($value)
Determines if a PHP value would require single quoting in YAML.
Definition: Escaper.php:75
const EOF
How fgetc() reports an End Of File.
Definition: JSMin_lib.php:92