Verzeichnisstruktur phpBB-3.3.16


Veröffentlicht
27.04.2026

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

CoreExtension.php

Zuletzt modifiziert: 01.05.2026, 11:27 - Dateigröße: 52.47 KiB


 0001  <?php
 0002   
 0003  /*
 0004   * This file is part of Twig.
 0005   *
 0006   * (c) Fabien Potencier
 0007   *
 0008   * For the full copyright and license information, please view the LICENSE
 0009   * file that was distributed with this source code.
 0010   */
 0011   
 0012  namespace Twig\Extension {
 0013  use Twig\ExpressionParser;
 0014  use Twig\Node\Expression\Binary\AddBinary;
 0015  use Twig\Node\Expression\Binary\AndBinary;
 0016  use Twig\Node\Expression\Binary\BitwiseAndBinary;
 0017  use Twig\Node\Expression\Binary\BitwiseOrBinary;
 0018  use Twig\Node\Expression\Binary\BitwiseXorBinary;
 0019  use Twig\Node\Expression\Binary\ConcatBinary;
 0020  use Twig\Node\Expression\Binary\DivBinary;
 0021  use Twig\Node\Expression\Binary\EndsWithBinary;
 0022  use Twig\Node\Expression\Binary\EqualBinary;
 0023  use Twig\Node\Expression\Binary\FloorDivBinary;
 0024  use Twig\Node\Expression\Binary\GreaterBinary;
 0025  use Twig\Node\Expression\Binary\GreaterEqualBinary;
 0026  use Twig\Node\Expression\Binary\InBinary;
 0027  use Twig\Node\Expression\Binary\LessBinary;
 0028  use Twig\Node\Expression\Binary\LessEqualBinary;
 0029  use Twig\Node\Expression\Binary\MatchesBinary;
 0030  use Twig\Node\Expression\Binary\ModBinary;
 0031  use Twig\Node\Expression\Binary\MulBinary;
 0032  use Twig\Node\Expression\Binary\NotEqualBinary;
 0033  use Twig\Node\Expression\Binary\NotInBinary;
 0034  use Twig\Node\Expression\Binary\OrBinary;
 0035  use Twig\Node\Expression\Binary\PowerBinary;
 0036  use Twig\Node\Expression\Binary\RangeBinary;
 0037  use Twig\Node\Expression\Binary\SpaceshipBinary;
 0038  use Twig\Node\Expression\Binary\StartsWithBinary;
 0039  use Twig\Node\Expression\Binary\SubBinary;
 0040  use Twig\Node\Expression\Filter\DefaultFilter;
 0041  use Twig\Node\Expression\NullCoalesceExpression;
 0042  use Twig\Node\Expression\Test\ConstantTest;
 0043  use Twig\Node\Expression\Test\DefinedTest;
 0044  use Twig\Node\Expression\Test\DivisiblebyTest;
 0045  use Twig\Node\Expression\Test\EvenTest;
 0046  use Twig\Node\Expression\Test\NullTest;
 0047  use Twig\Node\Expression\Test\OddTest;
 0048  use Twig\Node\Expression\Test\SameasTest;
 0049  use Twig\Node\Expression\Unary\NegUnary;
 0050  use Twig\Node\Expression\Unary\NotUnary;
 0051  use Twig\Node\Expression\Unary\PosUnary;
 0052  use Twig\NodeVisitor\MacroAutoImportNodeVisitor;
 0053  use Twig\TokenParser\ApplyTokenParser;
 0054  use Twig\TokenParser\BlockTokenParser;
 0055  use Twig\TokenParser\DeprecatedTokenParser;
 0056  use Twig\TokenParser\DoTokenParser;
 0057  use Twig\TokenParser\EmbedTokenParser;
 0058  use Twig\TokenParser\ExtendsTokenParser;
 0059  use Twig\TokenParser\FilterTokenParser;
 0060  use Twig\TokenParser\FlushTokenParser;
 0061  use Twig\TokenParser\ForTokenParser;
 0062  use Twig\TokenParser\FromTokenParser;
 0063  use Twig\TokenParser\IfTokenParser;
 0064  use Twig\TokenParser\ImportTokenParser;
 0065  use Twig\TokenParser\IncludeTokenParser;
 0066  use Twig\TokenParser\MacroTokenParser;
 0067  use Twig\TokenParser\SetTokenParser;
 0068  use Twig\TokenParser\SpacelessTokenParser;
 0069  use Twig\TokenParser\UseTokenParser;
 0070  use Twig\TokenParser\WithTokenParser;
 0071  use Twig\TwigFilter;
 0072  use Twig\TwigFunction;
 0073  use Twig\TwigTest;
 0074   
 0075  final class CoreExtension extends AbstractExtension
 0076  {
 0077      private $dateFormats = ['F j, Y H:i', '%d days'];
 0078      private $numberFormat = [0, '.', ','];
 0079      private $timezone = null;
 0080      private $escapers = [];
 0081   
 0082      /**
 0083       * Defines a new escaper to be used via the escape filter.
 0084       *
 0085       * @param string   $strategy The strategy name that should be used as a strategy in the escape call
 0086       * @param callable $callable A valid PHP callable
 0087       *
 0088       * @deprecated since Twig 2.11, to be removed in 3.0; use the same method on EscaperExtension instead
 0089       */
 0090      public function setEscaper($strategy, callable $callable)
 0091      {
 0092          @trigger_error(sprintf('The "%s" method is deprecated since Twig 2.11; use "%s::setEscaper" instead.', __METHOD__, EscaperExtension::class), \E_USER_DEPRECATED);
 0093   
 0094          $this->escapers[$strategy] = $callable;
 0095      }
 0096   
 0097      /**
 0098       * Gets all defined escapers.
 0099       *
 0100       * @return callable[] An array of escapers
 0101       *
 0102       * @deprecated since Twig 2.11, to be removed in 3.0; use the same method on EscaperExtension instead
 0103       */
 0104      public function getEscapers(/* $triggerDeprecation = true */)
 0105      {
 0106          if (0 === \func_num_args() || \func_get_arg(0)) {
 0107              @trigger_error(sprintf('The "%s" method is deprecated since Twig 2.11; use "%s::getEscapers" instead.', __METHOD__, EscaperExtension::class), \E_USER_DEPRECATED);
 0108          }
 0109   
 0110          return $this->escapers;
 0111      }
 0112   
 0113      /**
 0114       * Sets the default format to be used by the date filter.
 0115       *
 0116       * @param string $format             The default date format string
 0117       * @param string $dateIntervalFormat The default date interval format string
 0118       */
 0119      public function setDateFormat($format = null, $dateIntervalFormat = null)
 0120      {
 0121          if (null !== $format) {
 0122              $this->dateFormats[0] = $format;
 0123          }
 0124   
 0125          if (null !== $dateIntervalFormat) {
 0126              $this->dateFormats[1] = $dateIntervalFormat;
 0127          }
 0128      }
 0129   
 0130      /**
 0131       * Gets the default format to be used by the date filter.
 0132       *
 0133       * @return array The default date format string and the default date interval format string
 0134       */
 0135      public function getDateFormat()
 0136      {
 0137          return $this->dateFormats;
 0138      }
 0139   
 0140      /**
 0141       * Sets the default timezone to be used by the date filter.
 0142       *
 0143       * @param \DateTimeZone|string $timezone The default timezone string or a \DateTimeZone object
 0144       */
 0145      public function setTimezone($timezone)
 0146      {
 0147          $this->timezone = $timezone instanceof \DateTimeZone ? $timezone : new \DateTimeZone($timezone);
 0148      }
 0149   
 0150      /**
 0151       * Gets the default timezone to be used by the date filter.
 0152       *
 0153       * @return \DateTimeZone The default timezone currently in use
 0154       */
 0155      public function getTimezone()
 0156      {
 0157          if (null === $this->timezone) {
 0158              $this->timezone = new \DateTimeZone(date_default_timezone_get());
 0159          }
 0160   
 0161          return $this->timezone;
 0162      }
 0163   
 0164      /**
 0165       * Sets the default format to be used by the number_format filter.
 0166       *
 0167       * @param int    $decimal      the number of decimal places to use
 0168       * @param string $decimalPoint the character(s) to use for the decimal point
 0169       * @param string $thousandSep  the character(s) to use for the thousands separator
 0170       */
 0171      public function setNumberFormat($decimal, $decimalPoint, $thousandSep)
 0172      {
 0173          $this->numberFormat = [$decimal, $decimalPoint, $thousandSep];
 0174      }
 0175   
 0176      /**
 0177       * Get the default format used by the number_format filter.
 0178       *
 0179       * @return array The arguments for number_format()
 0180       */
 0181      public function getNumberFormat()
 0182      {
 0183          return $this->numberFormat;
 0184      }
 0185   
 0186      public function getTokenParsers()
 0187      {
 0188          return [
 0189              new ApplyTokenParser(),
 0190              new ForTokenParser(),
 0191              new IfTokenParser(),
 0192              new ExtendsTokenParser(),
 0193              new IncludeTokenParser(),
 0194              new BlockTokenParser(),
 0195              new UseTokenParser(),
 0196              new FilterTokenParser(),
 0197              new MacroTokenParser(),
 0198              new ImportTokenParser(),
 0199              new FromTokenParser(),
 0200              new SetTokenParser(),
 0201              new SpacelessTokenParser(),
 0202              new FlushTokenParser(),
 0203              new DoTokenParser(),
 0204              new EmbedTokenParser(),
 0205              new WithTokenParser(),
 0206              new DeprecatedTokenParser(),
 0207          ];
 0208      }
 0209   
 0210      public function getFilters()
 0211      {
 0212          return [
 0213              // formatting filters
 0214              new TwigFilter('date', 'twig_date_format_filter', ['needs_environment' => true]),
 0215              new TwigFilter('date_modify', 'twig_date_modify_filter', ['needs_environment' => true]),
 0216              new TwigFilter('format', 'twig_sprintf'),
 0217              new TwigFilter('replace', 'twig_replace_filter'),
 0218              new TwigFilter('number_format', 'twig_number_format_filter', ['needs_environment' => true]),
 0219              new TwigFilter('abs', 'abs'),
 0220              new TwigFilter('round', 'twig_round'),
 0221   
 0222              // encoding
 0223              new TwigFilter('url_encode', 'twig_urlencode_filter'),
 0224              new TwigFilter('json_encode', 'json_encode'),
 0225              new TwigFilter('convert_encoding', 'twig_convert_encoding'),
 0226   
 0227              // string filters
 0228              new TwigFilter('title', 'twig_title_string_filter', ['needs_environment' => true]),
 0229              new TwigFilter('capitalize', 'twig_capitalize_string_filter', ['needs_environment' => true]),
 0230              new TwigFilter('upper', 'twig_upper_filter', ['needs_environment' => true]),
 0231              new TwigFilter('lower', 'twig_lower_filter', ['needs_environment' => true]),
 0232              new TwigFilter('striptags', 'twig_striptags'),
 0233              new TwigFilter('trim', 'twig_trim_filter'),
 0234              new TwigFilter('nl2br', 'twig_nl2br', ['pre_escape' => 'html', 'is_safe' => ['html']]),
 0235              new TwigFilter('spaceless', 'twig_spaceless', ['is_safe' => ['html']]),
 0236   
 0237              // array helpers
 0238              new TwigFilter('join', 'twig_join_filter'),
 0239              new TwigFilter('split', 'twig_split_filter', ['needs_environment' => true]),
 0240              new TwigFilter('sort', 'twig_sort_filter', ['needs_environment' => true]),
 0241              new TwigFilter('merge', 'twig_array_merge'),
 0242              new TwigFilter('batch', 'twig_array_batch'),
 0243              new TwigFilter('column', 'twig_array_column'),
 0244              new TwigFilter('filter', 'twig_array_filter', ['needs_environment' => true]),
 0245              new TwigFilter('map', 'twig_array_map', ['needs_environment' => true]),
 0246              new TwigFilter('reduce', 'twig_array_reduce', ['needs_environment' => true]),
 0247   
 0248              // string/array filters
 0249              new TwigFilter('reverse', 'twig_reverse_filter', ['needs_environment' => true]),
 0250              new TwigFilter('length', 'twig_length_filter', ['needs_environment' => true]),
 0251              new TwigFilter('slice', 'twig_slice', ['needs_environment' => true]),
 0252              new TwigFilter('first', 'twig_first', ['needs_environment' => true]),
 0253              new TwigFilter('last', 'twig_last', ['needs_environment' => true]),
 0254   
 0255              // iteration and runtime
 0256              new TwigFilter('default', '_twig_default_filter', ['node_class' => DefaultFilter::class]),
 0257              new TwigFilter('keys', 'twig_get_array_keys_filter'),
 0258          ];
 0259      }
 0260   
 0261      public function getFunctions()
 0262      {
 0263          return [
 0264              new TwigFunction('max', 'max'),
 0265              new TwigFunction('min', 'min'),
 0266              new TwigFunction('range', 'range'),
 0267              new TwigFunction('constant', 'twig_constant'),
 0268              new TwigFunction('cycle', 'twig_cycle'),
 0269              new TwigFunction('random', 'twig_random', ['needs_environment' => true]),
 0270              new TwigFunction('date', 'twig_date_converter', ['needs_environment' => true]),
 0271              new TwigFunction('include', 'twig_include', ['needs_environment' => true, 'needs_context' => true, 'is_safe' => ['all']]),
 0272              new TwigFunction('source', 'twig_source', ['needs_environment' => true, 'is_safe' => ['all']]),
 0273          ];
 0274      }
 0275   
 0276      public function getTests()
 0277      {
 0278          return [
 0279              new TwigTest('even', null, ['node_class' => EvenTest::class]),
 0280              new TwigTest('odd', null, ['node_class' => OddTest::class]),
 0281              new TwigTest('defined', null, ['node_class' => DefinedTest::class]),
 0282              new TwigTest('same as', null, ['node_class' => SameasTest::class, 'one_mandatory_argument' => true]),
 0283              new TwigTest('none', null, ['node_class' => NullTest::class]),
 0284              new TwigTest('null', null, ['node_class' => NullTest::class]),
 0285              new TwigTest('divisible by', null, ['node_class' => DivisiblebyTest::class, 'one_mandatory_argument' => true]),
 0286              new TwigTest('constant', null, ['node_class' => ConstantTest::class]),
 0287              new TwigTest('empty', 'twig_test_empty'),
 0288              new TwigTest('iterable', 'twig_test_iterable'),
 0289          ];
 0290      }
 0291   
 0292      public function getNodeVisitors()
 0293      {
 0294          return [new MacroAutoImportNodeVisitor()];
 0295      }
 0296   
 0297      public function getOperators()
 0298      {
 0299          return [
 0300              [
 0301                  'not' => ['precedence' => 50, 'class' => NotUnary::class],
 0302                  '-' => ['precedence' => 500, 'class' => NegUnary::class],
 0303                  '+' => ['precedence' => 500, 'class' => PosUnary::class],
 0304              ],
 0305              [
 0306                  'or' => ['precedence' => 10, 'class' => OrBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0307                  'and' => ['precedence' => 15, 'class' => AndBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0308                  'b-or' => ['precedence' => 16, 'class' => BitwiseOrBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0309                  'b-xor' => ['precedence' => 17, 'class' => BitwiseXorBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0310                  'b-and' => ['precedence' => 18, 'class' => BitwiseAndBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0311                  '==' => ['precedence' => 20, 'class' => EqualBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0312                  '!=' => ['precedence' => 20, 'class' => NotEqualBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0313                  '<=>' => ['precedence' => 20, 'class' => SpaceshipBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0314                  '<' => ['precedence' => 20, 'class' => LessBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0315                  '>' => ['precedence' => 20, 'class' => GreaterBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0316                  '>=' => ['precedence' => 20, 'class' => GreaterEqualBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0317                  '<=' => ['precedence' => 20, 'class' => LessEqualBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0318                  'not in' => ['precedence' => 20, 'class' => NotInBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0319                  'in' => ['precedence' => 20, 'class' => InBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0320                  'matches' => ['precedence' => 20, 'class' => MatchesBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0321                  'starts with' => ['precedence' => 20, 'class' => StartsWithBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0322                  'ends with' => ['precedence' => 20, 'class' => EndsWithBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0323                  '..' => ['precedence' => 25, 'class' => RangeBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0324                  '+' => ['precedence' => 30, 'class' => AddBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0325                  '-' => ['precedence' => 30, 'class' => SubBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0326                  '~' => ['precedence' => 40, 'class' => ConcatBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0327                  '*' => ['precedence' => 60, 'class' => MulBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0328                  '/' => ['precedence' => 60, 'class' => DivBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0329                  '//' => ['precedence' => 60, 'class' => FloorDivBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0330                  '%' => ['precedence' => 60, 'class' => ModBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0331                  'is' => ['precedence' => 100, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0332                  'is not' => ['precedence' => 100, 'associativity' => ExpressionParser::OPERATOR_LEFT],
 0333                  '**' => ['precedence' => 200, 'class' => PowerBinary::class, 'associativity' => ExpressionParser::OPERATOR_RIGHT],
 0334                  '??' => ['precedence' => 300, 'class' => NullCoalesceExpression::class, 'associativity' => ExpressionParser::OPERATOR_RIGHT],
 0335              ],
 0336          ];
 0337      }
 0338  }
 0339   
 0340  class_alias('Twig\Extension\CoreExtension', 'Twig_Extension_Core');
 0341  }
 0342   
 0343  namespace {
 0344      use Twig\Environment;
 0345      use Twig\Error\LoaderError;
 0346      use Twig\Error\RuntimeError;
 0347      use Twig\Extension\CoreExtension;
 0348      use Twig\Extension\SandboxExtension;
 0349      use Twig\Markup;
 0350      use Twig\Source;
 0351      use Twig\Template;
 0352      use Twig\TemplateWrapper;
 0353   
 0354  /**
 0355   * Cycles over a value.
 0356   *
 0357   * @param \ArrayAccess|array $values
 0358   * @param int                $position The cycle position
 0359   *
 0360   * @return string The next value in the cycle
 0361   */
 0362  function twig_cycle($values, $position)
 0363  {
 0364      if (!\is_array($values) && !$values instanceof \ArrayAccess) {
 0365          return $values;
 0366      }
 0367   
 0368      return $values[$position % \count($values)];
 0369  }
 0370   
 0371  /**
 0372   * Returns a random value depending on the supplied parameter type:
 0373   * - a random item from a \Traversable or array
 0374   * - a random character from a string
 0375   * - a random integer between 0 and the integer parameter.
 0376   *
 0377   * @param \Traversable|array|int|float|string $values The values to pick a random item from
 0378   * @param int|null                            $max    Maximum value used when $values is an int
 0379   *
 0380   * @throws RuntimeError when $values is an empty array (does not apply to an empty string which is returned as is)
 0381   *
 0382   * @return mixed A random value from the given sequence
 0383   */
 0384  function twig_random(Environment $env, $values = null, $max = null)
 0385  {
 0386      if (null === $values) {
 0387          return null === $max ? mt_rand() : mt_rand(0, (int) $max);
 0388      }
 0389   
 0390      if (\is_int($values) || \is_float($values)) {
 0391          if (null === $max) {
 0392              if ($values < 0) {
 0393                  $max = 0;
 0394                  $min = $values;
 0395              } else {
 0396                  $max = $values;
 0397                  $min = 0;
 0398              }
 0399          } else {
 0400              $min = $values;
 0401              $max = $max;
 0402          }
 0403   
 0404          return mt_rand((int) $min, (int) $max);
 0405      }
 0406   
 0407      if (\is_string($values)) {
 0408          if ('' === $values) {
 0409              return '';
 0410          }
 0411   
 0412          $charset = $env->getCharset();
 0413   
 0414          if ('UTF-8' !== $charset) {
 0415              $values = twig_convert_encoding($values, 'UTF-8', $charset);
 0416          }
 0417   
 0418          // unicode version of str_split()
 0419          // split at all positions, but not after the start and not before the end
 0420          $values = preg_split('/(?<!^)(?!$)/u', $values);
 0421   
 0422          if ('UTF-8' !== $charset) {
 0423              foreach ($values as $i => $value) {
 0424                  $values[$i] = twig_convert_encoding($value, $charset, 'UTF-8');
 0425              }
 0426          }
 0427      }
 0428   
 0429      if (!twig_test_iterable($values)) {
 0430          return $values;
 0431      }
 0432   
 0433      $values = twig_to_array($values);
 0434   
 0435      if (0 === \count($values)) {
 0436          throw new RuntimeError('The random function cannot pick from an empty array.');
 0437      }
 0438   
 0439      return $values[array_rand($values, 1)];
 0440  }
 0441   
 0442  /**
 0443   * Converts a date to the given format.
 0444   *
 0445   *   {{ post.published_at|date("m/d/Y") }}
 0446   *
 0447   * @param \DateTimeInterface|\DateInterval|string $date     A date
 0448   * @param string|null                             $format   The target format, null to use the default
 0449   * @param \DateTimeZone|string|false|null         $timezone The target timezone, null to use the default, false to leave unchanged
 0450   *
 0451   * @return string The formatted date
 0452   */
 0453  function twig_date_format_filter(Environment $env, $date, $format = null, $timezone = null)
 0454  {
 0455      if (null === $format) {
 0456          $formats = $env->getExtension(CoreExtension::class)->getDateFormat();
 0457          $format = $date instanceof \DateInterval ? $formats[1] : $formats[0];
 0458      }
 0459   
 0460      if ($date instanceof \DateInterval) {
 0461          return $date->format($format);
 0462      }
 0463   
 0464      return twig_date_converter($env, $date, $timezone)->format($format);
 0465  }
 0466   
 0467  /**
 0468   * Returns a new date object modified.
 0469   *
 0470   *   {{ post.published_at|date_modify("-1day")|date("m/d/Y") }}
 0471   *
 0472   * @param \DateTimeInterface|string $date     A date
 0473   * @param string                    $modifier A modifier string
 0474   *
 0475   * @return \DateTimeInterface
 0476   */
 0477  function twig_date_modify_filter(Environment $env, $date, $modifier)
 0478  {
 0479      $date = twig_date_converter($env, $date, false);
 0480   
 0481      return $date->modify($modifier);
 0482  }
 0483   
 0484  /**
 0485   * Returns a formatted string.
 0486   *
 0487   * @param string|null $format
 0488   * @param ...$values
 0489   *
 0490   * @return string
 0491   */
 0492  function twig_sprintf($format, ...$values)
 0493  {
 0494      return sprintf($format ?? '', ...$values);
 0495  }
 0496   
 0497  /**
 0498   * Converts an input to a \DateTime instance.
 0499   *
 0500   *    {% if date(user.created_at) < date('+2days') %}
 0501   *      {# do something #}
 0502   *    {% endif %}
 0503   *
 0504   * @param \DateTimeInterface|string|null  $date     A date or null to use the current time
 0505   * @param \DateTimeZone|string|false|null $timezone The target timezone, null to use the default, false to leave unchanged
 0506   *
 0507   * @return \DateTimeInterface
 0508   */
 0509  function twig_date_converter(Environment $env, $date = null, $timezone = null)
 0510  {
 0511      // determine the timezone
 0512      if (false !== $timezone) {
 0513          if (null === $timezone) {
 0514              $timezone = $env->getExtension(CoreExtension::class)->getTimezone();
 0515          } elseif (!$timezone instanceof \DateTimeZone) {
 0516              $timezone = new \DateTimeZone($timezone);
 0517          }
 0518      }
 0519   
 0520      // immutable dates
 0521      if ($date instanceof \DateTimeImmutable) {
 0522          return false !== $timezone ? $date->setTimezone($timezone) : $date;
 0523      }
 0524   
 0525      if ($date instanceof \DateTimeInterface) {
 0526          $date = clone $date;
 0527          if (false !== $timezone) {
 0528              $date->setTimezone($timezone);
 0529          }
 0530   
 0531          return $date;
 0532      }
 0533   
 0534      if (null === $date || 'now' === $date) {
 0535          if (null === $date) {
 0536              $date = 'now';
 0537          }
 0538   
 0539          return new \DateTime($date, false !== $timezone ? $timezone : $env->getExtension(CoreExtension::class)->getTimezone());
 0540      }
 0541   
 0542      $asString = (string) $date;
 0543      if (ctype_digit($asString) || (!empty($asString) && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) {
 0544          $date = new \DateTime('@'.$date);
 0545      } else {
 0546          $date = new \DateTime($date, $env->getExtension(CoreExtension::class)->getTimezone());
 0547      }
 0548   
 0549      if (false !== $timezone) {
 0550          $date->setTimezone($timezone);
 0551      }
 0552   
 0553      return $date;
 0554  }
 0555   
 0556  /**
 0557   * Replaces strings within a string.
 0558   *
 0559   * @param string|null        $str  String to replace in
 0560   * @param array|\Traversable $from Replace values
 0561   *
 0562   * @return string
 0563   */
 0564  function twig_replace_filter($str, $from)
 0565  {
 0566      if (!twig_test_iterable($from)) {
 0567          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)));
 0568      }
 0569   
 0570      return strtr($str ?? '', twig_to_array($from));
 0571  }
 0572   
 0573  /**
 0574   * Rounds a number.
 0575   *
 0576   * @param int|float|string|null $value     The value to round
 0577   * @param int|float             $precision The rounding precision
 0578   * @param string                $method    The method to use for rounding
 0579   *
 0580   * @return int|float The rounded number
 0581   */
 0582  function twig_round($value, $precision = 0, $method = 'common')
 0583  {
 0584      $value = (float) $value;
 0585   
 0586      if ('common' === $method) {
 0587          return round($value, $precision);
 0588      }
 0589   
 0590      if ('ceil' !== $method && 'floor' !== $method) {
 0591          throw new RuntimeError('The round filter only supports the "common", "ceil", and "floor" methods.');
 0592      }
 0593   
 0594      return $method($value * 10 ** $precision) / 10 ** $precision;
 0595  }
 0596   
 0597  /**
 0598   * Number format filter.
 0599   *
 0600   * All of the formatting options can be left null, in that case the defaults will
 0601   * be used. Supplying any of the parameters will override the defaults set in the
 0602   * environment object.
 0603   *
 0604   * @param mixed  $number       A float/int/string of the number to format
 0605   * @param int    $decimal      the number of decimal points to display
 0606   * @param string $decimalPoint the character(s) to use for the decimal point
 0607   * @param string $thousandSep  the character(s) to use for the thousands separator
 0608   *
 0609   * @return string The formatted number
 0610   */
 0611  function twig_number_format_filter(Environment $env, $number, $decimal = null, $decimalPoint = null, $thousandSep = null)
 0612  {
 0613      $defaults = $env->getExtension(CoreExtension::class)->getNumberFormat();
 0614      if (null === $decimal) {
 0615          $decimal = $defaults[0];
 0616      }
 0617   
 0618      if (null === $decimalPoint) {
 0619          $decimalPoint = $defaults[1];
 0620      }
 0621   
 0622      if (null === $thousandSep) {
 0623          $thousandSep = $defaults[2];
 0624      }
 0625   
 0626      return number_format((float) $number, $decimal, $decimalPoint, $thousandSep);
 0627  }
 0628   
 0629  /**
 0630   * URL encodes (RFC 3986) a string as a path segment or an array as a query string.
 0631   *
 0632   * @param string|array|null $url A URL or an array of query parameters
 0633   *
 0634   * @return string The URL encoded value
 0635   */
 0636  function twig_urlencode_filter($url)
 0637  {
 0638      if (\is_array($url)) {
 0639          return http_build_query($url, '', '&', \PHP_QUERY_RFC3986);
 0640      }
 0641   
 0642      return rawurlencode($url ?? '');
 0643  }
 0644   
 0645  /**
 0646   * Merges an array with another one.
 0647   *
 0648   *  {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %}
 0649   *
 0650   *  {% set items = items|merge({ 'peugeot': 'car' }) %}
 0651   *
 0652   *  {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car' } #}
 0653   *
 0654   * @param array|\Traversable $arr1 An array
 0655   * @param array|\Traversable $arr2 An array
 0656   *
 0657   * @return array The merged array
 0658   */
 0659  function twig_array_merge($arr1, $arr2)
 0660  {
 0661      if (!twig_test_iterable($arr1)) {
 0662          throw new RuntimeError(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as first argument.', \gettype($arr1)));
 0663      }
 0664   
 0665      if (!twig_test_iterable($arr2)) {
 0666          throw new RuntimeError(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as second argument.', \gettype($arr2)));
 0667      }
 0668   
 0669      return array_merge(twig_to_array($arr1), twig_to_array($arr2));
 0670  }
 0671   
 0672  /**
 0673   * Slices a variable.
 0674   *
 0675   * @param mixed $item         A variable
 0676   * @param int   $start        Start of the slice
 0677   * @param int   $length       Size of the slice
 0678   * @param bool  $preserveKeys Whether to preserve key or not (when the input is an array)
 0679   *
 0680   * @return mixed The sliced variable
 0681   */
 0682  function twig_slice(Environment $env, $item, $start, $length = null, $preserveKeys = false)
 0683  {
 0684      if ($item instanceof \Traversable) {
 0685          while ($item instanceof \IteratorAggregate) {
 0686              $item = $item->getIterator();
 0687          }
 0688   
 0689          if ($start >= 0 && $length >= 0 && $item instanceof \Iterator) {
 0690              try {
 0691                  return iterator_to_array(new \LimitIterator($item, $start, null === $length ? -1 : $length), $preserveKeys);
 0692              } catch (\OutOfBoundsException $e) {
 0693                  return [];
 0694              }
 0695          }
 0696   
 0697          $item = iterator_to_array($item, $preserveKeys);
 0698      }
 0699   
 0700      if (\is_array($item)) {
 0701          return \array_slice($item, $start, $length, $preserveKeys);
 0702      }
 0703   
 0704      return (string) mb_substr((string) $item, $start, $length, $env->getCharset());
 0705  }
 0706   
 0707  /**
 0708   * Returns the first element of the item.
 0709   *
 0710   * @param mixed $item A variable
 0711   *
 0712   * @return mixed The first element of the item
 0713   */
 0714  function twig_first(Environment $env, $item)
 0715  {
 0716      $elements = twig_slice($env, $item, 0, 1, false);
 0717   
 0718      return \is_string($elements) ? $elements : current($elements);
 0719  }
 0720   
 0721  /**
 0722   * Returns the last element of the item.
 0723   *
 0724   * @param mixed $item A variable
 0725   *
 0726   * @return mixed The last element of the item
 0727   */
 0728  function twig_last(Environment $env, $item)
 0729  {
 0730      $elements = twig_slice($env, $item, -1, 1, false);
 0731   
 0732      return \is_string($elements) ? $elements : current($elements);
 0733  }
 0734   
 0735  /**
 0736   * Joins the values to a string.
 0737   *
 0738   * The separators between elements are empty strings per default, you can define them with the optional parameters.
 0739   *
 0740   *  {{ [1, 2, 3]|join(', ', ' and ') }}
 0741   *  {# returns 1, 2 and 3 #}
 0742   *
 0743   *  {{ [1, 2, 3]|join('|') }}
 0744   *  {# returns 1|2|3 #}
 0745   *
 0746   *  {{ [1, 2, 3]|join }}
 0747   *  {# returns 123 #}
 0748   *
 0749   * @param array       $value An array
 0750   * @param string      $glue  The separator
 0751   * @param string|null $and   The separator for the last pair
 0752   *
 0753   * @return string The concatenated string
 0754   */
 0755  function twig_join_filter($value, $glue = '', $and = null)
 0756  {
 0757      if (!twig_test_iterable($value)) {
 0758          $value = (array) $value;
 0759      }
 0760   
 0761      $value = twig_to_array($value, false);
 0762   
 0763      if (0 === \count($value)) {
 0764          return '';
 0765      }
 0766   
 0767      if (null === $and || $and === $glue) {
 0768          return implode($glue, $value);
 0769      }
 0770   
 0771      if (1 === \count($value)) {
 0772          return $value[0];
 0773      }
 0774   
 0775      return implode($glue, \array_slice($value, 0, -1)).$and.$value[\count($value) - 1];
 0776  }
 0777   
 0778  /**
 0779   * Splits the string into an array.
 0780   *
 0781   *  {{ "one,two,three"|split(',') }}
 0782   *  {# returns [one, two, three] #}
 0783   *
 0784   *  {{ "one,two,three,four,five"|split(',', 3) }}
 0785   *  {# returns [one, two, "three,four,five"] #}
 0786   *
 0787   *  {{ "123"|split('') }}
 0788   *  {# returns [1, 2, 3] #}
 0789   *
 0790   *  {{ "aabbcc"|split('', 2) }}
 0791   *  {# returns [aa, bb, cc] #}
 0792   *
 0793   * @param string|null $value     A string
 0794   * @param string      $delimiter The delimiter
 0795   * @param int         $limit     The limit
 0796   *
 0797   * @return array The split string as an array
 0798   */
 0799  function twig_split_filter(Environment $env, $value, $delimiter, $limit = null)
 0800  {
 0801      $value = $value ?? '';
 0802   
 0803      if (\strlen($delimiter) > 0) {
 0804          return null === $limit ? explode($delimiter, $value) : explode($delimiter, $value, $limit);
 0805      }
 0806   
 0807      if ($limit <= 1) {
 0808          return preg_split('/(?<!^)(?!$)/u', $value);
 0809      }
 0810   
 0811      $length = mb_strlen($value, $env->getCharset());
 0812      if ($length < $limit) {
 0813          return [$value];
 0814      }
 0815   
 0816      $r = [];
 0817      for ($i = 0; $i < $length; $i += $limit) {
 0818          $r[] = mb_substr($value, $i, $limit, $env->getCharset());
 0819      }
 0820   
 0821      return $r;
 0822  }
 0823   
 0824  // The '_default' filter is used internally to avoid using the ternary operator
 0825  // which costs a lot for big contexts (before PHP 5.4). So, on average,
 0826  // a function call is cheaper.
 0827  /**
 0828   * @internal
 0829   */
 0830  function _twig_default_filter($value, $default = '')
 0831  {
 0832      if (twig_test_empty($value)) {
 0833          return $default;
 0834      }
 0835   
 0836      return $value;
 0837  }
 0838   
 0839  /**
 0840   * Returns the keys for the given array.
 0841   *
 0842   * It is useful when you want to iterate over the keys of an array:
 0843   *
 0844   *  {% for key in array|keys %}
 0845   *      {# ... #}
 0846   *  {% endfor %}
 0847   *
 0848   * @param array $array An array
 0849   *
 0850   * @return array The keys
 0851   */
 0852  function twig_get_array_keys_filter($array)
 0853  {
 0854      if ($array instanceof \Traversable) {
 0855          while ($array instanceof \IteratorAggregate) {
 0856              $array = $array->getIterator();
 0857          }
 0858   
 0859          if ($array instanceof \Iterator) {
 0860              $keys = [];
 0861              $array->rewind();
 0862              while ($array->valid()) {
 0863                  $keys[] = $array->key();
 0864                  $array->next();
 0865              }
 0866   
 0867              return $keys;
 0868          }
 0869   
 0870          $keys = [];
 0871          foreach ($array as $key => $item) {
 0872              $keys[] = $key;
 0873          }
 0874   
 0875          return $keys;
 0876      }
 0877   
 0878      if (!\is_array($array)) {
 0879          return [];
 0880      }
 0881   
 0882      return array_keys($array);
 0883  }
 0884   
 0885  /**
 0886   * Reverses a variable.
 0887   *
 0888   * @param array|\Traversable|string|null $item         An array, a \Traversable instance, or a string
 0889   * @param bool                           $preserveKeys Whether to preserve key or not
 0890   *
 0891   * @return mixed The reversed input
 0892   */
 0893  function twig_reverse_filter(Environment $env, $item, $preserveKeys = false)
 0894  {
 0895      if ($item instanceof \Traversable) {
 0896          return array_reverse(iterator_to_array($item), $preserveKeys);
 0897      }
 0898   
 0899      if (\is_array($item)) {
 0900          return array_reverse($item, $preserveKeys);
 0901      }
 0902   
 0903      $string = (string) $item;
 0904   
 0905      $charset = $env->getCharset();
 0906   
 0907      if ('UTF-8' !== $charset) {
 0908          $string = twig_convert_encoding($string, 'UTF-8', $charset);
 0909      }
 0910   
 0911      preg_match_all('/./us', $string, $matches);
 0912   
 0913      $string = implode('', array_reverse($matches[0]));
 0914   
 0915      if ('UTF-8' !== $charset) {
 0916          $string = twig_convert_encoding($string, $charset, 'UTF-8');
 0917      }
 0918   
 0919      return $string;
 0920  }
 0921   
 0922  /**
 0923   * Sorts an array.
 0924   *
 0925   * @param array|\Traversable $array
 0926   *
 0927   * @return array
 0928   */
 0929  function twig_sort_filter(Environment $env, $array, $arrow = null)
 0930  {
 0931      if ($array instanceof \Traversable) {
 0932          $array = iterator_to_array($array);
 0933      } elseif (!\is_array($array)) {
 0934          throw new RuntimeError(sprintf('The sort filter only works with arrays or "Traversable", got "%s".', \gettype($array)));
 0935      }
 0936   
 0937      if (null !== $arrow) {
 0938          twig_check_arrow_in_sandbox($env, $arrow, 'sort', 'filter');
 0939   
 0940          uasort($array, $arrow);
 0941      } else {
 0942          asort($array);
 0943      }
 0944   
 0945      return $array;
 0946  }
 0947   
 0948  /**
 0949   * @internal
 0950   */
 0951  function twig_in_filter($value, $compare)
 0952  {
 0953      if ($value instanceof Markup) {
 0954          $value = (string) $value;
 0955      }
 0956      if ($compare instanceof Markup) {
 0957          $compare = (string) $compare;
 0958      }
 0959   
 0960      if (\is_array($compare)) {
 0961          return \in_array($value, $compare, \is_object($value) || \is_resource($value));
 0962      } elseif (\is_string($compare) && (\is_string($value) || \is_int($value) || \is_float($value))) {
 0963          return '' === $value || false !== strpos($compare, (string) $value);
 0964      } elseif ($compare instanceof \Traversable) {
 0965          if (\is_object($value) || \is_resource($value)) {
 0966              foreach ($compare as $item) {
 0967                  if ($item === $value) {
 0968                      return true;
 0969                  }
 0970              }
 0971          } else {
 0972              foreach ($compare as $item) {
 0973                  if ($item == $value) {
 0974                      return true;
 0975                  }
 0976              }
 0977          }
 0978   
 0979          return false;
 0980      }
 0981   
 0982      return false;
 0983  }
 0984   
 0985  /**
 0986   * Returns a trimmed string.
 0987   *
 0988   * @param string|null $string
 0989   * @param string|null $characterMask
 0990   * @param string      $side
 0991   *
 0992   * @return string
 0993   *
 0994   * @throws RuntimeError When an invalid trimming side is used (not a string or not 'left', 'right', or 'both')
 0995   */
 0996  function twig_trim_filter($string, $characterMask = null, $side = 'both')
 0997  {
 0998      if (null === $characterMask) {
 0999          $characterMask = " \t\n\r\0\x0B";
 1000      }
 1001   
 1002      switch ($side) {
 1003          case 'both':
 1004              return trim($string ?? '', $characterMask);
 1005          case 'left':
 1006              return ltrim($string ?? '', $characterMask);
 1007          case 'right':
 1008              return rtrim($string ?? '', $characterMask);
 1009          default:
 1010              throw new RuntimeError('Trimming side must be "left", "right" or "both".');
 1011      }
 1012  }
 1013   
 1014  /**
 1015   * Inserts HTML line breaks before all newlines in a string.
 1016   *
 1017   * @param string|null $string
 1018   *
 1019   * @return string
 1020   */
 1021  function twig_nl2br($string)
 1022  {
 1023      return nl2br($string ?? '');
 1024  }
 1025   
 1026  /**
 1027   * Removes whitespaces between HTML tags.
 1028   *
 1029   * @param string|null $string
 1030   *
 1031   * @return string
 1032   */
 1033  function twig_spaceless($content)
 1034  {
 1035      return trim(preg_replace('/>\s+</', '><', $content ?? ''));
 1036  }
 1037   
 1038  /**
 1039   * @param string|null $string
 1040   * @param string      $to
 1041   * @param string      $from
 1042   *
 1043   * @return string
 1044   */
 1045  function twig_convert_encoding($string, $to, $from)
 1046  {
 1047      if (!\function_exists('iconv')) {
 1048          throw new RuntimeError('Unable to convert encoding: required function iconv() does not exist. You should install ext-iconv or symfony/polyfill-iconv.');
 1049      }
 1050   
 1051      return iconv($from, $to, $string ?? '');
 1052  }
 1053   
 1054  /**
 1055   * Returns the length of a variable.
 1056   *
 1057   * @param mixed $thing A variable
 1058   *
 1059   * @return int The length of the value
 1060   */
 1061  function twig_length_filter(Environment $env, $thing)
 1062  {
 1063      if (null === $thing) {
 1064          return 0;
 1065      }
 1066   
 1067      if (is_scalar($thing)) {
 1068          return mb_strlen($thing, $env->getCharset());
 1069      }
 1070   
 1071      if ($thing instanceof \Countable || \is_array($thing) || $thing instanceof \SimpleXMLElement) {
 1072          return \count($thing);
 1073      }
 1074   
 1075      if ($thing instanceof \Traversable) {
 1076          return iterator_count($thing);
 1077      }
 1078   
 1079      if (method_exists($thing, '__toString') && !$thing instanceof \Countable) {
 1080          return mb_strlen((string) $thing, $env->getCharset());
 1081      }
 1082   
 1083      return 1;
 1084  }
 1085   
 1086  /**
 1087   * Converts a string to uppercase.
 1088   *
 1089   * @param string|null $string A string
 1090   *
 1091   * @return string The uppercased string
 1092   */
 1093  function twig_upper_filter(Environment $env, $string)
 1094  {
 1095      return mb_strtoupper($string ?? '', $env->getCharset());
 1096  }
 1097   
 1098  /**
 1099   * Converts a string to lowercase.
 1100   *
 1101   * @param string|null $string A string
 1102   *
 1103   * @return string The lowercased string
 1104   */
 1105  function twig_lower_filter(Environment $env, $string)
 1106  {
 1107      return mb_strtolower($string ?? '', $env->getCharset());
 1108  }
 1109   
 1110  /**
 1111   * Strips HTML and PHP tags from a string.
 1112   *
 1113   * @param string|null $string
 1114   * @param string[]|string|null $string
 1115   *
 1116   * @return string
 1117   */
 1118  function twig_striptags($string, $allowable_tags = null)
 1119  {
 1120      return strip_tags($string ?? '', $allowable_tags);
 1121  }
 1122   
 1123  /**
 1124   * Returns a titlecased string.
 1125   *
 1126   * @param string|null $string A string
 1127   *
 1128   * @return string The titlecased string
 1129   */
 1130  function twig_title_string_filter(Environment $env, $string)
 1131  {
 1132      if (null !== $charset = $env->getCharset()) {
 1133          return mb_convert_case($string ?? '', \MB_CASE_TITLE, $charset);
 1134      }
 1135   
 1136      return ucwords(strtolower($string ?? ''));
 1137  }
 1138   
 1139  /**
 1140   * Returns a capitalized string.
 1141   *
 1142   * @param string|null $string A string
 1143   *
 1144   * @return string The capitalized string
 1145   */
 1146  function twig_capitalize_string_filter(Environment $env, $string)
 1147  {
 1148      $charset = $env->getCharset();
 1149   
 1150      return mb_strtoupper(mb_substr($string ?? '', 0, 1, $charset), $charset).mb_strtolower(mb_substr($string ?? '', 1, null, $charset), $charset);
 1151  }
 1152   
 1153  /**
 1154   * @internal
 1155   */
 1156  function twig_call_macro(Template $template, string $method, array $args, int $lineno, array $context, Source $source)
 1157  {
 1158      if (!method_exists($template, $method)) {
 1159          $parent = $template;
 1160          while ($parent = $parent->getParent($context)) {
 1161              if (method_exists($parent, $method)) {
 1162                  return $parent->$method(...$args);
 1163              }
 1164          }
 1165   
 1166          throw new RuntimeError(sprintf('Macro "%s" is not defined in template "%s".', substr($method, \strlen('macro_')), $template->getTemplateName()), $lineno, $source);
 1167      }
 1168   
 1169      return $template->$method(...$args);
 1170  }
 1171   
 1172  /**
 1173   * @internal
 1174   */
 1175  function twig_ensure_traversable($seq)
 1176  {
 1177      if ($seq instanceof \Traversable || \is_array($seq)) {
 1178          return $seq;
 1179      }
 1180   
 1181      return [];
 1182  }
 1183   
 1184  /**
 1185   * @internal
 1186   */
 1187  function twig_to_array($seq, $preserveKeys = true)
 1188  {
 1189      if ($seq instanceof \Traversable) {
 1190          return iterator_to_array($seq, $preserveKeys);
 1191      }
 1192   
 1193      if (!\is_array($seq)) {
 1194          return $seq;
 1195      }
 1196   
 1197      return $preserveKeys ? $seq : array_values($seq);
 1198  }
 1199   
 1200  /**
 1201   * Checks if a variable is empty.
 1202   *
 1203   *    {# evaluates to true if the foo variable is null, false, or the empty string #}
 1204   *    {% if foo is empty %}
 1205   *        {# ... #}
 1206   *    {% endif %}
 1207   *
 1208   * @param mixed $value A variable
 1209   *
 1210   * @return bool true if the value is empty, false otherwise
 1211   */
 1212  function twig_test_empty($value)
 1213  {
 1214      if ($value instanceof \Countable) {
 1215          return 0 === \count($value);
 1216      }
 1217   
 1218      if ($value instanceof \Traversable) {
 1219          return !iterator_count($value);
 1220      }
 1221   
 1222      if (\is_object($value) && method_exists($value, '__toString')) {
 1223          return '' === (string) $value;
 1224      }
 1225   
 1226      return '' === $value || false === $value || null === $value || [] === $value;
 1227  }
 1228   
 1229  /**
 1230   * Checks if a variable is traversable.
 1231   *
 1232   *    {# evaluates to true if the foo variable is an array or a traversable object #}
 1233   *    {% if foo is iterable %}
 1234   *        {# ... #}
 1235   *    {% endif %}
 1236   *
 1237   * @param mixed $value A variable
 1238   *
 1239   * @return bool true if the value is traversable
 1240   */
 1241  function twig_test_iterable($value)
 1242  {
 1243      return $value instanceof \Traversable || \is_array($value);
 1244  }
 1245   
 1246  /**
 1247   * Renders a template.
 1248   *
 1249   * @param array        $context
 1250   * @param string|array $template      The template to render or an array of templates to try consecutively
 1251   * @param array        $variables     The variables to pass to the template
 1252   * @param bool         $withContext
 1253   * @param bool         $ignoreMissing Whether to ignore missing templates or not
 1254   * @param bool         $sandboxed     Whether to sandbox the template or not
 1255   *
 1256   * @return string The rendered template
 1257   */
 1258  function twig_include(Environment $env, $context, $template, $variables = [], $withContext = true, $ignoreMissing = false, $sandboxed = false)
 1259  {
 1260      $alreadySandboxed = false;
 1261      $sandbox = null;
 1262      if ($withContext) {
 1263          $variables = array_merge($context, $variables);
 1264      }
 1265   
 1266      if ($isSandboxed = $sandboxed && $env->hasExtension(SandboxExtension::class)) {
 1267          $sandbox = $env->getExtension(SandboxExtension::class);
 1268          if (!$alreadySandboxed = $sandbox->isSandboxed()) {
 1269              $sandbox->enableSandbox();
 1270          }
 1271      }
 1272   
 1273      try {
 1274          $loaded = null;
 1275          try {
 1276              $loaded = $env->resolveTemplate($template);
 1277          } catch (LoaderError $e) {
 1278              if (!$ignoreMissing) {
 1279                  throw $e;
 1280              }
 1281          }
 1282   
 1283          if ($isSandboxed && $loaded) {
 1284              $loaded->unwrap()->checkSecurity();
 1285          }
 1286   
 1287          return $loaded ? $loaded->render($variables) : '';
 1288      } finally {
 1289          if ($isSandboxed && !$alreadySandboxed) {
 1290              $sandbox->disableSandbox();
 1291          }
 1292      }
 1293  }
 1294   
 1295  /**
 1296   * Returns a template content without rendering it.
 1297   *
 1298   * @param string $name          The template name
 1299   * @param bool   $ignoreMissing Whether to ignore missing templates or not
 1300   *
 1301   * @return string The template source
 1302   */
 1303  function twig_source(Environment $env, $name, $ignoreMissing = false)
 1304  {
 1305      $loader = $env->getLoader();
 1306      try {
 1307          return $loader->getSourceContext($name)->getCode();
 1308      } catch (LoaderError $e) {
 1309          if (!$ignoreMissing) {
 1310              throw $e;
 1311          }
 1312      }
 1313  }
 1314   
 1315  /**
 1316   * Provides the ability to get constants from instances as well as class/global constants.
 1317   *
 1318   * @param string      $constant The name of the constant
 1319   * @param object|null $object   The object to get the constant from
 1320   *
 1321   * @return string
 1322   */
 1323  function twig_constant($constant, $object = null)
 1324  {
 1325      if (null !== $object) {
 1326          $constant = \get_class($object).'::'.$constant;
 1327      }
 1328   
 1329      if (!\defined($constant)) {
 1330          throw new RuntimeError(sprintf('Constant "%s" is undefined.', $constant));
 1331      }
 1332   
 1333      return \constant($constant);
 1334  }
 1335   
 1336  /**
 1337   * Checks if a constant exists.
 1338   *
 1339   * @param string      $constant The name of the constant
 1340   * @param object|null $object   The object to get the constant from
 1341   *
 1342   * @return bool
 1343   */
 1344  function twig_constant_is_defined($constant, $object = null)
 1345  {
 1346      if (null !== $object) {
 1347          $constant = \get_class($object).'::'.$constant;
 1348      }
 1349   
 1350      return \defined($constant);
 1351  }
 1352   
 1353  /**
 1354   * Batches item.
 1355   *
 1356   * @param array $items An array of items
 1357   * @param int   $size  The size of the batch
 1358   * @param mixed $fill  A value used to fill missing items
 1359   *
 1360   * @return array
 1361   */
 1362  function twig_array_batch($items, $size, $fill = null, $preserveKeys = true)
 1363  {
 1364      if (!twig_test_iterable($items)) {
 1365          throw new RuntimeError(sprintf('The "batch" filter expects an array or "Traversable", got "%s".', \is_object($items) ? \get_class($items) : \gettype($items)));
 1366      }
 1367   
 1368      $size = ceil($size);
 1369   
 1370      $result = array_chunk(twig_to_array($items, $preserveKeys), $size, $preserveKeys);
 1371   
 1372      if (null !== $fill && $result) {
 1373          $last = \count($result) - 1;
 1374          if ($fillCount = $size - \count($result[$last])) {
 1375              for ($i = 0; $i < $fillCount; ++$i) {
 1376                  $result[$last][] = $fill;
 1377              }
 1378          }
 1379      }
 1380   
 1381      return $result;
 1382  }
 1383   
 1384  /**
 1385   * Returns the attribute value for a given array/object.
 1386   *
 1387   * @param mixed  $object            The object or array from where to get the item
 1388   * @param mixed  $item              The item to get from the array or object
 1389   * @param array  $arguments         An array of arguments to pass if the item is an object method
 1390   * @param string $type              The type of attribute (@see \Twig\Template constants)
 1391   * @param bool   $isDefinedTest     Whether this is only a defined check
 1392   * @param bool   $ignoreStrictCheck Whether to ignore the strict attribute check or not
 1393   * @param int    $lineno            The template line where the attribute was called
 1394   *
 1395   * @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true
 1396   *
 1397   * @throws RuntimeError if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false
 1398   *
 1399   * @internal
 1400   */
 1401  function twig_get_attribute(Environment $env, Source $source, $object, $item, array $arguments = [], $type = /* Template::ANY_CALL */ 'any', $isDefinedTest = false, $ignoreStrictCheck = false, $sandboxed = false, int $lineno = -1)
 1402  {
 1403      // array
 1404      if (/* Template::METHOD_CALL */ 'method' !== $type) {
 1405          $arrayItem = \is_bool($item) || \is_float($item) ? (int) $item : $item;
 1406   
 1407          if (((\is_array($object) || $object instanceof \ArrayObject) && (isset($object[$arrayItem]) || \array_key_exists($arrayItem, (array) $object)))
 1408              || ($object instanceof ArrayAccess && isset($object[$arrayItem]))
 1409          ) {
 1410              if ($isDefinedTest) {
 1411                  return true;
 1412              }
 1413   
 1414              return $object[$arrayItem];
 1415          }
 1416   
 1417          if (/* Template::ARRAY_CALL */ 'array' === $type || !\is_object($object)) {
 1418              if ($isDefinedTest) {
 1419                  return false;
 1420              }
 1421   
 1422              if ($ignoreStrictCheck || !$env->isStrictVariables()) {
 1423                  return;
 1424              }
 1425   
 1426              if ($object instanceof ArrayAccess) {
 1427                  $message = sprintf('Key "%s" in object with ArrayAccess of class "%s" does not exist.', $arrayItem, \get_class($object));
 1428              } elseif (\is_object($object)) {
 1429                  $message = sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface.', $item, \get_class($object));
 1430              } elseif (\is_array($object)) {
 1431                  if (empty($object)) {
 1432                      $message = sprintf('Key "%s" does not exist as the array is empty.', $arrayItem);
 1433                  } else {
 1434                      $message = sprintf('Key "%s" for array with keys "%s" does not exist.', $arrayItem, implode(', ', array_keys($object)));
 1435                  }
 1436              } elseif (/* Template::ARRAY_CALL */ 'array' === $type) {
 1437                  if (null === $object) {
 1438                      $message = sprintf('Impossible to access a key ("%s") on a null variable.', $item);
 1439                  } else {
 1440                      $message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s").', $item, \gettype($object), $object);
 1441                  }
 1442              } elseif (null === $object) {
 1443                  $message = sprintf('Impossible to access an attribute ("%s") on a null variable.', $item);
 1444              } else {
 1445                  $message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s").', $item, \gettype($object), $object);
 1446              }
 1447   
 1448              throw new RuntimeError($message, $lineno, $source);
 1449          }
 1450      }
 1451   
 1452      if (!\is_object($object)) {
 1453          if ($isDefinedTest) {
 1454              return false;
 1455          }
 1456   
 1457          if ($ignoreStrictCheck || !$env->isStrictVariables()) {
 1458              return;
 1459          }
 1460   
 1461          if (null === $object) {
 1462              $message = sprintf('Impossible to invoke a method ("%s") on a null variable.', $item);
 1463          } elseif (\is_array($object)) {
 1464              $message = sprintf('Impossible to invoke a method ("%s") on an array.', $item);
 1465          } else {
 1466              $message = sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s").', $item, \gettype($object), $object);
 1467          }
 1468   
 1469          throw new RuntimeError($message, $lineno, $source);
 1470      }
 1471   
 1472      if ($object instanceof Template) {
 1473          throw new RuntimeError('Accessing \Twig\Template attributes is forbidden.', $lineno, $source);
 1474      }
 1475   
 1476      // object property
 1477      if (/* Template::METHOD_CALL */ 'method' !== $type) {
 1478          if (isset($object->$item) || \array_key_exists((string) $item, (array) $object)) {
 1479              if ($isDefinedTest) {
 1480                  return true;
 1481              }
 1482   
 1483              if ($sandboxed) {
 1484                  $env->getExtension(SandboxExtension::class)->checkPropertyAllowed($object, $item, $lineno, $source);
 1485              }
 1486   
 1487              return $object->$item;
 1488          }
 1489      }
 1490   
 1491      static $cache = [];
 1492   
 1493      $class = \get_class($object);
 1494   
 1495      // object method
 1496      // precedence: getXxx() > isXxx() > hasXxx()
 1497      if (!isset($cache[$class])) {
 1498          $methods = get_class_methods($object);
 1499          sort($methods);
 1500          $lcMethods = array_map(function ($value) { return strtr($value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); }, $methods);
 1501          $classCache = [];
 1502          foreach ($methods as $i => $method) {
 1503              $classCache[$method] = $method;
 1504              $classCache[$lcName = $lcMethods[$i]] = $method;
 1505   
 1506              if ('g' === $lcName[0] && 0 === strpos($lcName, 'get')) {
 1507                  $name = substr($method, 3);
 1508                  $lcName = substr($lcName, 3);
 1509              } elseif ('i' === $lcName[0] && 0 === strpos($lcName, 'is')) {
 1510                  $name = substr($method, 2);
 1511                  $lcName = substr($lcName, 2);
 1512              } elseif ('h' === $lcName[0] && 0 === strpos($lcName, 'has')) {
 1513                  $name = substr($method, 3);
 1514                  $lcName = substr($lcName, 3);
 1515                  if (\in_array('is'.$lcName, $lcMethods)) {
 1516                      continue;
 1517                  }
 1518              } else {
 1519                  continue;
 1520              }
 1521   
 1522              // skip get() and is() methods (in which case, $name is empty)
 1523              if ($name) {
 1524                  if (!isset($classCache[$name])) {
 1525                      $classCache[$name] = $method;
 1526                  }
 1527   
 1528                  if (!isset($classCache[$lcName])) {
 1529                      $classCache[$lcName] = $method;
 1530                  }
 1531              }
 1532          }
 1533          $cache[$class] = $classCache;
 1534      }
 1535   
 1536      $call = false;
 1537      if (isset($cache[$class][$item])) {
 1538          $method = $cache[$class][$item];
 1539      } elseif (isset($cache[$class][$lcItem = strtr($item, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')])) {
 1540          $method = $cache[$class][$lcItem];
 1541      } elseif (isset($cache[$class]['__call'])) {
 1542          $method = $item;
 1543          $call = true;
 1544      } else {
 1545          if ($isDefinedTest) {
 1546              return false;
 1547          }
 1548   
 1549          if ($ignoreStrictCheck || !$env->isStrictVariables()) {
 1550              return;
 1551          }
 1552   
 1553          throw new RuntimeError(sprintf('Neither the property "%1$s" nor one of the methods "%1$s()", "get%1$s()"/"is%1$s()"/"has%1$s()" or "__call()" exist and have public access in class "%2$s".', $item, $class), $lineno, $source);
 1554      }
 1555   
 1556      if ($isDefinedTest) {
 1557          return true;
 1558      }
 1559   
 1560      if ($sandboxed) {
 1561          $env->getExtension(SandboxExtension::class)->checkMethodAllowed($object, $method, $lineno, $source);
 1562      }
 1563   
 1564      // Some objects throw exceptions when they have __call, and the method we try
 1565      // to call is not supported. If ignoreStrictCheck is true, we should return null.
 1566      try {
 1567          $ret = $object->$method(...$arguments);
 1568      } catch (\BadMethodCallException $e) {
 1569          if ($call && ($ignoreStrictCheck || !$env->isStrictVariables())) {
 1570              return;
 1571          }
 1572          throw $e;
 1573      }
 1574   
 1575      return $ret;
 1576  }
 1577   
 1578  /**
 1579   * Returns the values from a single column in the input array.
 1580   *
 1581   * <pre>
 1582   *  {% set items = [{ 'fruit' : 'apple'}, {'fruit' : 'orange' }] %}
 1583   *
 1584   *  {% set fruits = items|column('fruit') %}
 1585   *
 1586   *  {# fruits now contains ['apple', 'orange'] #}
 1587   * </pre>
 1588   *
 1589   * @param array|Traversable $array An array
 1590   * @param mixed             $name  The column name
 1591   * @param mixed             $index The column to use as the index/keys for the returned array
 1592   *
 1593   * @return array The array of values
 1594   */
 1595  function twig_array_column($array, $name, $index = null): array
 1596  {
 1597      if ($array instanceof Traversable) {
 1598          $array = iterator_to_array($array);
 1599      } elseif (!\is_array($array)) {
 1600          throw new RuntimeError(sprintf('The column filter only works with arrays or "Traversable", got "%s" as first argument.', \gettype($array)));
 1601      }
 1602   
 1603      return array_column($array, $name, $index);
 1604  }
 1605   
 1606  function twig_array_filter(Environment $env, $array, $arrow)
 1607  {
 1608      if (!twig_test_iterable($array)) {
 1609          throw new RuntimeError(sprintf('The "filter" filter expects an array or "Traversable", got "%s".', \is_object($array) ? \get_class($array) : \gettype($array)));
 1610      }
 1611   
 1612      twig_check_arrow_in_sandbox($env, $arrow, 'filter', 'filter');
 1613   
 1614      if (\is_array($array)) {
 1615          return array_filter($array, $arrow, \ARRAY_FILTER_USE_BOTH);
 1616      }
 1617   
 1618      // the IteratorIterator wrapping is needed as some internal PHP classes are \Traversable but do not implement \Iterator
 1619      return new \CallbackFilterIterator(new \IteratorIterator($array), $arrow);
 1620  }
 1621   
 1622  function twig_array_map(Environment $env, $array, $arrow)
 1623  {
 1624      twig_check_arrow_in_sandbox($env, $arrow, 'map', 'filter');
 1625   
 1626      $r = [];
 1627      foreach ($array as $k => $v) {
 1628          $r[$k] = $arrow($v, $k);
 1629      }
 1630   
 1631      return $r;
 1632  }
 1633   
 1634  function twig_array_reduce(Environment $env, $array, $arrow, $initial = null)
 1635  {
 1636      twig_check_arrow_in_sandbox($env, $arrow, 'reduce', 'filter');
 1637   
 1638      if (!\is_array($array)) {
 1639          if (!$array instanceof \Traversable) {
 1640              throw new RuntimeError(sprintf('The "reduce" filter only works with arrays or "Traversable", got "%s" as first argument.', \gettype($array)));
 1641          }
 1642   
 1643          $array = iterator_to_array($array);
 1644      }
 1645   
 1646      return array_reduce($array, $arrow, $initial);
 1647  }
 1648   
 1649  function twig_check_arrow_in_sandbox(Environment $env, $arrow, $thing, $type)
 1650  {
 1651      if (!$arrow instanceof Closure && $env->hasExtension('\Twig\Extension\SandboxExtension') && $env->getExtension('\Twig\Extension\SandboxExtension')->isSandboxed()) {
 1652          throw new RuntimeError(sprintf('The callable passed to the "%s" %s must be a Closure in sandbox mode.', $thing, $type));
 1653      }
 1654  }
 1655  }
 1656