comparison vendor/symfony/yaml/Inline.php @ 14:1fec387a4317

Update Drupal core to 8.5.2 via Composer
author Chris Cannam
date Mon, 23 Apr 2018 09:46:53 +0100
parents 7a779792577d
children c2387f117808
comparison
equal deleted inserted replaced
13:5fb285c0d0e3 14:1fec387a4317
11 11
12 namespace Symfony\Component\Yaml; 12 namespace Symfony\Component\Yaml;
13 13
14 use Symfony\Component\Yaml\Exception\ParseException; 14 use Symfony\Component\Yaml\Exception\ParseException;
15 use Symfony\Component\Yaml\Exception\DumpException; 15 use Symfony\Component\Yaml\Exception\DumpException;
16 use Symfony\Component\Yaml\Tag\TaggedValue;
16 17
17 /** 18 /**
18 * Inline implements a YAML parser/dumper for the YAML inline syntax. 19 * Inline implements a YAML parser/dumper for the YAML inline syntax.
19 * 20 *
20 * @author Fabien Potencier <fabien@symfony.com> 21 * @author Fabien Potencier <fabien@symfony.com>
23 */ 24 */
24 class Inline 25 class Inline
25 { 26 {
26 const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')'; 27 const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')';
27 28
28 public static $parsedLineNumber; 29 public static $parsedLineNumber = -1;
30 public static $parsedFilename;
29 31
30 private static $exceptionOnInvalidType = false; 32 private static $exceptionOnInvalidType = false;
31 private static $objectSupport = false; 33 private static $objectSupport = false;
32 private static $objectForMap = false; 34 private static $objectForMap = false;
33 private static $constantSupport = false; 35 private static $constantSupport = false;
34 36
35 /** 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 /**
36 * Converts a YAML string to a PHP value. 56 * Converts a YAML string to a PHP value.
37 * 57 *
38 * @param string $value A YAML string 58 * @param string $value A YAML string
39 * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior 59 * @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 60 * @param array $references Mapping of variable names to values
44 * @throws ParseException 64 * @throws ParseException
45 */ 65 */
46 public static function parse($value, $flags = 0, $references = array()) 66 public static function parse($value, $flags = 0, $references = array())
47 { 67 {
48 if (is_bool($flags)) { 68 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); 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);
50 70
51 if ($flags) { 71 if ($flags) {
52 $flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE; 72 $flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE;
53 } else { 73 } else {
54 $flags = 0; 74 $flags = 0;
55 } 75 }
56 } 76 }
57 77
58 if (func_num_args() >= 3 && !is_array($references)) { 78 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); 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);
60 80
61 if ($references) { 81 if ($references) {
62 $flags |= Yaml::PARSE_OBJECT; 82 $flags |= Yaml::PARSE_OBJECT;
63 } 83 }
64 84
65 if (func_num_args() >= 4) { 85 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); 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);
67 87
68 if (func_get_arg(3)) { 88 if (func_get_arg(3)) {
69 $flags |= Yaml::PARSE_OBJECT_FOR_MAP; 89 $flags |= Yaml::PARSE_OBJECT_FOR_MAP;
70 } 90 }
71 } 91 }
75 } else { 95 } else {
76 $references = array(); 96 $references = array();
77 } 97 }
78 } 98 }
79 99
80 self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags); 100 self::initialize($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 101
85 $value = trim($value); 102 $value = trim($value);
86 103
87 if ('' === $value) { 104 if ('' === $value) {
88 return ''; 105 return '';
92 $mbEncoding = mb_internal_encoding(); 109 $mbEncoding = mb_internal_encoding();
93 mb_internal_encoding('ASCII'); 110 mb_internal_encoding('ASCII');
94 } 111 }
95 112
96 $i = 0; 113 $i = 0;
97 switch ($value[0]) { 114 $tag = self::parseTag($value, $i, $flags);
115 switch ($value[$i]) {
98 case '[': 116 case '[':
99 $result = self::parseSequence($value, $flags, $i, $references); 117 $result = self::parseSequence($value, $flags, $i, $references);
100 ++$i; 118 ++$i;
101 break; 119 break;
102 case '{': 120 case '{':
103 $result = self::parseMapping($value, $flags, $i, $references); 121 $result = self::parseMapping($value, $flags, $i, $references);
104 ++$i; 122 ++$i;
105 break; 123 break;
106 default: 124 default:
107 $result = self::parseScalar($value, $flags, null, array('"', "'"), $i, true, $references); 125 $result = self::parseScalar($value, $flags, null, $i, null === $tag, $references);
126 }
127
128 if (null !== $tag) {
129 return new TaggedValue($tag, $result);
108 } 130 }
109 131
110 // some comments are allowed at the end 132 // some comments are allowed at the end
111 if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) { 133 if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) {
112 throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i))); 134 throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
113 } 135 }
114 136
115 if (isset($mbEncoding)) { 137 if (isset($mbEncoding)) {
116 mb_internal_encoding($mbEncoding); 138 mb_internal_encoding($mbEncoding);
117 } 139 }
130 * @throws DumpException When trying to dump PHP resource 152 * @throws DumpException When trying to dump PHP resource
131 */ 153 */
132 public static function dump($value, $flags = 0) 154 public static function dump($value, $flags = 0)
133 { 155 {
134 if (is_bool($flags)) { 156 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); 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);
136 158
137 if ($flags) { 159 if ($flags) {
138 $flags = Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE; 160 $flags = Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE;
139 } else { 161 } else {
140 $flags = 0; 162 $flags = 0;
141 } 163 }
142 } 164 }
143 165
144 if (func_num_args() >= 3) { 166 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); 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);
146 168
147 if (func_get_arg(2)) { 169 if (func_get_arg(2)) {
148 $flags |= Yaml::DUMP_OBJECT; 170 $flags |= Yaml::DUMP_OBJECT;
149 } 171 }
150 } 172 }
157 179
158 return 'null'; 180 return 'null';
159 case $value instanceof \DateTimeInterface: 181 case $value instanceof \DateTimeInterface:
160 return $value->format('c'); 182 return $value->format('c');
161 case is_object($value): 183 case is_object($value):
184 if ($value instanceof TaggedValue) {
185 return '!'.$value->getTag().' '.self::dump($value->getValue(), $flags);
186 }
187
162 if (Yaml::DUMP_OBJECT & $flags) { 188 if (Yaml::DUMP_OBJECT & $flags) {
163 return '!php/object:'.serialize($value); 189 return '!php/object '.self::dump(serialize($value));
164 } 190 }
165 191
166 if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) { 192 if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) {
167 return self::dumpArray($value, $flags); 193 return self::dumpArray($value, $flags & ~Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE);
168 } 194 }
169 195
170 if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) { 196 if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) {
171 throw new DumpException('Object support when dumping a YAML file has been disabled.'); 197 throw new DumpException('Object support when dumping a YAML file has been disabled.');
172 } 198 }
254 * @return string The YAML string representing the PHP array 280 * @return string The YAML string representing the PHP array
255 */ 281 */
256 private static function dumpArray($value, $flags) 282 private static function dumpArray($value, $flags)
257 { 283 {
258 // array 284 // array
259 if ($value && !self::isHash($value)) { 285 if (($value || Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE & $flags) && !self::isHash($value)) {
260 $output = array(); 286 $output = array();
261 foreach ($value as $val) { 287 foreach ($value as $val) {
262 $output[] = self::dump($val, $flags); 288 $output[] = self::dump($val, $flags);
263 } 289 }
264 290
278 * Parses a YAML scalar. 304 * Parses a YAML scalar.
279 * 305 *
280 * @param string $scalar 306 * @param string $scalar
281 * @param int $flags 307 * @param int $flags
282 * @param string[] $delimiters 308 * @param string[] $delimiters
283 * @param string[] $stringDelimiters
284 * @param int &$i 309 * @param int &$i
285 * @param bool $evaluate 310 * @param bool $evaluate
286 * @param array $references 311 * @param array $references
287 * 312 *
288 * @return string 313 * @return string
289 * 314 *
290 * @throws ParseException When malformed inline YAML string is parsed 315 * @throws ParseException When malformed inline YAML string is parsed
291 * 316 *
292 * @internal 317 * @internal
293 */ 318 */
294 public static function parseScalar($scalar, $flags = 0, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true, $references = array()) 319 public static function parseScalar($scalar, $flags = 0, $delimiters = null, &$i = 0, $evaluate = true, $references = array(), $legacyOmittedKeySupport = false)
295 { 320 {
296 if (in_array($scalar[$i], $stringDelimiters)) { 321 if (in_array($scalar[$i], array('"', "'"))) {
297 // quoted scalar 322 // quoted scalar
298 $output = self::parseQuotedScalar($scalar, $i); 323 $output = self::parseQuotedScalar($scalar, $i);
299 324
300 if (null !== $delimiters) { 325 if (null !== $delimiters) {
301 $tmp = ltrim(substr($scalar, $i), ' '); 326 $tmp = ltrim(substr($scalar, $i), ' ');
302 if (!in_array($tmp[0], $delimiters)) { 327 if (!in_array($tmp[0], $delimiters)) {
303 throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i))); 328 throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
304 } 329 }
305 } 330 }
306 } else { 331 } else {
307 // "normal" string 332 // "normal" string
308 if (!$delimiters) { 333 if (!$delimiters) {
311 336
312 // remove comments 337 // remove comments
313 if (Parser::preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) { 338 if (Parser::preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) {
314 $output = substr($output, 0, $match[0][1]); 339 $output = substr($output, 0, $match[0][1]);
315 } 340 }
316 } elseif (Parser::preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) { 341 } elseif (Parser::preg_match('/^(.'.($legacyOmittedKeySupport ? '+' : '*').'?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
317 $output = $match[1]; 342 $output = $match[1];
318 $i += strlen($output); 343 $i += strlen($output);
319 } else { 344 } else {
320 throw new ParseException(sprintf('Malformed inline YAML string: %s.', $scalar)); 345 throw new ParseException(sprintf('Malformed inline YAML string: %s.', $scalar), self::$parsedLineNumber + 1, null, self::$parsedFilename);
321 } 346 }
322 347
323 // a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >) 348 // 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])) { 349 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])); 350 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);
326 } 351 }
327 352
328 if ($output && '%' === $output[0]) { 353 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); 354 @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);
330 } 355 }
331 356
332 if ($evaluate) { 357 if ($evaluate) {
333 $output = self::evaluateScalar($output, $flags, $references); 358 $output = self::evaluateScalar($output, $flags, $references);
334 } 359 }
348 * @throws ParseException When malformed inline YAML string is parsed 373 * @throws ParseException When malformed inline YAML string is parsed
349 */ 374 */
350 private static function parseQuotedScalar($scalar, &$i) 375 private static function parseQuotedScalar($scalar, &$i)
351 { 376 {
352 if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) { 377 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))); 378 throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
354 } 379 }
355 380
356 $output = substr($match[0], 1, strlen($match[0]) - 2); 381 $output = substr($match[0], 1, strlen($match[0]) - 2);
357 382
358 $unescaper = new Unescaper(); 383 $unescaper = new Unescaper();
385 $len = strlen($sequence); 410 $len = strlen($sequence);
386 ++$i; 411 ++$i;
387 412
388 // [foo, bar, ...] 413 // [foo, bar, ...]
389 while ($i < $len) { 414 while ($i < $len) {
415 if (']' === $sequence[$i]) {
416 return $output;
417 }
418 if (',' === $sequence[$i] || ' ' === $sequence[$i]) {
419 ++$i;
420
421 continue;
422 }
423
424 $tag = self::parseTag($sequence, $i, $flags);
390 switch ($sequence[$i]) { 425 switch ($sequence[$i]) {
391 case '[': 426 case '[':
392 // nested sequence 427 // nested sequence
393 $output[] = self::parseSequence($sequence, $flags, $i, $references); 428 $value = self::parseSequence($sequence, $flags, $i, $references);
394 break; 429 break;
395 case '{': 430 case '{':
396 // nested mapping 431 // nested mapping
397 $output[] = self::parseMapping($sequence, $flags, $i, $references); 432 $value = self::parseMapping($sequence, $flags, $i, $references);
398 break;
399 case ']':
400 return $output;
401 case ',':
402 case ' ':
403 break; 433 break;
404 default: 434 default:
405 $isQuoted = in_array($sequence[$i], array('"', "'")); 435 $isQuoted = in_array($sequence[$i], array('"', "'"));
406 $value = self::parseScalar($sequence, $flags, array(',', ']'), array('"', "'"), $i, true, $references); 436 $value = self::parseScalar($sequence, $flags, array(',', ']'), $i, null === $tag, $references);
407 437
408 // the value can be an array if a reference has been resolved to an array var 438 // 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, ': ')) { 439 if (is_string($value) && !$isQuoted && false !== strpos($value, ': ')) {
410 // embedded mapping? 440 // embedded mapping?
411 try { 441 try {
414 } catch (\InvalidArgumentException $e) { 444 } catch (\InvalidArgumentException $e) {
415 // no, it's not 445 // no, it's not
416 } 446 }
417 } 447 }
418 448
419 $output[] = $value;
420
421 --$i; 449 --$i;
422 } 450 }
423 451
452 if (null !== $tag) {
453 $value = new TaggedValue($tag, $value);
454 }
455
456 $output[] = $value;
457
424 ++$i; 458 ++$i;
425 } 459 }
426 460
427 throw new ParseException(sprintf('Malformed inline YAML string: %s.', $sequence)); 461 throw new ParseException(sprintf('Malformed inline YAML string: %s.', $sequence), self::$parsedLineNumber + 1, null, self::$parsedFilename);
428 } 462 }
429 463
430 /** 464 /**
431 * Parses a YAML mapping. 465 * Parses a YAML mapping.
432 * 466 *
442 private static function parseMapping($mapping, $flags, &$i = 0, $references = array()) 476 private static function parseMapping($mapping, $flags, &$i = 0, $references = array())
443 { 477 {
444 $output = array(); 478 $output = array();
445 $len = strlen($mapping); 479 $len = strlen($mapping);
446 ++$i; 480 ++$i;
481 $allowOverwrite = false;
447 482
448 // {foo: bar, bar:foo, ...} 483 // {foo: bar, bar:foo, ...}
449 while ($i < $len) { 484 while ($i < $len) {
450 switch ($mapping[$i]) { 485 switch ($mapping[$i]) {
451 case ' ': 486 case ' ':
460 return $output; 495 return $output;
461 } 496 }
462 497
463 // key 498 // key
464 $isKeyQuoted = in_array($mapping[$i], array('"', "'"), true); 499 $isKeyQuoted = in_array($mapping[$i], array('"', "'"), true);
465 $key = self::parseScalar($mapping, $flags, array(':', ' '), array('"', "'"), $i, false); 500 $key = self::parseScalar($mapping, $flags, array(':', ' '), $i, false, array(), true);
466 501
467 if (':' !== $key && false === $i = strpos($mapping, ':', $i)) { 502 if (':' !== $key && false === $i = strpos($mapping, ':', $i)) {
468 break; 503 break;
469 } 504 }
470 505
506 if (':' === $key) {
507 @trigger_error(self::getDeprecationMessage('Omitting the key of a mapping is deprecated and will throw a ParseException in 4.0.'), E_USER_DEPRECATED);
508 }
509
510 if (!$isKeyQuoted) {
511 $evaluatedKey = self::evaluateScalar($key, $flags, $references);
512
513 if ('' !== $key && $evaluatedKey !== $key && !is_string($evaluatedKey) && !is_int($evaluatedKey)) {
514 @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);
515 }
516 }
517
471 if (':' !== $key && !$isKeyQuoted && (!isset($mapping[$i + 1]) || !in_array($mapping[$i + 1], array(' ', ',', '[', ']', '{', '}'), true))) { 518 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); 519 @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);
473 } 520 }
474 521
475 // value 522 if ('<<' === $key) {
476 $done = false; 523 $allowOverwrite = true;
524 }
477 525
478 while ($i < $len) { 526 while ($i < $len) {
527 if (':' === $mapping[$i] || ' ' === $mapping[$i]) {
528 ++$i;
529
530 continue;
531 }
532
533 $tag = self::parseTag($mapping, $i, $flags);
479 switch ($mapping[$i]) { 534 switch ($mapping[$i]) {
480 case '[': 535 case '[':
481 // nested sequence 536 // nested sequence
482 $value = self::parseSequence($mapping, $flags, $i, $references); 537 $value = self::parseSequence($mapping, $flags, $i, $references);
483 // Spec: Keys MUST be unique; first one wins. 538 // Spec: Keys MUST be unique; first one wins.
484 // Parser cannot abort this mapping earlier, since lines 539 // Parser cannot abort this mapping earlier, since lines
485 // are processed sequentially. 540 // are processed sequentially.
486 if (!isset($output[$key])) { 541 // But overwriting is allowed when a merge node is used in current block.
487 $output[$key] = $value; 542 if ('<<' === $key) {
488 } else { 543 foreach ($value as $parsedValue) {
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); 544 $output += $parsedValue;
490 } 545 }
491 $done = true; 546 } elseif ($allowOverwrite || !isset($output[$key])) {
547 if (null !== $tag) {
548 $output[$key] = new TaggedValue($tag, $value);
549 } else {
550 $output[$key] = $value;
551 }
552 } elseif (isset($output[$key])) {
553 @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);
554 }
492 break; 555 break;
493 case '{': 556 case '{':
494 // nested mapping 557 // nested mapping
495 $value = self::parseMapping($mapping, $flags, $i, $references); 558 $value = self::parseMapping($mapping, $flags, $i, $references);
496 // Spec: Keys MUST be unique; first one wins. 559 // Spec: Keys MUST be unique; first one wins.
497 // Parser cannot abort this mapping earlier, since lines 560 // Parser cannot abort this mapping earlier, since lines
498 // are processed sequentially. 561 // are processed sequentially.
499 if (!isset($output[$key])) { 562 // But overwriting is allowed when a merge node is used in current block.
500 $output[$key] = $value; 563 if ('<<' === $key) {
501 } else { 564 $output += $value;
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); 565 } elseif ($allowOverwrite || !isset($output[$key])) {
503 } 566 if (null !== $tag) {
504 $done = true; 567 $output[$key] = new TaggedValue($tag, $value);
505 break; 568 } else {
506 case ':': 569 $output[$key] = $value;
507 case ' ': 570 }
571 } elseif (isset($output[$key])) {
572 @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);
573 }
508 break; 574 break;
509 default: 575 default:
510 $value = self::parseScalar($mapping, $flags, array(',', '}'), array('"', "'"), $i, true, $references); 576 $value = self::parseScalar($mapping, $flags, array(',', '}'), $i, null === $tag, $references);
511 // Spec: Keys MUST be unique; first one wins. 577 // Spec: Keys MUST be unique; first one wins.
512 // Parser cannot abort this mapping earlier, since lines 578 // Parser cannot abort this mapping earlier, since lines
513 // are processed sequentially. 579 // are processed sequentially.
514 if (!isset($output[$key])) { 580 // But overwriting is allowed when a merge node is used in current block.
515 $output[$key] = $value; 581 if ('<<' === $key) {
516 } else { 582 $output += $value;
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); 583 } elseif ($allowOverwrite || !isset($output[$key])) {
518 } 584 if (null !== $tag) {
519 $done = true; 585 $output[$key] = new TaggedValue($tag, $value);
586 } else {
587 $output[$key] = $value;
588 }
589 } elseif (isset($output[$key])) {
590 @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);
591 }
520 --$i; 592 --$i;
521 } 593 }
522
523 ++$i; 594 ++$i;
524 595
525 if ($done) { 596 continue 2;
526 continue 2; 597 }
527 } 598 }
528 } 599
529 } 600 throw new ParseException(sprintf('Malformed inline YAML string: %s.', $mapping), self::$parsedLineNumber + 1, null, self::$parsedFilename);
530
531 throw new ParseException(sprintf('Malformed inline YAML string: %s.', $mapping));
532 } 601 }
533 602
534 /** 603 /**
535 * Evaluates scalars and replaces magic values. 604 * Evaluates scalars and replaces magic values.
536 * 605 *
554 $value = substr($scalar, 1); 623 $value = substr($scalar, 1);
555 } 624 }
556 625
557 // an unquoted * 626 // an unquoted *
558 if (false === $value || '' === $value) { 627 if (false === $value || '' === $value) {
559 throw new ParseException('A reference must contain at least one character.'); 628 throw new ParseException('A reference must contain at least one character.', self::$parsedLineNumber + 1, $value, self::$parsedFilename);
560 } 629 }
561 630
562 if (!array_key_exists($value, $references)) { 631 if (!array_key_exists($value, $references)) {
563 throw new ParseException(sprintf('Reference "%s" does not exist.', $value)); 632 throw new ParseException(sprintf('Reference "%s" does not exist.', $value), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
564 } 633 }
565 634
566 return $references[$value]; 635 return $references[$value];
567 } 636 }
568 637
573 return; 642 return;
574 case 'true' === $scalarLower: 643 case 'true' === $scalarLower:
575 return true; 644 return true;
576 case 'false' === $scalarLower: 645 case 'false' === $scalarLower:
577 return false; 646 return false;
578 // Optimise for returning strings. 647 case '!' === $scalar[0]:
579 case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || $scalar[0] === '!' || is_numeric($scalar[0]):
580 switch (true) { 648 switch (true) {
581 case 0 === strpos($scalar, '!str'): 649 case 0 === strpos($scalar, '!str'):
650 @trigger_error(self::getDeprecationMessage('Support for the !str tag is deprecated since Symfony 3.4. Use the !!str tag instead.'), E_USER_DEPRECATED);
651
582 return (string) substr($scalar, 5); 652 return (string) substr($scalar, 5);
653 case 0 === strpos($scalar, '!!str '):
654 return (string) substr($scalar, 6);
583 case 0 === strpos($scalar, '! '): 655 case 0 === strpos($scalar, '! '):
656 @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);
657
584 return (int) self::parseScalar(substr($scalar, 2), $flags); 658 return (int) self::parseScalar(substr($scalar, 2), $flags);
585 case 0 === strpos($scalar, '!php/object:'): 659 case 0 === strpos($scalar, '!php/object:'):
586 if (self::$objectSupport) { 660 if (self::$objectSupport) {
661 @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);
662
587 return unserialize(substr($scalar, 12)); 663 return unserialize(substr($scalar, 12));
588 } 664 }
589 665
590 if (self::$exceptionOnInvalidType) { 666 if (self::$exceptionOnInvalidType) {
591 throw new ParseException('Object support when parsing a YAML file has been disabled.'); 667 throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
592 } 668 }
593 669
594 return; 670 return;
595 case 0 === strpos($scalar, '!!php/object:'): 671 case 0 === strpos($scalar, '!!php/object:'):
596 if (self::$objectSupport) { 672 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); 673 @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);
598 674
599 return unserialize(substr($scalar, 13)); 675 return unserialize(substr($scalar, 13));
600 } 676 }
601 677
602 if (self::$exceptionOnInvalidType) { 678 if (self::$exceptionOnInvalidType) {
603 throw new ParseException('Object support when parsing a YAML file has been disabled.'); 679 throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
680 }
681
682 return;
683 case 0 === strpos($scalar, '!php/object'):
684 if (self::$objectSupport) {
685 return unserialize(self::parseScalar(substr($scalar, 12)));
686 }
687
688 if (self::$exceptionOnInvalidType) {
689 throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
604 } 690 }
605 691
606 return; 692 return;
607 case 0 === strpos($scalar, '!php/const:'): 693 case 0 === strpos($scalar, '!php/const:'):
608 if (self::$constantSupport) { 694 if (self::$constantSupport) {
695 @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);
696
609 if (defined($const = substr($scalar, 11))) { 697 if (defined($const = substr($scalar, 11))) {
610 return constant($const); 698 return constant($const);
611 } 699 }
612 700
613 throw new ParseException(sprintf('The constant "%s" is not defined.', $const)); 701 throw new ParseException(sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
614 } 702 }
615 if (self::$exceptionOnInvalidType) { 703 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)); 704 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);
705 }
706
707 return;
708 case 0 === strpos($scalar, '!php/const'):
709 if (self::$constantSupport) {
710 $i = 0;
711 if (defined($const = self::parseScalar(substr($scalar, 11), 0, null, $i, false))) {
712 return constant($const);
713 }
714
715 throw new ParseException(sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
716 }
717 if (self::$exceptionOnInvalidType) {
718 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);
617 } 719 }
618 720
619 return; 721 return;
620 case 0 === strpos($scalar, '!!float '): 722 case 0 === strpos($scalar, '!!float '):
621 return (float) substr($scalar, 8); 723 return (float) substr($scalar, 8);
724 case 0 === strpos($scalar, '!!binary '):
725 return self::evaluateBinaryScalar(substr($scalar, 9));
726 default:
727 @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);
728 }
729
730 // Optimize for returning strings.
731 // no break
732 case '+' === $scalar[0] || '-' === $scalar[0] || '.' === $scalar[0] || is_numeric($scalar[0]):
733 switch (true) {
622 case Parser::preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar): 734 case Parser::preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar):
623 $scalar = str_replace('_', '', (string) $scalar); 735 $scalar = str_replace('_', '', (string) $scalar);
624 // omitting the break / return as integers are handled in the next case 736 // omitting the break / return as integers are handled in the next case
737 // no break
625 case ctype_digit($scalar): 738 case ctype_digit($scalar):
626 $raw = $scalar; 739 $raw = $scalar;
627 $cast = (int) $scalar; 740 $cast = (int) $scalar;
628 741
629 return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw); 742 return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
640 case '.inf' === $scalarLower: 753 case '.inf' === $scalarLower:
641 case '.nan' === $scalarLower: 754 case '.nan' === $scalarLower:
642 return -log(0); 755 return -log(0);
643 case '-.inf' === $scalarLower: 756 case '-.inf' === $scalarLower:
644 return log(0); 757 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): 758 case Parser::preg_match('/^(-|\+)?[0-9][0-9,]*(\.[0-9_]+)?$/', $scalar):
648 case Parser::preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar): 759 case Parser::preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar):
649 if (false !== strpos($scalar, ',')) { 760 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); 761 @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);
651 } 762 }
652 763
653 return (float) str_replace(array(',', '_'), '', $scalar); 764 return (float) str_replace(array(',', '_'), '', $scalar);
654 case Parser::preg_match(self::getTimestampRegex(), $scalar): 765 case Parser::preg_match(self::getTimestampRegex(), $scalar):
655 if (Yaml::PARSE_DATETIME & $flags) { 766 if (Yaml::PARSE_DATETIME & $flags) {
662 $time = strtotime($scalar); 773 $time = strtotime($scalar);
663 date_default_timezone_set($timeZone); 774 date_default_timezone_set($timeZone);
664 775
665 return $time; 776 return $time;
666 } 777 }
667 default: 778 }
668 return (string) $scalar; 779
669 } 780 return (string) $scalar;
781 }
782
783 /**
784 * @param string $value
785 * @param int &$i
786 * @param int $flags
787 *
788 * @return null|string
789 */
790 private static function parseTag($value, &$i, $flags)
791 {
792 if ('!' !== $value[$i]) {
793 return;
794 }
795
796 $tagLength = strcspn($value, " \t\n", $i + 1);
797 $tag = substr($value, $i + 1, $tagLength);
798
799 $nextOffset = $i + $tagLength + 1;
800 $nextOffset += strspn($value, ' ', $nextOffset);
801
802 // Is followed by a scalar
803 if ((!isset($value[$nextOffset]) || !in_array($value[$nextOffset], array('[', '{'), true)) && 'tagged' !== $tag) {
804 // Manage non-whitelisted scalars in {@link self::evaluateScalar()}
805 return;
806 }
807
808 // Built-in tags
809 if ($tag && '!' === $tag[0]) {
810 throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
811 }
812
813 if (Yaml::PARSE_CUSTOM_TAGS & $flags) {
814 $i = $nextOffset;
815
816 return $tag;
817 }
818
819 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);
670 } 820 }
671 821
672 /** 822 /**
673 * @param string $scalar 823 * @param string $scalar
674 * 824 *
679 public static function evaluateBinaryScalar($scalar) 829 public static function evaluateBinaryScalar($scalar)
680 { 830 {
681 $parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar)); 831 $parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar));
682 832
683 if (0 !== (strlen($parsedBinaryData) % 4)) { 833 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))); 834 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);
685 } 835 }
686 836
687 if (!Parser::preg_match('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) { 837 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)); 838 throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
689 } 839 }
690 840
691 return base64_decode($parsedBinaryData, true); 841 return base64_decode($parsedBinaryData, true);
692 } 842 }
693 843
728 */ 878 */
729 private static function getHexRegex() 879 private static function getHexRegex()
730 { 880 {
731 return '~^0x[0-9a-f_]++$~i'; 881 return '~^0x[0-9a-f_]++$~i';
732 } 882 }
883
884 private static function getDeprecationMessage($message)
885 {
886 $message = rtrim($message, '.');
887
888 if (null !== self::$parsedFilename) {
889 $message .= ' in '.self::$parsedFilename;
890 }
891
892 if (-1 !== self::$parsedLineNumber) {
893 $message .= ' on line '.(self::$parsedLineNumber + 1);
894 }
895
896 return $message.'.';
897 }
733 } 898 }