Verzeichnisstruktur phpBB-3.2.0
- Veröffentlicht
- 06.01.2017
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
Core.php
0001 <?php
0002
0003 if (!defined('ENT_SUBSTITUTE')) {
0004 // use 0 as hhvm does not support several flags yet
0005 define('ENT_SUBSTITUTE', 0);
0006 }
0007
0008 /*
0009 * This file is part of Twig.
0010 *
0011 * (c) 2009 Fabien Potencier
0012 *
0013 * For the full copyright and license information, please view the LICENSE
0014 * file that was distributed with this source code.
0015 */
0016 class Twig_Extension_Core extends Twig_Extension
0017 {
0018 protected $dateFormats = array('F j, Y H:i', '%d days');
0019 protected $numberFormat = array(0, '.', ',');
0020 protected $timezone = null;
0021 protected $escapers = array();
0022
0023 /**
0024 * Defines a new escaper to be used via the escape filter.
0025 *
0026 * @param string $strategy The strategy name that should be used as a strategy in the escape call
0027 * @param callable $callable A valid PHP callable
0028 */
0029 public function setEscaper($strategy, $callable)
0030 {
0031 $this->escapers[$strategy] = $callable;
0032 }
0033
0034 /**
0035 * Gets all defined escapers.
0036 *
0037 * @return array An array of escapers
0038 */
0039 public function getEscapers()
0040 {
0041 return $this->escapers;
0042 }
0043
0044 /**
0045 * Sets the default format to be used by the date filter.
0046 *
0047 * @param string $format The default date format string
0048 * @param string $dateIntervalFormat The default date interval format string
0049 */
0050 public function setDateFormat($format = null, $dateIntervalFormat = null)
0051 {
0052 if (null !== $format) {
0053 $this->dateFormats[0] = $format;
0054 }
0055
0056 if (null !== $dateIntervalFormat) {
0057 $this->dateFormats[1] = $dateIntervalFormat;
0058 }
0059 }
0060
0061 /**
0062 * Gets the default format to be used by the date filter.
0063 *
0064 * @return array The default date format string and the default date interval format string
0065 */
0066 public function getDateFormat()
0067 {
0068 return $this->dateFormats;
0069 }
0070
0071 /**
0072 * Sets the default timezone to be used by the date filter.
0073 *
0074 * @param DateTimeZone|string $timezone The default timezone string or a DateTimeZone object
0075 */
0076 public function setTimezone($timezone)
0077 {
0078 $this->timezone = $timezone instanceof DateTimeZone ? $timezone : new DateTimeZone($timezone);
0079 }
0080
0081 /**
0082 * Gets the default timezone to be used by the date filter.
0083 *
0084 * @return DateTimeZone The default timezone currently in use
0085 */
0086 public function getTimezone()
0087 {
0088 if (null === $this->timezone) {
0089 $this->timezone = new DateTimeZone(date_default_timezone_get());
0090 }
0091
0092 return $this->timezone;
0093 }
0094
0095 /**
0096 * Sets the default format to be used by the number_format filter.
0097 *
0098 * @param int $decimal The number of decimal places to use.
0099 * @param string $decimalPoint The character(s) to use for the decimal point.
0100 * @param string $thousandSep The character(s) to use for the thousands separator.
0101 */
0102 public function setNumberFormat($decimal, $decimalPoint, $thousandSep)
0103 {
0104 $this->numberFormat = array($decimal, $decimalPoint, $thousandSep);
0105 }
0106
0107 /**
0108 * Get the default format used by the number_format filter.
0109 *
0110 * @return array The arguments for number_format()
0111 */
0112 public function getNumberFormat()
0113 {
0114 return $this->numberFormat;
0115 }
0116
0117 public function getTokenParsers()
0118 {
0119 return array(
0120 new Twig_TokenParser_For(),
0121 new Twig_TokenParser_If(),
0122 new Twig_TokenParser_Extends(),
0123 new Twig_TokenParser_Include(),
0124 new Twig_TokenParser_Block(),
0125 new Twig_TokenParser_Use(),
0126 new Twig_TokenParser_Filter(),
0127 new Twig_TokenParser_Macro(),
0128 new Twig_TokenParser_Import(),
0129 new Twig_TokenParser_From(),
0130 new Twig_TokenParser_Set(),
0131 new Twig_TokenParser_Spaceless(),
0132 new Twig_TokenParser_Flush(),
0133 new Twig_TokenParser_Do(),
0134 new Twig_TokenParser_Embed(),
0135 );
0136 }
0137
0138 public function getFilters()
0139 {
0140 $filters = array(
0141 // formatting filters
0142 new Twig_SimpleFilter('date', 'twig_date_format_filter', array('needs_environment' => true)),
0143 new Twig_SimpleFilter('date_modify', 'twig_date_modify_filter', array('needs_environment' => true)),
0144 new Twig_SimpleFilter('format', 'sprintf'),
0145 new Twig_SimpleFilter('replace', 'twig_replace_filter'),
0146 new Twig_SimpleFilter('number_format', 'twig_number_format_filter', array('needs_environment' => true)),
0147 new Twig_SimpleFilter('abs', 'abs'),
0148 new Twig_SimpleFilter('round', 'twig_round'),
0149
0150 // encoding
0151 new Twig_SimpleFilter('url_encode', 'twig_urlencode_filter'),
0152 new Twig_SimpleFilter('json_encode', 'twig_jsonencode_filter'),
0153 new Twig_SimpleFilter('convert_encoding', 'twig_convert_encoding'),
0154
0155 // string filters
0156 new Twig_SimpleFilter('title', 'twig_title_string_filter', array('needs_environment' => true)),
0157 new Twig_SimpleFilter('capitalize', 'twig_capitalize_string_filter', array('needs_environment' => true)),
0158 new Twig_SimpleFilter('upper', 'strtoupper'),
0159 new Twig_SimpleFilter('lower', 'strtolower'),
0160 new Twig_SimpleFilter('striptags', 'strip_tags'),
0161 new Twig_SimpleFilter('trim', 'trim'),
0162 new Twig_SimpleFilter('nl2br', 'nl2br', array('pre_escape' => 'html', 'is_safe' => array('html'))),
0163
0164 // array helpers
0165 new Twig_SimpleFilter('join', 'twig_join_filter'),
0166 new Twig_SimpleFilter('split', 'twig_split_filter', array('needs_environment' => true)),
0167 new Twig_SimpleFilter('sort', 'twig_sort_filter'),
0168 new Twig_SimpleFilter('merge', 'twig_array_merge'),
0169 new Twig_SimpleFilter('batch', 'twig_array_batch'),
0170
0171 // string/array filters
0172 new Twig_SimpleFilter('reverse', 'twig_reverse_filter', array('needs_environment' => true)),
0173 new Twig_SimpleFilter('length', 'twig_length_filter', array('needs_environment' => true)),
0174 new Twig_SimpleFilter('slice', 'twig_slice', array('needs_environment' => true)),
0175 new Twig_SimpleFilter('first', 'twig_first', array('needs_environment' => true)),
0176 new Twig_SimpleFilter('last', 'twig_last', array('needs_environment' => true)),
0177
0178 // iteration and runtime
0179 new Twig_SimpleFilter('default', '_twig_default_filter', array('node_class' => 'Twig_Node_Expression_Filter_Default')),
0180 new Twig_SimpleFilter('keys', 'twig_get_array_keys_filter'),
0181
0182 // escaping
0183 new Twig_SimpleFilter('escape', 'twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')),
0184 new Twig_SimpleFilter('e', 'twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')),
0185 );
0186
0187 if (function_exists('mb_get_info')) {
0188 $filters[] = new Twig_SimpleFilter('upper', 'twig_upper_filter', array('needs_environment' => true));
0189 $filters[] = new Twig_SimpleFilter('lower', 'twig_lower_filter', array('needs_environment' => true));
0190 }
0191
0192 return $filters;
0193 }
0194
0195 public function getFunctions()
0196 {
0197 return array(
0198 new Twig_SimpleFunction('max', 'max'),
0199 new Twig_SimpleFunction('min', 'min'),
0200 new Twig_SimpleFunction('range', 'range'),
0201 new Twig_SimpleFunction('constant', 'twig_constant'),
0202 new Twig_SimpleFunction('cycle', 'twig_cycle'),
0203 new Twig_SimpleFunction('random', 'twig_random', array('needs_environment' => true)),
0204 new Twig_SimpleFunction('date', 'twig_date_converter', array('needs_environment' => true)),
0205 new Twig_SimpleFunction('include', 'twig_include', array('needs_environment' => true, 'needs_context' => true, 'is_safe' => array('all'))),
0206 new Twig_SimpleFunction('source', 'twig_source', array('needs_environment' => true, 'is_safe' => array('all'))),
0207 );
0208 }
0209
0210 public function getTests()
0211 {
0212 return array(
0213 new Twig_SimpleTest('even', null, array('node_class' => 'Twig_Node_Expression_Test_Even')),
0214 new Twig_SimpleTest('odd', null, array('node_class' => 'Twig_Node_Expression_Test_Odd')),
0215 new Twig_SimpleTest('defined', null, array('node_class' => 'Twig_Node_Expression_Test_Defined')),
0216 new Twig_SimpleTest('sameas', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas', 'deprecated' => '1.21', 'alternative' => 'same as')),
0217 new Twig_SimpleTest('same as', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas')),
0218 new Twig_SimpleTest('none', null, array('node_class' => 'Twig_Node_Expression_Test_Null')),
0219 new Twig_SimpleTest('null', null, array('node_class' => 'Twig_Node_Expression_Test_Null')),
0220 new Twig_SimpleTest('divisibleby', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby', 'deprecated' => '1.21', 'alternative' => 'divisible by')),
0221 new Twig_SimpleTest('divisible by', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby')),
0222 new Twig_SimpleTest('constant', null, array('node_class' => 'Twig_Node_Expression_Test_Constant')),
0223 new Twig_SimpleTest('empty', 'twig_test_empty'),
0224 new Twig_SimpleTest('iterable', 'twig_test_iterable'),
0225 );
0226 }
0227
0228 public function getOperators()
0229 {
0230 return array(
0231 array(
0232 'not' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
0233 '-' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Neg'),
0234 '+' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Pos'),
0235 ),
0236 array(
0237 'or' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0238 'and' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0239 'b-or' => array('precedence' => 16, 'class' => 'Twig_Node_Expression_Binary_BitwiseOr', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0240 'b-xor' => array('precedence' => 17, 'class' => 'Twig_Node_Expression_Binary_BitwiseXor', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0241 'b-and' => array('precedence' => 18, 'class' => 'Twig_Node_Expression_Binary_BitwiseAnd', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0242 '==' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Equal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0243 '!=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0244 '<' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Less', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0245 '>' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Greater', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0246 '>=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0247 '<=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0248 'not in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotIn', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0249 'in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_In', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0250 'matches' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Matches', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0251 'starts with' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_StartsWith', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0252 'ends with' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_EndsWith', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0253 '..' => array('precedence' => 25, 'class' => 'Twig_Node_Expression_Binary_Range', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0254 '+' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Add', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0255 '-' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Sub', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0256 '~' => array('precedence' => 40, 'class' => 'Twig_Node_Expression_Binary_Concat', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0257 '*' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mul', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0258 '/' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Div', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0259 '//' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_FloorDiv', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0260 '%' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0261 'is' => array('precedence' => 100, 'callable' => array($this, 'parseTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0262 'is not' => array('precedence' => 100, 'callable' => array($this, 'parseNotTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
0263 '**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT),
0264 '??' => array('precedence' => 300, 'class' => 'Twig_Node_Expression_NullCoalesce', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT),
0265 ),
0266 );
0267 }
0268
0269 public function parseNotTestExpression(Twig_Parser $parser, Twig_NodeInterface $node)
0270 {
0271 return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($parser, $node), $parser->getCurrentToken()->getLine());
0272 }
0273
0274 public function parseTestExpression(Twig_Parser $parser, Twig_NodeInterface $node)
0275 {
0276 $stream = $parser->getStream();
0277 list($name, $test) = $this->getTest($parser, $node->getLine());
0278
0279 if ($test instanceof Twig_SimpleTest && $test->isDeprecated()) {
0280 $message = sprintf('Twig Test "%s" is deprecated', $name);
0281 if (!is_bool($test->getDeprecatedVersion())) {
0282 $message .= sprintf(' since version %s', $test->getDeprecatedVersion());
0283 }
0284 if ($test->getAlternative()) {
0285 $message .= sprintf('. Use "%s" instead', $test->getAlternative());
0286 }
0287 $message .= sprintf(' in %s at line %d.', $stream->getFilename(), $stream->getCurrent()->getLine());
0288
0289 @trigger_error($message, E_USER_DEPRECATED);
0290 }
0291
0292 $class = $this->getTestNodeClass($parser, $test);
0293 $arguments = null;
0294 if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
0295 $arguments = $parser->getExpressionParser()->parseArguments(true);
0296 }
0297
0298 return new $class($node, $name, $arguments, $parser->getCurrentToken()->getLine());
0299 }
0300
0301 protected function getTest(Twig_Parser $parser, $line)
0302 {
0303 $stream = $parser->getStream();
0304 $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
0305 $env = $parser->getEnvironment();
0306
0307 if ($test = $env->getTest($name)) {
0308 return array($name, $test);
0309 }
0310
0311 if ($stream->test(Twig_Token::NAME_TYPE)) {
0312 // try 2-words tests
0313 $name = $name.' '.$parser->getCurrentToken()->getValue();
0314
0315 if ($test = $env->getTest($name)) {
0316 $parser->getStream()->next();
0317
0318 return array($name, $test);
0319 }
0320 }
0321
0322 $e = new Twig_Error_Syntax(sprintf('Unknown "%s" test.', $name), $line, $parser->getFilename());
0323 $e->addSuggestions($name, array_keys($env->getTests()));
0324
0325 throw $e;
0326 }
0327
0328 protected function getTestNodeClass(Twig_Parser $parser, $test)
0329 {
0330 if ($test instanceof Twig_SimpleTest) {
0331 return $test->getNodeClass();
0332 }
0333
0334 return $test instanceof Twig_Test_Node ? $test->getClass() : 'Twig_Node_Expression_Test';
0335 }
0336
0337 public function getName()
0338 {
0339 return 'core';
0340 }
0341 }
0342
0343 /**
0344 * Cycles over a value.
0345 *
0346 * @param ArrayAccess|array $values An array or an ArrayAccess instance
0347 * @param int $position The cycle position
0348 *
0349 * @return string The next value in the cycle
0350 */
0351 function twig_cycle($values, $position)
0352 {
0353 if (!is_array($values) && !$values instanceof ArrayAccess) {
0354 return $values;
0355 }
0356
0357 return $values[$position % count($values)];
0358 }
0359
0360 /**
0361 * Returns a random value depending on the supplied parameter type:
0362 * - a random item from a Traversable or array
0363 * - a random character from a string
0364 * - a random integer between 0 and the integer parameter.
0365 *
0366 * @param Twig_Environment $env A Twig_Environment instance
0367 * @param Traversable|array|int|string $values The values to pick a random item from
0368 *
0369 * @throws Twig_Error_Runtime When $values is an empty array (does not apply to an empty string which is returned as is).
0370 *
0371 * @return mixed A random value from the given sequence
0372 */
0373 function twig_random(Twig_Environment $env, $values = null)
0374 {
0375 if (null === $values) {
0376 return mt_rand();
0377 }
0378
0379 if (is_int($values) || is_float($values)) {
0380 return $values < 0 ? mt_rand($values, 0) : mt_rand(0, $values);
0381 }
0382
0383 if ($values instanceof Traversable) {
0384 $values = iterator_to_array($values);
0385 } elseif (is_string($values)) {
0386 if ('' === $values) {
0387 return '';
0388 }
0389 if (null !== $charset = $env->getCharset()) {
0390 if ('UTF-8' !== $charset) {
0391 $values = twig_convert_encoding($values, 'UTF-8', $charset);
0392 }
0393
0394 // unicode version of str_split()
0395 // split at all positions, but not after the start and not before the end
0396 $values = preg_split('/(?<!^)(?!$)/u', $values);
0397
0398 if ('UTF-8' !== $charset) {
0399 foreach ($values as $i => $value) {
0400 $values[$i] = twig_convert_encoding($value, $charset, 'UTF-8');
0401 }
0402 }
0403 } else {
0404 return $values[mt_rand(0, strlen($values) - 1)];
0405 }
0406 }
0407
0408 if (!is_array($values)) {
0409 return $values;
0410 }
0411
0412 if (0 === count($values)) {
0413 throw new Twig_Error_Runtime('The random function cannot pick from an empty array.');
0414 }
0415
0416 return $values[array_rand($values, 1)];
0417 }
0418
0419 /**
0420 * Converts a date to the given format.
0421 *
0422 * <pre>
0423 * {{ post.published_at|date("m/d/Y") }}
0424 * </pre>
0425 *
0426 * @param Twig_Environment $env A Twig_Environment instance
0427 * @param DateTime|DateTimeInterface|DateInterval|string $date A date
0428 * @param string|null $format The target format, null to use the default
0429 * @param DateTimeZone|string|null|false $timezone The target timezone, null to use the default, false to leave unchanged
0430 *
0431 * @return string The formatted date
0432 */
0433 function twig_date_format_filter(Twig_Environment $env, $date, $format = null, $timezone = null)
0434 {
0435 if (null === $format) {
0436 $formats = $env->getExtension('core')->getDateFormat();
0437 $format = $date instanceof DateInterval ? $formats[1] : $formats[0];
0438 }
0439
0440 if ($date instanceof DateInterval) {
0441 return $date->format($format);
0442 }
0443
0444 return twig_date_converter($env, $date, $timezone)->format($format);
0445 }
0446
0447 /**
0448 * Returns a new date object modified.
0449 *
0450 * <pre>
0451 * {{ post.published_at|date_modify("-1day")|date("m/d/Y") }}
0452 * </pre>
0453 *
0454 * @param Twig_Environment $env A Twig_Environment instance
0455 * @param DateTime|string $date A date
0456 * @param string $modifier A modifier string
0457 *
0458 * @return DateTime A new date object
0459 */
0460 function twig_date_modify_filter(Twig_Environment $env, $date, $modifier)
0461 {
0462 $date = twig_date_converter($env, $date, false);
0463 $resultDate = $date->modify($modifier);
0464
0465 // This is a hack to ensure PHP 5.2 support and support for DateTimeImmutable
0466 // DateTime::modify does not return the modified DateTime object < 5.3.0
0467 // and DateTimeImmutable does not modify $date.
0468 return null === $resultDate ? $date : $resultDate;
0469 }
0470
0471 /**
0472 * Converts an input to a DateTime instance.
0473 *
0474 * <pre>
0475 * {% if date(user.created_at) < date('+2days') %}
0476 * {# do something #}
0477 * {% endif %}
0478 * </pre>
0479 *
0480 * @param Twig_Environment $env A Twig_Environment instance
0481 * @param DateTime|DateTimeInterface|string|null $date A date
0482 * @param DateTimeZone|string|null|false $timezone The target timezone, null to use the default, false to leave unchanged
0483 *
0484 * @return DateTime A DateTime instance
0485 */
0486 function twig_date_converter(Twig_Environment $env, $date = null, $timezone = null)
0487 {
0488 // determine the timezone
0489 if (false !== $timezone) {
0490 if (null === $timezone) {
0491 $timezone = $env->getExtension('core')->getTimezone();
0492 } elseif (!$timezone instanceof DateTimeZone) {
0493 $timezone = new DateTimeZone($timezone);
0494 }
0495 }
0496
0497 // immutable dates
0498 if ($date instanceof DateTimeImmutable) {
0499 return false !== $timezone ? $date->setTimezone($timezone) : $date;
0500 }
0501
0502 if ($date instanceof DateTime || $date instanceof DateTimeInterface) {
0503 $date = clone $date;
0504 if (false !== $timezone) {
0505 $date->setTimezone($timezone);
0506 }
0507
0508 return $date;
0509 }
0510
0511 if (null === $date || 'now' === $date) {
0512 return new DateTime($date, false !== $timezone ? $timezone : $env->getExtension('core')->getTimezone());
0513 }
0514
0515 $asString = (string) $date;
0516 if (ctype_digit($asString) || (!empty($asString) && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) {
0517 $date = new DateTime('@'.$date);
0518 } else {
0519 $date = new DateTime($date, $env->getExtension('core')->getTimezone());
0520 }
0521
0522 if (false !== $timezone) {
0523 $date->setTimezone($timezone);
0524 }
0525
0526 return $date;
0527 }
0528
0529 /**
0530 * Replaces strings within a string.
0531 *
0532 * @param string $str String to replace in
0533 * @param array|Traversable $from Replace values
0534 * @param string|null $to Replace to, deprecated (@see http://php.net/manual/en/function.strtr.php)
0535 *
0536 * @return string
0537 */
0538 function twig_replace_filter($str, $from, $to = null)
0539 {
0540 if ($from instanceof Traversable) {
0541 $from = iterator_to_array($from);
0542 } elseif (is_string($from) && is_string($to)) {
0543 @trigger_error('Using "replace" with character by character replacement is deprecated since version 1.22 and will be removed in Twig 2.0', E_USER_DEPRECATED);
0544
0545 return strtr($str, $from, $to);
0546 } elseif (!is_array($from)) {
0547 throw new Twig_Error_Runtime(sprintf('The "replace" filter expects an array or "Traversable" as replace values, got "%s".',is_object($from) ? get_class($from) : gettype($from)));
0548 }
0549
0550 return strtr($str, $from);
0551 }
0552
0553 /**
0554 * Rounds a number.
0555 *
0556 * @param int|float $value The value to round
0557 * @param int|float $precision The rounding precision
0558 * @param string $method The method to use for rounding
0559 *
0560 * @return int|float The rounded number
0561 */
0562 function twig_round($value, $precision = 0, $method = 'common')
0563 {
0564 if ('common' == $method) {
0565 return round($value, $precision);
0566 }
0567
0568 if ('ceil' != $method && 'floor' != $method) {
0569 throw new Twig_Error_Runtime('The round filter only supports the "common", "ceil", and "floor" methods.');
0570 }
0571
0572 return $method($value * pow(10, $precision)) / pow(10, $precision);
0573 }
0574
0575 /**
0576 * Number format filter.
0577 *
0578 * All of the formatting options can be left null, in that case the defaults will
0579 * be used. Supplying any of the parameters will override the defaults set in the
0580 * environment object.
0581 *
0582 * @param Twig_Environment $env A Twig_Environment instance
0583 * @param mixed $number A float/int/string of the number to format
0584 * @param int $decimal The number of decimal points to display.
0585 * @param string $decimalPoint The character(s) to use for the decimal point.
0586 * @param string $thousandSep The character(s) to use for the thousands separator.
0587 *
0588 * @return string The formatted number
0589 */
0590 function twig_number_format_filter(Twig_Environment $env, $number, $decimal = null, $decimalPoint = null, $thousandSep = null)
0591 {
0592 $defaults = $env->getExtension('core')->getNumberFormat();
0593 if (null === $decimal) {
0594 $decimal = $defaults[0];
0595 }
0596
0597 if (null === $decimalPoint) {
0598 $decimalPoint = $defaults[1];
0599 }
0600
0601 if (null === $thousandSep) {
0602 $thousandSep = $defaults[2];
0603 }
0604
0605 return number_format((float) $number, $decimal, $decimalPoint, $thousandSep);
0606 }
0607
0608 /**
0609 * URL encodes (RFC 3986) a string as a path segment or an array as a query string.
0610 *
0611 * @param string|array $url A URL or an array of query parameters
0612 *
0613 * @return string The URL encoded value
0614 */
0615 function twig_urlencode_filter($url)
0616 {
0617 if (is_array($url)) {
0618 if (defined('PHP_QUERY_RFC3986')) {
0619 return http_build_query($url, '', '&', PHP_QUERY_RFC3986);
0620 }
0621
0622 return http_build_query($url, '', '&');
0623 }
0624
0625 return rawurlencode($url);
0626 }
0627
0628 if (PHP_VERSION_ID < 50300) {
0629 /**
0630 * JSON encodes a variable.
0631 *
0632 * @param mixed $value The value to encode.
0633 * @param int $options Not used on PHP 5.2.x
0634 *
0635 * @return mixed The JSON encoded value
0636 */
0637 function twig_jsonencode_filter($value, $options = 0)
0638 {
0639 if ($value instanceof Twig_Markup) {
0640 $value = (string) $value;
0641 } elseif (is_array($value)) {
0642 array_walk_recursive($value, '_twig_markup2string');
0643 }
0644
0645 return json_encode($value);
0646 }
0647 } else {
0648 /**
0649 * JSON encodes a variable.
0650 *
0651 * @param mixed $value The value to encode.
0652 * @param int $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
0653 *
0654 * @return mixed The JSON encoded value
0655 */
0656 function twig_jsonencode_filter($value, $options = 0)
0657 {
0658 if ($value instanceof Twig_Markup) {
0659 $value = (string) $value;
0660 } elseif (is_array($value)) {
0661 array_walk_recursive($value, '_twig_markup2string');
0662 }
0663
0664 return json_encode($value, $options);
0665 }
0666 }
0667
0668 function _twig_markup2string(&$value)
0669 {
0670 if ($value instanceof Twig_Markup) {
0671 $value = (string) $value;
0672 }
0673 }
0674
0675 /**
0676 * Merges an array with another one.
0677 *
0678 * <pre>
0679 * {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %}
0680 *
0681 * {% set items = items|merge({ 'peugeot': 'car' }) %}
0682 *
0683 * {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car' } #}
0684 * </pre>
0685 *
0686 * @param array|Traversable $arr1 An array
0687 * @param array|Traversable $arr2 An array
0688 *
0689 * @return array The merged array
0690 */
0691 function twig_array_merge($arr1, $arr2)
0692 {
0693 if ($arr1 instanceof Traversable) {
0694 $arr1 = iterator_to_array($arr1);
0695 } elseif (!is_array($arr1)) {
0696 throw new Twig_Error_Runtime(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as first argument.', gettype($arr1)));
0697 }
0698
0699 if ($arr2 instanceof Traversable) {
0700 $arr2 = iterator_to_array($arr2);
0701 } elseif (!is_array($arr2)) {
0702 throw new Twig_Error_Runtime(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as second argument.', gettype($arr2)));
0703 }
0704
0705 return array_merge($arr1, $arr2);
0706 }
0707
0708 /**
0709 * Slices a variable.
0710 *
0711 * @param Twig_Environment $env A Twig_Environment instance
0712 * @param mixed $item A variable
0713 * @param int $start Start of the slice
0714 * @param int $length Size of the slice
0715 * @param bool $preserveKeys Whether to preserve key or not (when the input is an array)
0716 *
0717 * @return mixed The sliced variable
0718 */
0719 function twig_slice(Twig_Environment $env, $item, $start, $length = null, $preserveKeys = false)
0720 {
0721 if ($item instanceof Traversable) {
0722 if ($item instanceof IteratorAggregate) {
0723 $item = $item->getIterator();
0724 }
0725
0726 if ($start >= 0 && $length >= 0 && $item instanceof Iterator) {
0727 try {
0728 return iterator_to_array(new LimitIterator($item, $start, $length === null ? -1 : $length), $preserveKeys);
0729 } catch (OutOfBoundsException $exception) {
0730 return array();
0731 }
0732 }
0733
0734 $item = iterator_to_array($item, $preserveKeys);
0735 }
0736
0737 if (is_array($item)) {
0738 return array_slice($item, $start, $length, $preserveKeys);
0739 }
0740
0741 $item = (string) $item;
0742
0743 if (function_exists('mb_get_info') && null !== $charset = $env->getCharset()) {
0744 return (string) mb_substr($item, $start, null === $length ? mb_strlen($item, $charset) - $start : $length, $charset);
0745 }
0746
0747 return (string) (null === $length ? substr($item, $start) : substr($item, $start, $length));
0748 }
0749
0750 /**
0751 * Returns the first element of the item.
0752 *
0753 * @param Twig_Environment $env A Twig_Environment instance
0754 * @param mixed $item A variable
0755 *
0756 * @return mixed The first element of the item
0757 */
0758 function twig_first(Twig_Environment $env, $item)
0759 {
0760 $elements = twig_slice($env, $item, 0, 1, false);
0761
0762 return is_string($elements) ? $elements : current($elements);
0763 }
0764
0765 /**
0766 * Returns the last element of the item.
0767 *
0768 * @param Twig_Environment $env A Twig_Environment instance
0769 * @param mixed $item A variable
0770 *
0771 * @return mixed The last element of the item
0772 */
0773 function twig_last(Twig_Environment $env, $item)
0774 {
0775 $elements = twig_slice($env, $item, -1, 1, false);
0776
0777 return is_string($elements) ? $elements : current($elements);
0778 }
0779
0780 /**
0781 * Joins the values to a string.
0782 *
0783 * The separator between elements is an empty string per default, you can define it with the optional parameter.
0784 *
0785 * <pre>
0786 * {{ [1, 2, 3]|join('|') }}
0787 * {# returns 1|2|3 #}
0788 *
0789 * {{ [1, 2, 3]|join }}
0790 * {# returns 123 #}
0791 * </pre>
0792 *
0793 * @param array $value An array
0794 * @param string $glue The separator
0795 *
0796 * @return string The concatenated string
0797 */
0798 function twig_join_filter($value, $glue = '')
0799 {
0800 if ($value instanceof Traversable) {
0801 $value = iterator_to_array($value, false);
0802 }
0803
0804 return implode($glue, (array) $value);
0805 }
0806
0807 /**
0808 * Splits the string into an array.
0809 *
0810 * <pre>
0811 * {{ "one,two,three"|split(',') }}
0812 * {# returns [one, two, three] #}
0813 *
0814 * {{ "one,two,three,four,five"|split(',', 3) }}
0815 * {# returns [one, two, "three,four,five"] #}
0816 *
0817 * {{ "123"|split('') }}
0818 * {# returns [1, 2, 3] #}
0819 *
0820 * {{ "aabbcc"|split('', 2) }}
0821 * {# returns [aa, bb, cc] #}
0822 * </pre>
0823 *
0824 * @param Twig_Environment $env A Twig_Environment instance
0825 * @param string $value A string
0826 * @param string $delimiter The delimiter
0827 * @param int $limit The limit
0828 *
0829 * @return array The split string as an array
0830 */
0831 function twig_split_filter(Twig_Environment $env, $value, $delimiter, $limit = null)
0832 {
0833 if (!empty($delimiter)) {
0834 return null === $limit ? explode($delimiter, $value) : explode($delimiter, $value, $limit);
0835 }
0836
0837 if (!function_exists('mb_get_info') || null === $charset = $env->getCharset()) {
0838 return str_split($value, null === $limit ? 1 : $limit);
0839 }
0840
0841 if ($limit <= 1) {
0842 return preg_split('/(?<!^)(?!$)/u', $value);
0843 }
0844
0845 $length = mb_strlen($value, $charset);
0846 if ($length < $limit) {
0847 return array($value);
0848 }
0849
0850 $r = array();
0851 for ($i = 0; $i < $length; $i += $limit) {
0852 $r[] = mb_substr($value, $i, $limit, $charset);
0853 }
0854
0855 return $r;
0856 }
0857
0858 // The '_default' filter is used internally to avoid using the ternary operator
0859 // which costs a lot for big contexts (before PHP 5.4). So, on average,
0860 // a function call is cheaper.
0861 /**
0862 * @internal
0863 */
0864 function _twig_default_filter($value, $default = '')
0865 {
0866 if (twig_test_empty($value)) {
0867 return $default;
0868 }
0869
0870 return $value;
0871 }
0872
0873 /**
0874 * Returns the keys for the given array.
0875 *
0876 * It is useful when you want to iterate over the keys of an array:
0877 *
0878 * <pre>
0879 * {% for key in array|keys %}
0880 * {# ... #}
0881 * {% endfor %}
0882 * </pre>
0883 *
0884 * @param array $array An array
0885 *
0886 * @return array The keys
0887 */
0888 function twig_get_array_keys_filter($array)
0889 {
0890 if ($array instanceof Traversable) {
0891 return array_keys(iterator_to_array($array));
0892 }
0893
0894 if (!is_array($array)) {
0895 return array();
0896 }
0897
0898 return array_keys($array);
0899 }
0900
0901 /**
0902 * Reverses a variable.
0903 *
0904 * @param Twig_Environment $env A Twig_Environment instance
0905 * @param array|Traversable|string $item An array, a Traversable instance, or a string
0906 * @param bool $preserveKeys Whether to preserve key or not
0907 *
0908 * @return mixed The reversed input
0909 */
0910 function twig_reverse_filter(Twig_Environment $env, $item, $preserveKeys = false)
0911 {
0912 if ($item instanceof Traversable) {
0913 return array_reverse(iterator_to_array($item), $preserveKeys);
0914 }
0915
0916 if (is_array($item)) {
0917 return array_reverse($item, $preserveKeys);
0918 }
0919
0920 if (null !== $charset = $env->getCharset()) {
0921 $string = (string) $item;
0922
0923 if ('UTF-8' !== $charset) {
0924 $item = twig_convert_encoding($string, 'UTF-8', $charset);
0925 }
0926
0927 preg_match_all('/./us', $item, $matches);
0928
0929 $string = implode('', array_reverse($matches[0]));
0930
0931 if ('UTF-8' !== $charset) {
0932 $string = twig_convert_encoding($string, $charset, 'UTF-8');
0933 }
0934
0935 return $string;
0936 }
0937
0938 return strrev((string) $item);
0939 }
0940
0941 /**
0942 * Sorts an array.
0943 *
0944 * @param array|Traversable $array
0945 *
0946 * @return array
0947 */
0948 function twig_sort_filter($array)
0949 {
0950 if ($array instanceof Traversable) {
0951 $array = iterator_to_array($array);
0952 } elseif (!is_array($array)) {
0953 throw new Twig_Error_Runtime(sprintf('The sort filter only works with arrays or "Traversable", got "%s".', gettype($array)));
0954 }
0955
0956 asort($array);
0957
0958 return $array;
0959 }
0960
0961 /**
0962 * @internal
0963 */
0964 function twig_in_filter($value, $compare)
0965 {
0966 if (is_array($compare)) {
0967 return in_array($value, $compare, is_object($value) || is_resource($value));
0968 } elseif (is_string($compare) && (is_string($value) || is_int($value) || is_float($value))) {
0969 return '' === $value || false !== strpos($compare, (string) $value);
0970 } elseif ($compare instanceof Traversable) {
0971 return in_array($value, iterator_to_array($compare, false), is_object($value) || is_resource($value));
0972 }
0973
0974 return false;
0975 }
0976
0977 /**
0978 * Escapes a string.
0979 *
0980 * @param Twig_Environment $env A Twig_Environment instance
0981 * @param string $string The value to be escaped
0982 * @param string $strategy The escaping strategy
0983 * @param string $charset The charset
0984 * @param bool $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false)
0985 *
0986 * @return string
0987 */
0988 function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false)
0989 {
0990 if ($autoescape && $string instanceof Twig_Markup) {
0991 return $string;
0992 }
0993
0994 if (!is_string($string)) {
0995 if (is_object($string) && method_exists($string, '__toString')) {
0996 $string = (string) $string;
0997 } elseif (in_array($strategy, array('html', 'js', 'css', 'html_attr', 'url'))) {
0998 return $string;
0999 }
1000 }
1001
1002 if (null === $charset) {
1003 $charset = $env->getCharset();
1004 }
1005
1006 switch ($strategy) {
1007 case 'html':
1008 // see http://php.net/htmlspecialchars
1009
1010 // Using a static variable to avoid initializing the array
1011 // each time the function is called. Moving the declaration on the
1012 // top of the function slow downs other escaping strategies.
1013 static $htmlspecialcharsCharsets;
1014
1015 if (null === $htmlspecialcharsCharsets) {
1016 if (defined('HHVM_VERSION')) {
1017 $htmlspecialcharsCharsets = array('utf-8' => true, 'UTF-8' => true);
1018 } else {
1019 $htmlspecialcharsCharsets = array(
1020 'ISO-8859-1' => true, 'ISO8859-1' => true,
1021 'ISO-8859-15' => true, 'ISO8859-15' => true,
1022 'utf-8' => true, 'UTF-8' => true,
1023 'CP866' => true, 'IBM866' => true, '866' => true,
1024 'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true,
1025 '1251' => true,
1026 'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true,
1027 'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true,
1028 'BIG5' => true, '950' => true,
1029 'GB2312' => true, '936' => true,
1030 'BIG5-HKSCS' => true,
1031 'SHIFT_JIS' => true, 'SJIS' => true, '932' => true,
1032 'EUC-JP' => true, 'EUCJP' => true,
1033 'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true,
1034 );
1035 }
1036 }
1037
1038 if (isset($htmlspecialcharsCharsets[$charset])) {
1039 return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
1040 }
1041
1042 if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) {
1043 // cache the lowercase variant for future iterations
1044 $htmlspecialcharsCharsets[$charset] = true;
1045
1046 return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
1047 }
1048
1049 $string = twig_convert_encoding($string, 'UTF-8', $charset);
1050 $string = htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
1051
1052 return twig_convert_encoding($string, $charset, 'UTF-8');
1053
1054 case 'js':
1055 // escape all non-alphanumeric characters
1056 // into their \xHH or \uHHHH representations
1057 if ('UTF-8' !== $charset) {
1058 $string = twig_convert_encoding($string, 'UTF-8', $charset);
1059 }
1060
1061 if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) {
1062 throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
1063 }
1064
1065 $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', '_twig_escape_js_callback', $string);
1066
1067 if ('UTF-8' !== $charset) {
1068 $string = twig_convert_encoding($string, $charset, 'UTF-8');
1069 }
1070
1071 return $string;
1072
1073 case 'css':
1074 if ('UTF-8' !== $charset) {
1075 $string = twig_convert_encoding($string, 'UTF-8', $charset);
1076 }
1077
1078 if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) {
1079 throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
1080 }
1081
1082 $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', '_twig_escape_css_callback', $string);
1083
1084 if ('UTF-8' !== $charset) {
1085 $string = twig_convert_encoding($string, $charset, 'UTF-8');
1086 }
1087
1088 return $string;
1089
1090 case 'html_attr':
1091 if ('UTF-8' !== $charset) {
1092 $string = twig_convert_encoding($string, 'UTF-8', $charset);
1093 }
1094
1095 if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) {
1096 throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
1097 }
1098
1099 $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', '_twig_escape_html_attr_callback', $string);
1100
1101 if ('UTF-8' !== $charset) {
1102 $string = twig_convert_encoding($string, $charset, 'UTF-8');
1103 }
1104
1105 return $string;
1106
1107 case 'url':
1108 if (PHP_VERSION_ID < 50300) {
1109 return str_replace('%7E', '~', rawurlencode($string));
1110 }
1111
1112 return rawurlencode($string);
1113
1114 default:
1115 static $escapers;
1116
1117 if (null === $escapers) {
1118 $escapers = $env->getExtension('core')->getEscapers();
1119 }
1120
1121 if (isset($escapers[$strategy])) {
1122 return call_user_func($escapers[$strategy], $env, $string, $charset);
1123 }
1124
1125 $validStrategies = implode(', ', array_merge(array('html', 'js', 'url', 'css', 'html_attr'), array_keys($escapers)));
1126
1127 throw new Twig_Error_Runtime(sprintf('Invalid escaping strategy "%s" (valid ones: %s).', $strategy, $validStrategies));
1128 }
1129 }
1130
1131 /**
1132 * @internal
1133 */
1134 function twig_escape_filter_is_safe(Twig_Node $filterArgs)
1135 {
1136 foreach ($filterArgs as $arg) {
1137 if ($arg instanceof Twig_Node_Expression_Constant) {
1138 return array($arg->getAttribute('value'));
1139 }
1140
1141 return array();
1142 }
1143
1144 return array('html');
1145 }
1146
1147 if (function_exists('mb_convert_encoding')) {
1148 function twig_convert_encoding($string, $to, $from)
1149 {
1150 return mb_convert_encoding($string, $to, $from);
1151 }
1152 } elseif (function_exists('iconv')) {
1153 function twig_convert_encoding($string, $to, $from)
1154 {
1155 return iconv($from, $to, $string);
1156 }
1157 } else {
1158 function twig_convert_encoding($string, $to, $from)
1159 {
1160 throw new Twig_Error_Runtime('No suitable convert encoding function (use UTF-8 as your encoding or install the iconv or mbstring extension).');
1161 }
1162 }
1163
1164 function _twig_escape_js_callback($matches)
1165 {
1166 $char = $matches[0];
1167
1168 // \xHH
1169 if (!isset($char[1])) {
1170 return '\\x'.strtoupper(substr('00'.bin2hex($char), -2));
1171 }
1172
1173 // \uHHHH
1174 $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
1175
1176 return '\\u'.strtoupper(substr('0000'.bin2hex($char), -4));
1177 }
1178
1179 function _twig_escape_css_callback($matches)
1180 {
1181 $char = $matches[0];
1182
1183 // \xHH
1184 if (!isset($char[1])) {
1185 $hex = ltrim(strtoupper(bin2hex($char)), '0');
1186 if (0 === strlen($hex)) {
1187 $hex = '0';
1188 }
1189
1190 return '\\'.$hex.' ';
1191 }
1192
1193 // \uHHHH
1194 $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
1195
1196 return '\\'.ltrim(strtoupper(bin2hex($char)), '0').' ';
1197 }
1198
1199 /**
1200 * This function is adapted from code coming from Zend Framework.
1201 *
1202 * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
1203 * @license http://framework.zend.com/license/new-bsd New BSD License
1204 */
1205 function _twig_escape_html_attr_callback($matches)
1206 {
1207 /*
1208 * While HTML supports far more named entities, the lowest common denominator
1209 * has become HTML5's XML Serialisation which is restricted to the those named
1210 * entities that XML supports. Using HTML entities would result in this error:
1211 * XML Parsing Error: undefined entity
1212 */
1213 static $entityMap = array(
1214 34 => 'quot', /* quotation mark */
1215 38 => 'amp', /* ampersand */
1216 60 => 'lt', /* less-than sign */
1217 62 => 'gt', /* greater-than sign */
1218 );
1219
1220 $chr = $matches[0];
1221 $ord = ord($chr);
1222
1223 /*
1224 * The following replaces characters undefined in HTML with the
1225 * hex entity for the Unicode replacement character.
1226 */
1227 if (($ord <= 0x1f && $chr != "\t" && $chr != "\n" && $chr != "\r") || ($ord >= 0x7f && $ord <= 0x9f)) {
1228 return '�';
1229 }
1230
1231 /*
1232 * Check if the current character to escape has a name entity we should
1233 * replace it with while grabbing the hex value of the character.
1234 */
1235 if (strlen($chr) == 1) {
1236 $hex = strtoupper(substr('00'.bin2hex($chr), -2));
1237 } else {
1238 $chr = twig_convert_encoding($chr, 'UTF-16BE', 'UTF-8');
1239 $hex = strtoupper(substr('0000'.bin2hex($chr), -4));
1240 }
1241
1242 $int = hexdec($hex);
1243 if (array_key_exists($int, $entityMap)) {
1244 return sprintf('&%s;', $entityMap[$int]);
1245 }
1246
1247 /*
1248 * Per OWASP recommendations, we'll use hex entities for any other
1249 * characters where a named entity does not exist.
1250 */
1251 return sprintf('&#x%s;', $hex);
1252 }
1253
1254 // add multibyte extensions if possible
1255 if (function_exists('mb_get_info')) {
1256 /**
1257 * Returns the length of a variable.
1258 *
1259 * @param Twig_Environment $env A Twig_Environment instance
1260 * @param mixed $thing A variable
1261 *
1262 * @return int The length of the value
1263 */
1264 function twig_length_filter(Twig_Environment $env, $thing)
1265 {
1266 return is_scalar($thing) ? mb_strlen($thing, $env->getCharset()) : count($thing);
1267 }
1268
1269 /**
1270 * Converts a string to uppercase.
1271 *
1272 * @param Twig_Environment $env A Twig_Environment instance
1273 * @param string $string A string
1274 *
1275 * @return string The uppercased string
1276 */
1277 function twig_upper_filter(Twig_Environment $env, $string)
1278 {
1279 if (null !== $charset = $env->getCharset()) {
1280 return mb_strtoupper($string, $charset);
1281 }
1282
1283 return strtoupper($string);
1284 }
1285
1286 /**
1287 * Converts a string to lowercase.
1288 *
1289 * @param Twig_Environment $env A Twig_Environment instance
1290 * @param string $string A string
1291 *
1292 * @return string The lowercased string
1293 */
1294 function twig_lower_filter(Twig_Environment $env, $string)
1295 {
1296 if (null !== $charset = $env->getCharset()) {
1297 return mb_strtolower($string, $charset);
1298 }
1299
1300 return strtolower($string);
1301 }
1302
1303 /**
1304 * Returns a titlecased string.
1305 *
1306 * @param Twig_Environment $env A Twig_Environment instance
1307 * @param string $string A string
1308 *
1309 * @return string The titlecased string
1310 */
1311 function twig_title_string_filter(Twig_Environment $env, $string)
1312 {
1313 if (null !== $charset = $env->getCharset()) {
1314 return mb_convert_case($string, MB_CASE_TITLE, $charset);
1315 }
1316
1317 return ucwords(strtolower($string));
1318 }
1319
1320 /**
1321 * Returns a capitalized string.
1322 *
1323 * @param Twig_Environment $env A Twig_Environment instance
1324 * @param string $string A string
1325 *
1326 * @return string The capitalized string
1327 */
1328 function twig_capitalize_string_filter(Twig_Environment $env, $string)
1329 {
1330 if (null !== $charset = $env->getCharset()) {
1331 return mb_strtoupper(mb_substr($string, 0, 1, $charset), $charset).mb_strtolower(mb_substr($string, 1, mb_strlen($string, $charset), $charset), $charset);
1332 }
1333
1334 return ucfirst(strtolower($string));
1335 }
1336 }
1337 // and byte fallback
1338 else {
1339 /**
1340 * Returns the length of a variable.
1341 *
1342 * @param Twig_Environment $env A Twig_Environment instance
1343 * @param mixed $thing A variable
1344 *
1345 * @return int The length of the value
1346 */
1347 function twig_length_filter(Twig_Environment $env, $thing)
1348 {
1349 return is_scalar($thing) ? strlen($thing) : count($thing);
1350 }
1351
1352 /**
1353 * Returns a titlecased string.
1354 *
1355 * @param Twig_Environment $env A Twig_Environment instance
1356 * @param string $string A string
1357 *
1358 * @return string The titlecased string
1359 */
1360 function twig_title_string_filter(Twig_Environment $env, $string)
1361 {
1362 return ucwords(strtolower($string));
1363 }
1364
1365 /**
1366 * Returns a capitalized string.
1367 *
1368 * @param Twig_Environment $env A Twig_Environment instance
1369 * @param string $string A string
1370 *
1371 * @return string The capitalized string
1372 */
1373 function twig_capitalize_string_filter(Twig_Environment $env, $string)
1374 {
1375 return ucfirst(strtolower($string));
1376 }
1377 }
1378
1379 /**
1380 * @internal
1381 */
1382 function twig_ensure_traversable($seq)
1383 {
1384 if ($seq instanceof Traversable || is_array($seq)) {
1385 return $seq;
1386 }
1387
1388 return array();
1389 }
1390
1391 /**
1392 * Checks if a variable is empty.
1393 *
1394 * <pre>
1395 * {# evaluates to true if the foo variable is null, false, or the empty string #}
1396 * {% if foo is empty %}
1397 * {# ... #}
1398 * {% endif %}
1399 * </pre>
1400 *
1401 * @param mixed $value A variable
1402 *
1403 * @return bool true if the value is empty, false otherwise
1404 */
1405 function twig_test_empty($value)
1406 {
1407 if ($value instanceof Countable) {
1408 return 0 == count($value);
1409 }
1410
1411 return '' === $value || false === $value || null === $value || array() === $value;
1412 }
1413
1414 /**
1415 * Checks if a variable is traversable.
1416 *
1417 * <pre>
1418 * {# evaluates to true if the foo variable is an array or a traversable object #}
1419 * {% if foo is traversable %}
1420 * {# ... #}
1421 * {% endif %}
1422 * </pre>
1423 *
1424 * @param mixed $value A variable
1425 *
1426 * @return bool true if the value is traversable
1427 */
1428 function twig_test_iterable($value)
1429 {
1430 return $value instanceof Traversable || is_array($value);
1431 }
1432
1433 /**
1434 * Renders a template.
1435 *
1436 * @param Twig_Environment $env
1437 * @param array $context
1438 * @param string|array $template The template to render or an array of templates to try consecutively
1439 * @param array $variables The variables to pass to the template
1440 * @param bool $withContext
1441 * @param bool $ignoreMissing Whether to ignore missing templates or not
1442 * @param bool $sandboxed Whether to sandbox the template or not
1443 *
1444 * @return string The rendered template
1445 */
1446 function twig_include(Twig_Environment $env, $context, $template, $variables = array(), $withContext = true, $ignoreMissing = false, $sandboxed = false)
1447 {
1448 $alreadySandboxed = false;
1449 $sandbox = null;
1450 if ($withContext) {
1451 $variables = array_merge($context, $variables);
1452 }
1453
1454 if ($isSandboxed = $sandboxed && $env->hasExtension('sandbox')) {
1455 $sandbox = $env->getExtension('sandbox');
1456 if (!$alreadySandboxed = $sandbox->isSandboxed()) {
1457 $sandbox->enableSandbox();
1458 }
1459 }
1460
1461 $result = null;
1462 try {
1463 $result = $env->resolveTemplate($template)->render($variables);
1464 } catch (Twig_Error_Loader $e) {
1465 if (!$ignoreMissing) {
1466 if ($isSandboxed && !$alreadySandboxed) {
1467 $sandbox->disableSandbox();
1468 }
1469
1470 throw $e;
1471 }
1472 }
1473
1474 if ($isSandboxed && !$alreadySandboxed) {
1475 $sandbox->disableSandbox();
1476 }
1477
1478 return $result;
1479 }
1480
1481 /**
1482 * Returns a template content without rendering it.
1483 *
1484 * @param Twig_Environment $env
1485 * @param string $name The template name
1486 * @param bool $ignoreMissing Whether to ignore missing templates or not
1487 *
1488 * @return string The template source
1489 */
1490 function twig_source(Twig_Environment $env, $name, $ignoreMissing = false)
1491 {
1492 try {
1493 return $env->getLoader()->getSource($name);
1494 } catch (Twig_Error_Loader $e) {
1495 if (!$ignoreMissing) {
1496 throw $e;
1497 }
1498 }
1499 }
1500
1501 /**
1502 * Provides the ability to get constants from instances as well as class/global constants.
1503 *
1504 * @param string $constant The name of the constant
1505 * @param null|object $object The object to get the constant from
1506 *
1507 * @return string
1508 */
1509 function twig_constant($constant, $object = null)
1510 {
1511 if (null !== $object) {
1512 $constant = get_class($object).'::'.$constant;
1513 }
1514
1515 return constant($constant);
1516 }
1517
1518 /**
1519 * Batches item.
1520 *
1521 * @param array $items An array of items
1522 * @param int $size The size of the batch
1523 * @param mixed $fill A value used to fill missing items
1524 *
1525 * @return array
1526 */
1527 function twig_array_batch($items, $size, $fill = null)
1528 {
1529 if ($items instanceof Traversable) {
1530 $items = iterator_to_array($items, false);
1531 }
1532
1533 $size = ceil($size);
1534
1535 $result = array_chunk($items, $size, true);
1536
1537 if (null !== $fill && !empty($result)) {
1538 $last = count($result) - 1;
1539 if ($fillCount = $size - count($result[$last])) {
1540 $result[$last] = array_merge(
1541 $result[$last],
1542 array_fill(0, $fillCount, $fill)
1543 );
1544 }
1545 }
1546
1547 return $result;
1548 }
1549