Skip to content

Commit

Permalink
More Twig updates/fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonkelly committed Sep 12, 2024
1 parent ed6e390 commit 41686b5
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 37 deletions.
117 changes: 80 additions & 37 deletions src/web/twig/Extension.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
use craft\helpers\Template as TemplateHelper;
use craft\helpers\UrlHelper;
use craft\i18n\Locale;
use craft\web\twig\nodes\expressions\binaries\HasEveryBinary;
use craft\web\twig\nodes\expressions\binaries\HasSomeBinary;
use craft\web\twig\nodevisitors\EventTagAdder;
use craft\web\twig\nodevisitors\EventTagFinder;
use craft\web\twig\nodevisitors\GetAttrAdjuster;
Expand Down Expand Up @@ -69,7 +71,9 @@
use Traversable;
use Twig\Environment as TwigEnvironment;
use Twig\Error\RuntimeError;
use Twig\ExpressionParser;
use Twig\Extension\AbstractExtension;
use Twig\Extension\CoreExtension;
use Twig\Extension\GlobalsInterface;
use Twig\TwigFilter;
use Twig\TwigFunction;
Expand All @@ -80,8 +84,6 @@
use yii\db\Expression;
use yii\db\QueryInterface;
use yii\helpers\Markdown;
use function twig_date_converter;
use function twig_date_format_filter;

/**
* Class Extension
Expand All @@ -91,6 +93,40 @@
*/
class Extension extends AbstractExtension implements GlobalsInterface
{
/**
* @since 4.12.2
*/
public static function arraySome(TwigEnvironment $env, $array, $arrow)
{
self::checkArrowFunction($arrow, 'has some', 'operator');
return CoreExtension::arraySome($env, $array, $arrow);
}

/**
* @since 4.12.2
*/
public static function arrayEvery(TwigEnvironment $env, $array, $arrow)
{
self::checkArrowFunction($arrow, 'has every', 'operator');
return CoreExtension::arrayEvery($env, $array, $arrow);
}

private static function checkArrowFunction(mixed $arrow, string $thing, string $type): void
{
if (
is_string($arrow) &&
in_array(ltrim(strtolower($arrow), '\\'), [
'system',
'passthru',
'exec',
'file_get_contents',
'file_put_contents',
])
) {
throw new RuntimeError(sprintf('The "%s" %s does not support passing "%s".', $thing, $type, $arrow));
}
}

/**
* @var View|null
*/
Expand Down Expand Up @@ -212,6 +248,7 @@ public function getFilters(): array
new TwigFilter('indexOf', [$this, 'indexOfFilter']),
new TwigFilter('integer', 'intval'),
new TwigFilter('intersect', 'array_intersect'),
new TwigFilter('find', [$this, 'findFilter'], ['needs_environment' => true]),
new TwigFilter('float', 'floatval'),
new TwigFilter('json_encode', [$this, 'jsonEncodeFilter']),
new TwigFilter('json_decode', [Json::class, 'decode']),
Expand Down Expand Up @@ -313,6 +350,20 @@ public function getTests(): array
];
}

