[ Index ]

PHP Cross Reference of phpBB-3.2.11-deutsch

title

Body

[close]

/vendor/twig/twig/src/Extension/ -> CoreExtension.php (source)

   1  <?php
   2  
   3  /*
   4   * This file is part of Twig.
   5   *
   6   * (c) Fabien Potencier
   7   *
   8   * For the full copyright and license information, please view the LICENSE
   9   * file that was distributed with this source code.
  10   */
  11  
  12  namespace Twig\Extension {
  13  use Twig\ExpressionParser;
  14  use Twig\TokenParser\ApplyTokenParser;
  15  use Twig\TokenParser\BlockTokenParser;
  16  use Twig\TokenParser\DeprecatedTokenParser;
  17  use Twig\TokenParser\DoTokenParser;
  18  use Twig\TokenParser\EmbedTokenParser;
  19  use Twig\TokenParser\ExtendsTokenParser;
  20  use Twig\TokenParser\FilterTokenParser;
  21  use Twig\TokenParser\FlushTokenParser;
  22  use Twig\TokenParser\ForTokenParser;
  23  use Twig\TokenParser\FromTokenParser;
  24  use Twig\TokenParser\IfTokenParser;
  25  use Twig\TokenParser\ImportTokenParser;
  26  use Twig\TokenParser\IncludeTokenParser;
  27  use Twig\TokenParser\MacroTokenParser;
  28  use Twig\TokenParser\SetTokenParser;
  29  use Twig\TokenParser\SpacelessTokenParser;
  30  use Twig\TokenParser\UseTokenParser;
  31  use Twig\TokenParser\WithTokenParser;
  32  use Twig\TwigFilter;
  33  use Twig\TwigFunction;
  34  use Twig\TwigTest;
  35  
  36  /**
  37   * @final
  38   */
  39  class CoreExtension extends AbstractExtension
  40  {
  41      protected $dateFormats = ['F j, Y H:i', '%d days'];
  42      protected $numberFormat = [0, '.', ','];
  43      protected $timezone = null;
  44      protected $escapers = [];
  45  
  46      /**
  47       * Defines a new escaper to be used via the escape filter.
  48       *
  49       * @param string   $strategy The strategy name that should be used as a strategy in the escape call
  50       * @param callable $callable A valid PHP callable
  51       */
  52      public function setEscaper($strategy, $callable)
  53      {
  54          $this->escapers[$strategy] = $callable;
  55      }
  56  
  57      /**
  58       * Gets all defined escapers.
  59       *
  60       * @return array An array of escapers
  61       */
  62      public function getEscapers()
  63      {
  64          return $this->escapers;
  65      }
  66  
  67      /**
  68       * Sets the default format to be used by the date filter.
  69       *
  70       * @param string $format             The default date format string
  71       * @param string $dateIntervalFormat The default date interval format string
  72       */
  73      public function setDateFormat($format = null, $dateIntervalFormat = null)
  74      {
  75          if (null !== $format) {
  76              $this->dateFormats[0] = $format;
  77          }
  78  
  79          if (null !== $dateIntervalFormat) {
  80              $this->dateFormats[1] = $dateIntervalFormat;
  81          }
  82      }
  83  
  84      /**
  85       * Gets the default format to be used by the date filter.
  86       *
  87       * @return array The default date format string and the default date interval format string
  88       */
  89      public function getDateFormat()
  90      {
  91          return $this->dateFormats;
  92      }
  93  
  94      /**
  95       * Sets the default timezone to be used by the date filter.
  96       *
  97       * @param \DateTimeZone|string $timezone The default timezone string or a \DateTimeZone object
  98       */
  99      public function setTimezone($timezone)
 100      {
 101          $this->timezone = $timezone instanceof \DateTimeZone ? $timezone : new \DateTimeZone($timezone);
 102      }
 103  
 104      /**
 105       * Gets the default timezone to be used by the date filter.
 106       *
 107       * @return \DateTimeZone The default timezone currently in use
 108       */
 109      public function getTimezone()
 110      {
 111          if (null === $this->timezone) {
 112              $this->timezone = new \DateTimeZone(date_default_timezone_get());
 113          }
 114  
 115          return $this->timezone;
 116      }
 117  
 118      /**
 119       * Sets the default format to be used by the number_format filter.
 120       *
 121       * @param int    $decimal      the number of decimal places to use
 122       * @param string $decimalPoint the character(s) to use for the decimal point
 123       * @param string $thousandSep  the character(s) to use for the thousands separator
 124       */
 125      public function setNumberFormat($decimal, $decimalPoint, $thousandSep)
 126      {
 127          $this->numberFormat = [$decimal, $decimalPoint, $thousandSep];
 128      }
 129  
 130      /**
 131       * Get the default format used by the number_format filter.
 132       *
 133       * @return array The arguments for number_format()
 134       */
 135      public function getNumberFormat()
 136      {
 137          return $this->numberFormat;
 138      }
 139  
 140      public function getTokenParsers()
 141      {
 142          return [
 143              new ApplyTokenParser(),
 144              new ForTokenParser(),
 145              new IfTokenParser(),
 146              new ExtendsTokenParser(),
 147              new IncludeTokenParser(),
 148              new BlockTokenParser(),
 149              new UseTokenParser(),
 150              new FilterTokenParser(),
 151              new MacroTokenParser(),
 152              new ImportTokenParser(),
 153              new FromTokenParser(),
 154              new SetTokenParser(),
 155              new SpacelessTokenParser(),
 156              new FlushTokenParser(),
 157              new DoTokenParser(),
 158              new EmbedTokenParser(),
 159              new WithTokenParser(),
 160              new DeprecatedTokenParser(),
 161          ];
 162      }
 163  
 164      public function getFilters()
 165      {
 166          $filters = [
 167              // formatting filters
 168              new TwigFilter('date', 'twig_date_format_filter', ['needs_environment' => true]),
 169              new TwigFilter('date_modify', 'twig_date_modify_filter', ['needs_environment' => true]),
 170              new TwigFilter('format', 'sprintf'),
 171              new TwigFilter('replace', 'twig_replace_filter'),
 172              new TwigFilter('number_format', 'twig_number_format_filter', ['needs_environment' => true]),
 173              new TwigFilter('abs', 'abs'),
 174              new TwigFilter('round', 'twig_round'),
 175  
 176              // encoding
 177              new TwigFilter('url_encode', 'twig_urlencode_filter'),
 178              new TwigFilter('json_encode', 'twig_jsonencode_filter'),
 179              new TwigFilter('convert_encoding', 'twig_convert_encoding'),
 180  
 181              // string filters
 182              new TwigFilter('title', 'twig_title_string_filter', ['needs_environment' => true]),
 183              new TwigFilter('capitalize', 'twig_capitalize_string_filter', ['needs_environment' => true]),
 184              new TwigFilter('upper', 'strtoupper'),
 185              new TwigFilter('lower', 'strtolower'),
 186              new TwigFilter('striptags', 'strip_tags'),
 187              new TwigFilter('trim', 'twig_trim_filter'),
 188              new TwigFilter('nl2br', 'nl2br', ['pre_escape' => 'html', 'is_safe' => ['html']]),
 189              new TwigFilter('spaceless', 'twig_spaceless', ['is_safe' => ['html']]),
 190  
 191              // array helpers
 192              new TwigFilter('join', 'twig_join_filter'),
 193              new TwigFilter('split', 'twig_split_filter', ['needs_environment' => true]),
 194              new TwigFilter('sort', 'twig_sort_filter'),
 195              new TwigFilter('merge', 'twig_array_merge'),
 196              new TwigFilter('batch', 'twig_array_batch'),
 197              new TwigFilter('filter', 'twig_array_filter'),
 198              new TwigFilter('map', 'twig_array_map'),
 199              new TwigFilter('reduce', 'twig_array_reduce'),
 200  
 201              // string/array filters
 202              new TwigFilter('reverse', 'twig_reverse_filter', ['needs_environment' => true]),
 203              new TwigFilter('length', 'twig_length_filter', ['needs_environment' => true]),
 204              new TwigFilter('slice', 'twig_slice', ['needs_environment' => true]),
 205              new TwigFilter('first', 'twig_first', ['needs_environment' => true]),
 206              new TwigFilter('last', 'twig_last', ['needs_environment' => true]),
 207  
 208              // iteration and runtime
 209              new TwigFilter('default', '_twig_default_filter', ['node_class' => '\Twig\Node\Expression\Filter\DefaultFilter']),
 210              new TwigFilter('keys', 'twig_get_array_keys_filter'),
 211  
 212              // escaping
 213              new TwigFilter('escape', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']),
 214              new TwigFilter('e', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']),
 215          ];
 216  
 217          if (\function_exists('mb_get_info')) {
 218              $filters[] = new TwigFilter('upper', 'twig_upper_filter', ['needs_environment' => true]);
 219              $filters[] = new TwigFilter('lower', 'twig_lower_filter', ['needs_environment' => true]);
 220          }
 221  
 222          return $filters;
 223      }
 224  
 225      public function getFunctions()
 226      {
 227          return [
 228              new TwigFunction('max', 'max'),
 229              new TwigFunction('min', 'min'),
 230              new TwigFunction('range', 'range'),
 231              new TwigFunction('constant', 'twig_constant'),
 232              new TwigFunction('cycle', 'twig_cycle'),
 233              new TwigFunction('random', 'twig_random', ['needs_environment' => true]),
 234              new TwigFunction('date', 'twig_date_converter', ['needs_environment' => true]),
 235              new TwigFunction('include', 'twig_include', ['needs_environment' => true, 'needs_context' => true, 'is_safe' => ['all']]),
 236              new TwigFunction('source', 'twig_source', ['needs_environment' => true, 'is_safe' => ['all']]),
 237          ];
 238      }
 239  
 240      public function getTests()
 241      {
 242          return [
 243              new TwigTest('even', null, ['node_class' => '\Twig\Node\Expression\Test\EvenTest']),
 244              new TwigTest('odd', null, ['node_class' => '\Twig\Node\Expression\Test\OddTest']),
 245              new TwigTest('defined', null, ['node_class' => '\Twig\Node\Expression\Test\DefinedTest']),
 246              new TwigTest('sameas', null, ['node_class' => '\Twig\Node\Expression\Test\SameasTest', 'deprecated' => '1.21', 'alternative' => 'same as']),
 247              new TwigTest('same as', null, ['node_class' => '\Twig\Node\Expression\Test\SameasTest']),
 248              new TwigTest('none', null, ['node_class' => '\Twig\Node\Expression\Test\NullTest']),
 249              new TwigTest('null', null, ['node_class' => '\Twig\Node\Expression\Test\NullTest']),
 250              new TwigTest('divisibleby', null, ['node_class' => '\Twig\Node\Expression\Test\DivisiblebyTest', 'deprecated' => '1.21', 'alternative' => 'divisible by']),
 251              new TwigTest('divisible by', null, ['node_class' => '\Twig\Node\Expression\Test\DivisiblebyTest']),
 252              new TwigTest('constant', null, ['node_class' => '\Twig\Node\Expression\Test\ConstantTest']),
 253              new TwigTest('empty', 'twig_test_empty'),
 254              new TwigTest('iterable', 'twig_test_iterable'),
 255          ];
 256      }
 257  
 258      public function getOperators()
 259      {
 260          return [
 261              [
 262                  'not' => ['precedence' => 50, 'class' => '\Twig\Node\Expression\Unary\NotUnary'],
 263                  '-' => ['precedence' => 500, 'class' => '\Twig\Node\Expression\Unary\NegUnary'],
 264                  '+' => ['precedence' => 500, 'class' => '\Twig\Node\Expression\Unary\PosUnary'],
 265              ],
 266              [
 267                  'or' => ['precedence' => 10, 'class' => '\Twig\Node\Expression\Binary\OrBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 268                  'and' => ['precedence' => 15, 'class' => '\Twig\Node\Expression\Binary\AndBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 269                  'b-or' => ['precedence' => 16, 'class' => '\Twig\Node\Expression\Binary\BitwiseOrBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 270                  'b-xor' => ['precedence' => 17, 'class' => '\Twig\Node\Expression\Binary\BitwiseXorBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 271                  'b-and' => ['precedence' => 18, 'class' => '\Twig\Node\Expression\Binary\BitwiseAndBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 272                  '==' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\EqualBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 273                  '!=' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\NotEqualBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 274                  '<' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\LessBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 275                  '>' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\GreaterBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 276                  '>=' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\GreaterEqualBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 277                  '<=' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\LessEqualBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 278                  'not in' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\NotInBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 279                  'in' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\InBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 280                  'matches' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\MatchesBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 281                  'starts with' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\StartsWithBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 282                  'ends with' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\EndsWithBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 283                  '..' => ['precedence' => 25, 'class' => '\Twig\Node\Expression\Binary\RangeBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 284                  '+' => ['precedence' => 30, 'class' => '\Twig\Node\Expression\Binary\AddBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 285                  '-' => ['precedence' => 30, 'class' => '\Twig\Node\Expression\Binary\SubBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 286                  '~' => ['precedence' => 40, 'class' => '\Twig\Node\Expression\Binary\ConcatBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 287                  '*' => ['precedence' => 60, 'class' => '\Twig\Node\Expression\Binary\MulBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 288                  '/' => ['precedence' => 60, 'class' => '\Twig\Node\Expression\Binary\DivBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 289                  '//' => ['precedence' => 60, 'class' => '\Twig\Node\Expression\Binary\FloorDivBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 290                  '%' => ['precedence' => 60, 'class' => '\Twig\Node\Expression\Binary\ModBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT],
 291                  'is' => ['precedence' => 100, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 292                  'is not' => ['precedence' => 100, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 293                  '**' => ['precedence' => 200, 'class' => '\Twig\Node\Expression\Binary\PowerBinary', 'associativity' => ExpressionParser::OPERATOR_RIGHT],
 294                  '??' => ['precedence' => 300, 'class' => '\Twig\Node\Expression\NullCoalesceExpression', 'associativity' => ExpressionParser::OPERATOR_RIGHT],
 295              ],
 296          ];
 297      }
 298  
 299      public function getName()
 300      {
 301          return 'core';
 302      }
 303  }
 304  
 305  class_alias('Twig\Extension\CoreExtension', 'Twig_Extension_Core');
 306  }
 307  
 308  namespace {
 309  use Twig\Environment;
 310  use Twig\Error\LoaderError;
 311  use Twig\Error\RuntimeError;
 312  use Twig\Loader\SourceContextLoaderInterface;
 313  use Twig\Markup;
 314  use Twig\Node\Expression\ConstantExpression;
 315  use Twig\Node\Node;
 316  
 317  /**
 318   * Cycles over a value.
 319   *
 320   * @param \ArrayAccess|array $values
 321   * @param int                $position The cycle position
 322   *
 323   * @return string The next value in the cycle
 324   */
 325  function twig_cycle($values, $position)
 326  {
 327      if (!\is_array($values) && !$values instanceof \ArrayAccess) {
 328          return $values;
 329      }
 330  
 331      return $values[$position % \count($values)];
 332  }
 333  
 334  /**
 335   * Returns a random value depending on the supplied parameter type:
 336   * - a random item from a \Traversable or array
 337   * - a random character from a string
 338   * - a random integer between 0 and the integer parameter.
 339   *
 340   * @param \Traversable|array|int|float|string $values The values to pick a random item from
 341   * @param int|null                            $max    Maximum value used when $values is an int
 342   *
 343   * @throws RuntimeError when $values is an empty array (does not apply to an empty string which is returned as is)
 344   *
 345   * @return mixed A random value from the given sequence
 346   */
 347  function twig_random(Environment $env, $values = null, $max = null)
 348  {
 349      if (null === $values) {
 350          return null === $max ? mt_rand() : mt_rand(0, $max);
 351      }
 352  
 353      if (\is_int($values) || \is_float($values)) {
 354          if (null === $max) {
 355              if ($values < 0) {
 356                  $max = 0;
 357                  $min = $values;
 358              } else {
 359                  $max = $values;
 360                  $min = 0;
 361              }
 362          } else {
 363              $min = $values;
 364              $max = $max;
 365          }
 366  
 367          return mt_rand($min, $max);
 368      }
 369  
 370      if (\is_string($values)) {
 371          if ('' === $values) {
 372              return '';
 373          }
 374          if (null !== $charset = $env->getCharset()) {
 375              if ('UTF-8' !== $charset) {
 376                  $values = twig_convert_encoding($values, 'UTF-8', $charset);
 377              }
 378  
 379              // unicode version of str_split()
 380              // split at all positions, but not after the start and not before the end
 381              $values = preg_split('/(?<!^)(?!$)/u', $values);
 382  
 383              if ('UTF-8' !== $charset) {
 384                  foreach ($values as $i => $value) {
 385                      $values[$i] = twig_convert_encoding($value, $charset, 'UTF-8');
 386                  }
 387              }
 388          } else {
 389              return $values[mt_rand(0, \strlen($values) - 1)];
 390          }
 391      }
 392  
 393      if (!twig_test_iterable($values)) {
 394          return $values;
 395      }
 396  
 397      $values = twig_to_array($values);
 398  
 399      if (0 === \count($values)) {
 400          throw new RuntimeError('The random function cannot pick from an empty array.');
 401      }
 402  
 403      return $values[array_rand($values, 1)];
 404  }
 405  
 406  /**
 407   * Converts a date to the given format.
 408   *
 409   *   {{ post.published_at|date("m/d/Y") }}
 410   *
 411   * @param \DateTime|\DateTimeInterface|\DateInterval|string $date     A date
 412   * @param string|null                                       $format   The target format, null to use the default
 413   * @param \DateTimeZone|string|false|null                   $timezone The target timezone, null to use the default, false to leave unchanged
 414   *
 415   * @return string The formatted date
 416   */
 417  function twig_date_format_filter(Environment $env, $date, $format = null, $timezone = null)
 418  {
 419      if (null === $format) {
 420          $formats = $env->getExtension('\Twig\Extension\CoreExtension')->getDateFormat();
 421          $format = $date instanceof \DateInterval ? $formats[1] : $formats[0];
 422      }
 423  
 424      if ($date instanceof \DateInterval) {
 425          return $date->format($format);
 426      }
 427  
 428      return twig_date_converter($env, $date, $timezone)->format($format);
 429  }
 430  
 431  /**
 432   * Returns a new date object modified.
 433   *
 434   *   {{ post.published_at|date_modify("-1day")|date("m/d/Y") }}
 435   *
 436   * @param \DateTime|string $date     A date
 437   * @param string           $modifier A modifier string
 438   *
 439   * @return \DateTime
 440   */
 441  function twig_date_modify_filter(Environment $env, $date, $modifier)
 442  {
 443      $date = twig_date_converter($env, $date, false);
 444      $resultDate = $date->modify($modifier);
 445  
 446      // This is a hack to ensure PHP 5.2 support and support for \DateTimeImmutable
 447      // \DateTime::modify does not return the modified \DateTime object < 5.3.0
 448      // and \DateTimeImmutable does not modify $date.
 449      return null === $resultDate ? $date : $resultDate;
 450  }
 451  
 452  /**
 453   * Converts an input to a \DateTime instance.
 454   *
 455   *    {% if date(user.created_at) < date('+2days') %}
 456   *      {# do something #}
 457   *    {% endif %}
 458   *
 459   * @param \DateTime|\DateTimeInterface|string|null $date     A date
 460   * @param \DateTimeZone|string|false|null          $timezone The target timezone, null to use the default, false to leave unchanged
 461   *
 462   * @return \DateTime
 463   */
 464  function twig_date_converter(Environment $env, $date = null, $timezone = null)
 465  {
 466      // determine the timezone
 467      if (false !== $timezone) {
 468          if (null === $timezone) {
 469              $timezone = $env->getExtension('\Twig\Extension\CoreExtension')->getTimezone();
 470          } elseif (!$timezone instanceof \DateTimeZone) {
 471              $timezone = new \DateTimeZone($timezone);
 472          }
 473      }
 474  
 475      // immutable dates
 476      if ($date instanceof \DateTimeImmutable) {
 477          return false !== $timezone ? $date->setTimezone($timezone) : $date;
 478      }
 479  
 480      if ($date instanceof \DateTime || $date instanceof \DateTimeInterface) {
 481          $date = clone $date;
 482          if (false !== $timezone) {
 483              $date->setTimezone($timezone);
 484          }
 485  
 486          return $date;
 487      }
 488  
 489      if (null === $date || 'now' === $date) {
 490          return new \DateTime($date, false !== $timezone ? $timezone : $env->getExtension('\Twig\Extension\CoreExtension')->getTimezone());
 491      }
 492  
 493      $asString = (string) $date;
 494      if (ctype_digit($asString) || (!empty($asString) && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) {
 495          $date = new \DateTime('@'.$date);
 496      } else {
 497          $date = new \DateTime($date, $env->getExtension('\Twig\Extension\CoreExtension')->getTimezone());
 498      }
 499  
 500      if (false !== $timezone) {
 501          $date->setTimezone($timezone);
 502      }
 503  
 504      return $date;
 505  }
 506  
 507  /**
 508   * Replaces strings within a string.
 509   *
 510   * @param string             $str  String to replace in
 511   * @param array|\Traversable $from Replace values
 512   * @param string|null        $to   Replace to, deprecated (@see https://secure.php.net/manual/en/function.strtr.php)
 513   *
 514   * @return string
 515   */
 516  function twig_replace_filter($str, $from, $to = null)
 517  {
 518      if (\is_string($from) && \is_string($to)) {
 519          @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);
 520  
 521          return strtr($str, $from, $to);
 522      }
 523  
 524      if (!twig_test_iterable($from)) {
 525          throw new RuntimeError(sprintf('The "replace" filter expects an array or "Traversable" as replace values, got "%s".', \is_object($from) ? \get_class($from) : \gettype($from)));
 526      }
 527  
 528      return strtr($str, twig_to_array($from));
 529  }
 530  
 531  /**
 532   * Rounds a number.
 533   *
 534   * @param int|float $value     The value to round
 535   * @param int|float $precision The rounding precision
 536   * @param string    $method    The method to use for rounding
 537   *
 538   * @return int|float The rounded number
 539   */
 540  function twig_round($value, $precision = 0, $method = 'common')
 541  {
 542      if ('common' == $method) {
 543          return round($value, $precision);
 544      }
 545  
 546      if ('ceil' != $method && 'floor' != $method) {
 547          throw new RuntimeError('The round filter only supports the "common", "ceil", and "floor" methods.');
 548      }
 549  
 550      return $method($value * pow(10, $precision)) / pow(10, $precision);
 551  }
 552  
 553  /**
 554   * Number format filter.
 555   *
 556   * All of the formatting options can be left null, in that case the defaults will
 557   * be used.  Supplying any of the parameters will override the defaults set in the
 558   * environment object.
 559   *
 560   * @param mixed  $number       A float/int/string of the number to format
 561   * @param int    $decimal      the number of decimal points to display
 562   * @param string $decimalPoint the character(s) to use for the decimal point
 563   * @param string $thousandSep  the character(s) to use for the thousands separator
 564   *
 565   * @return string The formatted number
 566   */
 567  function twig_number_format_filter(Environment $env, $number, $decimal = null, $decimalPoint = null, $thousandSep = null)
 568  {
 569      $defaults = $env->getExtension('\Twig\Extension\CoreExtension')->getNumberFormat();
 570      if (null === $decimal) {
 571          $decimal = $defaults[0];
 572      }
 573  
 574      if (null === $decimalPoint) {
 575          $decimalPoint = $defaults[1];
 576      }
 577  
 578      if (null === $thousandSep) {
 579          $thousandSep = $defaults[2];
 580      }
 581  
 582      return number_format((float) $number, $decimal, $decimalPoint, $thousandSep);
 583  }
 584  
 585  /**
 586   * URL encodes (RFC 3986) a string as a path segment or an array as a query string.
 587   *
 588   * @param string|array $url A URL or an array of query parameters
 589   *
 590   * @return string The URL encoded value
 591   */
 592  function twig_urlencode_filter($url)
 593  {
 594      if (\is_array($url)) {
 595          if (\defined('PHP_QUERY_RFC3986')) {
 596              return http_build_query($url, '', '&', PHP_QUERY_RFC3986);
 597          }
 598  
 599          return http_build_query($url, '', '&');
 600      }
 601  
 602      return rawurlencode($url);
 603  }
 604  
 605  /**
 606   * JSON encodes a variable.
 607   *
 608   * @param mixed $value   the value to encode
 609   * @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
 610   *
 611   * @return mixed The JSON encoded value
 612   */
 613  function twig_jsonencode_filter($value, $options = 0)
 614  {
 615      if ($value instanceof Markup) {
 616          $value = (string) $value;
 617      } elseif (\is_array($value)) {
 618          array_walk_recursive($value, '_twig_markup2string');
 619      }
 620  
 621      return json_encode($value, $options);
 622  }
 623  
 624  function _twig_markup2string(&$value)
 625  {
 626      if ($value instanceof Markup) {
 627          $value = (string) $value;
 628      }
 629  }
 630  
 631  /**
 632   * Merges an array with another one.
 633   *
 634   *  {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %}
 635   *
 636   *  {% set items = items|merge({ 'peugeot': 'car' }) %}
 637   *
 638   *  {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car' } #}
 639   *
 640   * @param array|\Traversable $arr1 An array
 641   * @param array|\Traversable $arr2 An array
 642   *
 643   * @return array The merged array
 644   */
 645  function twig_array_merge($arr1, $arr2)
 646  {
 647      if (!twig_test_iterable($arr1)) {
 648          throw new RuntimeError(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as first argument.', \gettype($arr1)));
 649      }
 650  
 651      if (!twig_test_iterable($arr2)) {
 652          throw new RuntimeError(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as second argument.', \gettype($arr2)));
 653      }
 654  
 655      return array_merge(twig_to_array($arr1), twig_to_array($arr2));
 656  }
 657  
 658  /**
 659   * Slices a variable.
 660   *
 661   * @param mixed $item         A variable
 662   * @param int   $start        Start of the slice
 663   * @param int   $length       Size of the slice
 664   * @param bool  $preserveKeys Whether to preserve key or not (when the input is an array)
 665   *
 666   * @return mixed The sliced variable
 667   */
 668  function twig_slice(Environment $env, $item, $start, $length = null, $preserveKeys = false)
 669  {
 670      if ($item instanceof \Traversable) {
 671          while ($item instanceof \IteratorAggregate) {
 672              $item = $item->getIterator();
 673          }
 674  
 675          if ($start >= 0 && $length >= 0 && $item instanceof \Iterator) {
 676              try {
 677                  return iterator_to_array(new \LimitIterator($item, $start, null === $length ? -1 : $length), $preserveKeys);
 678              } catch (\OutOfBoundsException $e) {
 679                  return [];
 680              }
 681          }
 682  
 683          $item = iterator_to_array($item, $preserveKeys);
 684      }
 685  
 686      if (\is_array($item)) {
 687          return \array_slice($item, $start, $length, $preserveKeys);
 688      }
 689  
 690      $item = (string) $item;
 691  
 692      if (\function_exists('mb_get_info') && null !== $charset = $env->getCharset()) {
 693          return (string) mb_substr($item, $start, null === $length ? mb_strlen($item, $charset) - $start : $length, $charset);
 694      }
 695  
 696      return (string) (null === $length ? substr($item, $start) : substr($item, $start, $length));
 697  }
 698  
 699  /**
 700   * Returns the first element of the item.
 701   *
 702   * @param mixed $item A variable
 703   *
 704   * @return mixed The first element of the item
 705   */
 706  function twig_first(Environment $env, $item)
 707  {
 708      $elements = twig_slice($env, $item, 0, 1, false);
 709  
 710      return \is_string($elements) ? $elements : current($elements);
 711  }
 712  
 713  /**
 714   * Returns the last element of the item.
 715   *
 716   * @param mixed $item A variable
 717   *
 718   * @return mixed The last element of the item
 719   */
 720  function twig_last(Environment $env, $item)
 721  {
 722      $elements = twig_slice($env, $item, -1, 1, false);
 723  
 724      return \is_string($elements) ? $elements : current($elements);
 725  }
 726  
 727  /**
 728   * Joins the values to a string.
 729   *
 730   * The separators between elements are empty strings per default, you can define them with the optional parameters.
 731   *
 732   *  {{ [1, 2, 3]|join(', ', ' and ') }}
 733   *  {# returns 1, 2 and 3 #}
 734   *
 735   *  {{ [1, 2, 3]|join('|') }}
 736   *  {# returns 1|2|3 #}
 737   *
 738   *  {{ [1, 2, 3]|join }}
 739   *  {# returns 123 #}
 740   *
 741   * @param array       $value An array
 742   * @param string      $glue  The separator
 743   * @param string|null $and   The separator for the last pair
 744   *
 745   * @return string The concatenated string
 746   */
 747  function twig_join_filter($value, $glue = '', $and = null)
 748  {
 749      if (!twig_test_iterable($value)) {
 750          $value = (array) $value;
 751      }
 752  
 753      $value = twig_to_array($value, false);
 754  
 755      if (0 === \count($value)) {
 756          return '';
 757      }
 758  
 759      if (null === $and || $and === $glue) {
 760          return implode($glue, $value);
 761      }
 762  
 763      if (1 === \count($value)) {
 764          return $value[0];
 765      }
 766  
 767      return implode($glue, \array_slice($value, 0, -1)).$and.$value[\count($value) - 1];
 768  }
 769  
 770  /**
 771   * Splits the string into an array.
 772   *
 773   *  {{ "one,two,three"|split(',') }}
 774   *  {# returns [one, two, three] #}
 775   *
 776   *  {{ "one,two,three,four,five"|split(',', 3) }}
 777   *  {# returns [one, two, "three,four,five"] #}
 778   *
 779   *  {{ "123"|split('') }}
 780   *  {# returns [1, 2, 3] #}
 781   *
 782   *  {{ "aabbcc"|split('', 2) }}
 783   *  {# returns [aa, bb, cc] #}
 784   *
 785   * @param string $value     A string
 786   * @param string $delimiter The delimiter
 787   * @param int    $limit     The limit
 788   *
 789   * @return array The split string as an array
 790   */
 791  function twig_split_filter(Environment $env, $value, $delimiter, $limit = null)
 792  {
 793      if (!empty($delimiter)) {
 794          return null === $limit ? explode($delimiter, $value) : explode($delimiter, $value, $limit);
 795      }
 796  
 797      if (!\function_exists('mb_get_info') || null === $charset = $env->getCharset()) {
 798          return str_split($value, null === $limit ? 1 : $limit);
 799      }
 800  
 801      if ($limit <= 1) {
 802          return preg_split('/(?<!^)(?!$)/u', $value);
 803      }
 804  
 805      $length = mb_strlen($value, $charset);
 806      if ($length < $limit) {
 807          return [$value];
 808      }
 809  
 810      $r = [];
 811      for ($i = 0; $i < $length; $i += $limit) {
 812          $r[] = mb_substr($value, $i, $limit, $charset);
 813      }
 814  
 815      return $r;
 816  }
 817  
 818  // The '_default' filter is used internally to avoid using the ternary operator
 819  // which costs a lot for big contexts (before PHP 5.4). So, on average,
 820  // a function call is cheaper.
 821  /**
 822   * @internal
 823   */
 824  function _twig_default_filter($value, $default = '')
 825  {
 826      if (twig_test_empty($value)) {
 827          return $default;
 828      }
 829  
 830      return $value;
 831  }
 832  
 833  /**
 834   * Returns the keys for the given array.
 835   *
 836   * It is useful when you want to iterate over the keys of an array:
 837   *
 838   *  {% for key in array|keys %}
 839   *      {# ... #}
 840   *  {% endfor %}
 841   *
 842   * @param array $array An array
 843   *
 844   * @return array The keys
 845   */
 846  function twig_get_array_keys_filter($array)
 847  {
 848      if ($array instanceof \Traversable) {
 849          while ($array instanceof \IteratorAggregate) {
 850              $array = $array->getIterator();
 851          }
 852  
 853          if ($array instanceof \Iterator) {
 854              $keys = [];
 855              $array->rewind();
 856              while ($array->valid()) {
 857                  $keys[] = $array->key();
 858                  $array->next();
 859              }
 860  
 861              return $keys;
 862          }
 863  
 864          $keys = [];
 865          foreach ($array as $key => $item) {
 866              $keys[] = $key;
 867          }
 868  
 869          return $keys;
 870      }
 871  
 872      if (!\is_array($array)) {
 873          return [];
 874      }
 875  
 876      return array_keys($array);
 877  }
 878  
 879  /**
 880   * Reverses a variable.
 881   *
 882   * @param array|\Traversable|string $item         An array, a \Traversable instance, or a string
 883   * @param bool                      $preserveKeys Whether to preserve key or not
 884   *
 885   * @return mixed The reversed input
 886   */
 887  function twig_reverse_filter(Environment $env, $item, $preserveKeys = false)
 888  {
 889      if ($item instanceof \Traversable) {
 890          return array_reverse(iterator_to_array($item), $preserveKeys);
 891      }
 892  
 893      if (\is_array($item)) {
 894          return array_reverse($item, $preserveKeys);
 895      }
 896  
 897      if (null !== $charset = $env->getCharset()) {
 898          $string = (string) $item;
 899  
 900          if ('UTF-8' !== $charset) {
 901              $item = twig_convert_encoding($string, 'UTF-8', $charset);
 902          }
 903  
 904          preg_match_all('/./us', $item, $matches);
 905  
 906          $string = implode('', array_reverse($matches[0]));
 907  
 908          if ('UTF-8' !== $charset) {
 909              $string = twig_convert_encoding($string, $charset, 'UTF-8');
 910          }
 911  
 912          return $string;
 913      }
 914  
 915      return strrev((string) $item);
 916  }
 917  
 918  /**
 919   * Sorts an array.
 920   *
 921   * @param array|\Traversable $array
 922   *
 923   * @return array
 924   */
 925  function twig_sort_filter($array)
 926  {
 927      if ($array instanceof \Traversable) {
 928          $array = iterator_to_array($array);
 929      } elseif (!\is_array($array)) {
 930          throw new RuntimeError(sprintf('The sort filter only works with arrays or "Traversable", got "%s".', \gettype($array)));
 931      }
 932  
 933      asort($array);
 934  
 935      return $array;
 936  }
 937  
 938  /**
 939   * @internal
 940   */
 941  function twig_in_filter($value, $compare)
 942  {
 943      if ($value instanceof Markup) {
 944          $value = (string) $value;
 945      }
 946      if ($compare instanceof Markup) {
 947          $compare = (string) $compare;
 948      }
 949  
 950      if (\is_array($compare)) {
 951          return \in_array($value, $compare, \is_object($value) || \is_resource($value));
 952      } elseif (\is_string($compare) && (\is_string($value) || \is_int($value) || \is_float($value))) {
 953          return '' === $value || false !== strpos($compare, (string) $value);
 954      } elseif ($compare instanceof \Traversable) {
 955          if (\is_object($value) || \is_resource($value)) {
 956              foreach ($compare as $item) {
 957                  if ($item === $value) {
 958                      return true;
 959                  }
 960              }
 961          } else {
 962              foreach ($compare as $item) {
 963                  if ($item == $value) {
 964                      return true;
 965                  }
 966              }
 967          }
 968  
 969          return false;
 970      }
 971  
 972      return false;
 973  }
 974  
 975  /**
 976   * Returns a trimmed string.
 977   *
 978   * @return string
 979   *
 980   * @throws RuntimeError When an invalid trimming side is used (not a string or not 'left', 'right', or 'both')
 981   */
 982  function twig_trim_filter($string, $characterMask = null, $side = 'both')
 983  {
 984      if (null === $characterMask) {
 985          $characterMask = " \t\n\r\0\x0B";
 986      }
 987  
 988      switch ($side) {
 989          case 'both':
 990              return trim($string, $characterMask);
 991          case 'left':
 992              return ltrim($string, $characterMask);
 993          case 'right':
 994              return rtrim($string, $characterMask);
 995          default:
 996              throw new RuntimeError('Trimming side must be "left", "right" or "both".');
 997      }
 998  }
 999  
1000  /**
1001   * Removes whitespaces between HTML tags.
1002   *
1003   * @return string
1004   */
1005  function twig_spaceless($content)
1006  {
1007      return trim(preg_replace('/>\s+</', '><', $content));
1008  }
1009  
1010  /**
1011   * Escapes a string.
1012   *
1013   * @param mixed  $string     The value to be escaped
1014   * @param string $strategy   The escaping strategy
1015   * @param string $charset    The charset
1016   * @param bool   $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false)
1017   *
1018   * @return string
1019   */
1020  function twig_escape_filter(Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false)
1021  {
1022      if ($autoescape && $string instanceof Markup) {
1023          return $string;
1024      }
1025  
1026      if (!\is_string($string)) {
1027          if (\is_object($string) && method_exists($string, '__toString')) {
1028              $string = (string) $string;
1029          } elseif (\in_array($strategy, ['html', 'js', 'css', 'html_attr', 'url'])) {
1030              return $string;
1031          }
1032      }
1033  
1034      if ('' === $string) {
1035          return '';
1036      }
1037  
1038      if (null === $charset) {
1039          $charset = $env->getCharset();
1040      }
1041  
1042      switch ($strategy) {
1043          case 'html':
1044              // see https://secure.php.net/htmlspecialchars
1045  
1046              // Using a static variable to avoid initializing the array
1047              // each time the function is called. Moving the declaration on the
1048              // top of the function slow downs other escaping strategies.
1049              static $htmlspecialcharsCharsets = [
1050                  'ISO-8859-1' => true, 'ISO8859-1' => true,
1051                  'ISO-8859-15' => true, 'ISO8859-15' => true,
1052                  'utf-8' => true, 'UTF-8' => true,
1053                  'CP866' => true, 'IBM866' => true, '866' => true,
1054                  'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true,
1055                  '1251' => true,
1056                  'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true,
1057                  'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true,
1058                  'BIG5' => true, '950' => true,
1059                  'GB2312' => true, '936' => true,
1060                  'BIG5-HKSCS' => true,
1061                  'SHIFT_JIS' => true, 'SJIS' => true, '932' => true,
1062                  'EUC-JP' => true, 'EUCJP' => true,
1063                  'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true,
1064              ];
1065  
1066              if (isset($htmlspecialcharsCharsets[$charset])) {
1067                  return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
1068              }
1069  
1070              if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) {
1071                  // cache the lowercase variant for future iterations
1072                  $htmlspecialcharsCharsets[$charset] = true;
1073  
1074                  return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
1075              }
1076  
1077              $string = twig_convert_encoding($string, 'UTF-8', $charset);
1078              $string = htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
1079  
1080              return twig_convert_encoding($string, $charset, 'UTF-8');
1081  
1082          case 'js':
1083              // escape all non-alphanumeric characters
1084              // into their \x or \uHHHH representations
1085              if ('UTF-8' !== $charset) {
1086                  $string = twig_convert_encoding($string, 'UTF-8', $charset);
1087              }
1088  
1089              if (!preg_match('//u', $string)) {
1090                  throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
1091              }
1092  
1093              $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', '_twig_escape_js_callback', $string);
1094  
1095              if ('UTF-8' !== $charset) {
1096                  $string = twig_convert_encoding($string, $charset, 'UTF-8');
1097              }
1098  
1099              return $string;
1100  
1101          case 'css':
1102              if ('UTF-8' !== $charset) {
1103                  $string = twig_convert_encoding($string, 'UTF-8', $charset);
1104              }
1105  
1106              if (!preg_match('//u', $string)) {
1107                  throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
1108              }
1109  
1110              $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', '_twig_escape_css_callback', $string);
1111  
1112              if ('UTF-8' !== $charset) {
1113                  $string = twig_convert_encoding($string, $charset, 'UTF-8');
1114              }
1115  
1116              return $string;
1117  
1118          case 'html_attr':
1119              if ('UTF-8' !== $charset) {
1120                  $string = twig_convert_encoding($string, 'UTF-8', $charset);
1121              }
1122  
1123              if (!preg_match('//u', $string)) {
1124                  throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
1125              }
1126  
1127              $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', '_twig_escape_html_attr_callback', $string);
1128  
1129              if ('UTF-8' !== $charset) {
1130                  $string = twig_convert_encoding($string, $charset, 'UTF-8');
1131              }
1132  
1133              return $string;
1134  
1135          case 'url':
1136              return rawurlencode($string);
1137  
1138          default:
1139              static $escapers;
1140  
1141              if (null === $escapers) {
1142                  $escapers = $env->getExtension('\Twig\Extension\CoreExtension')->getEscapers();
1143              }
1144  
1145              if (isset($escapers[$strategy])) {
1146                  return \call_user_func($escapers[$strategy], $env, $string, $charset);
1147              }
1148  
1149              $validStrategies = implode(', ', array_merge(['html', 'js', 'url', 'css', 'html_attr'], array_keys($escapers)));
1150  
1151              throw new RuntimeError(sprintf('Invalid escaping strategy "%s" (valid ones: %s).', $strategy, $validStrategies));
1152      }
1153  }
1154  
1155  /**
1156   * @internal
1157   */
1158  function twig_escape_filter_is_safe(Node $filterArgs)
1159  {
1160      foreach ($filterArgs as $arg) {
1161          if ($arg instanceof ConstantExpression) {
1162              return [$arg->getAttribute('value')];
1163          }
1164  
1165          return [];
1166      }
1167  
1168      return ['html'];
1169  }
1170  
1171  if (\function_exists('mb_convert_encoding')) {
1172      function twig_convert_encoding($string, $to, $from)
1173      {
1174          return mb_convert_encoding($string, $to, $from);
1175      }
1176  } elseif (\function_exists('iconv')) {
1177      function twig_convert_encoding($string, $to, $from)
1178      {
1179          return iconv($from, $to, $string);
1180      }
1181  } else {
1182      function twig_convert_encoding($string, $to, $from)
1183      {
1184          throw new RuntimeError('No suitable convert encoding function (use UTF-8 as your encoding or install the iconv or mbstring extension).');
1185      }
1186  }
1187  
1188  if (\function_exists('mb_ord')) {
1189      function twig_ord($string)
1190      {
1191          return mb_ord($string, 'UTF-8');
1192      }
1193  } else {
1194      function twig_ord($string)
1195      {
1196          $code = ($string = unpack('C*', substr($string, 0, 4))) ? $string[1] : 0;
1197          if (0xF0 <= $code) {
1198              return (($code - 0xF0) << 18) + (($string[2] - 0x80) << 12) + (($string[3] - 0x80) << 6) + $string[4] - 0x80;
1199          }
1200          if (0xE0 <= $code) {
1201              return (($code - 0xE0) << 12) + (($string[2] - 0x80) << 6) + $string[3] - 0x80;
1202          }
1203          if (0xC0 <= $code) {
1204              return (($code - 0xC0) << 6) + $string[2] - 0x80;
1205          }
1206  
1207          return $code;
1208      }
1209  }
1210  
1211  function _twig_escape_js_callback($matches)
1212  {
1213      $char = $matches[0];
1214  
1215      /*
1216       * A few characters have short escape sequences in JSON and JavaScript.
1217       * Escape sequences supported only by JavaScript, not JSON, are ommitted.
1218       * \" is also supported but omitted, because the resulting string is not HTML safe.
1219       */
1220      static $shortMap = [
1221          '\\' => '\\\\',
1222          '/' => '\\/',
1223          "\x08" => '\b',
1224          "\x0C" => '\f',
1225          "\x0A" => '\n',
1226          "\x0D" => '\r',
1227          "\x09" => '\t',
1228      ];
1229  
1230      if (isset($shortMap[$char])) {
1231          return $shortMap[$char];
1232      }
1233  
1234      // \uHHHH
1235      $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
1236      $char = strtoupper(bin2hex($char));
1237  
1238      if (4 >= \strlen($char)) {
1239          return sprintf('\u%04s', $char);
1240      }
1241  
1242      return sprintf('\u%04s\u%04s', substr($char, 0, -4), substr($char, -4));
1243  }
1244  
1245  function _twig_escape_css_callback($matches)
1246  {
1247      $char = $matches[0];
1248  
1249      return sprintf('\\%X ', 1 === \strlen($char) ? \ord($char) : twig_ord($char));
1250  }
1251  
1252  /**
1253   * This function is adapted from code coming from Zend Framework.
1254   *
1255   * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (https://www.zend.com)
1256   * @license   https://framework.zend.com/license/new-bsd New BSD License
1257   */
1258  function _twig_escape_html_attr_callback($matches)
1259  {
1260      $chr = $matches[0];
1261      $ord = \ord($chr);
1262  
1263      /*
1264       * The following replaces characters undefined in HTML with the
1265       * hex entity for the Unicode replacement character.
1266       */
1267      if (($ord <= 0x1f && "\t" != $chr && "\n" != $chr && "\r" != $chr) || ($ord >= 0x7f && $ord <= 0x9f)) {
1268          return '&#xFFFD;';
1269      }
1270  
1271      /*
1272       * Check if the current character to escape has a name entity we should
1273       * replace it with while grabbing the hex value of the character.
1274       */
1275      if (1 == \strlen($chr)) {
1276          /*
1277           * While HTML supports far more named entities, the lowest common denominator
1278           * has become HTML5's XML Serialisation which is restricted to the those named
1279           * entities that XML supports. Using HTML entities would result in this error:
1280           *     XML Parsing Error: undefined entity
1281           */
1282          static $entityMap = [
1283              34 => '&quot;', /* quotation mark */
1284              38 => '&amp;',  /* ampersand */
1285              60 => '&lt;',   /* less-than sign */
1286              62 => '&gt;',   /* greater-than sign */
1287          ];
1288  
1289          if (isset($entityMap[$ord])) {
1290              return $entityMap[$ord];
1291          }
1292  
1293          return sprintf('&#x%02X;', $ord);
1294      }
1295  
1296      /*
1297       * Per OWASP recommendations, we'll use hex entities for any other
1298       * characters where a named entity does not exist.
1299       */
1300      return sprintf('&#x%04X;', twig_ord($chr));
1301  }
1302  
1303  // add multibyte extensions if possible
1304  if (\function_exists('mb_get_info')) {
1305      /**
1306       * Returns the length of a variable.
1307       *
1308       * @param mixed $thing A variable
1309       *
1310       * @return int The length of the value
1311       */
1312      function twig_length_filter(Environment $env, $thing)
1313      {
1314          if (null === $thing) {
1315              return 0;
1316          }
1317  
1318          if (is_scalar($thing)) {
1319              return mb_strlen($thing, $env->getCharset());
1320          }
1321  
1322          if ($thing instanceof \Countable || \is_array($thing) || $thing instanceof \SimpleXMLElement) {
1323              return \count($thing);
1324          }
1325  
1326          if ($thing instanceof \Traversable) {
1327              return iterator_count($thing);
1328          }
1329  
1330          if (\is_object($thing) && method_exists($thing, '__toString')) {
1331              return mb_strlen((string) $thing, $env->getCharset());
1332          }
1333  
1334          return 1;
1335      }
1336  
1337      /**
1338       * Converts a string to uppercase.
1339       *
1340       * @param string $string A string
1341       *
1342       * @return string The uppercased string
1343       */
1344      function twig_upper_filter(Environment $env, $string)
1345      {
1346          if (null !== $charset = $env->getCharset()) {
1347              return mb_strtoupper($string, $charset);
1348          }
1349  
1350          return strtoupper($string);
1351      }
1352  
1353      /**
1354       * Converts a string to lowercase.
1355       *
1356       * @param string $string A string
1357       *
1358       * @return string The lowercased string
1359       */
1360      function twig_lower_filter(Environment $env, $string)
1361      {
1362          if (null !== $charset = $env->getCharset()) {
1363              return mb_strtolower($string, $charset);
1364          }
1365  
1366          return strtolower($string);
1367      }
1368  
1369      /**
1370       * Returns a titlecased string.
1371       *
1372       * @param string $string A string
1373       *
1374       * @return string The titlecased string
1375       */
1376      function twig_title_string_filter(Environment $env, $string)
1377      {
1378          if (null !== $charset = $env->getCharset()) {
1379              return mb_convert_case($string, MB_CASE_TITLE, $charset);
1380          }
1381  
1382          return ucwords(strtolower($string));
1383      }
1384  
1385      /**
1386       * Returns a capitalized string.
1387       *
1388       * @param string $string A string
1389       *
1390       * @return string The capitalized string
1391       */
1392      function twig_capitalize_string_filter(Environment $env, $string)
1393      {
1394          if (null !== $charset = $env->getCharset()) {
1395              return mb_strtoupper(mb_substr($string, 0, 1, $charset), $charset).mb_strtolower(mb_substr($string, 1, mb_strlen($string, $charset), $charset), $charset);
1396          }
1397  
1398          return ucfirst(strtolower($string));
1399      }
1400  }
1401  // and byte fallback
1402  else {
1403      /**
1404       * Returns the length of a variable.
1405       *
1406       * @param mixed $thing A variable
1407       *
1408       * @return int The length of the value
1409       */
1410      function twig_length_filter(Environment $env, $thing)
1411      {
1412          if (null === $thing) {
1413              return 0;
1414          }
1415  
1416          if (is_scalar($thing)) {
1417              return \strlen($thing);
1418          }
1419  
1420          if ($thing instanceof \SimpleXMLElement) {
1421              return \count($thing);
1422          }
1423  
1424          if (\is_object($thing) && method_exists($thing, '__toString') && !$thing instanceof \Countable) {
1425              return \strlen((string) $thing);
1426          }
1427  
1428          if ($thing instanceof \Countable || \is_array($thing)) {
1429              return \count($thing);
1430          }
1431  
1432          if ($thing instanceof \IteratorAggregate) {
1433              return iterator_count($thing);
1434          }
1435  
1436          return 1;
1437      }
1438  
1439      /**
1440       * Returns a titlecased string.
1441       *
1442       * @param string $string A string
1443       *
1444       * @return string The titlecased string
1445       */
1446      function twig_title_string_filter(Environment $env, $string)
1447      {
1448          return ucwords(strtolower($string));
1449      }
1450  
1451      /**
1452       * Returns a capitalized string.
1453       *
1454       * @param string $string A string
1455       *
1456       * @return string The capitalized string
1457       */
1458      function twig_capitalize_string_filter(Environment $env, $string)
1459      {
1460          return ucfirst(strtolower($string));
1461      }
1462  }
1463  
1464  /**
1465   * @internal
1466   */
1467  function twig_ensure_traversable($seq)
1468  {
1469      if ($seq instanceof \Traversable || \is_array($seq)) {
1470          return $seq;
1471      }
1472  
1473      return [];
1474  }
1475  
1476  /**
1477   * @internal
1478   */
1479  function twig_to_array($seq, $preserveKeys = true)
1480  {
1481      if ($seq instanceof \Traversable) {
1482          return iterator_to_array($seq, $preserveKeys);
1483      }
1484  
1485      if (!\is_array($seq)) {
1486          return $seq;
1487      }
1488  
1489      return $preserveKeys ? $seq : array_values($seq);
1490  }
1491  
1492  /**
1493   * Checks if a variable is empty.
1494   *
1495   *    {# evaluates to true if the foo variable is null, false, or the empty string #}
1496   *    {% if foo is empty %}
1497   *        {# ... #}
1498   *    {% endif %}
1499   *
1500   * @param mixed $value A variable
1501   *
1502   * @return bool true if the value is empty, false otherwise
1503   */
1504  function twig_test_empty($value)
1505  {
1506      if ($value instanceof \Countable) {
1507          return 0 == \count($value);
1508      }
1509  
1510      if (\is_object($value) && method_exists($value, '__toString')) {
1511          return '' === (string) $value;
1512      }
1513  
1514      return '' === $value || false === $value || null === $value || [] === $value;
1515  }
1516  
1517  /**
1518   * Checks if a variable is traversable.
1519   *
1520   *    {# evaluates to true if the foo variable is an array or a traversable object #}
1521   *    {% if foo is iterable %}
1522   *        {# ... #}
1523   *    {% endif %}
1524   *
1525   * @param mixed $value A variable
1526   *
1527   * @return bool true if the value is traversable
1528   */
1529  function twig_test_iterable($value)
1530  {
1531      return $value instanceof \Traversable || \is_array($value);
1532  }
1533  
1534  /**
1535   * Renders a template.
1536   *
1537   * @param array        $context
1538   * @param string|array $template      The template to render or an array of templates to try consecutively
1539   * @param array        $variables     The variables to pass to the template
1540   * @param bool         $withContext
1541   * @param bool         $ignoreMissing Whether to ignore missing templates or not
1542   * @param bool         $sandboxed     Whether to sandbox the template or not
1543   *
1544   * @return string The rendered template
1545   */
1546  function twig_include(Environment $env, $context, $template, $variables = [], $withContext = true, $ignoreMissing = false, $sandboxed = false)
1547  {
1548      $alreadySandboxed = false;
1549      $sandbox = null;
1550      if ($withContext) {
1551          $variables = array_merge($context, $variables);
1552      }
1553  
1554      if ($isSandboxed = $sandboxed && $env->hasExtension('\Twig\Extension\SandboxExtension')) {
1555          $sandbox = $env->getExtension('\Twig\Extension\SandboxExtension');
1556          if (!$alreadySandboxed = $sandbox->isSandboxed()) {
1557              $sandbox->enableSandbox();
1558          }
1559      }
1560  
1561      $loaded = null;
1562      try {
1563          $loaded = $env->resolveTemplate($template);
1564      } catch (LoaderError $e) {
1565          if (!$ignoreMissing) {
1566              if ($isSandboxed && !$alreadySandboxed) {
1567                  $sandbox->disableSandbox();
1568              }
1569  
1570              throw $e;
1571          }
1572      } catch (\Throwable $e) {
1573          if ($isSandboxed && !$alreadySandboxed) {
1574              $sandbox->disableSandbox();
1575          }
1576  
1577          throw $e;
1578      } catch (\Exception $e) {
1579          if ($isSandboxed && !$alreadySandboxed) {
1580              $sandbox->disableSandbox();
1581          }
1582  
1583          throw $e;
1584      }
1585  
1586      try {
1587          $ret = $loaded ? $loaded->render($variables) : '';
1588      } catch (\Exception $e) {
1589          if ($isSandboxed && !$alreadySandboxed) {
1590              $sandbox->disableSandbox();
1591          }
1592  
1593          throw $e;
1594      }
1595  
1596      if ($isSandboxed && !$alreadySandboxed) {
1597          $sandbox->disableSandbox();
1598      }
1599  
1600      return $ret;
1601  }
1602  
1603  /**
1604   * Returns a template content without rendering it.
1605   *
1606   * @param string $name          The template name
1607   * @param bool   $ignoreMissing Whether to ignore missing templates or not
1608   *
1609   * @return string The template source
1610   */
1611  function twig_source(Environment $env, $name, $ignoreMissing = false)
1612  {
1613      $loader = $env->getLoader();
1614      try {
1615          if (!$loader instanceof SourceContextLoaderInterface) {
1616              return $loader->getSource($name);
1617          } else {
1618              return $loader->getSourceContext($name)->getCode();
1619          }
1620      } catch (LoaderError $e) {
1621          if (!$ignoreMissing) {
1622              throw $e;
1623          }
1624      }
1625  }
1626  
1627  /**
1628   * Provides the ability to get constants from instances as well as class/global constants.
1629   *
1630   * @param string      $constant The name of the constant
1631   * @param object|null $object   The object to get the constant from
1632   *
1633   * @return string
1634   */
1635  function twig_constant($constant, $object = null)
1636  {
1637      if (null !== $object) {
1638          $constant = \get_class($object).'::'.$constant;
1639      }
1640  
1641      return \constant($constant);
1642  }
1643  
1644  /**
1645   * Checks if a constant exists.
1646   *
1647   * @param string      $constant The name of the constant
1648   * @param object|null $object   The object to get the constant from
1649   *
1650   * @return bool
1651   */
1652  function twig_constant_is_defined($constant, $object = null)
1653  {
1654      if (null !== $object) {
1655          $constant = \get_class($object).'::'.$constant;
1656      }
1657  
1658      return \defined($constant);
1659  }
1660  
1661  /**
1662   * Batches item.
1663   *
1664   * @param array $items An array of items
1665   * @param int   $size  The size of the batch
1666   * @param mixed $fill  A value used to fill missing items
1667   *
1668   * @return array
1669   */
1670  function twig_array_batch($items, $size, $fill = null, $preserveKeys = true)
1671  {
1672      if (!twig_test_iterable($items)) {
1673          throw new RuntimeError(sprintf('The "batch" filter expects an array or "Traversable", got "%s".', \is_object($items) ? \get_class($items) : \gettype($items)));
1674      }
1675  
1676      $size = ceil($size);
1677  
1678      $result = array_chunk(twig_to_array($items, $preserveKeys), $size, $preserveKeys);
1679  
1680      if (null !== $fill && $result) {
1681          $last = \count($result) - 1;
1682          if ($fillCount = $size - \count($result[$last])) {
1683              for ($i = 0; $i < $fillCount; ++$i) {
1684                  $result[$last][] = $fill;
1685              }
1686          }
1687      }
1688  
1689      return $result;
1690  }
1691  
1692  function twig_array_filter($array, $arrow)
1693  {
1694      if (\is_array($array)) {
1695          if (\PHP_VERSION_ID >= 50600) {
1696              return array_filter($array, $arrow, \ARRAY_FILTER_USE_BOTH);
1697          }
1698  
1699          return array_filter($array, $arrow);
1700      }
1701  
1702      // the IteratorIterator wrapping is needed as some internal PHP classes are \Traversable but do not implement \Iterator
1703      return new \CallbackFilterIterator(new \IteratorIterator($array), $arrow);
1704  }
1705  
1706  function twig_array_map($array, $arrow)
1707  {
1708      $r = [];
1709      foreach ($array as $k => $v) {
1710          $r[$k] = $arrow($v, $k);
1711      }
1712  
1713      return $r;
1714  }
1715  
1716  function twig_array_reduce($array, $arrow, $initial = null)
1717  {
1718      if (!\is_array($array)) {
1719          $array = iterator_to_array($array);
1720      }
1721  
1722      return array_reduce($array, $arrow, $initial);
1723  }
1724  }


Generated: Wed Nov 11 20:33:01 2020 Cross-referenced by PHPXref 0.7.1