PageRenderTime 26ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/yaml/sfYamlInline.php

https://github.com/Fil/textwheel
PHP | 421 lines | 298 code | 35 blank | 88 comment | 44 complexity | f98adb348c0c761ce68e7eb6dd952238 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the symfony package.
  4. * (c) Fabien Potencier <fabien.potencier@symfony-project.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. /**
  10. * sfYamlInline implements a YAML parser/dumper for the YAML inline syntax.
  11. *
  12. * @package symfony
  13. * @subpackage yaml
  14. * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  15. * @version SVN: $Id: sfYamlInline.class.php 16177 2009-03-11 08:32:48Z fabien $
  16. */
  17. class sfYamlInline
  18. {
  19. /**
  20. * Convert a YAML string to a PHP array.
  21. *
  22. * @param string $value A YAML string
  23. *
  24. * @return array A PHP array representing the YAML string
  25. */
  26. static public function load($value)
  27. {
  28. $value = trim($value);
  29. if (0 == strlen($value))
  30. {
  31. return '';
  32. }
  33. switch ($value[0])
  34. {
  35. case '[':
  36. return self::parseSequence($value);
  37. case '{':
  38. return self::parseMapping($value);
  39. default:
  40. return self::parseScalar($value);
  41. }
  42. }
  43. /**
  44. * Dumps a given PHP variable to a YAML string.
  45. *
  46. * @param mixed $value The PHP variable to convert
  47. *
  48. * @return string The YAML string representing the PHP array
  49. */
  50. static public function dump($value)
  51. {
  52. $trueValues = '1.1' == sfYaml::getSpecVersion() ? array('true', 'on', '+', 'yes', 'y') : array('true');
  53. $falseValues = '1.1' == sfYaml::getSpecVersion() ? array('false', 'off', '-', 'no', 'n') : array('false');
  54. switch (true)
  55. {
  56. case is_resource($value):
  57. throw new InvalidArgumentException('Unable to dump PHP resources in a YAML file.');
  58. case is_object($value):
  59. return '!!php/object:'.serialize($value);
  60. case is_array($value):
  61. return self::dumpArray($value);
  62. case null === $value:
  63. return 'null';
  64. case true === $value:
  65. return 'true';
  66. case false === $value:
  67. return 'false';
  68. case ctype_digit($value):
  69. return is_string($value) ? "'$value'" : (int) $value;
  70. case is_numeric($value):
  71. return is_infinite($value) ? str_ireplace('INF', '.Inf', strval($value)) : (is_string($value) ? "'$value'" : $value);
  72. case false !== strpos($value, "\n"):
  73. return sprintf('"%s"', str_replace(array('"', "\n", "\r"), array('\\"', '\n', '\r'), $value));
  74. case preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \#] | \A[ - ? | < > = ! % @ ]/x', $value):
  75. return sprintf("'%s'", str_replace('\'', '\'\'', $value));
  76. case '' == $value:
  77. return "''";
  78. case preg_match(self::getTimestampRegex(), $value):
  79. return "'$value'";
  80. case in_array(strtolower($value), $trueValues):
  81. return "'$value'";
  82. case in_array(strtolower($value), $falseValues):
  83. return "'$value'";
  84. case in_array(strtolower($value), array('null', '~')):
  85. return "'$value'";
  86. default:
  87. return $value;
  88. }
  89. }
  90. /**
  91. * Dumps a PHP array to a YAML string.
  92. *
  93. * @param array $value The PHP array to dump
  94. *
  95. * @return string The YAML string representing the PHP array
  96. */
  97. static protected function dumpArray($value)
  98. {
  99. // array
  100. $keys = array_keys($value);
  101. if (
  102. (1 == count($keys) && '0' == $keys[0])
  103. ||
  104. (count($keys) > 1 && array_reduce($keys, create_function('$v,$w', 'return (integer) $v + $w;'), 0) == count($keys) * (count($keys) - 1) / 2))
  105. {
  106. $output = array();
  107. foreach ($value as $val)
  108. {
  109. $output[] = self::dump($val);
  110. }
  111. return sprintf('[%s]', implode(', ', $output));
  112. }
  113. // mapping
  114. $output = array();
  115. foreach ($value as $key => $val)
  116. {
  117. $output[] = sprintf('%s: %s', self::dump($key), self::dump($val));
  118. }
  119. return sprintf('{ %s }', implode(', ', $output));
  120. }
  121. /**
  122. * Parses a scalar to a YAML string.
  123. *
  124. * @param scalar $scalar
  125. * @param string $delimiters
  126. * @param array $stringDelimiter
  127. * @param integer $i
  128. * @param boolean $evaluate
  129. *
  130. * @return string A YAML string
  131. */
  132. static public function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true)
  133. {
  134. if (in_array($scalar[$i], $stringDelimiters))
  135. {
  136. // quoted scalar
  137. $output = self::parseQuotedScalar($scalar, $i);
  138. // skip next delimiter
  139. ++$i;
  140. }
  141. else
  142. {
  143. // "normal" string
  144. if (!$delimiters)
  145. {
  146. $output = substr($scalar, $i);
  147. $i += strlen($output);
  148. // remove comments
  149. if (false !== $strpos = strpos($output, ' #'))
  150. {
  151. $output = rtrim(substr($output, 0, $strpos));
  152. }
  153. }
  154. else if (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match))
  155. {
  156. $output = $match[1];
  157. $i += strlen($output);
  158. }
  159. else
  160. {
  161. throw new InvalidArgumentException(sprintf('Malformed inline YAML string (%s).', $scalar));
  162. }
  163. $output = $evaluate ? self::evaluateScalar($output) : $output;
  164. }
  165. return $output;
  166. }
  167. /**
  168. * Parses a quoted scalar to YAML.
  169. *
  170. * @param string $scalar
  171. * @param integer $i
  172. *
  173. * @return string A YAML string
  174. */
  175. static protected function parseQuotedScalar($scalar, &$i)
  176. {
  177. $delimiter = $scalar[$i];
  178. ++$i;
  179. $buffer = '';
  180. $len = strlen($scalar);
  181. $escaped = '"' == $delimiter ? '\\"' : "''";
  182. while ($i < $len)
  183. {
  184. if (isset($scalar[$i + 1]) && $escaped == $scalar[$i].$scalar[$i + 1])
  185. {
  186. $buffer .= $delimiter;
  187. ++$i;
  188. }
  189. else if ($delimiter == $scalar[$i])
  190. {
  191. break;
  192. }
  193. else
  194. {
  195. $buffer .= $scalar[$i];
  196. }
  197. ++$i;
  198. }
  199. if ('"' == $delimiter)
  200. {
  201. // evaluate the string
  202. $buffer = str_replace(array('\\n', '\\r'), array("\n", "\r"), $buffer);
  203. }
  204. return $buffer;
  205. }
  206. /**
  207. * Parses a sequence to a YAML string.
  208. *
  209. * @param string $sequence
  210. * @param integer $i
  211. *
  212. * @return string A YAML string
  213. */
  214. static protected function parseSequence($sequence, &$i = 0)
  215. {
  216. $output = array();
  217. $len = strlen($sequence);
  218. $i += 1;
  219. // [foo, bar, ...]
  220. while ($i < $len)
  221. {
  222. switch ($sequence[$i])
  223. {
  224. case '[':
  225. // nested sequence
  226. $output[] = self::parseSequence($sequence, $i);
  227. break;
  228. case '{':
  229. // nested mapping
  230. $output[] = self::parseMapping($sequence, $i);
  231. break;
  232. case ']':
  233. return $output;
  234. case ',':
  235. case ' ':
  236. break;
  237. default:
  238. $isQuoted = in_array($sequence[$i], array('"', "'"));
  239. $value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i);
  240. if (!$isQuoted && false !== strpos($value, ': '))
  241. {
  242. // embedded mapping?
  243. try
  244. {
  245. $value = self::parseMapping('{'.$value.'}');
  246. }
  247. catch (InvalidArgumentException $e)
  248. {
  249. // no, it's not
  250. }
  251. }
  252. $output[] = $value;
  253. --$i;
  254. }
  255. ++$i;
  256. }
  257. throw new InvalidArgumentException(sprintf('Malformed inline YAML string %s', $sequence));
  258. }
  259. /**
  260. * Parses a mapping to a YAML string.
  261. *
  262. * @param string $mapping
  263. * @param integer $i
  264. *
  265. * @return string A YAML string
  266. */
  267. static protected function parseMapping($mapping, &$i = 0)
  268. {
  269. $output = array();
  270. $len = strlen($mapping);
  271. $i += 1;
  272. // {foo: bar, bar:foo, ...}
  273. while ($i < $len)
  274. {
  275. switch ($mapping[$i])
  276. {
  277. case ' ':
  278. case ',':
  279. ++$i;
  280. continue 2;
  281. case '}':
  282. return $output;
  283. }
  284. // key
  285. $key = self::parseScalar($mapping, array(':', ' '), array('"', "'"), $i, false);
  286. // value
  287. $done = false;
  288. while ($i < $len)
  289. {
  290. switch ($mapping[$i])
  291. {
  292. case '[':
  293. // nested sequence
  294. $output[$key] = self::parseSequence($mapping, $i);
  295. $done = true;
  296. break;
  297. case '{':
  298. // nested mapping
  299. $output[$key] = self::parseMapping($mapping, $i);
  300. $done = true;
  301. break;
  302. case ':':
  303. case ' ':
  304. break;
  305. default:
  306. $output[$key] = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i);
  307. $done = true;
  308. --$i;
  309. }
  310. ++$i;
  311. if ($done)
  312. {
  313. continue 2;
  314. }
  315. }
  316. }
  317. throw new InvalidArgumentException(sprintf('Malformed inline YAML string %s', $mapping));
  318. }
  319. /**
  320. * Evaluates scalars and replaces magic values.
  321. *
  322. * @param string $scalar
  323. *
  324. * @return string A YAML string
  325. */
  326. static protected function evaluateScalar($scalar)
  327. {
  328. $scalar = trim($scalar);
  329. $trueValues = '1.1' == sfYaml::getSpecVersion() ? array('true', 'on', '+', 'yes', 'y') : array('true');
  330. $falseValues = '1.1' == sfYaml::getSpecVersion() ? array('false', 'off', '-', 'no', 'n') : array('false');
  331. switch (true)
  332. {
  333. case 'null' == strtolower($scalar):
  334. case '' == $scalar:
  335. case '~' == $scalar:
  336. return null;
  337. case 0 === strpos($scalar, '!str'):
  338. return (string) substr($scalar, 5);
  339. case 0 === strpos($scalar, '! '):
  340. return intval(self::parseScalar(substr($scalar, 2)));
  341. case 0 === strpos($scalar, '!!php/object:'):
  342. return unserialize(substr($scalar, 13));
  343. case ctype_digit($scalar):
  344. $raw = $scalar;
  345. $cast = intval($scalar);
  346. return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
  347. case in_array(strtolower($scalar), $trueValues):
  348. return true;
  349. case in_array(strtolower($scalar), $falseValues):
  350. return false;
  351. case is_numeric($scalar):
  352. return '0x' == $scalar[0].$scalar[1] ? hexdec($scalar) : floatval($scalar);
  353. case 0 == strcasecmp($scalar, '.inf'):
  354. case 0 == strcasecmp($scalar, '.NaN'):
  355. return -log(0);
  356. case 0 == strcasecmp($scalar, '-.inf'):
  357. return log(0);
  358. case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar):
  359. return floatval(str_replace(',', '', $scalar));
  360. case preg_match(self::getTimestampRegex(), $scalar):
  361. return strtotime($scalar);
  362. default:
  363. return (string) $scalar;
  364. }
  365. }
  366. static protected function getTimestampRegex()
  367. {
  368. return <<<EOF
  369. ~^
  370. (?P<year>[0-9][0-9][0-9][0-9])
  371. -(?P<month>[0-9][0-9]?)
  372. -(?P<day>[0-9][0-9]?)
  373. (?:(?:[Tt]|[ \t]+)
  374. (?P<hour>[0-9][0-9]?)
  375. :(?P<minute>[0-9][0-9])
  376. :(?P<second>[0-9][0-9])
  377. (?:\.(?P<fraction>[0-9]*))?
  378. (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
  379. (?::(?P<tz_minute>[0-9][0-9]))?))?)?
  380. $~x
  381. EOF;
  382. }
  383. }