Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

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: 02.04.2025, 15:03 - Dateigröße: 52.71 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          foreach ((\is_array($template) ? $template : [$template]) as $name) {
1273              // if a Template instance is passed, it might have been instantiated outside of a sandbox, check security
1274              if ($name instanceof TemplateWrapper || $name instanceof Template) {
1275                  $name->unwrap()->checkSecurity();
1276              }
1277          }
1278      }
1279   
1280      try {
1281          $loaded = null;
1282          try {
1283              $loaded = $env->resolveTemplate($template);
1284          } catch (LoaderError $e) {
1285              if (!$ignoreMissing) {
1286                  throw $e;
1287              }
1288          }
1289   
1290          return $loaded ? $loaded->render($variables) : '';
1291      } finally {
1292          if ($isSandboxed && !$alreadySandboxed) {
1293              $sandbox->disableSandbox();
1294          }
1295      }
1296  }
1297   
1298  /**
1299   * Returns a template content without rendering it.
1300   *
1301   * @param string $name          The template name
1302   * @param bool   $ignoreMissing Whether to ignore missing templates or not
1303   *
1304   * @return string The template source
1305   */
1306  function twig_source(Environment $env, $name, $ignoreMissing = false)
1307  {
1308      $loader = $env->getLoader();
1309      try {
1310          return $loader->getSourceContext($name)->getCode();
1311      } catch (LoaderError $e) {
1312          if (!$ignoreMissing) {
1313              throw $e;
1314          }
1315      }
1316  }
1317   
1318  /**
1319   * Provides the ability to get constants from instances as well as class/global constants.
1320   *
1321   * @param string      $constant The name of the constant
1322   * @param object|null $object   The object to get the constant from
1323   *
1324   * @return string
1325   */
1326  function twig_constant($constant, $object = null)
1327  {
1328      if (null !== $object) {
1329          $constant = \get_class($object).'::'.$constant;
1330      }
1331   
1332      if (!\defined($constant)) {
1333          throw new RuntimeError(sprintf('Constant "%s" is undefined.', $constant));
1334      }
1335   
1336      return \constant($constant);
1337  }
1338   
1339  /**
1340   * Checks if a constant exists.
1341   *
1342   * @param string      $constant The name of the constant
1343   * @param object|null $object   The object to get the constant from
1344   *
1345   * @return bool
1346   */
1347  function twig_constant_is_defined($constant, $object = null)
1348  {
1349      if (null !== $object) {
1350          $constant = \get_class($object).'::'.$constant;
1351      }
1352   
1353      return \defined($constant);
1354  }
1355   
1356  /**
1357   * Batches item.
1358   *
1359   * @param array $items An array of items
1360   * @param int   $size  The size of the batch
1361   * @param mixed $fill  A value used to fill missing items
1362   *
1363   * @return array
1364   */
1365  function twig_array_batch($items, $size, $fill = null, $preserveKeys = true)
1366  {
1367      if (!twig_test_iterable($items)) {
1368          throw new RuntimeError(sprintf('The "batch" filter expects an array or "Traversable", got "%s".', \is_object($items) ? \get_class($items) : \gettype($items)));
1369      }
1370   
1371      $size = ceil($size);
1372   
1373      $result = array_chunk(twig_to_array($items, $preserveKeys), $size, $preserveKeys);
1374   
1375      if (null !== $fill && $result) {
1376          $last = \count($result) - 1;
1377          if ($fillCount = $size - \count($result[$last])) {
1378              for ($i = 0; $i < $fillCount; ++$i) {
1379                  $result[$last][] = $fill;
1380              }
1381          }
1382      }
1383   
1384      return $result;
1385  }
1386   
1387  /**
1388   * Returns the attribute value for a given array/object.
1389   *
1390   * @param mixed  $object            The object or array from where to get the item
1391   * @param mixed  $item              The item to get from the array or object
1392   * @param array  $arguments         An array of arguments to pass if the item is an object method
1393   * @param string $type              The type of attribute (@see \Twig\Template constants)
1394   * @param bool   $isDefinedTest     Whether this is only a defined check
1395   * @param bool   $ignoreStrictCheck Whether to ignore the strict attribute check or not
1396   * @param int    $lineno            The template line where the attribute was called
1397   *
1398   * @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true
1399   *
1400   * @throws RuntimeError if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false
1401   *
1402   * @internal
1403   */
1404  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)
1405  {
1406      // array
1407      if (/* Template::METHOD_CALL */ 'method' !== $type) {
1408          $arrayItem = \is_bool($item) || \is_float($item) ? (int) $item : $item;
1409   
1410          if (((\is_array($object) || $object instanceof \ArrayObject) && (isset($object[$arrayItem]) || \array_key_exists($arrayItem, (array) $object)))
1411              || ($object instanceof ArrayAccess && isset($object[$arrayItem]))
1412          ) {
1413              if ($isDefinedTest) {
1414                  return true;
1415              }
1416   
1417              return $object[$arrayItem];
1418          }
1419   
1420          if (/* Template::ARRAY_CALL */ 'array' === $type || !\is_object($object)) {
1421              if ($isDefinedTest) {
1422                  return false;
1423              }
1424   
1425              if ($ignoreStrictCheck || !$env->isStrictVariables()) {
1426                  return;
1427              }
1428   
1429              if ($object instanceof ArrayAccess) {
1430                  $message = sprintf('Key "%s" in object with ArrayAccess of class "%s" does not exist.', $arrayItem, \get_class($object));
1431              } elseif (\is_object($object)) {
1432                  $message = sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface.', $item, \get_class($object));
1433              } elseif (\is_array($object)) {
1434                  if (empty($object)) {
1435                      $message = sprintf('Key "%s" does not exist as the array is empty.', $arrayItem);
1436                  } else {
1437                      $message = sprintf('Key "%s" for array with keys "%s" does not exist.', $arrayItem, implode(', ', array_keys($object)));
1438                  }
1439              } elseif (/* Template::ARRAY_CALL */ 'array' === $type) {
1440                  if (null === $object) {
1441                      $message = sprintf('Impossible to access a key ("%s") on a null variable.', $item);
1442                  } else {
1443                      $message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s").', $item, \gettype($object), $object);
1444                  }
1445              } elseif (null === $object) {
1446                  $message = sprintf('Impossible to access an attribute ("%s") on a null variable.', $item);
1447              } else {
1448                  $message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s").', $item, \gettype($object), $object);
1449              }
1450   
1451              throw new RuntimeError($message, $lineno, $source);
1452          }
1453      }
1454   
1455      if (!\is_object($object)) {
1456          if ($isDefinedTest) {
1457              return false;
1458          }
1459   
1460          if ($ignoreStrictCheck || !$env->isStrictVariables()) {
1461              return;
1462          }
1463   
1464          if (null === $object) {
1465              $message = sprintf('Impossible to invoke a method ("%s") on a null variable.', $item);
1466          } elseif (\is_array($object)) {
1467              $message = sprintf('Impossible to invoke a method ("%s") on an array.', $item);
1468          } else {
1469              $message = sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s").', $item, \gettype($object), $object);
1470          }
1471   
1472          throw new RuntimeError($message, $lineno, $source);
1473      }
1474   
1475      if ($object instanceof Template) {
1476          throw new RuntimeError('Accessing \Twig\Template attributes is forbidden.', $lineno, $source);
1477      }
1478   
1479      // object property
1480      if (/* Template::METHOD_CALL */ 'method' !== $type) {
1481          if (isset($object->$item) || \array_key_exists((string) $item, (array) $object)) {
1482              if ($isDefinedTest) {
1483                  return true;
1484              }
1485   
1486              if ($sandboxed) {
1487                  $env->getExtension(SandboxExtension::class)->checkPropertyAllowed($object, $item, $lineno, $source);
1488              }
1489   
1490              return $object->$item;
1491          }
1492      }
1493   
1494      static $cache = [];
1495   
1496      $class = \get_class($object);
1497   
1498      // object method
1499      // precedence: getXxx() > isXxx() > hasXxx()
1500      if (!isset($cache[$class])) {
1501          $methods = get_class_methods($object);
1502          sort($methods);
1503          $lcMethods = array_map(function ($value) { return strtr($value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); }, $methods);
1504          $classCache = [];
1505          foreach ($methods as $i => $method) {
1506              $classCache[$method] = $method;
1507              $classCache[$lcName = $lcMethods[$i]] = $method;
1508   
1509              if ('g' === $lcName[0] && 0 === strpos($lcName, 'get')) {
1510                  $name = substr($method, 3);
1511                  $lcName = substr($lcName, 3);
1512              } elseif ('i' === $lcName[0] && 0 === strpos($lcName, 'is')) {
1513                  $name = substr($method, 2);
1514                  $lcName = substr($lcName, 2);
1515              } elseif ('h' === $lcName[0] && 0 === strpos($lcName, 'has')) {
1516                  $name = substr($method, 3);
1517                  $lcName = substr($lcName, 3);
1518                  if (\in_array('is'.$lcName, $lcMethods)) {
1519                      continue;
1520                  }
1521              } else {
1522                  continue;
1523              }
1524   
1525              // skip get() and is() methods (in which case, $name is empty)
1526              if ($name) {
1527                  if (!isset($classCache[$name])) {
1528                      $classCache[$name] = $method;
1529                  }
1530   
1531                  if (!isset($classCache[$lcName])) {
1532                      $classCache[$lcName] = $method;
1533                  }
1534              }
1535          }
1536          $cache[$class] = $classCache;
1537      }
1538   
1539      $call = false;
1540      if (isset($cache[$class][$item])) {
1541          $method = $cache[$class][$item];
1542      } elseif (isset($cache[$class][$lcItem = strtr($item, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')])) {
1543          $method = $cache[$class][$lcItem];
1544      } elseif (isset($cache[$class]['__call'])) {
1545          $method = $item;
1546          $call = true;
1547      } else {
1548          if ($isDefinedTest) {
1549              return false;
1550          }
1551   
1552          if ($ignoreStrictCheck || !$env->isStrictVariables()) {
1553              return;
1554          }
1555   
1556          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);
1557      }
1558   
1559      if ($isDefinedTest) {
1560          return true;
1561      }
1562   
1563      if ($sandboxed) {
1564          $env->getExtension(SandboxExtension::class)->checkMethodAllowed($object, $method, $lineno, $source);
1565      }
1566   
1567      // Some objects throw exceptions when they have __call, and the method we try
1568      // to call is not supported. If ignoreStrictCheck is true, we should return null.
1569      try {
1570          $ret = $object->$method(...$arguments);
1571      } catch (\BadMethodCallException $e) {
1572          if ($call && ($ignoreStrictCheck || !$env->isStrictVariables())) {
1573              return;
1574          }
1575          throw $e;
1576      }
1577   
1578      return $ret;
1579  }
1580   
1581  /**
1582   * Returns the values from a single column in the input array.
1583   *
1584   * <pre>
1585   *  {% set items = [{ 'fruit' : 'apple'}, {'fruit' : 'orange' }] %}
1586   *
1587   *  {% set fruits = items|column('fruit') %}
1588   *
1589   *  {# fruits now contains ['apple', 'orange'] #}
1590   * </pre>
1591   *
1592   * @param array|Traversable $array An array
1593   * @param mixed             $name  The column name
1594   * @param mixed             $index The column to use as the index/keys for the returned array
1595   *
1596   * @return array The array of values
1597   */
1598  function twig_array_column($array, $name, $index = null): array
1599  {
1600      if ($array instanceof Traversable) {
1601          $array = iterator_to_array($array);
1602      } elseif (!\is_array($array)) {
1603          throw new RuntimeError(sprintf('The column filter only works with arrays or "Traversable", got "%s" as first argument.', \gettype($array)));
1604      }
1605   
1606      return array_column($array, $name, $index);
1607  }
1608   
1609  function twig_array_filter(Environment $env, $array, $arrow)
1610  {
1611      if (!twig_test_iterable($array)) {
1612          throw new RuntimeError(sprintf('The "filter" filter expects an array or "Traversable", got "%s".', \is_object($array) ? \get_class($array) : \gettype($array)));
1613      }
1614   
1615      twig_check_arrow_in_sandbox($env, $arrow, 'filter', 'filter');
1616   
1617      if (\is_array($array)) {
1618          return array_filter($array, $arrow, \ARRAY_FILTER_USE_BOTH);
1619      }
1620   
1621      // the IteratorIterator wrapping is needed as some internal PHP classes are \Traversable but do not implement \Iterator
1622      return new \CallbackFilterIterator(new \IteratorIterator($array), $arrow);
1623  }
1624   
1625  function twig_array_map(Environment $env, $array, $arrow)
1626  {
1627      twig_check_arrow_in_sandbox($env, $arrow, 'map', 'filter');
1628   
1629      $r = [];
1630      foreach ($array as $k => $v) {
1631          $r[$k] = $arrow($v, $k);
1632      }
1633   
1634      return $r;
1635  }
1636   
1637  function twig_array_reduce(Environment $env, $array, $arrow, $initial = null)
1638  {
1639      twig_check_arrow_in_sandbox($env, $arrow, 'reduce', 'filter');
1640   
1641      if (!\is_array($array)) {
1642          if (!$array instanceof \Traversable) {
1643              throw new RuntimeError(sprintf('The "reduce" filter only works with arrays or "Traversable", got "%s" as first argument.', \gettype($array)));
1644          }
1645   
1646          $array = iterator_to_array($array);
1647      }
1648   
1649      return array_reduce($array, $arrow, $initial);
1650  }
1651   
1652  function twig_check_arrow_in_sandbox(Environment $env, $arrow, $thing, $type)
1653  {
1654      if (!$arrow instanceof Closure && $env->hasExtension('\Twig\Extension\SandboxExtension') && $env->getExtension('\Twig\Extension\SandboxExtension')->isSandboxed()) {
1655          throw new RuntimeError(sprintf('The callable passed to the "%s" %s must be a Closure in sandbox mode.', $thing, $type));
1656      }
1657  }
1658  }
1659