t\n\r\v\f"); if (!\is_numeric($aTrim)) { return $a <=> (string) $b; } return (float) $aTrim <=> $b; } // fallback to <=> return $a <=> $b; } 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(); } } public static function trim($string, $characterMask = null, $side = 'both') : string { if (null === $characterMask) { $characterMask = " \t\n\r\x00\v"; } switch ($side) { case 'both': return \trim($string ?? '', $characterMask); case 'left': return \ltrim($string ?? '', $characterMask); case 'right': return \rtrim($string ?? '', $characterMask); default: throw new RuntimeError('Trimming side must be "left", "right" or "both".'); } } public static function nl2br($string) : string { return \nl2br($string ?? ''); } public static function spaceless($content) : string { return \trim(\preg_replace('/>\\s+<', $content ?? '')); } 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 ?? ''); } 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 (\method_exists($thing, '__toString')) { return \mb_strlen((string) $thing, $charset); } return 1; } public static function upper(string $charset, $string) : string { return \mb_strtoupper($string ?? '', $charset); } public static function lower(string $charset, $string) : string { return \mb_strtolower($string ?? '', $charset); } public static function striptags($string, $allowable_tags = null) : string { return \strip_tags($string ?? '', $allowable_tags); } public static function titleCase(string $charset, $string) : string { return \mb_convert_case($string ?? '', \MB_CASE_TITLE, $charset); } 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); } 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); } public static function ensureTraversable($seq) { if (\is_iterable($seq)) { return $seq; } return []; } 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); } public static function testEmpty($value) : bool { if ($value instanceof \Countable) { return 0 === \count($value); } if ($value instanceof \Traversable) { return !\iterator_count($value); } if (\is_object($value) && \method_exists($value, '__toString')) { return '' === (string) $value; } return '' === $value || \false === $value || null === $value || [] === $value; } 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); } 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); } 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; } } if ($isSandboxed && $loaded) { $loaded->unwrap()->checkSecurity(); } return $loaded ? $loaded->render($variables) : ''; } finally { if ($isSandboxed && !$alreadySandboxed) { $sandbox->disableSandbox(); } } } 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 ''; } } public static function constant($constant, $object = null) { if (null !== $object) { if ('class' === $constant) { return \get_class($object); } $constant = \get_class($object) . '::' . $constant; } if (!\defined($constant)) { 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 \constant($constant); } public static function constantIsDefined($constant, $object = null) : bool { if (null !== $object) { if ('class' === $constant) { return \true; } $constant = \get_class($object) . '::' . $constant; } return \defined($constant); } 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/mapping or "Traversable", got "%s".', \is_object($items) ? \get_class($items) : \gettype($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; } public static function getAttribute(Environment $env, Source $source, $object, $item, array $arguments = [], $type = 'any', $isDefinedTest = \false, $ignoreStrictCheck = \false, $sandboxed = \false, int $lineno = -1) { $propertyNotAllowedError = null; // array if ('method' !== $type) { $arrayItem = \is_bool($item) || \is_float($item) ? (int) $item : $item; if ($sandboxed && $object instanceof \ArrayAccess && !\in_array(\get_class($object), self::ARRAY_LIKE_CLASSES, \true)) { try { $env->getExtension(SandboxExtension::class)->checkPropertyAllowed($object, $arrayItem, $lineno, $source); } catch (SecurityNotAllowedPropertyError $propertyNotAllowedError) { goto methodCheck; } } if ((\is_array($object) || $object instanceof \ArrayObject) && (isset($object[$arrayItem]) || \array_key_exists($arrayItem, (array) $object)) || $object instanceof \ArrayAccess && isset($object[$arrayItem])) { if ($isDefinedTest) { return \true; } return $object[$arrayItem]; } if ('array' === $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 (empty($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 ('array' === $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, \gettype($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, \gettype($object), $object); } throw new RuntimeError($message, $lineno, $source); } } 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, \gettype($object), $object); } throw new RuntimeError($message, $lineno, $source); } if ($object instanceof Template) { throw new RuntimeError('Accessing \\Twig\\Template attributes is forbidden.', $lineno, $source); } // object property if ('method' !== $type) { if ($sandboxed) { try { $env->getExtension(SandboxExtension::class)->checkPropertyAllowed($object, $item, $lineno, $source); } catch (SecurityNotAllowedPropertyError $propertyNotAllowedError) { goto methodCheck; } } if (isset($object->{$item}) || \array_key_exists((string) $item, (array) $object)) { if ($isDefinedTest) { return \true; } return $object->{$item}; } } methodCheck: static $cache = []; $class = \get_class($object); // object method // precedence: getXxx() > isXxx() > hasXxx() if (!isset($cache[$class])) { $methods = \get_class_methods($object); \sort($methods); $lcMethods = \array_map(function ($value) { return \strtr($value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); }, $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 = \strtr($item, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')])) { $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; } public static function column($array, $name, $index = null) : array { if ($array instanceof \Traversable) { $array = \iterator_to_array($array); } elseif (!\is_array($array)) { throw new RuntimeError(\sprintf('The column filter only works with sequences/mappings or "Traversable", got "%s" as first argument.', \gettype($array))); } return \array_column($array, $name, $index); } 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".', \is_object($array) ? \get_class($array) : \gettype($array))); } self::checkArrowInSandbox($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 \Iterator return new \CallbackFilterIterator(new \IteratorIterator($array), $arrow); } public static function find(Environment $env, $array, $arrow) { self::checkArrowInSandbox($env, $arrow, 'find', 'filter'); foreach ($array as $k => $v) { if ($arrow($v, $k)) { return $v; } } return null; } public static function map(Environment $env, $array, $arrow) { self::checkArrowInSandbox($env, $arrow, 'map', 'filter'); $r = []; foreach ($array as $k => $v) { $r[$k] = $arrow($v, $k); } return $r; } public static function reduce(Environment $env, $array, $arrow, $initial = null) { self::checkArrowInSandbox($env, $arrow, 'reduce', 'filter'); if (!\is_array($array) && !$array instanceof \Traversable) { throw new RuntimeError(\sprintf('The "reduce" filter only works with sequences/mappings or "Traversable", got "%s" as first argument.', \gettype($array))); } $accumulator = $initial; foreach ($array as $key => $value) { $accumulator = $arrow($accumulator, $value, $key); } return $accumulator; } public static function arraySome(Environment $env, $array, $arrow) { self::checkArrowInSandbox($env, $arrow, 'has some', 'operator'); foreach ($array as $k => $v) { if ($arrow($v, $k)) { return \true; } } return \false; } public static function arrayEvery(Environment $env, $array, $arrow) { self::checkArrowInSandbox($env, $arrow, 'has every', 'operator'); foreach ($array as $k => $v) { if (!$arrow($v, $k)) { return \false; } } return \true; } public static function checkArrowInSandbox(Environment $env, $arrow, $thing, $type) { if (!$arrow instanceof \Closure && $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)); } } public static function captureOutput(iterable $body) : string { $output = ''; $level = \ob_get_level(); \ob_start(); try { foreach ($body as $data) { if (\ob_get_length()) { $output .= \ob_get_clean(); \ob_start(); } $output .= $data; } if (\ob_get_length()) { $output .= \ob_get_clean(); } } finally { while (\ob_get_level() > $level) { \ob_end_clean(); } } return $output; } }