[ Index ]

PHP Cross Reference of phpBB-3.3.0-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'),
 245              new TwigFilter('map', 'twig_array_map'),
 246              new TwigFilter('reduce', 'twig_array_reduce'),
 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 = iconv($charset, 'UTF-8', $values);
 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] = iconv('UTF-8', $charset, $value);
 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 \DateTime
 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 = iconv($charset, 'UTF-8', $string);
 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 = iconv('UTF-8', $charset, $string);
 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      return iconv($from, $to, $string);
1001  }
1002  
1003  /**
1004   * Returns the length of a variable.
1005   *
1006   * @param mixed $thing A variable
1007   *
1008   * @return int The length of the value
1009   */
1010  function twig_length_filter(Environment $env, $thing)
1011  {
1012      if (null === $thing) {
1013          return 0;
1014      }
1015  
1016      if (is_scalar($thing)) {
1017          return mb_strlen($thing, $env->getCharset());
1018      }
1019  
1020      if ($thing instanceof \Countable || \is_array($thing) || $thing instanceof \SimpleXMLElement) {
1021          return \count($thing);
1022      }
1023  
1024      if ($thing instanceof \Traversable) {
1025          return iterator_count($thing);
1026      }
1027  
1028      if (method_exists($thing, '__toString') && !$thing instanceof \Countable) {
1029          return mb_strlen((string) $thing, $env->getCharset());
1030      }
1031  
1032      return 1;
1033  }
1034  
1035  /**
1036   * Converts a string to uppercase.
1037   *
1038   * @param string $string A string
1039   *
1040   * @return string The uppercased string
1041   */
1042  function twig_upper_filter(Environment $env, $string)
1043  {
1044      return mb_strtoupper($string, $env->getCharset());
1045  }
1046  
1047  /**
1048   * Converts a string to lowercase.
1049   *
1050   * @param string $string A string
1051   *
1052   * @return string The lowercased string
1053   */
1054  function twig_lower_filter(Environment $env, $string)
1055  {
1056      return mb_strtolower($string, $env->getCharset());
1057  }
1058  
1059  /**
1060   * Returns a titlecased string.
1061   *
1062   * @param string $string A string
1063   *
1064   * @return string The titlecased string
1065   */
1066  function twig_title_string_filter(Environment $env, $string)
1067  {
1068      if (null !== $charset = $env->getCharset()) {
1069          return mb_convert_case($string, MB_CASE_TITLE, $charset);
1070      }
1071  
1072      return ucwords(strtolower($string));
1073  }
1074  
1075  /**
1076   * Returns a capitalized string.
1077   *
1078   * @param string $string A string
1079   *
1080   * @return string The capitalized string
1081   */
1082  function twig_capitalize_string_filter(Environment $env, $string)
1083  {
1084      $charset = $env->getCharset();
1085  
1086      return mb_strtoupper(mb_substr($string, 0, 1, $charset), $charset).mb_strtolower(mb_substr($string, 1, null, $charset), $charset);
1087  }
1088  
1089  /**
1090   * @internal
1091   */
1092  function twig_call_macro(Template $template, string $method, array $args, int $lineno, array $context, Source $source)
1093  {
1094      if (!method_exists($template, $method)) {
1095          $parent = $template;
1096          while ($parent = $parent->getParent($context)) {
1097              if (method_exists($parent, $method)) {
1098                  return $parent->$method(...$args);
1099              }
1100          }
1101  
1102          throw new RuntimeError(sprintf('Macro "%s" is not defined in template "%s".', substr($method, \strlen('macro_')), $template->getTemplateName()), $lineno, $source);
1103      }
1104  
1105      return $template->$method(...$args);
1106  }
1107  
1108  /**
1109   * @internal
1110   */
1111  function twig_ensure_traversable($seq)
1112  {
1113      if ($seq instanceof \Traversable || \is_array($seq)) {
1114          return $seq;
1115      }
1116  
1117      return [];
1118  }
1119  
1120  /**
1121   * @internal
1122   */
1123  function twig_to_array($seq, $preserveKeys = true)
1124  {
1125      if ($seq instanceof \Traversable) {
1126          return iterator_to_array($seq, $preserveKeys);
1127      }
1128  
1129      if (!\is_array($seq)) {
1130          return $seq;
1131      }
1132  
1133      return $preserveKeys ? $seq : array_values($seq);
1134  }
1135  
1136  /**
1137   * Checks if a variable is empty.
1138   *
1139   *    {# evaluates to true if the foo variable is null, false, or the empty string #}
1140   *    {% if foo is empty %}
1141   *        {# ... #}
1142   *    {% endif %}
1143   *
1144   * @param mixed $value A variable
1145   *
1146   * @return bool true if the value is empty, false otherwise
1147   */
1148  function twig_test_empty($value)
1149  {
1150      if ($value instanceof \Countable) {
1151          return 0 == \count($value);
1152      }
1153  
1154      if ($value instanceof \Traversable) {
1155          return !iterator_count($value);
1156      }
1157  
1158      if (\is_object($value) && method_exists($value, '__toString')) {
1159          return '' === (string) $value;
1160      }
1161  
1162      return '' === $value || false === $value || null === $value || [] === $value;
1163  }
1164  
1165  /**
1166   * Checks if a variable is traversable.
1167   *
1168   *    {# evaluates to true if the foo variable is an array or a traversable object #}
1169   *    {% if foo is iterable %}
1170   *        {# ... #}
1171   *    {% endif %}
1172   *
1173   * @param mixed $value A variable
1174   *
1175   * @return bool true if the value is traversable
1176   */
1177  function twig_test_iterable($value)
1178  {
1179      return $value instanceof \Traversable || \is_array($value);
1180  }
1181  
1182  /**
1183   * Renders a template.
1184   *
1185   * @param array        $context
1186   * @param string|array $template      The template to render or an array of templates to try consecutively
1187   * @param array        $variables     The variables to pass to the template
1188   * @param bool         $withContext
1189   * @param bool         $ignoreMissing Whether to ignore missing templates or not
1190   * @param bool         $sandboxed     Whether to sandbox the template or not
1191   *
1192   * @return string The rendered template
1193   */
1194  function twig_include(Environment $env, $context, $template, $variables = [], $withContext = true, $ignoreMissing = false, $sandboxed = false)
1195  {
1196      $alreadySandboxed = false;
1197      $sandbox = null;
1198      if ($withContext) {
1199          $variables = array_merge($context, $variables);
1200      }
1201  
1202      if ($isSandboxed = $sandboxed && $env->hasExtension(SandboxExtension::class)) {
1203          $sandbox = $env->getExtension(SandboxExtension::class);
1204          if (!$alreadySandboxed = $sandbox->isSandboxed()) {
1205              $sandbox->enableSandbox();
1206          }
1207      }
1208  
1209      try {
1210          $loaded = null;
1211          try {
1212              $loaded = $env->resolveTemplate($template);
1213          } catch (LoaderError $e) {
1214              if (!$ignoreMissing) {
1215                  throw $e;
1216              }
1217          }
1218  
1219          return $loaded ? $loaded->render($variables) : '';
1220      } finally {
1221          if ($isSandboxed && !$alreadySandboxed) {
1222              $sandbox->disableSandbox();
1223          }
1224      }
1225  }
1226  
1227  /**
1228   * Returns a template content without rendering it.
1229   *
1230   * @param string $name          The template name
1231   * @param bool   $ignoreMissing Whether to ignore missing templates or not
1232   *
1233   * @return string The template source
1234   */
1235  function twig_source(Environment $env, $name, $ignoreMissing = false)
1236  {
1237      $loader = $env->getLoader();
1238      try {
1239          return $loader->getSourceContext($name)->getCode();
1240      } catch (LoaderError $e) {
1241          if (!$ignoreMissing) {
1242              throw $e;
1243          }
1244      }
1245  }
1246  
1247  /**
1248   * Provides the ability to get constants from instances as well as class/global constants.
1249   *
1250   * @param string      $constant The name of the constant
1251   * @param object|null $object   The object to get the constant from
1252   *
1253   * @return string
1254   */
1255  function twig_constant($constant, $object = null)
1256  {
1257      if (null !== $object) {
1258          $constant = \get_class($object).'::'.$constant;
1259      }
1260  
1261      return \constant($constant);
1262  }
1263  
1264  /**
1265   * Checks if a constant exists.
1266   *
1267   * @param string      $constant The name of the constant
1268   * @param object|null $object   The object to get the constant from
1269   *
1270   * @return bool
1271   */
1272  function twig_constant_is_defined($constant, $object = null)
1273  {
1274      if (null !== $object) {
1275          $constant = \get_class($object).'::'.$constant;
1276      }
1277  
1278      return \defined($constant);
1279  }
1280  
1281  /**
1282   * Batches item.
1283   *
1284   * @param array $items An array of items
1285   * @param int   $size  The size of the batch
1286   * @param mixed $fill  A value used to fill missing items
1287   *
1288   * @return array
1289   */
1290  function twig_array_batch($items, $size, $fill = null, $preserveKeys = true)
1291  {
1292      if (!twig_test_iterable($items)) {
1293          throw new RuntimeError(sprintf('The "batch" filter expects an array or "Traversable", got "%s".', \is_object($items) ? \get_class($items) : \gettype($items)));
1294      }
1295  
1296      $size = ceil($size);
1297  
1298      $result = array_chunk(twig_to_array($items, $preserveKeys), $size, $preserveKeys);
1299  
1300      if (null !== $fill && $result) {
1301          $last = \count($result) - 1;
1302          if ($fillCount = $size - \count($result[$last])) {
1303              for ($i = 0; $i < $fillCount; ++$i) {
1304                  $result[$last][] = $fill;
1305              }
1306          }
1307      }
1308  
1309      return $result;
1310  }
1311  
1312  /**
1313   * Returns the attribute value for a given array/object.
1314   *
1315   * @param mixed  $object            The object or array from where to get the item
1316   * @param mixed  $item              The item to get from the array or object
1317   * @param array  $arguments         An array of arguments to pass if the item is an object method
1318   * @param string $type              The type of attribute (@see \Twig\Template constants)
1319   * @param bool   $isDefinedTest     Whether this is only a defined check
1320   * @param bool   $ignoreStrictCheck Whether to ignore the strict attribute check or not
1321   * @param int    $lineno            The template line where the attribute was called
1322   *
1323   * @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true
1324   *
1325   * @throws RuntimeError if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false
1326   *
1327   * @internal
1328   */
1329  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)
1330  {
1331      // array
1332      if (/* Template::METHOD_CALL */ 'method' !== $type) {
1333          $arrayItem = \is_bool($item) || \is_float($item) ? (int) $item : $item;
1334  
1335          if (((\is_array($object) || $object instanceof \ArrayObject) && (isset($object[$arrayItem]) || \array_key_exists($arrayItem, (array) $object)))
1336              || ($object instanceof ArrayAccess && isset($object[$arrayItem]))
1337          ) {
1338              if ($isDefinedTest) {
1339                  return true;
1340              }
1341  
1342              return $object[$arrayItem];
1343          }
1344  
1345          if (/* Template::ARRAY_CALL */ 'array' === $type || !\is_object($object)) {
1346              if ($isDefinedTest) {
1347                  return false;
1348              }
1349  
1350              if ($ignoreStrictCheck || !$env->isStrictVariables()) {
1351                  return;
1352              }
1353  
1354              if ($object instanceof ArrayAccess) {
1355                  $message = sprintf('Key "%s" in object with ArrayAccess of class "%s" does not exist.', $arrayItem, \get_class($object));
1356              } elseif (\is_object($object)) {
1357                  $message = sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface.', $item, \get_class($object));
1358              } elseif (\is_array($object)) {
1359                  if (empty($object)) {
1360                      $message = sprintf('Key "%s" does not exist as the array is empty.', $arrayItem);
1361                  } else {
1362                      $message = sprintf('Key "%s" for array with keys "%s" does not exist.', $arrayItem, implode(', ', array_keys($object)));
1363                  }
1364              } elseif (/* Template::ARRAY_CALL */ 'array' === $type) {
1365                  if (null === $object) {
1366                      $message = sprintf('Impossible to access a key ("%s") on a null variable.', $item);
1367                  } else {
1368                      $message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s").', $item, \gettype($object), $object);
1369                  }
1370              } elseif (null === $object) {
1371                  $message = sprintf('Impossible to access an attribute ("%s") on a null variable.', $item);
1372              } else {
1373                  $message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s").', $item, \gettype($object), $object);
1374              }
1375  
1376              throw new RuntimeError($message, $lineno, $source);
1377          }
1378      }
1379  
1380      if (!\is_object($object)) {
1381          if ($isDefinedTest) {
1382              return false;
1383          }
1384  
1385          if ($ignoreStrictCheck || !$env->isStrictVariables()) {
1386              return;
1387          }
1388  
1389          if (null === $object) {
1390              $message = sprintf('Impossible to invoke a method ("%s") on a null variable.', $item);
1391          } elseif (\is_array($object)) {
1392              $message = sprintf('Impossible to invoke a method ("%s") on an array.', $item);
1393          } else {
1394              $message = sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s").', $item, \gettype($object), $object);
1395          }
1396  
1397          throw new RuntimeError($message, $lineno, $source);
1398      }
1399  
1400      if ($object instanceof Template) {
1401          throw new RuntimeError('Accessing \Twig\Template attributes is forbidden.', $lineno, $source);
1402      }
1403  
1404      // object property
1405      if (/* Template::METHOD_CALL */ 'method' !== $type) {
1406          if (isset($object->$item) || \array_key_exists((string) $item, (array) $object)) {
1407              if ($isDefinedTest) {
1408                  return true;
1409              }
1410  
1411              if ($sandboxed) {
1412                  $env->getExtension(SandboxExtension::class)->checkPropertyAllowed($object, $item, $lineno, $source);
1413              }
1414  
1415              return $object->$item;
1416          }
1417      }
1418  
1419      static $cache = [];
1420  
1421      $class = \get_class($object);
1422  
1423      // object method
1424      // precedence: getXxx() > isXxx() > hasXxx()
1425      if (!isset($cache[$class])) {
1426          $methods = get_class_methods($object);
1427          sort($methods);
1428          $lcMethods = array_map('strtolower', $methods);
1429          $classCache = [];
1430          foreach ($methods as $i => $method) {
1431              $classCache[$method] = $method;
1432              $classCache[$lcName = $lcMethods[$i]] = $method;
1433  
1434              if ('g' === $lcName[0] && 0 === strpos($lcName, 'get')) {
1435                  $name = substr($method, 3);
1436                  $lcName = substr($lcName, 3);
1437              } elseif ('i' === $lcName[0] && 0 === strpos($lcName, 'is')) {
1438                  $name = substr($method, 2);
1439                  $lcName = substr($lcName, 2);
1440              } elseif ('h' === $lcName[0] && 0 === strpos($lcName, 'has')) {
1441                  $name = substr($method, 3);
1442                  $lcName = substr($lcName, 3);
1443                  if (\in_array('is'.$lcName, $lcMethods)) {
1444                      continue;
1445                  }
1446              } else {
1447                  continue;
1448              }
1449  
1450              // skip get() and is() methods (in which case, $name is empty)
1451              if ($name) {
1452                  if (!isset($classCache[$name])) {
1453                      $classCache[$name] = $method;
1454                  }
1455  
1456                  if (!isset($classCache[$lcName])) {
1457                      $classCache[$lcName] = $method;
1458                  }
1459              }
1460          }
1461          $cache[$class] = $classCache;
1462      }
1463  
1464      $call = false;
1465      if (isset($cache[$class][$item])) {
1466          $method = $cache[$class][$item];
1467      } elseif (isset($cache[$class][$lcItem = strtolower($item)])) {
1468          $method = $cache[$class][$lcItem];
1469      } elseif (isset($cache[$class]['__call'])) {
1470          $method = $item;
1471          $call = true;
1472      } else {
1473          if ($isDefinedTest) {
1474              return false;
1475          }
1476  
1477          if ($ignoreStrictCheck || !$env->isStrictVariables()) {
1478              return;
1479          }
1480  
1481          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);
1482      }
1483  
1484      if ($isDefinedTest) {
1485          return true;
1486      }
1487  
1488      if ($sandboxed) {
1489          $env->getExtension(SandboxExtension::class)->checkMethodAllowed($object, $method, $lineno, $source);
1490      }
1491  
1492      // Some objects throw exceptions when they have __call, and the method we try
1493      // to call is not supported. If ignoreStrictCheck is true, we should return null.
1494      try {
1495          $ret = $object->$method(...$arguments);
1496      } catch (\BadMethodCallException $e) {
1497          if ($call && ($ignoreStrictCheck || !$env->isStrictVariables())) {
1498              return;
1499          }
1500          throw $e;
1501      }
1502  
1503      return $ret;
1504  }
1505  
1506  /**
1507   * Returns the values from a single column in the input array.
1508   *
1509   * <pre>
1510   *  {% set items = [{ 'fruit' : 'apple'}, {'fruit' : 'orange' }] %}
1511   *
1512   *  {% set fruits = items|column('fruit') %}
1513   *
1514   *  {# fruits now contains ['apple', 'orange'] #}
1515   * </pre>
1516   *
1517   * @param array|Traversable $array An array
1518   * @param mixed             $name  The column name
1519   * @param mixed             $index The column to use as the index/keys for the returned array
1520   *
1521   * @return array The array of values
1522   */
1523  function twig_array_column($array, $name, $index = null): array
1524  {
1525      if ($array instanceof Traversable) {
1526          $array = iterator_to_array($array);
1527      } elseif (!\is_array($array)) {
1528          throw new RuntimeError(sprintf('The column filter only works with arrays or "Traversable", got "%s" as first argument.', \gettype($array)));
1529      }
1530  
1531      return array_column($array, $name, $index);
1532  }
1533  
1534  function twig_array_filter($array, $arrow)
1535  {
1536      if (\is_array($array)) {
1537          return array_filter($array, $arrow, \ARRAY_FILTER_USE_BOTH);
1538      }
1539  
1540      // the IteratorIterator wrapping is needed as some internal PHP classes are \Traversable but do not implement \Iterator
1541      return new \CallbackFilterIterator(new \IteratorIterator($array), $arrow);
1542  }
1543  
1544  function twig_array_map($array, $arrow)
1545  {
1546      $r = [];
1547      foreach ($array as $k => $v) {
1548          $r[$k] = $arrow($v, $k);
1549      }
1550  
1551      return $r;
1552  }
1553  
1554  function twig_array_reduce($array, $arrow, $initial = null)
1555  {
1556      if (!\is_array($array)) {
1557          $array = iterator_to_array($array);
1558      }
1559  
1560      return array_reduce($array, $arrow, $initial);
1561  }
1562  }


Generated: Tue Apr 7 19:44:41 2020 Cross-referenced by PHPXref 0.7.1