[ Index ]

PHP Cross Reference of phpBB-3.3.2-deutsch

title

Body

[close]

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

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


Generated: Wed Nov 11 20:28:18 2020 Cross-referenced by PHPXref 0.7.1