/**
* @inheritdoc
*/
public function getOperators(): array
{
return [
[],
[
'has some' => ['precedence' => 20, 'class' => HasSomeBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
'has every' => ['precedence' => 20, 'class' => HasEveryBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
],
];
}

/**
* @param Address|null $address
* @param array $options
Expand Down Expand Up @@ -498,8 +549,8 @@ public function snakeFilter(mixed $string): string
*/
public function sortFilter(TwigEnvironment $env, iterable $array, string|callable|null $arrow = null): array
{
$this->_checkFilterSupport($arrow);
return twig_sort_filter($env, $array, $arrow);
self::checkArrowFunction($arrow, 'sort', 'filter');
return CoreExtension::sort($env, $array, $arrow);
}

/**
Expand All @@ -515,8 +566,8 @@ public function sortFilter(TwigEnvironment $env, iterable $array, string|callabl
*/
public function reduceFilter(TwigEnvironment $env, mixed $array, mixed $arrow, mixed $initial = null): mixed
{
$this->_checkFilterSupport($arrow);
return twig_array_reduce($env, $array, $arrow, $initial);
self::checkArrowFunction($arrow, 'reduce', 'filter');
return CoreExtension::reduce($env, $array, $arrow, $initial);
}

/**
Expand All @@ -531,8 +582,8 @@ public function reduceFilter(TwigEnvironment $env, mixed $array, mixed $arrow, m
*/
public function mapFilter(TwigEnvironment $env, mixed $array, mixed $arrow = null): array
{
$this->_checkFilterSupport($arrow);
return twig_array_map($env, $array, $arrow);
self::checkArrowFunction($arrow, 'map', 'filter');
return CoreExtension::map($env, $array, $arrow);
}


Expand Down Expand Up @@ -651,6 +702,15 @@ public function timestampFilter(mixed $value, ?string $format = null, bool $with
}
}

/**
* @since 4.12.2
*/
public function findFilter(TwigEnvironment $env, $array, $arrow): mixed
{
self::checkArrowFunction($arrow, 'find', 'filter');
return CoreExtension::find($env, $array, $arrow);
}

/**
* This method will JSON encode a variable. We're overriding Twig's default implementation to set some stricter
* encoding options on text/html/xml requests.
Expand Down Expand Up @@ -954,7 +1014,7 @@ private function isRegex(string $str): bool
public function dateFilter(TwigEnvironment $env, mixed $date, ?string $format = null, mixed $timezone = null, ?string $locale = null): string
{
if ($date instanceof DateInterval) {
return twig_date_format_filter($env, $date, $format, $timezone);
return $env->getExtension(CoreExtension::class)->formatDate($date, $format, $timezone);
}

// Is this a custom PHP date format?
Expand All @@ -966,7 +1026,7 @@ public function dateFilter(TwigEnvironment $env, mixed $date, ?string $format =
}
}

$date = twig_date_converter($env, $date, $timezone);
$date = $env->getExtension(CoreExtension::class)->convertDate($date, $timezone);
$formatter = $locale ? (new Locale($locale))->getFormatter() : Craft::$app->getFormatter();
$fmtTimeZone = $formatter->timeZone;
$formatter->timeZone = $timezone !== null ? $date->getTimezone()->getName() : $formatter->timeZone;
Expand Down Expand Up @@ -1006,7 +1066,7 @@ public function appendFilter(string $tag, string $html, ?string $ifExists = null
*/
public function atomFilter(TwigEnvironment $env, mixed $date, mixed $timezone = null): string
{
return twig_date_format_filter($env, $date, DateTime::ATOM, $timezone);
return $env->getExtension(CoreExtension::class)->formatDate($date, DateTime::ATOM, $timezone);
}

/**
Expand Down Expand Up @@ -1037,7 +1097,7 @@ public function attrFilter(string $tag, array $attributes): string
*/
public function rssFilter(TwigEnvironment $env, mixed $date, mixed $timezone = null): string
{
return twig_date_format_filter($env, $date, DateTime::RSS, $timezone);
return $env->getExtension(CoreExtension::class)->formatDate($date, DateTime::RSS, $timezone);
}

/**
Expand All @@ -1061,7 +1121,7 @@ public function timeFilter(TwigEnvironment $env, mixed $date, ?string $format =
}
}

$date = twig_date_converter($env, $date, $timezone);
$date = $env->getExtension(CoreExtension::class)->convertDate($date, $timezone);
$formatter = $locale ? (new Locale($locale))->getFormatter() : Craft::$app->getFormatter();
$fmtTimeZone = $formatter->timeZone;
$formatter->timeZone = $timezone !== null ? $date->getTimezone()->getName() : $formatter->timeZone;
Expand Down Expand Up @@ -1091,7 +1151,7 @@ public function datetimeFilter(TwigEnvironment $env, mixed $date, ?string $forma
}
}

$date = twig_date_converter($env, $date, $timezone);
$date = $env->getExtension(CoreExtension::class)->convertDate($date, $timezone);
$formatter = $locale ? (new Locale($locale))->getFormatter() : Craft::$app->getFormatter();
$fmtTimeZone = $formatter->timeZone;
$formatter->timeZone = $timezone !== null ? $date->getTimezone()->getName() : $formatter->timeZone;
Expand Down Expand Up @@ -1122,7 +1182,7 @@ public function encencFilter(mixed $str): string
*/
public function filterFilter(TwigEnvironment $env, iterable $arr, ?callable $arrow = null): array
{
$this->_checkFilterSupport($arrow);
self::checkArrowFunction($arrow, 'filter', 'filter');

/** @var array|Traversable $arr */
if ($arrow === null) {
Expand All @@ -1132,7 +1192,7 @@ public function filterFilter(TwigEnvironment $env, iterable $arr, ?callable $arr
return array_filter($arr);
}

$filtered = twig_array_filter($env, $arr, $arrow);
$filtered = CoreExtension::filter($env, $arr, $arrow);

if (is_array($filtered)) {
return $filtered;
Expand Down Expand Up @@ -1182,7 +1242,7 @@ public function groupFilter(iterable $arr, callable|string $arrow): array
*/
public function httpdateFilter(TwigEnvironment $env, mixed $date, mixed $timezone = null): string
{
return twig_date_format_filter($env, $date, DateTime::RFC7231, $timezone);
return $env->getExtension(CoreExtension::class)->formatDate($date, DateTime::RFC7231, $timezone);
}


Expand Down Expand Up @@ -1232,7 +1292,7 @@ public function lengthFilter(TwigEnvironment $env, mixed $value): int
return $value->count();
}

return twig_length_filter($env, $value);
return CoreExtension::length($env->getCharset(), $value);
}

/**
Expand Down Expand Up @@ -1304,7 +1364,7 @@ public function mergeFilter(iterable $arr1, iterable $arr2, bool $recursive = fa
return ArrayHelper::merge($arr1, $arr2);
}

return twig_array_merge($arr1, $arr2);
return CoreExtension::merge($arr1, $arr2);
}

/**
Expand Down Expand Up @@ -1467,7 +1527,7 @@ public function dateFunction(TwigEnvironment $env, mixed $date = null, mixed $ti
}
}

return twig_date_converter($env, $date, $timezone);
return $env->getExtension(CoreExtension::class)->convertDate($date, $timezone);
}

/**
Expand Down Expand Up @@ -1693,21 +1753,4 @@ public function getGlobals(): array
'yesterday' => DateTimeHelper::yesterday(),
];
}

/**
* @param mixed $arrow
* @throws RuntimeError
*/
private function _checkFilterSupport(mixed $arrow): void
{
if (is_string($arrow) && in_array(ltrim(strtolower($arrow), '\\'), [
'system',
'passthru',
'exec',
'file_get_contents',
'file_put_contents',
])) {
throw new RuntimeError('Not supported in this filter.');
}
}
}
37 changes: 37 additions & 0 deletions src/web/twig/nodes/expressions/binaries/HasEveryBinary.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php
/**
* @link https://craftcms.com/
* @copyright Copyright (c) Pixel & Tonic, Inc.
* @license https://craftcms.github.io/license/
*/

namespace craft\web\twig\nodes\expressions\binaries;

use craft\web\twig\Extension;
use Twig\Compiler;
use Twig\Node\Expression\Binary\AbstractBinary;

/**
* Class HasEveryBinary
*
* @author Pixel & Tonic, Inc. <[email protected]>
* @since 4.12.2
*/
class HasEveryBinary extends AbstractBinary
{
public function compile(Compiler $compiler): void
{
$compiler
->raw(sprintf('%s::arrayEvery($this->env, ', Extension::class))
->subcompile($this->getNode('left'))
->raw(', ')
->subcompile($this->getNode('right'))
->raw(')')
;
}

public function operator(Compiler $compiler): Compiler
{
return $compiler->raw('');
}
}
37 changes: 37 additions & 0 deletions src/web/twig/nodes/expressions/binaries/HasSomeBinary.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php
/**
* @link https://craftcms.com/
* @copyright Copyright (c) Pixel & Tonic, Inc.
* @license https://craftcms.github.io/license/
*/

namespace craft\web\twig\nodes\expressions\binaries;

use craft\web\twig\Extension;
use Twig\Compiler;
use Twig\Node\Expression\Binary\AbstractBinary;

/**
* Class HasSomeBinary
*
* @author Pixel & Tonic, Inc. <[email protected]>
* @since 4.12.2
*/
class HasSomeBinary extends AbstractBinary
{
public function compile(Compiler $compiler): void
{
$compiler
->raw(sprintf('%s::arraySome($this->env, ', Extension::class))
->subcompile($this->getNode('left'))
->raw(', ')
->subcompile($this->getNode('right'))
->raw(')')
;
}

public function operator(Compiler $compiler): Compiler
{
return $compiler->raw('');
}
}

0 comments on commit 41686b5

Please sign in to comment.