vendor/twig/twig/src/Extension/CoreExtension.php line 1882
<?php/** This file is part of Twig.** (c) Fabien Potencier** For the full copyright and license information, please view the LICENSE* file that was distributed with this source code.*/namespace Twig\Extension;use Twig\DeprecatedCallableInfo;use Twig\Environment;use Twig\Error\LoaderError;use Twig\Error\RuntimeError;use Twig\Error\SyntaxError;use Twig\ExpressionParser;use Twig\Markup;use Twig\Node\Expression\AbstractExpression;use Twig\Node\Expression\Binary\AddBinary;use Twig\Node\Expression\Binary\AndBinary;use Twig\Node\Expression\Binary\BitwiseAndBinary;use Twig\Node\Expression\Binary\BitwiseOrBinary;use Twig\Node\Expression\Binary\BitwiseXorBinary;use Twig\Node\Expression\Binary\ConcatBinary;use Twig\Node\Expression\Binary\DivBinary;use Twig\Node\Expression\Binary\ElvisBinary;use Twig\Node\Expression\Binary\EndsWithBinary;use Twig\Node\Expression\Binary\EqualBinary;use Twig\Node\Expression\Binary\FloorDivBinary;use Twig\Node\Expression\Binary\GreaterBinary;use Twig\Node\Expression\Binary\GreaterEqualBinary;use Twig\Node\Expression\Binary\HasEveryBinary;use Twig\Node\Expression\Binary\HasSomeBinary;use Twig\Node\Expression\Binary\InBinary;use Twig\Node\Expression\Binary\LessBinary;use Twig\Node\Expression\Binary\LessEqualBinary;use Twig\Node\Expression\Binary\MatchesBinary;use Twig\Node\Expression\Binary\ModBinary;use Twig\Node\Expression\Binary\MulBinary;use Twig\Node\Expression\Binary\NotEqualBinary;use Twig\Node\Expression\Binary\NotInBinary;use Twig\Node\Expression\Binary\NullCoalesceBinary;use Twig\Node\Expression\Binary\OrBinary;use Twig\Node\Expression\Binary\PowerBinary;use Twig\Node\Expression\Binary\RangeBinary;use Twig\Node\Expression\Binary\SpaceshipBinary;use Twig\Node\Expression\Binary\StartsWithBinary;use Twig\Node\Expression\Binary\SubBinary;use Twig\Node\Expression\Binary\XorBinary;use Twig\Node\Expression\BlockReferenceExpression;use Twig\Node\Expression\Filter\DefaultFilter;use Twig\Node\Expression\FunctionNode\EnumCasesFunction;use Twig\Node\Expression\FunctionNode\EnumFunction;use Twig\Node\Expression\GetAttrExpression;use Twig\Node\Expression\ParentExpression;use Twig\Node\Expression\Test\ConstantTest;use Twig\Node\Expression\Test\DefinedTest;use Twig\Node\Expression\Test\DivisiblebyTest;use Twig\Node\Expression\Test\EvenTest;use Twig\Node\Expression\Test\NullTest;use Twig\Node\Expression\Test\OddTest;use Twig\Node\Expression\Test\SameasTest;use Twig\Node\Expression\Unary\NegUnary;use Twig\Node\Expression\Unary\NotUnary;use Twig\Node\Expression\Unary\PosUnary;use Twig\Node\Node;use Twig\OperatorPrecedenceChange;use Twig\Parser;use Twig\Sandbox\SecurityNotAllowedMethodError;use Twig\Sandbox\SecurityNotAllowedPropertyError;use Twig\Source;use Twig\Template;use Twig\TemplateWrapper;use Twig\TokenParser\ApplyTokenParser;use Twig\TokenParser\BlockTokenParser;use Twig\TokenParser\DeprecatedTokenParser;use Twig\TokenParser\DoTokenParser;use Twig\TokenParser\EmbedTokenParser;use Twig\TokenParser\ExtendsTokenParser;use Twig\TokenParser\FlushTokenParser;use Twig\TokenParser\ForTokenParser;use Twig\TokenParser\FromTokenParser;use Twig\TokenParser\GuardTokenParser;use Twig\TokenParser\IfTokenParser;use Twig\TokenParser\ImportTokenParser;use Twig\TokenParser\IncludeTokenParser;use Twig\TokenParser\MacroTokenParser;use Twig\TokenParser\SetTokenParser;use Twig\TokenParser\TypesTokenParser;use Twig\TokenParser\UseTokenParser;use Twig\TokenParser\WithTokenParser;use Twig\TwigFilter;use Twig\TwigFunction;use Twig\TwigTest;use Twig\Util\CallableArgumentsExtractor;final class CoreExtension extends AbstractExtension{public const ARRAY_LIKE_CLASSES = ['ArrayIterator','ArrayObject','CachingIterator','RecursiveArrayIterator','RecursiveCachingIterator','SplDoublyLinkedList','SplFixedArray','SplObjectStorage','SplQueue','SplStack','WeakMap',];private const DEFAULT_TRIM_CHARS = " \t\n\r\0\x0B";private $dateFormats = ['F j, Y H:i', '%d days'];private $numberFormat = [0, '.', ','];private $timezone = null;/*** Sets the default format to be used by the date filter.** @param string|null $format The default date format string* @param string|null $dateIntervalFormat The default date interval format string*/public function setDateFormat($format = null, $dateIntervalFormat = null){if (null !== $format) {$this->dateFormats[0] = $format;}if (null !== $dateIntervalFormat) {$this->dateFormats[1] = $dateIntervalFormat;}}/*** Gets the default format to be used by the date filter.** @return array The default date format string and the default date interval format string*/public function getDateFormat(){return $this->dateFormats;}/*** Sets the default timezone to be used by the date filter.** @param \DateTimeZone|string $timezone The default timezone string or a \DateTimeZone object*/public function setTimezone($timezone){$this->timezone = $timezone instanceof \DateTimeZone ? $timezone : new \DateTimeZone($timezone);}/*** Gets the default timezone to be used by the date filter.** @return \DateTimeZone The default timezone currently in use*/public function getTimezone(){if (null === $this->timezone) {$this->timezone = new \DateTimeZone(date_default_timezone_get());}return $this->timezone;}/*** Sets the default format to be used by the number_format filter.** @param int $decimal the number of decimal places to use* @param string $decimalPoint the character(s) to use for the decimal point* @param string $thousandSep the character(s) to use for the thousands separator*/public function setNumberFormat($decimal, $decimalPoint, $thousandSep){$this->numberFormat = [$decimal, $decimalPoint, $thousandSep];}/*** Get the default format used by the number_format filter.** @return array The arguments for number_format()*/public function getNumberFormat(){return $this->numberFormat;}public function getTokenParsers(): array{return [new ApplyTokenParser(),new ForTokenParser(),new IfTokenParser(),new ExtendsTokenParser(),new IncludeTokenParser(),new BlockTokenParser(),new UseTokenParser(),new MacroTokenParser(),new ImportTokenParser(),new FromTokenParser(),new SetTokenParser(),new TypesTokenParser(),new FlushTokenParser(),new DoTokenParser(),new EmbedTokenParser(),new WithTokenParser(),new DeprecatedTokenParser(),new GuardTokenParser(),];}public function getFilters(): array{return [// formatting filtersnew TwigFilter('date', [$this, 'formatDate']),new TwigFilter('date_modify', [$this, 'modifyDate']),new TwigFilter('format', [self::class, 'sprintf']),new TwigFilter('replace', [self::class, 'replace']),new TwigFilter('number_format', [$this, 'formatNumber']),new TwigFilter('abs', 'abs'),new TwigFilter('round', [self::class, 'round']),// encodingnew TwigFilter('url_encode', [self::class, 'urlencode']),new TwigFilter('json_encode', 'json_encode'),new TwigFilter('convert_encoding', [self::class, 'convertEncoding']),// string filtersnew TwigFilter('title', [self::class, 'titleCase'], ['needs_charset' => true]),new TwigFilter('capitalize', [self::class, 'capitalize'], ['needs_charset' => true]),new TwigFilter('upper', [self::class, 'upper'], ['needs_charset' => true]),new TwigFilter('lower', [self::class, 'lower'], ['needs_charset' => true]),new TwigFilter('striptags', [self::class, 'striptags']),new TwigFilter('trim', [self::class, 'trim']),new TwigFilter('nl2br', [self::class, 'nl2br'], ['pre_escape' => 'html', 'is_safe' => ['html']]),new TwigFilter('spaceless', [self::class, 'spaceless'], ['is_safe' => ['html'], 'deprecation_info' => new DeprecatedCallableInfo('twig/twig', '3.12')]),// array helpersnew TwigFilter('join', [self::class, 'join']),new TwigFilter('split', [self::class, 'split'], ['needs_charset' => true]),new TwigFilter('sort', [self::class, 'sort'], ['needs_environment' => true]),new TwigFilter('merge', [self::class, 'merge']),new TwigFilter('batch', [self::class, 'batch']),new TwigFilter('column', [self::class, 'column']),new TwigFilter('filter', [self::class, 'filter'], ['needs_environment' => true]),new TwigFilter('map', [self::class, 'map'], ['needs_environment' => true]),new TwigFilter('reduce', [self::class, 'reduce'], ['needs_environment' => true]),new TwigFilter('find', [self::class, 'find'], ['needs_environment' => true]),// string/array filtersnew TwigFilter('reverse', [self::class, 'reverse'], ['needs_charset' => true]),new TwigFilter('shuffle', [self::class, 'shuffle'], ['needs_charset' => true]),new TwigFilter('length', [self::class, 'length'], ['needs_charset' => true]),new TwigFilter('slice', [self::class, 'slice'], ['needs_charset' => true]),new TwigFilter('first', [self::class, 'first'], ['needs_charset' => true]),new TwigFilter('last', [self::class, 'last'], ['needs_charset' => true]),// iteration and runtimenew TwigFilter('default', [self::class, 'default'], ['node_class' => DefaultFilter::class]),new TwigFilter('keys', [self::class, 'keys']),new TwigFilter('invoke', [self::class, 'invoke']),];}public function getFunctions(): array{return [new TwigFunction('parent', null, ['parser_callable' => [self::class, 'parseParentFunction']]),new TwigFunction('block', null, ['parser_callable' => [self::class, 'parseBlockFunction']]),new TwigFunction('attribute', null, ['parser_callable' => [self::class, 'parseAttributeFunction']]),new TwigFunction('max', 'max'),new TwigFunction('min', 'min'),new TwigFunction('range', 'range'),new TwigFunction('constant', [self::class, 'constant']),new TwigFunction('cycle', [self::class, 'cycle']),new TwigFunction('random', [self::class, 'random'], ['needs_charset' => true]),new TwigFunction('date', [$this, 'convertDate']),new TwigFunction('include', [self::class, 'include'], ['needs_environment' => true, 'needs_context' => true, 'is_safe' => ['all']]),new TwigFunction('source', [self::class, 'source'], ['needs_environment' => true, 'is_safe' => ['all']]),new TwigFunction('enum_cases', [self::class, 'enumCases'], ['node_class' => EnumCasesFunction::class]),new TwigFunction('enum', [self::class, 'enum'], ['node_class' => EnumFunction::class]),];}public function getTests(): array{return [new TwigTest('even', null, ['node_class' => EvenTest::class]),new TwigTest('odd', null, ['node_class' => OddTest::class]),new TwigTest('defined', null, ['node_class' => DefinedTest::class]),new TwigTest('same as', null, ['node_class' => SameasTest::class, 'one_mandatory_argument' => true]),new TwigTest('none', null, ['node_class' => NullTest::class]),new TwigTest('null', null, ['node_class' => NullTest::class]),new TwigTest('divisible by', null, ['node_class' => DivisiblebyTest::class, 'one_mandatory_argument' => true]),new TwigTest('constant', null, ['node_class' => ConstantTest::class]),new TwigTest('empty', [self::class, 'testEmpty']),new TwigTest('iterable', 'is_iterable'),new TwigTest('sequence', [self::class, 'testSequence']),new TwigTest('mapping', [self::class, 'testMapping']),];}public function getNodeVisitors(): array{return [];}public function getOperators(): array{return [['not' => ['precedence' => 50, 'precedence_change' => new OperatorPrecedenceChange('twig/twig', '3.15', 70), 'class' => NotUnary::class],'-' => ['precedence' => 500, 'class' => NegUnary::class],'+' => ['precedence' => 500, 'class' => PosUnary::class],],['? :' => ['precedence' => 5, 'class' => ElvisBinary::class, 'associativity' => ExpressionParser::OPERATOR_RIGHT],'?:' => ['precedence' => 5, 'class' => ElvisBinary::class, 'associativity' => ExpressionParser::OPERATOR_RIGHT],'??' => ['precedence' => 300, 'precedence_change' => new OperatorPrecedenceChange('twig/twig', '3.15', 5), 'class' => NullCoalesceBinary::class, 'associativity' => ExpressionParser::OPERATOR_RIGHT],'or' => ['precedence' => 10, 'class' => OrBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'xor' => ['precedence' => 12, 'class' => XorBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'and' => ['precedence' => 15, 'class' => AndBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'b-or' => ['precedence' => 16, 'class' => BitwiseOrBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'b-xor' => ['precedence' => 17, 'class' => BitwiseXorBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'b-and' => ['precedence' => 18, 'class' => BitwiseAndBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'==' => ['precedence' => 20, 'class' => EqualBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'!=' => ['precedence' => 20, 'class' => NotEqualBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'<=>' => ['precedence' => 20, 'class' => SpaceshipBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'<' => ['precedence' => 20, 'class' => LessBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'>' => ['precedence' => 20, 'class' => GreaterBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'>=' => ['precedence' => 20, 'class' => GreaterEqualBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'<=' => ['precedence' => 20, 'class' => LessEqualBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'not in' => ['precedence' => 20, 'class' => NotInBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'in' => ['precedence' => 20, 'class' => InBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'matches' => ['precedence' => 20, 'class' => MatchesBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'starts with' => ['precedence' => 20, 'class' => StartsWithBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'ends with' => ['precedence' => 20, 'class' => EndsWithBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'has some' => ['precedence' => 20, 'class' => HasSomeBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'has every' => ['precedence' => 20, 'class' => HasEveryBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'..' => ['precedence' => 25, 'class' => RangeBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'+' => ['precedence' => 30, 'class' => AddBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'-' => ['precedence' => 30, 'class' => SubBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'~' => ['precedence' => 40, 'precedence_change' => new OperatorPrecedenceChange('twig/twig', '3.15', 27), 'class' => ConcatBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'*' => ['precedence' => 60, 'class' => MulBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'/' => ['precedence' => 60, 'class' => DivBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'//' => ['precedence' => 60, 'class' => FloorDivBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'%' => ['precedence' => 60, 'class' => ModBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],'is' => ['precedence' => 100, 'associativity' => ExpressionParser::OPERATOR_LEFT],'is not' => ['precedence' => 100, 'associativity' => ExpressionParser::OPERATOR_LEFT],'**' => ['precedence' => 200, 'class' => PowerBinary::class, 'associativity' => ExpressionParser::OPERATOR_RIGHT],],];}/*** Cycles over a sequence.** @param array|\ArrayAccess $values A non-empty sequence of values* @param int<0, max> $position The position of the value to return in the cycle** @return mixed The value at the given position in the sequence, wrapping around as needed** @internal*/public static function cycle($values, $position): mixed{if (!\is_array($values)) {if (!$values instanceof \ArrayAccess) {throw new RuntimeError('The "cycle" function expects an array or "ArrayAccess" as first argument.');}if (!is_countable($values)) {// To be uncommented in 4.0// throw new RuntimeError('The "cycle" function expects a countable sequence as first argument.');trigger_deprecation('twig/twig', '3.12', 'Passing a non-countable sequence of values to "%s()" is deprecated.', __METHOD__);return $values;}$values = self::toArray($values, false);}if (!$count = \count($values)) {throw new RuntimeError('The "cycle" function expects a non-empty sequence.');}return $values[$position % $count];}/*** Returns a random value depending on the supplied parameter type:* - a random item from a \Traversable or array* - a random character from a string* - a random integer between 0 and the integer parameter.** @param \Traversable|array|int|float|string $values The values to pick a random item from* @param int|null $max Maximum value used when $values is an int** @return mixed A random value from the given sequence** @throws RuntimeError when $values is an empty array (does not apply to an empty string which is returned as is)** @internal*/public static function random(string $charset, $values = null, $max = null){if (null === $values) {return null === $max ? mt_rand() : mt_rand(0, (int) $max);}if (\is_int($values) || \is_float($values)) {if (null === $max) {if ($values < 0) {$max = 0;$min = $values;} else {$max = $values;$min = 0;}} else {$min = $values;}return mt_rand((int) $min, (int) $max);}if (\is_string($values)) {if ('' === $values) {return '';}if ('UTF-8' !== $charset) {$values = self::convertEncoding($values, 'UTF-8', $charset);}// unicode version of str_split()// split at all positions, but not after the start and not before the end$values = preg_split('/(?<!^)(?!$)/u', $values);if ('UTF-8' !== $charset) {foreach ($values as $i => $value) {$values[$i] = self::convertEncoding($value, $charset, 'UTF-8');}}}if (!is_iterable($values)) {return $values;}$values = self::toArray($values);if (0 === \count($values)) {throw new RuntimeError('The "random" function cannot pick from an empty sequence or mapping.');}return $values[array_rand($values, 1)];}/*** Formats a date.** {{ post.published_at|date("m/d/Y") }}** @param \DateTimeInterface|\DateInterval|string|int|null $date A date, a timestamp or null to use the current time* @param string|null $format The target format, null to use the default* @param \DateTimeZone|string|false|null $timezone The target timezone, null to use the default, false to leave unchanged*/public function formatDate($date, $format = null, $timezone = null): string{if (null === $format) {$formats = $this->getDateFormat();$format = $date instanceof \DateInterval ? $formats[1] : $formats[0];}if ($date instanceof \DateInterval) {return $date->format($format);}return $this->convertDate($date, $timezone)->format($format);}/*** Returns a new date object modified.** {{ post.published_at|date_modify("-1day")|date("m/d/Y") }}** @param \DateTimeInterface|string|int|null $date A date, a timestamp or null to use the current time* @param string $modifier A modifier string** @return \DateTime|\DateTimeImmutable** @internal*/public function modifyDate($date, $modifier){return $this->convertDate($date, false)->modify($modifier);}/*** Returns a formatted string.** @param string|null $format* @param ...$values** @internal*/public static function sprintf($format, ...$values): string{return \sprintf($format ?? '', ...$values);}/*** @internal*/public static function dateConverter(Environment $env, $date, $format = null, $timezone = null): string{return $env->getExtension(self::class)->formatDate($date, $format, $timezone);}/*** Converts an input to a \DateTime instance.** {% if date(user.created_at) < date('+2days') %}* {# do something #}* {% endif %}** @param \DateTimeInterface|string|int|null $date A date, a timestamp or null to use the current time* @param \DateTimeZone|string|false|null $timezone The target timezone, null to use the default, false to leave unchanged** @return \DateTime|\DateTimeImmutable*/public function convertDate($date = null, $timezone = null){// determine the timezoneif (false !== $timezone) {if (null === $timezone) {$timezone = $this->getTimezone();} elseif (!$timezone instanceof \DateTimeZone) {$timezone = new \DateTimeZone($timezone);}}// immutable datesif ($date instanceof \DateTimeImmutable) {return false !== $timezone ? $date->setTimezone($timezone) : $date;}if ($date instanceof \DateTime) {$date = clone $date;if (false !== $timezone) {$date->setTimezone($timezone);}return $date;}if (null === $date || 'now' === $date) {if (null === $date) {$date = 'now';}return new \DateTime($date, false !== $timezone ? $timezone : $this->getTimezone());}$asString = (string) $date;if (ctype_digit($asString) || ('' !== $asString && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) {$date = new \DateTime('@'.$date);} else {$date = new \DateTime($date);}if (false !== $timezone) {$date->setTimezone($timezone);}return $date;}/*** Replaces strings within a string.** @param string|null $str String to replace in* @param array|\Traversable $from Replace values** @internal*/public static function replace($str, $from): string{if (!is_iterable($from)) {throw new RuntimeError(\sprintf('The "replace" filter expects a sequence or a mapping, got "%s".', get_debug_type($from)));}return strtr($str ?? '', self::toArray($from));}/*** Rounds a number.** @param int|float|string|null $value The value to round* @param int|float $precision The rounding precision* @param 'common'|'ceil'|'floor' $method The method to use for rounding** @return float The rounded number** @internal*/public static function round($value, $precision = 0, $method = 'common'){$value = (float) $value;if ('common' === $method) {return round($value, $precision);}if ('ceil' !== $method && 'floor' !== $method) {throw new RuntimeError('The "round" filter only supports the "common", "ceil", and "floor" methods.');}return $method($value * 10 ** $precision) / 10 ** $precision;}/*** Formats a number.** All of the formatting options can be left null, in that case the defaults will* be used. Supplying any of the parameters will override the defaults set in the* environment object.** @param mixed $number A float/int/string of the number to format* @param int|null $decimal the number of decimal points to display* @param string|null $decimalPoint the character(s) to use for the decimal point* @param string|null $thousandSep the character(s) to use for the thousands separator*/public function formatNumber($number, $decimal = null, $decimalPoint = null, $thousandSep = null): string{$defaults = $this->getNumberFormat();if (null === $decimal) {$decimal = $defaults[0];}if (null === $decimalPoint) {$decimalPoint = $defaults[1];}if (null === $thousandSep) {$thousandSep = $defaults[2];}return number_format((float) $number, $decimal, $decimalPoint, $thousandSep);}/*** URL encodes (RFC 3986) a string as a path segment or an array as a query string.** @param string|array|null $url A URL or an array of query parameters** @internal*/public static function urlencode($url): string{if (\is_array($url)) {return http_build_query($url, '', '&', \PHP_QUERY_RFC3986);}return rawurlencode($url ?? '');}/*** Merges any number of arrays or Traversable objects.** {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %}** {% set items = items|merge({ 'peugeot': 'car' }, { 'banana': 'fruit' }) %}** {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car', 'banana': 'fruit' } #}** @param array|\Traversable ...$arrays Any number of arrays or Traversable objects to merge** @internal*/public static function merge(...$arrays): array{$result = [];foreach ($arrays as $argNumber => $array) {if (!is_iterable($array)) {throw new RuntimeError(\sprintf('The "merge" filter expects a sequence or a mapping, got "%s" for argument %d.', get_debug_type($array), $argNumber + 1));}$result = array_merge($result, self::toArray($array));}return $result;}/*** Slices a variable.** @param mixed $item A variable* @param int $start Start of the slice* @param int $length Size of the slice* @param bool $preserveKeys Whether to preserve key or not (when the input is an array)** @return mixed The sliced variable** @internal*/public static function slice(string $charset, $item, $start, $length = null, $preserveKeys = false){if ($item instanceof \Traversable) {while ($item instanceof \IteratorAggregate) {$item = $item->getIterator();}if ($start >= 0 && $length >= 0 && $item instanceof \Iterator) {try {return iterator_to_array(new \LimitIterator($item, $start, $length ?? -1), $preserveKeys);} catch (\OutOfBoundsException $e) {return [];}}$item = iterator_to_array($item, $preserveKeys);}if (\is_array($item)) {return \array_slice($item, $start, $length, $preserveKeys);}return mb_substr((string) $item, $start, $length, $charset);}/*** Returns the first element of the item.** @param mixed $item A variable** @return mixed The first element of the item** @internal*/public static function first(string $charset, $item){$elements = self::slice($charset, $item, 0, 1, false);return \is_string($elements) ? $elements : current($elements);}/*** Returns the last element of the item.** @param mixed $item A variable** @return mixed The last element of the item** @internal*/public static function last(string $charset, $item){$elements = self::slice($charset, $item, -1, 1, false);return \is_string($elements) ? $elements : current($elements);}/*** Joins the values to a string.** The separators between elements are empty strings per default, you can define them with the optional parameters.** {{ [1, 2, 3]|join(', ', ' and ') }}* {# returns 1, 2 and 3 #}** {{ [1, 2, 3]|join('|') }}* {# returns 1|2|3 #}** {{ [1, 2, 3]|join }}* {# returns 123 #}** @param iterable|array|string|float|int|bool|null $value An array* @param string $glue The separator* @param string|null $and The separator for the last pair** @internal*/public static function join($value, $glue = '', $and = null): string{if (!is_iterable($value)) {$value = (array) $value;}$value = self::toArray($value, false);if (0 === \count($value)) {return '';}if (null === $and || $and === $glue) {return implode($glue, $value);}if (1 === \count($value)) {return $value[0];}return implode($glue, \array_slice($value, 0, -1)).$and.$value[\count($value) - 1];}/*** Splits the string into an array.** {{ "one,two,three"|split(',') }}* {# returns [one, two, three] #}** {{ "one,two,three,four,five"|split(',', 3) }}* {# returns [one, two, "three,four,five"] #}** {{ "123"|split('') }}* {# returns [1, 2, 3] #}** {{ "aabbcc"|split('', 2) }}* {# returns [aa, bb, cc] #}** @param string|null $value A string* @param string $delimiter The delimiter* @param int|null $limit The limit** @internal*/public static function split(string $charset, $value, $delimiter, $limit = null): array{$value = $value ?? '';if ('' !== $delimiter) {return null === $limit ? explode($delimiter, $value) : explode($delimiter, $value, $limit);}if ($limit <= 1) {return preg_split('/(?<!^)(?!$)/u', $value);}$length = mb_strlen($value, $charset);if ($length < $limit) {return [$value];}$r = [];for ($i = 0; $i < $length; $i += $limit) {$r[] = mb_substr($value, $i, $limit, $charset);}return $r;}/*** @internal*/public static function default($value, $default = ''){if (self::testEmpty($value)) {return $default;}return $value;}/*** Returns the keys for the given array.** It is useful when you want to iterate over the keys of an array:** {% for key in array|keys %}* {# ... #}* {% endfor %}** @internal*/public static function keys($array): array{if ($array instanceof \Traversable) {while ($array instanceof \IteratorAggregate) {$array = $array->getIterator();}$keys = [];if ($array instanceof \Iterator) {$array->rewind();while ($array->valid()) {$keys[] = $array->key();$array->next();}return $keys;}foreach ($array as $key => $item) {$keys[] = $key;}return $keys;}if (!\is_array($array)) {return [];}return array_keys($array);}/*** Invokes a callable.** @internal*/public static function invoke(\Closure $arrow, ...$arguments): mixed{return $arrow(...$arguments);}/*** Reverses a variable.** @param array|\Traversable|string|null $item An array, a \Traversable instance, or a string* @param bool $preserveKeys Whether to preserve key or not** @return mixed The reversed input** @internal*/public static function reverse(string $charset, $item, $preserveKeys = false){if ($item instanceof \Traversable) {return array_reverse(iterator_to_array($item), $preserveKeys);}if (\is_array($item)) {return array_reverse($item, $preserveKeys);}$string = (string) $item;if ('UTF-8' !== $charset) {$string = self::convertEncoding($string, 'UTF-8', $charset);}preg_match_all('/./us', $string, $matches);$string = implode('', array_reverse($matches[0]));if ('UTF-8' !== $charset) {$string = self::convertEncoding($string, $charset, 'UTF-8');}return $string;}/*** Shuffles an array, a \Traversable instance, or a string.* The function does not preserve keys.** @param array|\Traversable|string|null $item** @return mixed** @internal*/public static function shuffle(string $charset, $item){if (\is_string($item)) {if ('UTF-8' !== $charset) {$item = self::convertEncoding($item, 'UTF-8', $charset);}$item = preg_split('/(?<!^)(?!$)/u', $item, -1);shuffle($item);$item = implode('', $item);if ('UTF-8' !== $charset) {$item = self::convertEncoding($item, $charset, 'UTF-8');}return $item;}if (is_iterable($item)) {$item = self::toArray($item, false);shuffle($item);}return $item;}/*** Sorts an array.** @param array|\Traversable $array* @param ?\Closure $arrow** @internal*/public static function sort(Environment $env, $array, $arrow = null): array{if ($array instanceof \Traversable) {$array = iterator_to_array($array);} elseif (!\is_array($array)) {throw new RuntimeError(\sprintf('The "sort" filter expects a sequence or a mapping, got "%s".', get_debug_type($array)));}if (null !== $arrow) {self::checkArrow($env, $arrow, 'sort', 'filter');uasort($array, $arrow);} else {asort($array);}return $array;}/*** @internal*/public static function inFilter($value, $compare){if ($value instanceof Markup) {$value = (string) $value;}if ($compare instanceof Markup) {$compare = (string) $compare;}if (\is_string($compare)) {if (\is_string($value) || \is_int($value) || \is_float($value)) {return '' === $value || str_contains($compare, (string) $value);}return false;}if (!is_iterable($compare)) {return false;}if (\is_object($value) || \is_resource($value)) {if (!\is_array($compare)) {foreach ($compare as $item) {if ($item === $value) {return true;}}return false;}return \in_array($value, $compare, true);}foreach ($compare as $item) {if (0 === self::compare($value, $item)) {return true;}}return false;}/*** Compares two values using a more strict version of the PHP non-strict comparison operator.** @see https://wiki.php.net/rfc/string_to_number_comparison* @see https://wiki.php.net/rfc/trailing_whitespace_numerics** @internal*/public static function compare($a, $b){// int <=> stringif (\is_int($a) && \is_string($b)) {$bTrim = trim($b, " \t\n\r\v\f");if (!is_numeric($bTrim)) {return (string) $a <=> $b;}if ((int) $bTrim == $bTrim) {return $a <=> (int) $bTrim;} else {return (float) $a <=> (float) $bTrim;}}if (\is_string($a) && \is_int($b)) {$aTrim = trim($a, " \t\n\r\v\f");if (!is_numeric($aTrim)) {return $a <=> (string) $b;}if ((int) $aTrim == $aTrim) {return (int) $aTrim <=> $b;} else {return (float) $aTrim <=> (float) $b;}}// float <=> stringif (\is_float($a) && \is_string($b)) {if (is_nan($a)) {return 1;}$bTrim = trim($b, " \t\n\r\v\f");if (!is_numeric($bTrim)) {return (string) $a <=> $b;}return $a <=> (float) $bTrim;}if (\is_string($a) && \is_float($b)) {if (is_nan($b)) {return 1;}$aTrim = trim($a, " \t\n\r\v\f");if (!is_numeric($aTrim)) {return $a <=> (string) $b;}return (float) $aTrim <=> $b;}// fallback to <=>return $a <=> $b;}/*** @throws RuntimeError When an invalid pattern is used** @internal*/public static function matches(string $regexp, ?string $str): int{set_error_handler(function ($t, $m) use ($regexp) {throw new RuntimeError(\sprintf('Regexp "%s" passed to "matches" is not valid', $regexp).substr($m, 12));});try {return preg_match($regexp, $str ?? '');} finally {restore_error_handler();}}/*** Returns a trimmed string.** @param string|\Stringable|null $string* @param string|null $characterMask* @param string $side left, right, or both** @throws RuntimeError When an invalid trimming side is used** @internal*/public static function trim($string, $characterMask = null, $side = 'both'): string|\Stringable{if (null === $characterMask) {$characterMask = self::DEFAULT_TRIM_CHARS;}$trimmed = match ($side) {'both' => trim($string ?? '', $characterMask),'left' => ltrim($string ?? '', $characterMask),'right' => rtrim($string ?? '', $characterMask),default => throw new RuntimeError('Trimming side must be "left", "right" or "both".'),};// trimming a safe string with the default character mask always returns a safe string (independently of the context)return $string instanceof Markup && self::DEFAULT_TRIM_CHARS === $characterMask ? new Markup($trimmed, $string->getCharset()) : $trimmed;}/*** Inserts HTML line breaks before all newlines in a string.** @param string|null $string** @internal*/public static function nl2br($string): string{return nl2br($string ?? '');}/*** Removes whitespaces between HTML tags.** @param string|null $content** @internal*/public static function spaceless($content): string{return trim(preg_replace('/>\s+</', '><', $content ?? ''));}/*** @param string|null $string* @param string $to* @param string $from** @internal*/public static function convertEncoding($string, $to, $from): string{if (!\function_exists('iconv')) {throw new RuntimeError('Unable to convert encoding: required function iconv() does not exist. You should install ext-iconv or symfony/polyfill-iconv.');}return iconv($from, $to, $string ?? '');}/*** Returns the length of a variable.** @param mixed $thing A variable** @internal*/public static function length(string $charset, $thing): int{if (null === $thing) {return 0;}if (\is_scalar($thing)) {return mb_strlen($thing, $charset);}if ($thing instanceof \Countable || \is_array($thing) || $thing instanceof \SimpleXMLElement) {return \count($thing);}if ($thing instanceof \Traversable) {return iterator_count($thing);}if ($thing instanceof \Stringable) {return mb_strlen((string) $thing, $charset);}return 1;}/*** Converts a string to uppercase.** @param string|null $string A string** @internal*/public static function upper(string $charset, $string): string{return mb_strtoupper($string ?? '', $charset);}/*** Converts a string to lowercase.** @param string|null $string A string** @internal*/public static function lower(string $charset, $string): string{return mb_strtolower($string ?? '', $charset);}/*** Strips HTML and PHP tags from a string.** @param string|null $string* @param string[]|string|null $allowable_tags** @internal*/public static function striptags($string, $allowable_tags = null): string{return strip_tags($string ?? '', $allowable_tags);}/*** Returns a titlecased string.** @param string|null $string A string** @internal*/public static function titleCase(string $charset, $string): string{return mb_convert_case($string ?? '', \MB_CASE_TITLE, $charset);}/*** Returns a capitalized string.** @param string|null $string A string** @internal*/public static function capitalize(string $charset, $string): string{return mb_strtoupper(mb_substr($string ?? '', 0, 1, $charset), $charset).mb_strtolower(mb_substr($string ?? '', 1, null, $charset), $charset);}/*** @internal** to be removed in 4.0*/public static function callMacro(Template $template, string $method, array $args, int $lineno, array $context, Source $source){if (!method_exists($template, $method)) {$parent = $template;while ($parent = $parent->getParent($context)) {if (method_exists($parent, $method)) {return $parent->$method(...$args);}}throw new RuntimeError(\sprintf('Macro "%s" is not defined in template "%s".', substr($method, \strlen('macro_')), $template->getTemplateName()), $lineno, $source);}return $template->$method(...$args);}/*** @template TSequence** @param TSequence $seq** @return ($seq is iterable ? TSequence : array{})** @internal*/public static function ensureTraversable($seq){if (is_iterable($seq)) {return $seq;}return [];}/*** @internal*/public static function toArray($seq, $preserveKeys = true){if ($seq instanceof \Traversable) {return iterator_to_array($seq, $preserveKeys);}if (!\is_array($seq)) {return $seq;}return $preserveKeys ? $seq : array_values($seq);}/*** Checks if a variable is empty.** {# evaluates to true if the foo variable is null, false, or the empty string #}* {% if foo is empty %}* {# ... #}* {% endif %}** @param mixed $value A variable** @internal*/public static function testEmpty($value): bool{if ($value instanceof \Countable) {return 0 === \count($value);}if ($value instanceof \Traversable) {return !iterator_count($value);}if ($value instanceof \Stringable) {return '' === (string) $value;}return '' === $value || false === $value || null === $value || [] === $value;}/*** Checks if a variable is a sequence.** {# evaluates to true if the foo variable is a sequence #}* {% if foo is sequence %}* {# ... #}* {% endif %}** @param mixed $value** @internal*/public static function testSequence($value): bool{if ($value instanceof \ArrayObject) {$value = $value->getArrayCopy();}if ($value instanceof \Traversable) {$value = iterator_to_array($value);}return \is_array($value) && array_is_list($value);}/*** Checks if a variable is a mapping.** {# evaluates to true if the foo variable is a mapping #}* {% if foo is mapping %}* {# ... #}* {% endif %}** @param mixed $value** @internal*/public static function testMapping($value): bool{if ($value instanceof \ArrayObject) {$value = $value->getArrayCopy();}if ($value instanceof \Traversable) {$value = iterator_to_array($value);}return (\is_array($value) && !array_is_list($value)) || \is_object($value);}/*** Renders a template.** @param array $context* @param string|array|TemplateWrapper $template The template to render or an array of templates to try consecutively* @param array $variables The variables to pass to the template* @param bool $withContext* @param bool $ignoreMissing Whether to ignore missing templates or not* @param bool $sandboxed Whether to sandbox the template or not** @internal*/public static function include(Environment $env, $context, $template, $variables = [], $withContext = true, $ignoreMissing = false, $sandboxed = false): string{$alreadySandboxed = false;$sandbox = null;if ($withContext) {$variables = array_merge($context, $variables);}if ($isSandboxed = $sandboxed && $env->hasExtension(SandboxExtension::class)) {$sandbox = $env->getExtension(SandboxExtension::class);if (!$alreadySandboxed = $sandbox->isSandboxed()) {$sandbox->enableSandbox();}}try {$loaded = null;try {$loaded = $env->resolveTemplate($template);} catch (LoaderError $e) {if (!$ignoreMissing) {throw $e;}return '';}if ($isSandboxed) {$loaded->unwrap()->checkSecurity();}return $loaded->render($variables);} finally {if ($isSandboxed && !$alreadySandboxed) {$sandbox->disableSandbox();}}}/*** Returns a template content without rendering it.** @param string $name The template name* @param bool $ignoreMissing Whether to ignore missing templates or not** @internal*/public static function source(Environment $env, $name, $ignoreMissing = false): string{$loader = $env->getLoader();try {return $loader->getSourceContext($name)->getCode();} catch (LoaderError $e) {if (!$ignoreMissing) {throw $e;}return '';}}/*** Returns the list of cases of the enum.** @template T of \UnitEnum** @param class-string<T> $enum** @return list<T>** @internal*/public static function enumCases(string $enum): array{if (!enum_exists($enum)) {throw new RuntimeError(\sprintf('Enum "%s" does not exist.', $enum));}return $enum::cases();}/*** Provides the ability to access enums by their class names.** @template T of \UnitEnum** @param class-string<T> $enum** @return T** @internal*/public static function enum(string $enum): \UnitEnum{if (!enum_exists($enum)) {throw new RuntimeError(\sprintf('"%s" is not an enum.', $enum));}if (!$cases = $enum::cases()) {throw new RuntimeError(\sprintf('"%s" is an empty enum.', $enum));}return $cases[0];}/*** Provides the ability to get constants from instances as well as class/global constants.** @param string $constant The name of the constant* @param object|null $object The object to get the constant from* @param bool $checkDefined Whether to check if the constant is defined or not** @return mixed Class constants can return many types like scalars, arrays, and* objects depending on the PHP version (\BackedEnum, \UnitEnum, etc.)* When $checkDefined is true, returns true when the constant is defined, false otherwise** @internal*/public static function constant($constant, $object = null, bool $checkDefined = false){if (null !== $object) {if ('class' === $constant) {return $checkDefined ? true : \get_class($object);}$constant = \get_class($object).'::'.$constant;}if (!\defined($constant)) {if ($checkDefined) {return false;}if ('::class' === strtolower(substr($constant, -7))) {throw new RuntimeError(\sprintf('You cannot use the Twig function "constant" to access "%s". You could provide an object and call constant("class", $object) or use the class name directly as a string.', $constant));}throw new RuntimeError(\sprintf('Constant "%s" is undefined.', $constant));}return $checkDefined ? true : \constant($constant);}/*** Batches item.** @param array $items An array of items* @param int $size The size of the batch* @param mixed $fill A value used to fill missing items** @internal*/public static function batch($items, $size, $fill = null, $preserveKeys = true): array{if (!is_iterable($items)) {throw new RuntimeError(\sprintf('The "batch" filter expects a sequence or a mapping, got "%s".', get_debug_type($items)));}$size = (int) ceil($size);$result = array_chunk(self::toArray($items, $preserveKeys), $size, $preserveKeys);if (null !== $fill && $result) {$last = \count($result) - 1;if ($fillCount = $size - \count($result[$last])) {for ($i = 0; $i < $fillCount; ++$i) {$result[$last][] = $fill;}}}return $result;}/*** Returns the attribute value for a given array/object.** @param mixed $object The object or array from where to get the item* @param mixed $item The item to get from the array or object* @param array $arguments An array of arguments to pass if the item is an object method* @param string $type The type of attribute (@see \Twig\Template constants)* @param bool $isDefinedTest Whether this is only a defined check* @param bool $ignoreStrictCheck Whether to ignore the strict attribute check or not* @param int $lineno The template line where the attribute was called** @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true** @throws RuntimeError if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false** @internal*/public static function getAttribute(Environment $env, Source $source, $object, $item, array $arguments = [], $type = Template::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false, $sandboxed = false, int $lineno = -1){$propertyNotAllowedError = null;// arrayif (Template::METHOD_CALL !== $type) {$arrayItem = \is_bool($item) || \is_float($item) ? (int) $item : $item;if ($sandboxed && $object instanceof \ArrayAccess && !\in_array($object::class, self::ARRAY_LIKE_CLASSES, true)) {try {$env->getExtension(SandboxExtension::class)->checkPropertyAllowed($object, $arrayItem, $lineno, $source);} catch (SecurityNotAllowedPropertyError $propertyNotAllowedError) {goto methodCheck;}}if (match (true) {\is_array($object) => \array_key_exists($arrayItem, $object),$object instanceof \ArrayAccess => $object->offsetExists($arrayItem),default => false,}) {if ($isDefinedTest) {return true;}return $object[$arrayItem];}if (Template::ARRAY_CALL === $type || !\is_object($object)) {if ($isDefinedTest) {return false;}if ($ignoreStrictCheck || !$env->isStrictVariables()) {return;}if ($object instanceof \ArrayAccess) {$message = \sprintf('Key "%s" in object with ArrayAccess of class "%s" does not exist.', $arrayItem, \get_class($object));} elseif (\is_object($object)) {$message = \sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface.', $item, \get_class($object));} elseif (\is_array($object)) {if (!$object) {$message = \sprintf('Key "%s" does not exist as the sequence/mapping is empty.', $arrayItem);} else {$message = \sprintf('Key "%s" for sequence/mapping with keys "%s" does not exist.', $arrayItem, implode(', ', array_keys($object)));}} elseif (Template::ARRAY_CALL === $type) {if (null === $object) {$message = \sprintf('Impossible to access a key ("%s") on a null variable.', $item);} else {$message = \sprintf('Impossible to access a key ("%s") on a %s variable ("%s").', $item, get_debug_type($object), $object);}} elseif (null === $object) {$message = \sprintf('Impossible to access an attribute ("%s") on a null variable.', $item);} else {$message = \sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s").', $item, get_debug_type($object), $object);}throw new RuntimeError($message, $lineno, $source);}}$item = (string) $item;if (!\is_object($object)) {if ($isDefinedTest) {return false;}if ($ignoreStrictCheck || !$env->isStrictVariables()) {return;}if (null === $object) {$message = \sprintf('Impossible to invoke a method ("%s") on a null variable.', $item);} elseif (\is_array($object)) {$message = \sprintf('Impossible to invoke a method ("%s") on a sequence/mapping.', $item);} else {$message = \sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s").', $item, get_debug_type($object), $object);}throw new RuntimeError($message, $lineno, $source);}if ($object instanceof Template) {throw new RuntimeError('Accessing \Twig\Template attributes is forbidden.', $lineno, $source);}// object propertyif (Template::METHOD_CALL !== $type) {if ($sandboxed) {try {$env->getExtension(SandboxExtension::class)->checkPropertyAllowed($object, $item, $lineno, $source);} catch (SecurityNotAllowedPropertyError $propertyNotAllowedError) {goto methodCheck;}}static $propertyCheckers = [];if ($object instanceof \Closure && '__invoke' === $item) {return $isDefinedTest ? true : $object();}if (isset($object->$item)|| ($propertyCheckers[$object::class][$item] ??= self::getPropertyChecker($object::class, $item))($object, $item)) {if ($isDefinedTest) {return true;}return $object->$item;}if ($object instanceof \DateTimeInterface && \in_array($item, ['date', 'timezone', 'timezone_type'], true)) {if ($isDefinedTest) {return true;}return ((array) $object)[$item];}if (\defined($object::class.'::'.$item)) {if ($isDefinedTest) {return true;}return \constant($object::class.'::'.$item);}}methodCheck:static $cache = [];$class = \get_class($object);// object method// precedence: getXxx() > isXxx() > hasXxx()if (!isset($cache[$class])) {$methods = get_class_methods($object);if ($object instanceof \Closure) {$methods[] = '__invoke';}sort($methods);$lcMethods = array_map('strtolower', $methods);$classCache = [];foreach ($methods as $i => $method) {$classCache[$method] = $method;$classCache[$lcName = $lcMethods[$i]] = $method;if ('g' === $lcName[0] && str_starts_with($lcName, 'get')) {$name = substr($method, 3);$lcName = substr($lcName, 3);} elseif ('i' === $lcName[0] && str_starts_with($lcName, 'is')) {$name = substr($method, 2);$lcName = substr($lcName, 2);} elseif ('h' === $lcName[0] && str_starts_with($lcName, 'has')) {$name = substr($method, 3);$lcName = substr($lcName, 3);if (\in_array('is'.$lcName, $lcMethods)) {continue;}} else {continue;}// skip get() and is() methods (in which case, $name is empty)if ($name) {if (!isset($classCache[$name])) {$classCache[$name] = $method;}if (!isset($classCache[$lcName])) {$classCache[$lcName] = $method;}}}$cache[$class] = $classCache;}$call = false;if (isset($cache[$class][$item])) {$method = $cache[$class][$item];} elseif (isset($cache[$class][$lcItem = strtolower($item)])) {$method = $cache[$class][$lcItem];} elseif (isset($cache[$class]['__call'])) {$method = $item;$call = true;} else {if ($isDefinedTest) {return false;}if ($propertyNotAllowedError) {throw $propertyNotAllowedError;}if ($ignoreStrictCheck || !$env->isStrictVariables()) {return;}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);}if ($sandboxed) {try {$env->getExtension(SandboxExtension::class)->checkMethodAllowed($object, $method, $lineno, $source);} catch (SecurityNotAllowedMethodError $e) {if ($isDefinedTest) {return false;}if ($propertyNotAllowedError) {throw $propertyNotAllowedError;}throw $e;}}if ($isDefinedTest) {return true;}// Some objects throw exceptions when they have __call, and the method we try// to call is not supported. If ignoreStrictCheck is true, we should return null.try {$ret = $object->$method(...$arguments);} catch (\BadMethodCallException $e) {if ($call && ($ignoreStrictCheck || !$env->isStrictVariables())) {return;}throw $e;}return $ret;}/*** Returns the values from a single column in the input array.** <pre>* {% set items = [{ 'fruit' : 'apple'}, {'fruit' : 'orange' }] %}** {% set fruits = items|column('fruit') %}** {# fruits now contains ['apple', 'orange'] #}* </pre>** @param array|\Traversable $array An array* @param int|string $name The column name* @param int|string|null $index The column to use as the index/keys for the returned array** @return array The array of values** @internal*/public static function column($array, $name, $index = null): array{if (!is_iterable($array)) {throw new RuntimeError(\sprintf('The "column" filter expects a sequence or a mapping, got "%s".', get_debug_type($array)));}if ($array instanceof \Traversable) {$array = iterator_to_array($array);}return array_column($array, $name, $index);}/*** @param \Closure $arrow** @internal*/public static function filter(Environment $env, $array, $arrow){if (!is_iterable($array)) {throw new RuntimeError(\sprintf('The "filter" filter expects a sequence/mapping or "Traversable", got "%s".', get_debug_type($array)));}self::checkArrow($env, $arrow, 'filter', 'filter');if (\is_array($array)) {return array_filter($array, $arrow, \ARRAY_FILTER_USE_BOTH);}// the IteratorIterator wrapping is needed as some internal PHP classes are \Traversable but do not implement \Iteratorreturn new \CallbackFilterIterator(new \IteratorIterator($array), $arrow);}/*** @param \Closure $arrow** @internal*/public static function find(Environment $env, $array, $arrow){if (!is_iterable($array)) {throw new RuntimeError(\sprintf('The "find" filter expects a sequence or a mapping, got "%s".', get_debug_type($array)));}self::checkArrow($env, $arrow, 'find', 'filter');foreach ($array as $k => $v) {if ($arrow($v, $k)) {return $v;}}return null;}/*** @param \Closure $arrow** @internal*/public static function map(Environment $env, $array, $arrow){if (!is_iterable($array)) {throw new RuntimeError(\sprintf('The "map" filter expects a sequence or a mapping, got "%s".', get_debug_type($array)));}self::checkArrow($env, $arrow, 'map', 'filter');$r = [];foreach ($array as $k => $v) {$r[$k] = $arrow($v, $k);}return $r;}/*** @param \Closure $arrow** @internal*/public static function reduce(Environment $env, $array, $arrow, $initial = null){if (!is_iterable($array)) {throw new RuntimeError(\sprintf('The "reduce" filter expects a sequence or a mapping, got "%s".', get_debug_type($array)));}self::checkArrow($env, $arrow, 'reduce', 'filter');$accumulator = $initial;foreach ($array as $key => $value) {$accumulator = $arrow($accumulator, $value, $key);}return $accumulator;}/*** @param \Closure $arrow** @internal*/public static function arraySome(Environment $env, $array, $arrow){if (!is_iterable($array)) {throw new RuntimeError(\sprintf('The "has some" test expects a sequence or a mapping, got "%s".', get_debug_type($array)));}self::checkArrow($env, $arrow, 'has some', 'operator');foreach ($array as $k => $v) {if ($arrow($v, $k)) {return true;}}return false;}/*** @param \Closure $arrow** @internal*/public static function arrayEvery(Environment $env, $array, $arrow){if (!is_iterable($array)) {throw new RuntimeError(\sprintf('The "has every" test expects a sequence or a mapping, got "%s".', get_debug_type($array)));}self::checkArrow($env, $arrow, 'has every', 'operator');foreach ($array as $k => $v) {if (!$arrow($v, $k)) {return false;}}return true;}/*** @internal*/public static function checkArrow(Environment $env, $arrow, $thing, $type){if ($arrow instanceof \Closure) {return;}if ($env->hasExtension(SandboxExtension::class) && $env->getExtension(SandboxExtension::class)->isSandboxed()) {throw new RuntimeError(\sprintf('The callable passed to the "%s" %s must be a Closure in sandbox mode.', $thing, $type));}trigger_deprecation('twig/twig', '3.15', 'Passing a callable that is not a PHP \Closure as an argument to the "%s" %s is deprecated.', $thing, $type);}/*** @internal to be removed in Twig 4*/public static function captureOutput(iterable $body): string{$level = ob_get_level();ob_start();try {foreach ($body as $data) {echo $data;}} catch (\Throwable $e) {while (ob_get_level() > $level) {ob_end_clean();}throw $e;}return ob_get_clean();}/*** @internal*/public static function parseParentFunction(Parser $parser, Node $fakeNode, $args, int $line): AbstractExpression{if (!$blockName = $parser->peekBlockStack()) {throw new SyntaxError('Calling the "parent" function outside of a block is forbidden.', $line, $parser->getStream()->getSourceContext());}if (!$parser->hasInheritance()) {throw new SyntaxError('Calling the "parent" function on a template that does not call "extends" or "use" is forbidden.', $line, $parser->getStream()->getSourceContext());}return new ParentExpression($blockName, $line);}/*** @internal*/public static function parseBlockFunction(Parser $parser, Node $fakeNode, $args, int $line): AbstractExpression{$fakeFunction = new TwigFunction('block', fn ($name, $template = null) => null);$args = (new CallableArgumentsExtractor($fakeNode, $fakeFunction))->extractArguments($args);return new BlockReferenceExpression($args[0], $args[1] ?? null, $line);}/*** @internal*/public static function parseAttributeFunction(Parser $parser, Node $fakeNode, $args, int $line): AbstractExpression{$fakeFunction = new TwigFunction('attribute', fn ($variable, $attribute, $arguments = null) => null);$args = (new CallableArgumentsExtractor($fakeNode, $fakeFunction))->extractArguments($args);/*Deprecation to uncomment sometimes during the lifetime of the 4.x branch$src = $parser->getStream()->getSourceContext();$dep = new DeprecatedCallableInfo('twig/twig', '3.15', 'The "attribute" function is deprecated, use the "." notation instead.');$dep->setName('attribute');$dep->setType('function');$dep->triggerDeprecation($src->getPath() ?: $src->getName(), $line);*/return new GetAttrExpression($args[0], $args[1], $args[2] ?? null, Template::ANY_CALL, $line);}private static function getPropertyChecker(string $class, string $property): \Closure{static $classReflectors = [];$class = $classReflectors[$class] ??= new \ReflectionClass($class);if (!$class->hasProperty($property)) {static $propertyExists;return $propertyExists ??= \Closure::fromCallable('property_exists');}$property = $class->getProperty($property);if (!$property->isPublic() || $property->isStatic()) {static $false;return $false ??= static fn () => false;}return static fn ($object) => $property->isInitialized($object);}}