Просмотр файла vendor/symfony/yaml/Inline.php

Размер файла: 27.45Kb
  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((array) $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 preg_match('{^[0-9]+[_0-9]*$}', $value):
  214. case preg_match(self::getHexRegex(), $value):
  215. case 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 $value The PHP array to check
  228. *
  229. * @return bool true if value is hash array, false otherwise
  230. */
  231. public static function isHash(array $value)
  232. {
  233. $expectedKey = 0;
  234.  
  235. foreach ($value as $key => $val) {
  236. if ($key !== $expectedKey++) {
  237. return true;
  238. }
  239. }
  240.  
  241. return false;
  242. }
  243.  
  244. /**
  245. * Dumps a PHP array to a YAML string.
  246. *
  247. * @param array $value The PHP array to dump
  248. * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
  249. *
  250. * @return string The YAML string representing the PHP array
  251. */
  252. private static function dumpArray($value, $flags)
  253. {
  254. // array
  255. if ($value && !self::isHash($value)) {
  256. $output = array();
  257. foreach ($value as $val) {
  258. $output[] = self::dump($val, $flags);
  259. }
  260.  
  261. return sprintf('[%s]', implode(', ', $output));
  262. }
  263.  
  264. // hash
  265. $output = array();
  266. foreach ($value as $key => $val) {
  267. $output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags));
  268. }
  269.  
  270. return sprintf('{ %s }', implode(', ', $output));
  271. }
  272.  
  273. /**
  274. * Parses a YAML scalar.
  275. *
  276. * @param string $scalar
  277. * @param int $flags
  278. * @param string $delimiters
  279. * @param array $stringDelimiters
  280. * @param int &$i
  281. * @param bool $evaluate
  282. * @param array $references
  283. *
  284. * @return string
  285. *
  286. * @throws ParseException When malformed inline YAML string is parsed
  287. *
  288. * @internal
  289. */
  290. public static function parseScalar($scalar, $flags = 0, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true, $references = array())
  291. {
  292. if (in_array($scalar[$i], $stringDelimiters)) {
  293. // quoted scalar
  294. $output = self::parseQuotedScalar($scalar, $i);
  295.  
  296. if (null !== $delimiters) {
  297. $tmp = ltrim(substr($scalar, $i), ' ');
  298. if (!in_array($tmp[0], $delimiters)) {
  299. throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)));
  300. }
  301. }
  302. } else {
  303. // "normal" string
  304. if (!$delimiters) {
  305. $output = substr($scalar, $i);
  306. $i += strlen($output);
  307.  
  308. // remove comments
  309. if (preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) {
  310. $output = substr($output, 0, $match[0][1]);
  311. }
  312. } elseif (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
  313. $output = $match[1];
  314. $i += strlen($output);
  315. } else {
  316. throw new ParseException(sprintf('Malformed inline YAML string: %s.', $scalar));
  317. }
  318.  
  319. // a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >)
  320. if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0])) {
  321. throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]));
  322. }
  323.  
  324. if ($output && '%' === $output[0]) {
  325. @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);
  326. }
  327.  
  328. if ($evaluate) {
  329. $output = self::evaluateScalar($output, $flags, $references);
  330. }
  331. }
  332.  
  333. return $output;
  334. }
  335.  
  336. /**
  337. * Parses a YAML quoted scalar.
  338. *
  339. * @param string $scalar
  340. * @param int &$i
  341. *
  342. * @return string
  343. *
  344. * @throws ParseException When malformed inline YAML string is parsed
  345. */
  346. private static function parseQuotedScalar($scalar, &$i)
  347. {
  348. if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
  349. throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i)));
  350. }
  351.  
  352. $output = substr($match[0], 1, strlen($match[0]) - 2);
  353.  
  354. $unescaper = new Unescaper();
  355. if ('"' == $scalar[$i]) {
  356. $output = $unescaper->unescapeDoubleQuotedString($output);
  357. } else {
  358. $output = $unescaper->unescapeSingleQuotedString($output);
  359. }
  360.  
  361. $i += strlen($match[0]);
  362.  
  363. return $output;
  364. }
  365.  
  366. /**
  367. * Parses a YAML sequence.
  368. *
  369. * @param string $sequence
  370. * @param int $flags
  371. * @param int &$i
  372. * @param array $references
  373. *
  374. * @return array
  375. *
  376. * @throws ParseException When malformed inline YAML string is parsed
  377. */
  378. private static function parseSequence($sequence, $flags, &$i = 0, $references = array())
  379. {
  380. $output = array();
  381. $len = strlen($sequence);
  382. ++$i;
  383.  
  384. // [foo, bar, ...]
  385. while ($i < $len) {
  386. switch ($sequence[$i]) {
  387. case '[':
  388. // nested sequence
  389. $output[] = self::parseSequence($sequence, $flags, $i, $references);
  390. break;
  391. case '{':
  392. // nested mapping
  393. $output[] = self::parseMapping($sequence, $flags, $i, $references);
  394. break;
  395. case ']':
  396. return $output;
  397. case ',':
  398. case ' ':
  399. break;
  400. default:
  401. $isQuoted = in_array($sequence[$i], array('"', "'"));
  402. $value = self::parseScalar($sequence, $flags, array(',', ']'), array('"', "'"), $i, true, $references);
  403.  
  404. // the value can be an array if a reference has been resolved to an array var
  405. if (is_string($value) && !$isQuoted && false !== strpos($value, ': ')) {
  406. // embedded mapping?
  407. try {
  408. $pos = 0;
  409. $value = self::parseMapping('{'.$value.'}', $flags, $pos, $references);
  410. } catch (\InvalidArgumentException $e) {
  411. // no, it's not
  412. }
  413. }
  414.  
  415. $output[] = $value;
  416.  
  417. --$i;
  418. }
  419.  
  420. ++$i;
  421. }
  422.  
  423. throw new ParseException(sprintf('Malformed inline YAML string: %s.', $sequence));
  424. }
  425.  
  426. /**
  427. * Parses a YAML mapping.
  428. *
  429. * @param string $mapping
  430. * @param int $flags
  431. * @param int &$i
  432. * @param array $references
  433. *
  434. * @return array|\stdClass
  435. *
  436. * @throws ParseException When malformed inline YAML string is parsed
  437. */
  438. private static function parseMapping($mapping, $flags, &$i = 0, $references = array())
  439. {
  440. $output = array();
  441. $len = strlen($mapping);
  442. ++$i;
  443.  
  444. // {foo: bar, bar:foo, ...}
  445. while ($i < $len) {
  446. switch ($mapping[$i]) {
  447. case ' ':
  448. case ',':
  449. ++$i;
  450. continue 2;
  451. case '}':
  452. if (self::$objectForMap) {
  453. return (object) $output;
  454. }
  455.  
  456. return $output;
  457. }
  458.  
  459. // key
  460. $key = self::parseScalar($mapping, $flags, array(':', ' '), array('"', "'"), $i, false);
  461.  
  462. if (false === $i = strpos($mapping, ':', $i)) {
  463. break;
  464. }
  465.  
  466. if (!isset($mapping[$i + 1]) || !in_array($mapping[$i + 1], array(' ', ',', '[', ']', '{', '}'), true)) {
  467. @trigger_error('Using a colon 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);
  468. }
  469.  
  470. // value
  471. $done = false;
  472.  
  473. while ($i < $len) {
  474. switch ($mapping[$i]) {
  475. case '[':
  476. // nested sequence
  477. $value = self::parseSequence($mapping, $flags, $i, $references);
  478. // Spec: Keys MUST be unique; first one wins.
  479. // Parser cannot abort this mapping earlier, since lines
  480. // are processed sequentially.
  481. if (!isset($output[$key])) {
  482. $output[$key] = $value;
  483. } else {
  484. @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);
  485. }
  486. $done = true;
  487. break;
  488. case '{':
  489. // nested mapping
  490. $value = self::parseMapping($mapping, $flags, $i, $references);
  491. // Spec: Keys MUST be unique; first one wins.
  492. // Parser cannot abort this mapping earlier, since lines
  493. // are processed sequentially.
  494. if (!isset($output[$key])) {
  495. $output[$key] = $value;
  496. } else {
  497. @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);
  498. }
  499. $done = true;
  500. break;
  501. case ':':
  502. case ' ':
  503. break;
  504. default:
  505. $value = self::parseScalar($mapping, $flags, array(',', '}'), array('"', "'"), $i, true, $references);
  506. // Spec: Keys MUST be unique; first one wins.
  507. // Parser cannot abort this mapping earlier, since lines
  508. // are processed sequentially.
  509. if (!isset($output[$key])) {
  510. $output[$key] = $value;
  511. } else {
  512. @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);
  513. }
  514. $done = true;
  515. --$i;
  516. }
  517.  
  518. ++$i;
  519.  
  520. if ($done) {
  521. continue 2;
  522. }
  523. }
  524. }
  525.  
  526. throw new ParseException(sprintf('Malformed inline YAML string: %s.', $mapping));
  527. }
  528.  
  529. /**
  530. * Evaluates scalars and replaces magic values.
  531. *
  532. * @param string $scalar
  533. * @param int $flags
  534. * @param array $references
  535. *
  536. * @return string A YAML string
  537. *
  538. * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved
  539. */
  540. private static function evaluateScalar($scalar, $flags, $references = array())
  541. {
  542. $scalar = trim($scalar);
  543. $scalarLower = strtolower($scalar);
  544.  
  545. if (0 === strpos($scalar, '*')) {
  546. if (false !== $pos = strpos($scalar, '#')) {
  547. $value = substr($scalar, 1, $pos - 2);
  548. } else {
  549. $value = substr($scalar, 1);
  550. }
  551.  
  552. // an unquoted *
  553. if (false === $value || '' === $value) {
  554. throw new ParseException('A reference must contain at least one character.');
  555. }
  556.  
  557. if (!array_key_exists($value, $references)) {
  558. throw new ParseException(sprintf('Reference "%s" does not exist.', $value));
  559. }
  560.  
  561. return $references[$value];
  562. }
  563.  
  564. switch (true) {
  565. case 'null' === $scalarLower:
  566. case '' === $scalar:
  567. case '~' === $scalar:
  568. return;
  569. case 'true' === $scalarLower:
  570. return true;
  571. case 'false' === $scalarLower:
  572. return false;
  573. // Optimise for returning strings.
  574. case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || $scalar[0] === '!' || is_numeric($scalar[0]):
  575. switch (true) {
  576. case 0 === strpos($scalar, '!str'):
  577. return (string) substr($scalar, 5);
  578. case 0 === strpos($scalar, '! '):
  579. return (int) self::parseScalar(substr($scalar, 2), $flags);
  580. case 0 === strpos($scalar, '!php/object:'):
  581. if (self::$objectSupport) {
  582. return unserialize(substr($scalar, 12));
  583. }
  584.  
  585. if (self::$exceptionOnInvalidType) {
  586. throw new ParseException('Object support when parsing a YAML file has been disabled.');
  587. }
  588.  
  589. return;
  590. case 0 === strpos($scalar, '!!php/object:'):
  591. if (self::$objectSupport) {
  592. @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);
  593.  
  594. return unserialize(substr($scalar, 13));
  595. }
  596.  
  597. if (self::$exceptionOnInvalidType) {
  598. throw new ParseException('Object support when parsing a YAML file has been disabled.');
  599. }
  600.  
  601. return;
  602. case 0 === strpos($scalar, '!php/const:'):
  603. if (self::$constantSupport) {
  604. if (defined($const = substr($scalar, 11))) {
  605. return constant($const);
  606. }
  607.  
  608. throw new ParseException(sprintf('The constant "%s" is not defined.', $const));
  609. }
  610. if (self::$exceptionOnInvalidType) {
  611. 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));
  612. }
  613.  
  614. return;
  615. case 0 === strpos($scalar, '!!float '):
  616. return (float) substr($scalar, 8);
  617. case preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar):
  618. $scalar = str_replace('_', '', (string) $scalar);
  619. // omitting the break / return as integers are handled in the next case
  620. case ctype_digit($scalar):
  621. $raw = $scalar;
  622. $cast = (int) $scalar;
  623.  
  624. return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
  625. case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)):
  626. $raw = $scalar;
  627. $cast = (int) $scalar;
  628.  
  629. return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw);
  630. case is_numeric($scalar):
  631. case preg_match(self::getHexRegex(), $scalar):
  632. $scalar = str_replace('_', '', $scalar);
  633.  
  634. return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar;
  635. case '.inf' === $scalarLower:
  636. case '.nan' === $scalarLower:
  637. return -log(0);
  638. case '-.inf' === $scalarLower:
  639. return log(0);
  640. case 0 === strpos($scalar, '!!binary '):
  641. return self::evaluateBinaryScalar(substr($scalar, 9));
  642. case preg_match('/^(-|\+)?[0-9][0-9,]*(\.[0-9_]+)?$/', $scalar):
  643. case preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar):
  644. if (false !== strpos($scalar, ',')) {
  645. @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);
  646. }
  647.  
  648. return (float) str_replace(array(',', '_'), '', $scalar);
  649. case preg_match(self::getTimestampRegex(), $scalar):
  650. if (Yaml::PARSE_DATETIME & $flags) {
  651. // When no timezone is provided in the parsed date, YAML spec says we must assume UTC.
  652. return new \DateTime($scalar, new \DateTimeZone('UTC'));
  653. }
  654.  
  655. $timeZone = date_default_timezone_get();
  656. date_default_timezone_set('UTC');
  657. $time = strtotime($scalar);
  658. date_default_timezone_set($timeZone);
  659.  
  660. return $time;
  661. }
  662. default:
  663. return (string) $scalar;
  664. }
  665. }
  666.  
  667. /**
  668. * @param string $scalar
  669. *
  670. * @return string
  671. *
  672. * @internal
  673. */
  674. public static function evaluateBinaryScalar($scalar)
  675. {
  676. $parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar));
  677.  
  678. if (0 !== (strlen($parsedBinaryData) % 4)) {
  679. 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)));
  680. }
  681.  
  682. if (!preg_match('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) {
  683. throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData));
  684. }
  685.  
  686. return base64_decode($parsedBinaryData, true);
  687. }
  688.  
  689. private static function isBinaryString($value)
  690. {
  691. return !preg_match('//u', $value) || preg_match('/[^\x09-\x0d\x20-\xff]/', $value);
  692. }
  693.  
  694. /**
  695. * Gets a regex that matches a YAML date.
  696. *
  697. * @return string The regular expression
  698. *
  699. * @see http://www.yaml.org/spec/1.2/spec.html#id2761573
  700. */
  701. private static function getTimestampRegex()
  702. {
  703. return <<<EOF
  704. ~^
  705. (?P<year>[0-9][0-9][0-9][0-9])
  706. -(?P<month>[0-9][0-9]?)
  707. -(?P<day>[0-9][0-9]?)
  708. (?:(?:[Tt]|[ \t]+)
  709. (?P<hour>[0-9][0-9]?)
  710. :(?P<minute>[0-9][0-9])
  711. :(?P<second>[0-9][0-9])
  712. (?:\.(?P<fraction>[0-9]*))?
  713. (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
  714. (?::(?P<tz_minute>[0-9][0-9]))?))?)?
  715. $~x
  716. EOF;
  717. }
  718.  
  719. /**
  720. * Gets a regex that matches a YAML number in hexadecimal notation.
  721. *
  722. * @return string
  723. */
  724. private static function getHexRegex()
  725. {
  726. return '~^0x[0-9a-f_]++$~i';
  727. }
  728. }