PageRenderTime 25ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/symfony/yaml/Inline.php

https://gitlab.com/galaxy-pidev/AllForDealWeb
PHP | 556 lines | 364 code | 57 blank | 135 comment | 61 complexity | ec2b61ad9861b7a1e7e4352ecabb0149 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Yaml;
  11. use Symfony\Component\Yaml\Exception\ParseException;
  12. use Symfony\Component\Yaml\Exception\DumpException;
  13. /**
  14. * Inline implements a YAML parser/dumper for the YAML inline syntax.
  15. *
  16. * @author Fabien Potencier <fabien@symfony.com>
  17. */
  18. class Inline
  19. {
  20. const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')';
  21. private static $exceptionOnInvalidType = false;
  22. private static $objectSupport = false;
  23. private static $objectForMap = false;
  24. /**
  25. * Converts a YAML string to a PHP array.
  26. *
  27. * @param string $value A YAML string
  28. * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
  29. * @param bool $objectSupport true if object support is enabled, false otherwise
  30. * @param bool $objectForMap true if maps should return a stdClass instead of array()
  31. * @param array $references Mapping of variable names to values
  32. *
  33. * @return array A PHP array representing the YAML string
  34. *
  35. * @throws ParseException
  36. */
  37. public static function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false, $references = array())
  38. {
  39. self::$exceptionOnInvalidType = $exceptionOnInvalidType;
  40. self::$objectSupport = $objectSupport;
  41. self::$objectForMap = $objectForMap;
  42. $value = trim($value);
  43. if ('' === $value) {
  44. return '';
  45. }
  46. if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) {
  47. $mbEncoding = mb_internal_encoding();
  48. mb_internal_encoding('ASCII');
  49. }
  50. $i = 0;
  51. switch ($value[0]) {
  52. case '[':
  53. $result = self::parseSequence($value, $i, $references);
  54. ++$i;
  55. break;
  56. case '{':
  57. $result = self::parseMapping($value, $i, $references);
  58. ++$i;
  59. break;
  60. default:
  61. $result = self::parseScalar($value, null, array('"', "'"), $i, true, $references);
  62. }
  63. // some comments are allowed at the end
  64. if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) {
  65. throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)));
  66. }
  67. if (isset($mbEncoding)) {
  68. mb_internal_encoding($mbEncoding);
  69. }
  70. return $result;
  71. }
  72. /**
  73. * Dumps a given PHP variable to a YAML string.
  74. *
  75. * @param mixed $value The PHP variable to convert
  76. * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
  77. * @param bool $objectSupport true if object support is enabled, false otherwise
  78. *
  79. * @return string The YAML string representing the PHP array
  80. *
  81. * @throws DumpException When trying to dump PHP resource
  82. */
  83. public static function dump($value, $exceptionOnInvalidType = false, $objectSupport = false)
  84. {
  85. switch (true) {
  86. case is_resource($value):
  87. if ($exceptionOnInvalidType) {
  88. throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value)));
  89. }
  90. return 'null';
  91. case is_object($value):
  92. if ($objectSupport) {
  93. return '!!php/object:'.serialize($value);
  94. }
  95. if ($exceptionOnInvalidType) {
  96. throw new DumpException('Object support when dumping a YAML file has been disabled.');
  97. }
  98. return 'null';
  99. case is_array($value):
  100. return self::dumpArray($value, $exceptionOnInvalidType, $objectSupport);
  101. case null === $value:
  102. return 'null';
  103. case true === $value:
  104. return 'true';
  105. case false === $value:
  106. return 'false';
  107. case ctype_digit($value):
  108. return is_string($value) ? "'$value'" : (int) $value;
  109. case is_numeric($value):
  110. $locale = setlocale(LC_NUMERIC, 0);
  111. if (false !== $locale) {
  112. setlocale(LC_NUMERIC, 'C');
  113. }
  114. if (is_float($value)) {
  115. $repr = (string) $value;
  116. if (is_infinite($value)) {
  117. $repr = str_ireplace('INF', '.Inf', $repr);
  118. } elseif (floor($value) == $value && $repr == $value) {
  119. // Preserve float data type since storing a whole number will result in integer value.
  120. $repr = '!!float '.$repr;
  121. }
  122. } else {
  123. $repr = is_string($value) ? "'$value'" : (string) $value;
  124. }
  125. if (false !== $locale) {
  126. setlocale(LC_NUMERIC, $locale);
  127. }
  128. return $repr;
  129. case '' == $value:
  130. return "''";
  131. case Escaper::requiresDoubleQuoting($value):
  132. return Escaper::escapeWithDoubleQuotes($value);
  133. case Escaper::requiresSingleQuoting($value):
  134. case preg_match(self::getHexRegex(), $value):
  135. case preg_match(self::getTimestampRegex(), $value):
  136. return Escaper::escapeWithSingleQuotes($value);
  137. default:
  138. return $value;
  139. }
  140. }
  141. /**
  142. * Dumps a PHP array to a YAML string.
  143. *
  144. * @param array $value The PHP array to dump
  145. * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
  146. * @param bool $objectSupport true if object support is enabled, false otherwise
  147. *
  148. * @return string The YAML string representing the PHP array
  149. */
  150. private static function dumpArray($value, $exceptionOnInvalidType, $objectSupport)
  151. {
  152. // array
  153. $keys = array_keys($value);
  154. $keysCount = count($keys);
  155. if ((1 === $keysCount && '0' == $keys[0])
  156. || ($keysCount > 1 && array_reduce($keys, function ($v, $w) { return (int) $v + $w; }, 0) === $keysCount * ($keysCount - 1) / 2)
  157. ) {
  158. $output = array();
  159. foreach ($value as $val) {
  160. $output[] = self::dump($val, $exceptionOnInvalidType, $objectSupport);
  161. }
  162. return sprintf('[%s]', implode(', ', $output));
  163. }
  164. // mapping
  165. $output = array();
  166. foreach ($value as $key => $val) {
  167. $output[] = sprintf('%s: %s', self::dump($key, $exceptionOnInvalidType, $objectSupport), self::dump($val, $exceptionOnInvalidType, $objectSupport));
  168. }
  169. return sprintf('{ %s }', implode(', ', $output));
  170. }
  171. /**
  172. * Parses a scalar to a YAML string.
  173. *
  174. * @param string $scalar
  175. * @param string $delimiters
  176. * @param array $stringDelimiters
  177. * @param int &$i
  178. * @param bool $evaluate
  179. * @param array $references
  180. *
  181. * @return string A YAML string
  182. *
  183. * @throws ParseException When malformed inline YAML string is parsed
  184. *
  185. * @internal
  186. */
  187. public static function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true, $references = array())
  188. {
  189. if (in_array($scalar[$i], $stringDelimiters)) {
  190. // quoted scalar
  191. $output = self::parseQuotedScalar($scalar, $i);
  192. if (null !== $delimiters) {
  193. $tmp = ltrim(substr($scalar, $i), ' ');
  194. if (!in_array($tmp[0], $delimiters)) {
  195. throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)));
  196. }
  197. }
  198. } else {
  199. // "normal" string
  200. if (!$delimiters) {
  201. $output = substr($scalar, $i);
  202. $i += strlen($output);
  203. // remove comments
  204. if (preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) {
  205. $output = substr($output, 0, $match[0][1]);
  206. }
  207. } elseif (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
  208. $output = $match[1];
  209. $i += strlen($output);
  210. } else {
  211. throw new ParseException(sprintf('Malformed inline YAML string (%s).', $scalar));
  212. }
  213. // a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >)
  214. if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0])) {
  215. @trigger_error(sprintf('Not quoting the scalar "%s" starting with "%s" is deprecated since Symfony 2.8 and will throw a ParseException in 3.0.', $output, $output[0]), E_USER_DEPRECATED);
  216. // to be thrown in 3.0
  217. // throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]));
  218. }
  219. if ($evaluate) {
  220. $output = self::evaluateScalar($output, $references);
  221. }
  222. }
  223. return $output;
  224. }
  225. /**
  226. * Parses a quoted scalar to YAML.
  227. *
  228. * @param string $scalar
  229. * @param int &$i
  230. *
  231. * @return string A YAML string
  232. *
  233. * @throws ParseException When malformed inline YAML string is parsed
  234. */
  235. private static function parseQuotedScalar($scalar, &$i)
  236. {
  237. if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
  238. throw new ParseException(sprintf('Malformed inline YAML string (%s).', substr($scalar, $i)));
  239. }
  240. $output = substr($match[0], 1, strlen($match[0]) - 2);
  241. $unescaper = new Unescaper();
  242. if ('"' == $scalar[$i]) {
  243. $output = $unescaper->unescapeDoubleQuotedString($output);
  244. } else {
  245. $output = $unescaper->unescapeSingleQuotedString($output);
  246. }
  247. $i += strlen($match[0]);
  248. return $output;
  249. }
  250. /**
  251. * Parses a sequence to a YAML string.
  252. *
  253. * @param string $sequence
  254. * @param int &$i
  255. * @param array $references
  256. *
  257. * @return string A YAML string
  258. *
  259. * @throws ParseException When malformed inline YAML string is parsed
  260. */
  261. private static function parseSequence($sequence, &$i = 0, $references = array())
  262. {
  263. $output = array();
  264. $len = strlen($sequence);
  265. ++$i;
  266. // [foo, bar, ...]
  267. while ($i < $len) {
  268. switch ($sequence[$i]) {
  269. case '[':
  270. // nested sequence
  271. $output[] = self::parseSequence($sequence, $i, $references);
  272. break;
  273. case '{':
  274. // nested mapping
  275. $output[] = self::parseMapping($sequence, $i, $references);
  276. break;
  277. case ']':
  278. return $output;
  279. case ',':
  280. case ' ':
  281. break;
  282. default:
  283. $isQuoted = in_array($sequence[$i], array('"', "'"));
  284. $value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i, true, $references);
  285. // the value can be an array if a reference has been resolved to an array var
  286. if (!is_array($value) && !$isQuoted && false !== strpos($value, ': ')) {
  287. // embedded mapping?
  288. try {
  289. $pos = 0;
  290. $value = self::parseMapping('{'.$value.'}', $pos, $references);
  291. } catch (\InvalidArgumentException $e) {
  292. // no, it's not
  293. }
  294. }
  295. $output[] = $value;
  296. --$i;
  297. }
  298. ++$i;
  299. }
  300. throw new ParseException(sprintf('Malformed inline YAML string %s', $sequence));
  301. }
  302. /**
  303. * Parses a mapping to a YAML string.
  304. *
  305. * @param string $mapping
  306. * @param int &$i
  307. * @param array $references
  308. *
  309. * @return string A YAML string
  310. *
  311. * @throws ParseException When malformed inline YAML string is parsed
  312. */
  313. private static function parseMapping($mapping, &$i = 0, $references = array())
  314. {
  315. $output = array();
  316. $len = strlen($mapping);
  317. ++$i;
  318. // {foo: bar, bar:foo, ...}
  319. while ($i < $len) {
  320. switch ($mapping[$i]) {
  321. case ' ':
  322. case ',':
  323. ++$i;
  324. continue 2;
  325. case '}':
  326. if (self::$objectForMap) {
  327. return (object) $output;
  328. }
  329. return $output;
  330. }
  331. // key
  332. $key = self::parseScalar($mapping, array(':', ' '), array('"', "'"), $i, false);
  333. // value
  334. $done = false;
  335. while ($i < $len) {
  336. switch ($mapping[$i]) {
  337. case '[':
  338. // nested sequence
  339. $value = self::parseSequence($mapping, $i, $references);
  340. // Spec: Keys MUST be unique; first one wins.
  341. // Parser cannot abort this mapping earlier, since lines
  342. // are processed sequentially.
  343. if (!isset($output[$key])) {
  344. $output[$key] = $value;
  345. }
  346. $done = true;
  347. break;
  348. case '{':
  349. // nested mapping
  350. $value = self::parseMapping($mapping, $i, $references);
  351. // Spec: Keys MUST be unique; first one wins.
  352. // Parser cannot abort this mapping earlier, since lines
  353. // are processed sequentially.
  354. if (!isset($output[$key])) {
  355. $output[$key] = $value;
  356. }
  357. $done = true;
  358. break;
  359. case ':':
  360. case ' ':
  361. break;
  362. default:
  363. $value = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i, true, $references);
  364. // Spec: Keys MUST be unique; first one wins.
  365. // Parser cannot abort this mapping earlier, since lines
  366. // are processed sequentially.
  367. if (!isset($output[$key])) {
  368. $output[$key] = $value;
  369. }
  370. $done = true;
  371. --$i;
  372. }
  373. ++$i;
  374. if ($done) {
  375. continue 2;
  376. }
  377. }
  378. }
  379. throw new ParseException(sprintf('Malformed inline YAML string %s', $mapping));
  380. }
  381. /**
  382. * Evaluates scalars and replaces magic values.
  383. *
  384. * @param string $scalar
  385. * @param array $references
  386. *
  387. * @return string A YAML string
  388. *
  389. * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved
  390. */
  391. private static function evaluateScalar($scalar, $references = array())
  392. {
  393. $scalar = trim($scalar);
  394. $scalarLower = strtolower($scalar);
  395. if (0 === strpos($scalar, '*')) {
  396. if (false !== $pos = strpos($scalar, '#')) {
  397. $value = substr($scalar, 1, $pos - 2);
  398. } else {
  399. $value = substr($scalar, 1);
  400. }
  401. // an unquoted *
  402. if (false === $value || '' === $value) {
  403. throw new ParseException('A reference must contain at least one character.');
  404. }
  405. if (!array_key_exists($value, $references)) {
  406. throw new ParseException(sprintf('Reference "%s" does not exist.', $value));
  407. }
  408. return $references[$value];
  409. }
  410. switch (true) {
  411. case 'null' === $scalarLower:
  412. case '' === $scalar:
  413. case '~' === $scalar:
  414. return;
  415. case 'true' === $scalarLower:
  416. return true;
  417. case 'false' === $scalarLower:
  418. return false;
  419. // Optimise for returning strings.
  420. case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || $scalar[0] === '!' || is_numeric($scalar[0]):
  421. switch (true) {
  422. case 0 === strpos($scalar, '!str'):
  423. return (string) substr($scalar, 5);
  424. case 0 === strpos($scalar, '! '):
  425. return (int) self::parseScalar(substr($scalar, 2));
  426. case 0 === strpos($scalar, '!!php/object:'):
  427. if (self::$objectSupport) {
  428. return unserialize(substr($scalar, 13));
  429. }
  430. if (self::$exceptionOnInvalidType) {
  431. throw new ParseException('Object support when parsing a YAML file has been disabled.');
  432. }
  433. return;
  434. case 0 === strpos($scalar, '!!float '):
  435. return (float) substr($scalar, 8);
  436. case ctype_digit($scalar):
  437. $raw = $scalar;
  438. $cast = (int) $scalar;
  439. return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
  440. case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)):
  441. $raw = $scalar;
  442. $cast = (int) $scalar;
  443. return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw);
  444. case is_numeric($scalar):
  445. case preg_match(self::getHexRegex(), $scalar):
  446. return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar;
  447. case '.inf' === $scalarLower:
  448. case '.nan' === $scalarLower:
  449. return -log(0);
  450. case '-.inf' === $scalarLower:
  451. return log(0);
  452. case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar):
  453. return (float) str_replace(',', '', $scalar);
  454. case preg_match(self::getTimestampRegex(), $scalar):
  455. return strtotime($scalar);
  456. }
  457. default:
  458. return (string) $scalar;
  459. }
  460. }
  461. /**
  462. * Gets a regex that matches a YAML date.
  463. *
  464. * @return string The regular expression
  465. *
  466. * @see http://www.yaml.org/spec/1.2/spec.html#id2761573
  467. */
  468. private static function getTimestampRegex()
  469. {
  470. return <<<EOF
  471. ~^
  472. (?P<year>[0-9][0-9][0-9][0-9])
  473. -(?P<month>[0-9][0-9]?)
  474. -(?P<day>[0-9][0-9]?)
  475. (?:(?:[Tt]|[ \t]+)
  476. (?P<hour>[0-9][0-9]?)
  477. :(?P<minute>[0-9][0-9])
  478. :(?P<second>[0-9][0-9])
  479. (?:\.(?P<fraction>[0-9]*))?
  480. (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
  481. (?::(?P<tz_minute>[0-9][0-9]))?))?)?
  482. $~x
  483. EOF;
  484. }
  485. /**
  486. * Gets a regex that matches a YAML number in hexadecimal notation.
  487. *
  488. * @return string
  489. */
  490. private static function getHexRegex()
  491. {
  492. return '~^0x[0-9a-f]++$~i';
  493. }
  494. }