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.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

Core.php

Zuletzt modifiziert: 09.10.2024, 12:58 - Dateigröße: 51.98 KiB


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 '&#xFFFD;';
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