PageRenderTime 53ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/symfony/yaml/Symfony/Component/Yaml/Inline.php

https://bitbucket.org/alexpozdnyakov/kohana
PHP | 424 lines | 281 code | 43 blank | 100 comment | 39 complexity | a2a3e4cd8f7d16f344ebd179064abe21 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. * (c) Fabien Potencier <fabien@symfony.com>
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. namespace Symfony\Component\Yaml;
  10. use Symfony\Component\Yaml\Exception\ParseException;
  11. use Symfony\Component\Yaml\Exception\DumpException;
  12. /**
  13. * Inline implements a YAML parser/dumper for the YAML inline syntax.
  14. *
  15. * @author Fabien Potencier <fabien@symfony.com>
  16. */
  17. class Inline
  18. {
  19. const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')';
  20. /**
  21. * Converts a YAML string to a PHP array.
  22. *
  23. * @param string $value A YAML string
  24. *
  25. * @return array A PHP array representing the YAML string
  26. */
  27. public static function parse($value)
  28. {
  29. $value = trim($value);
  30. if (0 == strlen($value)) {
  31. return '';
  32. }
  33. if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
  34. $mbEncoding = mb_internal_encoding();
  35. mb_internal_encoding('ASCII');
  36. }
  37. switch ($value[0]) {
  38. case '[':
  39. $result = self::parseSequence($value);
  40. break;
  41. case '{':
  42. $result = self::parseMapping($value);
  43. break;
  44. default:
  45. $i = 0;
  46. $result = self::parseScalar($value, null, array('"', "'"), $i);
  47. // some comment can end the scalar
  48. if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) {
  49. throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)));
  50. }
  51. }
  52. if (isset($mbEncoding)) {
  53. mb_internal_encoding($mbEncoding);
  54. }
  55. return $result;
  56. }
  57. /**
  58. * Dumps a given PHP variable to a YAML string.
  59. *
  60. * @param mixed $value The PHP variable to convert
  61. *
  62. * @return string The YAML string representing the PHP array
  63. *
  64. * @throws DumpException When trying to dump PHP resource
  65. */
  66. public static function dump($value)
  67. {
  68. switch (true) {
  69. case is_resource($value):
  70. throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value)));
  71. case is_object($value):
  72. return '!!php/object:'.serialize($value);
  73. case is_array($value):
  74. return self::dumpArray($value);
  75. case null === $value:
  76. return 'null';
  77. case true === $value:
  78. return 'true';
  79. case false === $value:
  80. return 'false';
  81. case ctype_digit($value):
  82. return is_string($value) ? "'$value'" : (int) $value;
  83. case is_numeric($value):
  84. $locale = setlocale(LC_NUMERIC, 0);
  85. if (false !== $locale) {
  86. setlocale(LC_NUMERIC, 'C');
  87. }
  88. $repr = is_string($value) ? "'$value'" : (is_infinite($value) ? str_ireplace('INF', '.Inf', strval($value)) : strval($value));
  89. if (false !== $locale) {
  90. setlocale(LC_NUMERIC, $locale);
  91. }
  92. return $repr;
  93. case Escaper::requiresDoubleQuoting($value):
  94. return Escaper::escapeWithDoubleQuotes($value);
  95. case Escaper::requiresSingleQuoting($value):
  96. return Escaper::escapeWithSingleQuotes($value);
  97. case '' == $value:
  98. return "''";
  99. case preg_match(self::getTimestampRegex(), $value):
  100. case in_array(strtolower($value), array('null', '~', 'true', 'false')):
  101. return "'$value'";
  102. default:
  103. return $value;
  104. }
  105. }
  106. /**
  107. * Dumps a PHP array to a YAML string.
  108. *
  109. * @param array $value The PHP array to dump
  110. *
  111. * @return string The YAML string representing the PHP array
  112. */
  113. private static function dumpArray($value)
  114. {
  115. // array
  116. $keys = array_keys($value);
  117. if ((1 == count($keys) && '0' == $keys[0])
  118. || (count($keys) > 1 && array_reduce($keys, function ($v, $w) { return (integer) $v + $w; }, 0) == count($keys) * (count($keys) - 1) / 2)
  119. ) {
  120. $output = array();
  121. foreach ($value as $val) {
  122. $output[] = self::dump($val);
  123. }
  124. return sprintf('[%s]', implode(', ', $output));
  125. }
  126. // mapping
  127. $output = array();
  128. foreach ($value as $key => $val) {
  129. $output[] = sprintf('%s: %s', self::dump($key), self::dump($val));
  130. }
  131. return sprintf('{ %s }', implode(', ', $output));
  132. }
  133. /**
  134. * Parses a scalar to a YAML string.
  135. *
  136. * @param scalar $scalar
  137. * @param string $delimiters
  138. * @param array $stringDelimiters
  139. * @param integer &$i
  140. * @param Boolean $evaluate
  141. *
  142. * @return string A YAML string
  143. *
  144. * @throws ParseException When malformed inline YAML string is parsed
  145. */
  146. public static function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true)
  147. {
  148. if (in_array($scalar[$i], $stringDelimiters)) {
  149. // quoted scalar
  150. $output = self::parseQuotedScalar($scalar, $i);
  151. if (null !== $delimiters) {
  152. $tmp = ltrim(substr($scalar, $i), ' ');
  153. if (!in_array($tmp[0], $delimiters)) {
  154. throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)));
  155. }
  156. }
  157. } else {
  158. // "normal" string
  159. if (!$delimiters) {
  160. $output = substr($scalar, $i);
  161. $i += strlen($output);
  162. // remove comments
  163. if (false !== $strpos = strpos($output, ' #')) {
  164. $output = rtrim(substr($output, 0, $strpos));
  165. }
  166. } elseif (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
  167. $output = $match[1];
  168. $i += strlen($output);
  169. } else {
  170. throw new ParseException(sprintf('Malformed inline YAML string (%s).', $scalar));
  171. }
  172. $output = $evaluate ? self::evaluateScalar($output) : $output;
  173. }
  174. return $output;
  175. }
  176. /**
  177. * Parses a quoted scalar to YAML.
  178. *
  179. * @param string $scalar
  180. * @param integer &$i
  181. *
  182. * @return string A YAML string
  183. *
  184. * @throws ParseException When malformed inline YAML string is parsed
  185. */
  186. private static function parseQuotedScalar($scalar, &$i)
  187. {
  188. // Only check the current item we're dealing with (for sequences)
  189. $subject = substr($scalar, $i);
  190. $items = preg_split('/[\'"]\s*(?:[,:]|[}\]]\s*,)/', $subject);
  191. $subject = substr($subject, 0, strlen($items[0]) + 1);
  192. if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
  193. throw new ParseException(sprintf('Malformed inline YAML string (%s).', substr($scalar, $i)));
  194. }
  195. $output = substr($match[0], 1, strlen($match[0]) - 2);
  196. $unescaper = new Unescaper();
  197. if ('"' == $scalar[$i]) {
  198. $output = $unescaper->unescapeDoubleQuotedString($output);
  199. } else {
  200. $output = $unescaper->unescapeSingleQuotedString($output);
  201. }
  202. $i += strlen($match[0]);
  203. return $output;
  204. }
  205. /**
  206. * Parses a sequence to a YAML string.
  207. *
  208. * @param string $sequence
  209. * @param integer &$i
  210. *
  211. * @return string A YAML string
  212. *
  213. * @throws ParseException When malformed inline YAML string is parsed
  214. */
  215. private static function parseSequence($sequence, &$i = 0)
  216. {
  217. $output = array();
  218. $len = strlen($sequence);
  219. $i += 1;
  220. // [foo, bar, ...]
  221. while ($i < $len) {
  222. switch ($sequence[$i]) {
  223. case '[':
  224. // nested sequence
  225. $output[] = self::parseSequence($sequence, $i);
  226. break;
  227. case '{':
  228. // nested mapping
  229. $output[] = self::parseMapping($sequence, $i);
  230. break;
  231. case ']':
  232. return $output;
  233. case ',':
  234. case ' ':
  235. break;
  236. default:
  237. $isQuoted = in_array($sequence[$i], array('"', "'"));
  238. $value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i);
  239. if (!$isQuoted && false !== strpos($value, ': ')) {
  240. // embedded mapping?
  241. try {
  242. $value = self::parseMapping('{'.$value.'}');
  243. } catch (\InvalidArgumentException $e) {
  244. // no, it's not
  245. }
  246. }
  247. $output[] = $value;
  248. --$i;
  249. }
  250. ++$i;
  251. }
  252. throw new ParseException(sprintf('Malformed inline YAML string %s', $sequence));
  253. }
  254. /**
  255. * Parses a mapping to a YAML string.
  256. *
  257. * @param string $mapping
  258. * @param integer &$i
  259. *
  260. * @return string A YAML string
  261. *
  262. * @throws ParseException When malformed inline YAML string is parsed
  263. */
  264. private static function parseMapping($mapping, &$i = 0)
  265. {
  266. $output = array();
  267. $len = strlen($mapping);
  268. $i += 1;
  269. // {foo: bar, bar:foo, ...}
  270. while ($i < $len) {
  271. switch ($mapping[$i]) {
  272. case ' ':
  273. case ',':
  274. ++$i;
  275. continue 2;
  276. case '}':
  277. return $output;
  278. }
  279. // key
  280. $key = self::parseScalar($mapping, array(':', ' '), array('"', "'"), $i, false);
  281. // value
  282. $done = false;
  283. while ($i < $len) {
  284. switch ($mapping[$i]) {
  285. case '[':
  286. // nested sequence
  287. $output[$key] = self::parseSequence($mapping, $i);
  288. $done = true;
  289. break;
  290. case '{':
  291. // nested mapping
  292. $output[$key] = self::parseMapping($mapping, $i);
  293. $done = true;
  294. break;
  295. case ':':
  296. case ' ':
  297. break;
  298. default:
  299. $output[$key] = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i);
  300. $done = true;
  301. --$i;
  302. }
  303. ++$i;
  304. if ($done) {
  305. continue 2;
  306. }
  307. }
  308. }
  309. throw new ParseException(sprintf('Malformed inline YAML string %s', $mapping));
  310. }
  311. /**
  312. * Evaluates scalars and replaces magic values.
  313. *
  314. * @param string $scalar
  315. *
  316. * @return string A YAML string
  317. */
  318. private static function evaluateScalar($scalar)
  319. {
  320. $scalar = trim($scalar);
  321. switch (true) {
  322. case 'null' == strtolower($scalar):
  323. case '' == $scalar:
  324. case '~' == $scalar:
  325. return null;
  326. case 0 === strpos($scalar, '!str'):
  327. return (string) substr($scalar, 5);
  328. case 0 === strpos($scalar, '! '):
  329. return intval(self::parseScalar(substr($scalar, 2)));
  330. case 0 === strpos($scalar, '!!php/object:'):
  331. return unserialize(substr($scalar, 13));
  332. case ctype_digit($scalar):
  333. $raw = $scalar;
  334. $cast = intval($scalar);
  335. return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
  336. case 'true' === strtolower($scalar):
  337. return true;
  338. case 'false' === strtolower($scalar):
  339. return false;
  340. case is_numeric($scalar):
  341. return '0x' == $scalar[0].$scalar[1] ? hexdec($scalar) : floatval($scalar);
  342. case 0 == strcasecmp($scalar, '.inf'):
  343. case 0 == strcasecmp($scalar, '.NaN'):
  344. return -log(0);
  345. case 0 == strcasecmp($scalar, '-.inf'):
  346. return log(0);
  347. case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar):
  348. return floatval(str_replace(',', '', $scalar));
  349. case preg_match(self::getTimestampRegex(), $scalar):
  350. return strtotime($scalar);
  351. default:
  352. return (string) $scalar;
  353. }
  354. }
  355. /**
  356. * Gets a regex that matches an unix timestamp
  357. *
  358. * @return string The regular expression
  359. */
  360. private static function getTimestampRegex()
  361. {
  362. return <<<EOF
  363. ~^
  364. (?P<year>[0-9][0-9][0-9][0-9])
  365. -(?P<month>[0-9][0-9]?)
  366. -(?P<day>[0-9][0-9]?)
  367. (?:(?:[Tt]|[ \t]+)
  368. (?P<hour>[0-9][0-9]?)
  369. :(?P<minute>[0-9][0-9])
  370. :(?P<second>[0-9][0-9])
  371. (?:\.(?P<fraction>[0-9]*))?
  372. (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
  373. (?::(?P<tz_minute>[0-9][0-9]))?))?)?
  374. $~x
  375. EOF;
  376. }
  377. }