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

/modules/kohana-twig/vendor/Twig/lib/Twig/Extension/Core.php

https://bitbucket.org/sapphiriq/assets-example
PHP | 873 lines | 472 code | 99 blank | 302 comment | 78 complexity | 643079f6e8189a093647c4c4ca7b39d3 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. if (!defined('ENT_SUBSTITUTE')) {
  3. define('ENT_SUBSTITUTE', 8);
  4. }
  5. /*
  6. * This file is part of Twig.
  7. *
  8. * (c) 2009 Fabien Potencier
  9. *
  10. * For the full copyright and license information, please view the LICENSE
  11. * file that was distributed with this source code.
  12. */
  13. class Twig_Extension_Core extends Twig_Extension
  14. {
  15. protected $dateFormats = array('F j, Y H:i', '%d days');
  16. protected $numberFormat = array(0, '.', ',');
  17. /**
  18. * Sets the default format to be used by the date filter.
  19. *
  20. * @param string $format The default date format string
  21. * @param string $dateIntervalFormat The default date interval format string
  22. */
  23. public function setDateFormat($format = null, $dateIntervalFormat = null)
  24. {
  25. if (null !== $format) {
  26. $this->dateFormats[0] = $format;
  27. }
  28. if (null !== $dateIntervalFormat) {
  29. $this->dateFormats[1] = $dateIntervalFormat;
  30. }
  31. }
  32. /**
  33. * Gets the default format to be used by the date filter.
  34. *
  35. * @return array The default date format string and the default date interval format string
  36. */
  37. public function getDateFormat()
  38. {
  39. return $this->dateFormats;
  40. }
  41. /**
  42. * Sets the default format to be used by the number_format filter.
  43. *
  44. * @param integer $decimal The number of decimal places to use.
  45. * @param string $decimalPoint The character(s) to use for the decimal point.
  46. * @param string $thousandSep The character(s) to use for the thousands separator.
  47. */
  48. public function setNumberFormat($decimal, $decimalPoint, $thousandSep)
  49. {
  50. $this->numberFormat = array($decimal, $decimalPoint, $thousandSep);
  51. }
  52. /**
  53. * Get the default format used by the number_format filter.
  54. *
  55. * @return array The arguments for number_format()
  56. */
  57. public function getNumberFormat()
  58. {
  59. return $this->numberFormat;
  60. }
  61. /**
  62. * Returns the token parser instance to add to the existing list.
  63. *
  64. * @return array An array of Twig_TokenParser instances
  65. */
  66. public function getTokenParsers()
  67. {
  68. return array(
  69. new Twig_TokenParser_For(),
  70. new Twig_TokenParser_If(),
  71. new Twig_TokenParser_Extends(),
  72. new Twig_TokenParser_Include(),
  73. new Twig_TokenParser_Block(),
  74. new Twig_TokenParser_Use(),
  75. new Twig_TokenParser_Filter(),
  76. new Twig_TokenParser_Macro(),
  77. new Twig_TokenParser_Import(),
  78. new Twig_TokenParser_From(),
  79. new Twig_TokenParser_Set(),
  80. new Twig_TokenParser_Spaceless(),
  81. new Twig_TokenParser_Flush(),
  82. new Twig_TokenParser_Do(),
  83. );
  84. }
  85. /**
  86. * Returns a list of filters to add to the existing list.
  87. *
  88. * @return array An array of filters
  89. */
  90. public function getFilters()
  91. {
  92. $filters = array(
  93. // formatting filters
  94. 'date' => new Twig_Filter_Function('twig_date_format_filter', array('needs_environment' => true)),
  95. 'format' => new Twig_Filter_Function('sprintf'),
  96. 'replace' => new Twig_Filter_Function('strtr'),
  97. 'number_format' => new Twig_Filter_Function('twig_number_format_filter', array('needs_environment' => true)),
  98. // encoding
  99. 'url_encode' => new Twig_Filter_Function('twig_urlencode_filter'),
  100. 'json_encode' => new Twig_Filter_Function('twig_jsonencode_filter'),
  101. 'convert_encoding' => new Twig_Filter_Function('twig_convert_encoding'),
  102. // string filters
  103. 'title' => new Twig_Filter_Function('twig_title_string_filter', array('needs_environment' => true)),
  104. 'capitalize' => new Twig_Filter_Function('twig_capitalize_string_filter', array('needs_environment' => true)),
  105. 'upper' => new Twig_Filter_Function('strtoupper'),
  106. 'lower' => new Twig_Filter_Function('strtolower'),
  107. 'striptags' => new Twig_Filter_Function('strip_tags'),
  108. 'nl2br' => new Twig_Filter_Function('nl2br', array('pre_escape' => 'html', 'is_safe' => array('html'))),
  109. // array helpers
  110. 'join' => new Twig_Filter_Function('twig_join_filter'),
  111. 'reverse' => new Twig_Filter_Function('twig_reverse_filter'),
  112. 'length' => new Twig_Filter_Function('twig_length_filter', array('needs_environment' => true)),
  113. 'sort' => new Twig_Filter_Function('twig_sort_filter'),
  114. 'merge' => new Twig_Filter_Function('twig_array_merge'),
  115. // iteration and runtime
  116. 'default' => new Twig_Filter_Node('Twig_Node_Expression_Filter_Default'),
  117. '_default' => new Twig_Filter_Function('_twig_default_filter'),
  118. 'keys' => new Twig_Filter_Function('twig_get_array_keys_filter'),
  119. // escaping
  120. 'escape' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')),
  121. 'e' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')),
  122. );
  123. if (function_exists('mb_get_info')) {
  124. $filters['upper'] = new Twig_Filter_Function('twig_upper_filter', array('needs_environment' => true));
  125. $filters['lower'] = new Twig_Filter_Function('twig_lower_filter', array('needs_environment' => true));
  126. }
  127. return $filters;
  128. }
  129. /**
  130. * Returns a list of global functions to add to the existing list.
  131. *
  132. * @return array An array of global functions
  133. */
  134. public function getFunctions()
  135. {
  136. return array(
  137. 'range' => new Twig_Function_Function('range'),
  138. 'constant' => new Twig_Function_Function('constant'),
  139. 'cycle' => new Twig_Function_Function('twig_cycle'),
  140. 'random' => new Twig_Function_Function('twig_random'),
  141. );
  142. }
  143. /**
  144. * Returns a list of filters to add to the existing list.
  145. *
  146. * @return array An array of filters
  147. */
  148. public function getTests()
  149. {
  150. return array(
  151. 'even' => new Twig_Test_Node('Twig_Node_Expression_Test_Even'),
  152. 'odd' => new Twig_Test_Node('Twig_Node_Expression_Test_Odd'),
  153. 'defined' => new Twig_Test_Node('Twig_Node_Expression_Test_Defined'),
  154. 'sameas' => new Twig_Test_Node('Twig_Node_Expression_Test_Sameas'),
  155. 'none' => new Twig_Test_Node('Twig_Node_Expression_Test_Null'),
  156. 'null' => new Twig_Test_Node('Twig_Node_Expression_Test_Null'),
  157. 'divisibleby' => new Twig_Test_Node('Twig_Node_Expression_Test_Divisibleby'),
  158. 'constant' => new Twig_Test_Node('Twig_Node_Expression_Test_Constant'),
  159. 'empty' => new Twig_Test_Function('twig_test_empty'),
  160. );
  161. }
  162. /**
  163. * Returns a list of operators to add to the existing list.
  164. *
  165. * @return array An array of operators
  166. */
  167. public function getOperators()
  168. {
  169. return array(
  170. array(
  171. 'not' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
  172. '-' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Neg'),
  173. '+' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Pos'),
  174. ),
  175. array(
  176. 'b-and' => array('precedence' => 5, 'class' => 'Twig_Node_Expression_Binary_BitwiseAnd', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  177. 'b-xor' => array('precedence' => 5, 'class' => 'Twig_Node_Expression_Binary_BitwiseXor', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  178. 'b-or' => array('precedence' => 5, 'class' => 'Twig_Node_Expression_Binary_BitwiseOr', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  179. 'or' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  180. 'and' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  181. '==' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Equal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  182. '!=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  183. '<' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Less', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  184. '>' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Greater', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  185. '>=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  186. '<=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  187. 'not in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotIn', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  188. 'in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_In', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  189. '..' => array('precedence' => 25, 'class' => 'Twig_Node_Expression_Binary_Range', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  190. '+' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Add', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  191. '-' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Sub', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  192. '~' => array('precedence' => 40, 'class' => 'Twig_Node_Expression_Binary_Concat', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  193. '*' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mul', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  194. '/' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Div', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  195. '//' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_FloorDiv', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  196. '%' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  197. 'is' => array('precedence' => 100, 'callable' => array($this, 'parseTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  198. 'is not' => array('precedence' => 100, 'callable' => array($this, 'parseNotTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
  199. '**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT),
  200. ),
  201. );
  202. }
  203. public function parseNotTestExpression(Twig_Parser $parser, $node)
  204. {
  205. return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($parser, $node), $parser->getCurrentToken()->getLine());
  206. }
  207. public function parseTestExpression(Twig_Parser $parser, $node)
  208. {
  209. $stream = $parser->getStream();
  210. $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
  211. $arguments = null;
  212. if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
  213. $arguments = $parser->getExpressionParser()->parseArguments();
  214. }
  215. $class = $this->getTestNodeClass($parser->getEnvironment(), $name);
  216. return new $class($node, $name, $arguments, $parser->getCurrentToken()->getLine());
  217. }
  218. protected function getTestNodeClass(Twig_Environment $env, $name)
  219. {
  220. $testMap = $env->getTests();
  221. if (isset($testMap[$name]) && $testMap[$name] instanceof Twig_Test_Node) {
  222. return $testMap[$name]->getClass();
  223. }
  224. return 'Twig_Node_Expression_Test';
  225. }
  226. /**
  227. * Returns the name of the extension.
  228. *
  229. * @return string The extension name
  230. */
  231. public function getName()
  232. {
  233. return 'core';
  234. }
  235. }
  236. /**
  237. * Cycles over a value.
  238. *
  239. * @param ArrayAccess|array $values An array or an ArrayAccess instance
  240. * @param integer $i The cycle value
  241. *
  242. * @return string The next value in the cycle
  243. */
  244. function twig_cycle($values, $i)
  245. {
  246. if (!is_array($values) && !$values instanceof ArrayAccess) {
  247. return $values;
  248. }
  249. return $values[$i % count($values)];
  250. }
  251. /**
  252. * Returns a random item from sequence.
  253. *
  254. * @param Iterator|array $values An array or an ArrayAccess instance
  255. *
  256. * @return mixed A random value from the given sequence
  257. */
  258. function twig_random($values)
  259. {
  260. if (!is_array($values) && !$values instanceof Traversable) {
  261. return $values;
  262. }
  263. if (is_object($values)) {
  264. $values = iterator_to_array($values);
  265. }
  266. $keys = array_keys($values);
  267. return $values[$keys[mt_rand(0, count($values) - 1)]];
  268. }
  269. /**
  270. * Converts a date to the given format.
  271. *
  272. * <pre>
  273. * {{ post.published_at|date("m/d/Y") }}
  274. * </pre>
  275. *
  276. * @param Twig_Environment $env A Twig_Environment instance
  277. * @param DateTime|DateInterval|string $date A date
  278. * @param string $format A format
  279. * @param DateTimeZone|string $timezone A timezone
  280. *
  281. * @return string The formatter date
  282. */
  283. function twig_date_format_filter(Twig_Environment $env, $date, $format = null, $timezone = null)
  284. {
  285. if (null === $format) {
  286. $formats = $env->getExtension('core')->getDateFormat();
  287. $format = $date instanceof DateInterval ? $formats[1] : $formats[0];
  288. }
  289. if ($date instanceof DateInterval || $date instanceof DateTime) {
  290. return $date->format($format);
  291. }
  292. // convert to a DateTime
  293. $asString = (string) $date;
  294. if (ctype_digit($asString) || (!empty($asString) && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) {
  295. $date = new DateTime('@'.$date);
  296. $date->setTimezone(new DateTimeZone(date_default_timezone_get()));
  297. } else {
  298. $date = new DateTime($date);
  299. }
  300. // set Timezone
  301. if (null !== $timezone) {
  302. if (!$timezone instanceof DateTimeZone) {
  303. $timezone = new DateTimeZone($timezone);
  304. }
  305. $date->setTimezone($timezone);
  306. }
  307. return $date->format($format);
  308. }
  309. /**
  310. * Number format filter.
  311. *
  312. * All of the formatting options can be left null, in that case the defaults will
  313. * be used. Supplying any of the parameters will override the defaults set in the
  314. * environment object.
  315. *
  316. * @param Twig_Environment $env A Twig_Environment instance
  317. * @param mixed $number A float/int/string of the number to format
  318. * @param int $decimal The number of decimal points to display.
  319. * @param string $decimalPoint The character(s) to use for the decimal point.
  320. * @param string $thousandSep The character(s) to use for the thousands separator.
  321. *
  322. * @return string The formatted number
  323. */
  324. function twig_number_format_filter(Twig_Environment $env, $number, $decimal = null, $decimalPoint = null, $thousandSep = null)
  325. {
  326. $defaults = $env->getExtension('core')->getNumberFormat();
  327. if (null === $decimal) {
  328. $decimal = $defaults[0];
  329. }
  330. if (null === $decimalPoint) {
  331. $decimalPoint = $defaults[1];
  332. }
  333. if (null === $thousandSep) {
  334. $thousandSep = $defaults[2];
  335. }
  336. return number_format((float) $number, $decimal, $decimalPoint, $thousandSep);
  337. }
  338. /**
  339. * URL encodes a string.
  340. *
  341. * @param string $url A URL
  342. * @param bool $raw true to use rawurlencode() instead of urlencode
  343. *
  344. * @return string The URL encoded value
  345. */
  346. function twig_urlencode_filter($url, $raw = false)
  347. {
  348. if ($raw) {
  349. return rawurlencode($url);
  350. }
  351. return urlencode($url);
  352. }
  353. if (version_compare(PHP_VERSION, '5.3.0', '<')) {
  354. /**
  355. * JSON encodes a PHP variable.
  356. *
  357. * @param mixed $value The value to encode.
  358. * @param integer $options Not used on PHP 5.2.x
  359. *
  360. * @return mixed The JSON encoded value
  361. */
  362. function twig_jsonencode_filter($value, $options = 0)
  363. {
  364. if ($value instanceof Twig_Markup) {
  365. $value = (string) $value;
  366. } elseif (is_array($value)) {
  367. array_walk_recursive($value, '_twig_markup2string');
  368. }
  369. return json_encode($value);
  370. }
  371. } else {
  372. /**
  373. * JSON encodes a PHP variable.
  374. *
  375. * @param mixed $value The value to encode.
  376. * @param integer $options Bitmask consisting of JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_NUMERIC_CHECK, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_FORCE_OBJECT
  377. *
  378. * @return mixed The JSON encoded value
  379. */
  380. function twig_jsonencode_filter($value, $options = 0)
  381. {
  382. if ($value instanceof Twig_Markup) {
  383. $value = (string) $value;
  384. } elseif (is_array($value)) {
  385. array_walk_recursive($value, '_twig_markup2string');
  386. }
  387. return json_encode($value, $options);
  388. }
  389. }
  390. function _twig_markup2string(&$value)
  391. {
  392. if ($value instanceof Twig_Markup) {
  393. $value = (string) $value;
  394. }
  395. }
  396. /**
  397. * Merges an array with another one.
  398. *
  399. * <pre>
  400. * {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %}
  401. *
  402. * {% set items = items|merge({ 'peugeot': 'car' }) %}
  403. *
  404. * {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car' } #}
  405. * </pre>
  406. *
  407. * @param array $arr1 An array
  408. * @param array $arr2 An array
  409. *
  410. * @return array The merged array
  411. */
  412. function twig_array_merge($arr1, $arr2)
  413. {
  414. if (!is_array($arr1) || !is_array($arr2)) {
  415. throw new Twig_Error_Runtime('The merge filter only work with arrays or hashes.');
  416. }
  417. return array_merge($arr1, $arr2);
  418. }
  419. /**
  420. * Joins the values to a string.
  421. *
  422. * The separator between elements is an empty string per default, you can define it with the optional parameter.
  423. *
  424. * <pre>
  425. * {{ [1, 2, 3]|join('|') }}
  426. * {# returns 1|2|3 #}
  427. *
  428. * {{ [1, 2, 3]|join }}
  429. * {# returns 123 #}
  430. * </pre>
  431. *
  432. * @param array $value An array
  433. * @param string $glue The separator
  434. *
  435. * @return string The concatenated string
  436. */
  437. function twig_join_filter($value, $glue = '')
  438. {
  439. if ($value instanceof Traversable) {
  440. $value = iterator_to_array($value, false);
  441. }
  442. return implode($glue, (array) $value);
  443. }
  444. // The '_default' filter is used internally to avoid using the ternary operator
  445. // which costs a lot for big contexts (before PHP 5.4). So, on average,
  446. // a function call is cheaper.
  447. function _twig_default_filter($value, $default = '')
  448. {
  449. if (twig_test_empty($value)) {
  450. return $default;
  451. } else {
  452. return $value;
  453. }
  454. }
  455. /**
  456. * Returns the keys for the given array.
  457. *
  458. * It is useful when you want to iterate over the keys of an array:
  459. *
  460. * <pre>
  461. * {% for key in array|keys %}
  462. * {# ... #}
  463. * {% endfor %}
  464. * </pre>
  465. *
  466. * @param array $array An array
  467. *
  468. * @return array The keys
  469. */
  470. function twig_get_array_keys_filter($array)
  471. {
  472. if (is_object($array) && $array instanceof Traversable) {
  473. return array_keys(iterator_to_array($array));
  474. }
  475. if (!is_array($array)) {
  476. return array();
  477. }
  478. return array_keys($array);
  479. }
  480. /**
  481. * Reverses an array.
  482. *
  483. * @param array|Traversable $array An array or a Traversable instance
  484. * @param Boolean $preserveKeys Whether to preserve key or not
  485. *
  486. * return array The array reversed
  487. */
  488. function twig_reverse_filter($array, $preserveKeys = false)
  489. {
  490. if (is_object($array) && $array instanceof Traversable) {
  491. return array_reverse(iterator_to_array($array), $preserveKeys);
  492. }
  493. if (!is_array($array)) {
  494. return array();
  495. }
  496. return array_reverse($array, $preserveKeys);
  497. }
  498. /**
  499. * Sorts an array.
  500. *
  501. * @param array $array An array
  502. */
  503. function twig_sort_filter($array)
  504. {
  505. asort($array);
  506. return $array;
  507. }
  508. /* used internally */
  509. function twig_in_filter($value, $compare)
  510. {
  511. if (is_array($compare)) {
  512. return in_array($value, $compare);
  513. } elseif (is_string($compare)) {
  514. if (!strlen((string) $value)) {
  515. return empty($compare);
  516. }
  517. return false !== strpos($compare, (string) $value);
  518. } elseif (is_object($compare) && $compare instanceof Traversable) {
  519. return in_array($value, iterator_to_array($compare, false));
  520. }
  521. return false;
  522. }
  523. /**
  524. * Escapes a string.
  525. *
  526. * @param Twig_Environment $env A Twig_Environment instance
  527. * @param string $string The value to be escaped
  528. * @param string $type The escaping strategy
  529. * @param string $charset The charset
  530. * @param Boolean $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false)
  531. */
  532. function twig_escape_filter(Twig_Environment $env, $string, $type = 'html', $charset = null, $autoescape = false)
  533. {
  534. if ($autoescape && is_object($string) && $string instanceof Twig_Markup) {
  535. return $string;
  536. }
  537. if (!is_string($string) && !(is_object($string) && method_exists($string, '__toString'))) {
  538. return $string;
  539. }
  540. if (null === $charset) {
  541. $charset = $env->getCharset();
  542. }
  543. $string = (string) $string;
  544. switch ($type) {
  545. case 'js':
  546. // escape all non-alphanumeric characters
  547. // into their \xHH or \uHHHH representations
  548. if ('UTF-8' != $charset) {
  549. $string = twig_convert_encoding($string, 'UTF-8', $charset);
  550. }
  551. if (null === $string = preg_replace_callback('#[^\p{L}\p{N} ]#u', '_twig_escape_js_callback', $string)) {
  552. throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
  553. }
  554. if ('UTF-8' != $charset) {
  555. $string = twig_convert_encoding($string, $charset, 'UTF-8');
  556. }
  557. return $string;
  558. case 'html':
  559. // see http://php.net/htmlspecialchars
  560. // Using a static variable to avoid initializing the array
  561. // each time the function is called. Moving the declaration on the
  562. // top of the function slow downs other escaping types.
  563. static $htmlspecialcharsCharsets = array(
  564. 'iso-8859-1' => true, 'iso8859-1' => true,
  565. 'iso-8859-15' => true, 'iso8859-15' => true,
  566. 'utf-8' => true,
  567. 'cp866' => true, 'ibm866' => true, '866' => true,
  568. 'cp1251' => true, 'windows-1251' => true, 'win-1251' => true,
  569. '1251' => true,
  570. 'cp1252' => true, 'windows-1252' => true, '1252' => true,
  571. 'koi8-r' => true, 'koi8-ru' => true, 'koi8r' => true,
  572. 'big5' => true, '950' => true,
  573. 'gb2312' => true, '936' => true,
  574. 'big5-hkscs' => true,
  575. 'shift_jis' => true, 'sjis' => true, '932' => true,
  576. 'euc-jp' => true, 'eucjp' => true,
  577. 'iso8859-5' => true, 'iso-8859-5' => true, 'macroman' => true,
  578. );
  579. if (isset($htmlspecialcharsCharsets[strtolower($charset)])) {
  580. return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
  581. }
  582. $string = twig_convert_encoding($string, 'UTF-8', $charset);
  583. $string = htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
  584. return twig_convert_encoding($string, $charset, 'UTF-8');
  585. default:
  586. throw new Twig_Error_Runtime(sprintf('Invalid escape type "%s".', $type));
  587. }
  588. }
  589. /* used internally */
  590. function twig_escape_filter_is_safe(Twig_Node $filterArgs)
  591. {
  592. foreach ($filterArgs as $arg) {
  593. if ($arg instanceof Twig_Node_Expression_Constant) {
  594. return array($arg->getAttribute('value'));
  595. } else {
  596. return array();
  597. }
  598. break;
  599. }
  600. return array('html');
  601. }
  602. if (function_exists('iconv')) {
  603. function twig_convert_encoding($string, $to, $from)
  604. {
  605. return iconv($from, $to, $string);
  606. }
  607. } elseif (function_exists('mb_convert_encoding')) {
  608. function twig_convert_encoding($string, $to, $from)
  609. {
  610. return mb_convert_encoding($string, $to, $from);
  611. }
  612. } else {
  613. function twig_convert_encoding($string, $to, $from)
  614. {
  615. throw new Twig_Error_Runtime('No suitable convert encoding function (use UTF-8 as your encoding or install the iconv or mbstring extension).');
  616. }
  617. }
  618. function _twig_escape_js_callback($matches)
  619. {
  620. $char = $matches[0];
  621. // \xHH
  622. if (!isset($char[1])) {
  623. return '\\x'.substr('00'.bin2hex($char), -2);
  624. }
  625. // \uHHHH
  626. $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
  627. return '\\u'.substr('0000'.bin2hex($char), -4);
  628. }
  629. // add multibyte extensions if possible
  630. if (function_exists('mb_get_info')) {
  631. /**
  632. * Returns the length of a PHP variable.
  633. *
  634. * @param Twig_Environment $env A Twig_Environment instance
  635. * @param mixed $thing A PHP variable
  636. *
  637. * @return integer The length of the value
  638. */
  639. function twig_length_filter(Twig_Environment $env, $thing)
  640. {
  641. return is_scalar($thing) ? mb_strlen($thing, $env->getCharset()) : count($thing);
  642. }
  643. /**
  644. * Converts a string to uppercase.
  645. *
  646. * @param Twig_Environment $env A Twig_Environment instance
  647. * @param string $string A string
  648. *
  649. * @return string The uppercased string
  650. */
  651. function twig_upper_filter(Twig_Environment $env, $string)
  652. {
  653. if (null !== ($charset = $env->getCharset())) {
  654. return mb_strtoupper($string, $charset);
  655. }
  656. return strtoupper($string);
  657. }
  658. /**
  659. * Converts a string to lowercase.
  660. *
  661. * @param Twig_Environment $env A Twig_Environment instance
  662. * @param string $string A string
  663. *
  664. * @return string The lowercased string
  665. */
  666. function twig_lower_filter(Twig_Environment $env, $string)
  667. {
  668. if (null !== ($charset = $env->getCharset())) {
  669. return mb_strtolower($string, $charset);
  670. }
  671. return strtolower($string);
  672. }
  673. /**
  674. * Returns a titlecased string.
  675. *
  676. * @param Twig_Environment $env A Twig_Environment instance
  677. * @param string $string A string
  678. *
  679. * @return string The titlecased string
  680. */
  681. function twig_title_string_filter(Twig_Environment $env, $string)
  682. {
  683. if (null !== ($charset = $env->getCharset())) {
  684. return mb_convert_case($string, MB_CASE_TITLE, $charset);
  685. }
  686. return ucwords(strtolower($string));
  687. }
  688. /**
  689. * Returns a capitalized string.
  690. *
  691. * @param Twig_Environment $env A Twig_Environment instance
  692. * @param string $string A string
  693. *
  694. * @return string The capitalized string
  695. */
  696. function twig_capitalize_string_filter(Twig_Environment $env, $string)
  697. {
  698. if (null !== ($charset = $env->getCharset())) {
  699. return mb_strtoupper(mb_substr($string, 0, 1, $charset), $charset).
  700. mb_strtolower(mb_substr($string, 1, mb_strlen($string, $charset), $charset), $charset);
  701. }
  702. return ucfirst(strtolower($string));
  703. }
  704. }
  705. // and byte fallback
  706. else
  707. {
  708. /**
  709. * Returns the length of a PHP variable.
  710. *
  711. * @param Twig_Environment $env A Twig_Environment instance
  712. * @param mixed $thing A PHP variable
  713. *
  714. * @return integer The length of the value
  715. */
  716. function twig_length_filter(Twig_Environment $env, $thing)
  717. {
  718. return is_scalar($thing) ? strlen($thing) : count($thing);
  719. }
  720. /**
  721. * Returns a titlecased string.
  722. *
  723. * @param Twig_Environment $env A Twig_Environment instance
  724. * @param string $string A string
  725. *
  726. * @return string The titlecased string
  727. */
  728. function twig_title_string_filter(Twig_Environment $env, $string)
  729. {
  730. return ucwords(strtolower($string));
  731. }
  732. /**
  733. * Returns a capitalized string.
  734. *
  735. * @param Twig_Environment $env A Twig_Environment instance
  736. * @param string $string A string
  737. *
  738. * @return string The capitalized string
  739. */
  740. function twig_capitalize_string_filter(Twig_Environment $env, $string)
  741. {
  742. return ucfirst(strtolower($string));
  743. }
  744. }
  745. /* used internally */
  746. function twig_ensure_traversable($seq)
  747. {
  748. if (is_array($seq) || (is_object($seq) && $seq instanceof Traversable)) {
  749. return $seq;
  750. } else {
  751. return array();
  752. }
  753. }
  754. /**
  755. * Checks if a variable is empty.
  756. *
  757. * <pre>
  758. * {# evaluates to true if the foo variable is null, false, or the empty string #}
  759. * {% if foo is empty %}
  760. * {# ... #}
  761. * {% endif %}
  762. * </pre>
  763. *
  764. * @param mixed $value A PHP variable
  765. *
  766. * @return Boolean true if the value is empty, false otherwise
  767. */
  768. function twig_test_empty($value)
  769. {
  770. if ($value instanceof Countable) {
  771. return 0 == count($value);
  772. }
  773. return false === $value || (empty($value) && '0' != $value);
  774. }