mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-05 06:56:51 +00:00
Merge branch '3.x' into 4.x
* 3.x: Fixing minor typo in Update inline_css.rst Ignore static properties when using the dot operator Fix CS Fix constant() behavior when used with ?? Finish the work fix indentation Apply suggestions from code review typo fix merge docs and changelog typehint instead of checkArrow checkArrow, typehints invoke filter
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
``inline_css``
|
||||
==============
|
||||
|
||||
The ``inline_css`` filter inline CSS styles in HTML documents:
|
||||
The ``inline_css`` filter inlines CSS styles in HTML documents:
|
||||
|
||||
.. code-block:: html+twig
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
``invoke``
|
||||
==========
|
||||
|
||||
.. versionadded:: 3.19
|
||||
|
||||
The ``invoke`` filter has been added in Twig 3.19.
|
||||
|
||||
The ``invoke`` filter invokes an arrow function with the given arguments:
|
||||
|
||||
.. code-block:: twig
|
||||
|
||||
{% set person = { first: "Bob", last: "Smith" } %}
|
||||
{% set func = p => "#{p.first} #{p.last}" %}
|
||||
|
||||
{{ func|invoke(person) }}
|
||||
{# outputs Bob Smith #}
|
||||
@@ -994,6 +994,9 @@ The following operators don't fit into any of the other categories:
|
||||
|
||||
{{ people|map(first_name_fn)|join(', ') }}
|
||||
|
||||
Arrow functions can be called using the :doc:`invoke </filters/invoke>`
|
||||
filter.
|
||||
|
||||
Operators
|
||||
~~~~~~~~~
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class IntegrationTest extends IntegrationTestCase
|
||||
protected function getRuntimeLoaders()
|
||||
{
|
||||
return [
|
||||
new class() implements RuntimeLoaderInterface {
|
||||
new class implements RuntimeLoaderInterface {
|
||||
public function load(string $class): ?object
|
||||
{
|
||||
return CacheRuntime::class === $class ? new CacheRuntime(new ArrayAdapter()) : null;
|
||||
|
||||
@@ -36,7 +36,7 @@ Hello
|
||||
=====
|
||||
|
||||
Great!
|
||||
EOF
|
||||
EOF,
|
||||
]));
|
||||
$twig->addExtension(new MarkdownExtension());
|
||||
$twig->addRuntimeLoader(new class($class) implements RuntimeLoaderInterface {
|
||||
|
||||
@@ -53,7 +53,7 @@ abstract class AbstractExtension implements LastModifiedExtensionInterface
|
||||
$lastModified = filemtime($filename);
|
||||
|
||||
// Track modifications of the runtime class if it exists and follows the naming convention
|
||||
if (str_ends_with($filename, 'Extension.php') && is_file($filename = substr($filename, 0, -13) . 'Runtime.php')) {
|
||||
if (str_ends_with($filename, 'Extension.php') && is_file($filename = substr($filename, 0, -13).'Runtime.php')) {
|
||||
$lastModified = max($lastModified, filemtime($filename));
|
||||
}
|
||||
|
||||
|
||||
@@ -268,6 +268,7 @@ final class CoreExtension extends AbstractExtension
|
||||
// iteration and runtime
|
||||
new TwigFilter('default', self::default(...), ['node_class' => DefaultFilter::class]),
|
||||
new TwigFilter('keys', self::keys(...)),
|
||||
new TwigFilter('invoke', self::invoke(...)),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -908,6 +909,16 @@ final class CoreExtension extends AbstractExtension
|
||||
return array_keys($array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes a callable.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function invoke(\Closure $arrow, ...$arguments): mixed
|
||||
{
|
||||
return $arrow(...$arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverses a variable.
|
||||
*
|
||||
@@ -2027,7 +2038,7 @@ final class CoreExtension extends AbstractExtension
|
||||
|
||||
$property = $class->getProperty($property);
|
||||
|
||||
if (!$property->isPublic()) {
|
||||
if (!$property->isPublic() || $property->isStatic()) {
|
||||
static $false;
|
||||
|
||||
return $false ??= static fn () => false;
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ class Lexer
|
||||
(?<DNUM>(?&LNUM)(?:(?&FRAC))?) # Decimal number 123_456.456
|
||||
)(?:(?&DNUM)(?:(?&EXPONENT))?) # 123_456.456E+10
|
||||
/Ax';
|
||||
|
||||
|
||||
public const REGEX_DQ_STRING_DELIM = '/"/A';
|
||||
public const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As';
|
||||
public const REGEX_INLINE_COMMENT = '/#[^\n]*/A';
|
||||
|
||||
@@ -98,6 +98,13 @@ abstract class Node implements \Countable, \IteratorAggregate
|
||||
return $repr;
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
foreach ($this->nodes as $name => $node) {
|
||||
$this->nodes[$name] = clone $node;
|
||||
}
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
foreach ($this->nodes as $node) {
|
||||
|
||||
+1
-1
@@ -442,7 +442,7 @@ abstract class Template
|
||||
return $parent->hasMacro($name, $context);
|
||||
}
|
||||
|
||||
protected function getTemplateForMacro(string $name, array $context, int $line, Source $source): Template
|
||||
protected function getTemplateForMacro(string $name, array $context, int $line, Source $source): self
|
||||
{
|
||||
if (method_exists($this, $name)) {
|
||||
return $this;
|
||||
|
||||
@@ -178,7 +178,7 @@ class EnvironmentTest extends TestCase
|
||||
|
||||
// force compilation
|
||||
$twig = new Environment($loader = new ArrayLoader(['index' => '{{ foo }}']), $options);
|
||||
$twig->addExtension($extension = new class() extends AbstractExtension {
|
||||
$twig->addExtension($extension = new class extends AbstractExtension {
|
||||
public bool $throw = false;
|
||||
|
||||
public function getFilters(): array
|
||||
@@ -464,7 +464,7 @@ class EnvironmentTest extends TestCase
|
||||
public function testResettingGlobals()
|
||||
{
|
||||
$twig = new Environment(new ArrayLoader(['index' => '']));
|
||||
$twig->addExtension(new class() extends AbstractExtension implements GlobalsInterface {
|
||||
$twig->addExtension(new class extends AbstractExtension implements GlobalsInterface {
|
||||
public function getGlobals(): array
|
||||
{
|
||||
return [
|
||||
|
||||
+5
-5
@@ -42,7 +42,7 @@ class ErrorTest extends TestCase
|
||||
{% block foo %}
|
||||
{{ foo.bar }}
|
||||
{% endblock %}
|
||||
EOHTML
|
||||
EOHTML,
|
||||
]);
|
||||
|
||||
$twig = new Environment($loader, ['strict_variables' => true, 'debug' => true, 'cache' => false]);
|
||||
@@ -71,7 +71,7 @@ EOHTML
|
||||
{% block foo %}
|
||||
{{ foo.bar }}
|
||||
{% endblock %}
|
||||
EOHTML
|
||||
EOHTML,
|
||||
]);
|
||||
$twig = new Environment($loader, ['strict_variables' => true, 'debug' => true, 'cache' => false]);
|
||||
|
||||
@@ -162,7 +162,7 @@ EOHTML
|
||||
{% for n in variable|filter(x => x > 3) %}
|
||||
This list contains {{n}}.
|
||||
{% endfor %}
|
||||
EOHTML
|
||||
EOHTML,
|
||||
]);
|
||||
|
||||
$twig = new Environment($loader, ['debug' => true, 'cache' => false]);
|
||||
@@ -189,7 +189,7 @@ EOHTML
|
||||
{% for n in variable|map(x => x * 3) %}
|
||||
{{- n -}}
|
||||
{% endfor %}
|
||||
EOHTML
|
||||
EOHTML,
|
||||
]);
|
||||
|
||||
$twig = new Environment($loader, ['debug' => true, 'cache' => false]);
|
||||
@@ -214,7 +214,7 @@ EOHTML
|
||||
'reduce-null.html' => <<<EOHTML
|
||||
{# We expect a runtime error if `variable` is not traversable #}
|
||||
{{ variable|reduce((carry, x) => carry + x) }}
|
||||
EOHTML
|
||||
EOHTML,
|
||||
]);
|
||||
|
||||
$twig = new Environment($loader, ['debug' => true, 'cache' => false]);
|
||||
|
||||
@@ -395,7 +395,7 @@ class ExpressionParserTest extends TestCase
|
||||
public function testCompiledCodeForDynamicTest()
|
||||
{
|
||||
$env = new Environment(new ArrayLoader(['index' => '{{ "a" is foo_foo_bar_bar }}']), ['cache' => false, 'autoescape' => false]);
|
||||
$env->addExtension(new class() extends AbstractExtension {
|
||||
$env->addExtension(new class extends AbstractExtension {
|
||||
public function getTests(): array
|
||||
{
|
||||
return [
|
||||
@@ -410,7 +410,7 @@ class ExpressionParserTest extends TestCase
|
||||
public function testCompiledCodeForDynamicFunction()
|
||||
{
|
||||
$env = new Environment(new ArrayLoader(['index' => '{{ foo_foo_bar_bar("a") }}']), ['cache' => false, 'autoescape' => false]);
|
||||
$env->addExtension(new class() extends AbstractExtension {
|
||||
$env->addExtension(new class extends AbstractExtension {
|
||||
public function getFunctions(): array
|
||||
{
|
||||
return [
|
||||
@@ -425,7 +425,7 @@ class ExpressionParserTest extends TestCase
|
||||
public function testCompiledCodeForDynamicFilter()
|
||||
{
|
||||
$env = new Environment(new ArrayLoader(['index' => '{{ "a"|foo_foo_bar_bar }}']), ['cache' => false, 'autoescape' => false]);
|
||||
$env->addExtension(new class() extends AbstractExtension {
|
||||
$env->addExtension(new class extends AbstractExtension {
|
||||
public function getFilters(): array
|
||||
{
|
||||
return [
|
||||
@@ -451,10 +451,10 @@ class ExpressionParserTest extends TestCase
|
||||
public function testUnaryPrecedenceChange()
|
||||
{
|
||||
$env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]);
|
||||
$env->addExtension(new class () extends AbstractExtension {
|
||||
$env->addExtension(new class extends AbstractExtension {
|
||||
public function getOperators(): array
|
||||
{
|
||||
$class = new class (new ConstantExpression('foo', 1), 1) extends AbstractUnary {
|
||||
$class = new class(new ConstantExpression('foo', 1), 1) extends AbstractUnary {
|
||||
public function operator(Compiler $compiler): Compiler
|
||||
{
|
||||
return $compiler->raw('!');
|
||||
|
||||
@@ -55,7 +55,7 @@ class CoreTest extends TestCase
|
||||
{
|
||||
return [
|
||||
'empty' => [[]],
|
||||
'non-countable' => [new class() extends \ArrayObject {
|
||||
'non-countable' => [new class extends \ArrayObject {
|
||||
}],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -507,7 +507,7 @@ EOF
|
||||
|
||||
public function testSandboxSourcePolicyEnableReturningFalse()
|
||||
{
|
||||
$twig = $this->getEnvironment(false, [], self::$templates, [], [], [], [], [], new class() implements \Twig\Sandbox\SourcePolicyInterface {
|
||||
$twig = $this->getEnvironment(false, [], self::$templates, [], [], [], [], [], new class implements \Twig\Sandbox\SourcePolicyInterface {
|
||||
public function enableSandbox(Source $source): bool
|
||||
{
|
||||
return '1_basic' != $source->getName();
|
||||
@@ -518,7 +518,7 @@ EOF
|
||||
|
||||
public function testSandboxSourcePolicyEnableReturningTrue()
|
||||
{
|
||||
$twig = $this->getEnvironment(false, [], self::$templates, [], [], [], [], [], new class() implements \Twig\Sandbox\SourcePolicyInterface {
|
||||
$twig = $this->getEnvironment(false, [], self::$templates, [], [], [], [], [], new class implements \Twig\Sandbox\SourcePolicyInterface {
|
||||
public function enableSandbox(Source $source): bool
|
||||
{
|
||||
return '1_basic' === $source->getName();
|
||||
@@ -530,7 +530,7 @@ EOF
|
||||
|
||||
public function testSandboxSourcePolicyFalseDoesntOverrideOtherEnables()
|
||||
{
|
||||
$twig = $this->getEnvironment(true, [], self::$templates, [], [], [], [], [], new class() implements \Twig\Sandbox\SourcePolicyInterface {
|
||||
$twig = $this->getEnvironment(true, [], self::$templates, [], [], [], [], [], new class implements \Twig\Sandbox\SourcePolicyInterface {
|
||||
public function enableSandbox(Source $source): bool
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
--TEST--
|
||||
"invoke" filter
|
||||
--TEMPLATE--
|
||||
{% set func = x => 'Hello '~x %}
|
||||
{{ func|invoke('World') }}
|
||||
{% set func2 = (x, y) => x+y %}
|
||||
{{ func2|invoke(3, 2) }}
|
||||
--DATA--
|
||||
return []
|
||||
--CONFIG--
|
||||
return []
|
||||
--EXPECT--
|
||||
Hello World
|
||||
5
|
||||
@@ -4,9 +4,11 @@
|
||||
{{ constant('DATE_W3C') == expect ? 'true' : 'false' }}
|
||||
{{ constant('ARRAY_AS_PROPS', object) }}
|
||||
{{ constant('class', object) }}
|
||||
{{ constant('ARRAY_AS_PROPS', object) ?? 'KO' }}
|
||||
--DATA--
|
||||
return ['expect' => DATE_W3C, 'object' => new \ArrayObject(['hi'])]
|
||||
--EXPECT--
|
||||
true
|
||||
2
|
||||
ArrayObject
|
||||
2
|
||||
|
||||
@@ -67,6 +67,8 @@ class TwigTestFoo implements \Iterator
|
||||
public $position = 0;
|
||||
public $array = [1, 2];
|
||||
|
||||
public static $foo = 'Foo';
|
||||
|
||||
public function bar($param1 = null, $param2 = null)
|
||||
{
|
||||
return 'bar'.($param1 ? '_'.$param1 : '').($param2 ? '-'.$param2 : '');
|
||||
|
||||
@@ -176,7 +176,7 @@ class FilterTest extends NodeTestCase
|
||||
|
||||
private static function createExtension(): AbstractExtension
|
||||
{
|
||||
return new class() extends AbstractExtension {
|
||||
return new class extends AbstractExtension {
|
||||
public function getFilters(): array
|
||||
{
|
||||
return [
|
||||
|
||||
Reference in New Issue
Block a user