Verzeichnisstruktur phpBB-3.1.0


Veröffentlicht
27.10.2014

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: 44.33 KiB


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