Skip to content

Commit

Permalink
Merge branch '3.x' into 4.x
Browse files Browse the repository at this point in the history
* 3.x:
  Fix a @return type hint
  Fix CS
  Fix a test
  Fix type
  Simplify sandbox code
  Get rid of weird code
  [html-extra] filter classes
  Fix some inconsistencies
  • Loading branch information
fabpot committed Apr 30, 2024
2 parents b450e75 + 82f8864 commit 5fdb1ee
Show file tree
Hide file tree
Showing 13 changed files with 35 additions and 38 deletions.
2 changes: 1 addition & 1 deletion extra/html-extra/HtmlExtension.php
Expand Up @@ -108,6 +108,6 @@ public static function htmlClasses(...$args): string
}
}

return implode(' ', array_unique($classes));
return implode(' ', array_unique(array_filter($classes, static function($v) { return '' !== $v; })));
}
}
4 changes: 2 additions & 2 deletions extra/html-extra/Tests/Fixtures/html_classes.test
@@ -1,12 +1,12 @@
--TEST--
"html_classes" function
--TEMPLATE--
{{ html_classes('a', {'b': true, 'c': false}, 'd') }}
{{ html_classes('a', {'b': true, 'c': false}, 'd', false ? 'e', true ? 'f', '0') }}
{% set class_a = 'a' %}
{% set class_b = 'b' %}
{{ html_classes(class_a, {(class_b): true})}}
--DATA--
return []
--EXPECT--
a b d
a b d f 0
a b
6 changes: 3 additions & 3 deletions src/Extension/CoreExtension.php
Expand Up @@ -329,7 +329,7 @@ public static function cycle($values, $position)
}

if (!\count($values)) {
throw new RuntimeError('The "cycle" function does not work on empty arrays');
throw new RuntimeError('The "cycle" function does not work on empty arrays.');
}

return $values[$position % \count($values)];
Expand Down Expand Up @@ -476,7 +476,7 @@ public static function sprintf($format, ...$values)
*
* @internal
*/
public static function dateConverter(Environment $env, $date = null, $timezone = null): \DateTimeImmutable
public static function dateConverter(Environment $env, $date = null, $timezone = null): \DateTime|\DateTimeImmutable
{
// determine the timezone
if (false !== $timezone) {
Expand All @@ -492,7 +492,7 @@ public static function dateConverter(Environment $env, $date = null, $timezone =
return false !== $timezone ? $date->setTimezone($timezone) : $date;
}

if ($date instanceof \DateTimeInterface) {
if ($date instanceof \DateTime) {
$date = \DateTimeImmutable::createFromInterface($date);
if (false !== $timezone) {
return $date->setTimezone($timezone);
Expand Down
28 changes: 11 additions & 17 deletions src/Node/CheckSecurityNode.php
Expand Up @@ -24,6 +24,11 @@ class CheckSecurityNode extends Node
private array $usedTags;
private array $usedFunctions;

/**
* @param array<string, int> $usedFilters
* @param array<string, int> $usedTags
* @param array<string, int> $usedFunctions
*/
public function __construct(array $usedFilters, array $usedTags, array $usedFunctions)
{
$this->usedFilters = $usedFilters;
Expand All @@ -35,32 +40,21 @@ public function __construct(array $usedFilters, array $usedTags, array $usedFunc

public function compile(Compiler $compiler): void
{
$tags = $filters = $functions = [];
foreach (['tags', 'filters', 'functions'] as $type) {
foreach ($this->{'used'.ucfirst($type)} as $name => $node) {
if ($node instanceof Node) {
${$type}[$name] = $node->getTemplateLine();
} else {
${$type}[$node] = null;
}
}
}

$compiler
->write("\n")
->write("public function checkSecurity()\n")
->write("{\n")
->indent()
->write('static $tags = ')->repr(array_filter($tags))->raw(";\n")
->write('static $filters = ')->repr(array_filter($filters))->raw(";\n")
->write('static $functions = ')->repr(array_filter($functions))->raw(";\n\n")
->write('static $tags = ')->repr(array_filter($this->usedTags))->raw(";\n")
->write('static $filters = ')->repr(array_filter($this->usedFilters))->raw(";\n")
->write('static $functions = ')->repr(array_filter($this->usedFunctions))->raw(";\n\n")
->write("try {\n")
->indent()
->write("\$this->sandbox->checkSecurity(\n")
->indent()
->write(!$tags ? "[],\n" : "['".implode("', '", array_keys($tags))."'],\n")
->write(!$filters ? "[],\n" : "['".implode("', '", array_keys($filters))."'],\n")
->write(!$functions ? "[],\n" : "['".implode("', '", array_keys($functions))."'],\n")
->write(!$this->usedTags ? "[],\n" : "['".implode("', '", array_keys($this->usedTags))."'],\n")
->write(!$this->usedFilters ? "[],\n" : "['".implode("', '", array_keys($this->usedFilters))."'],\n")
->write(!$this->usedFunctions ? "[],\n" : "['".implode("', '", array_keys($this->usedFunctions))."'],\n")
->write("\$this->source\n")
->outdent()
->write(");\n")
Expand Down
11 changes: 7 additions & 4 deletions src/NodeVisitor/SandboxNodeVisitor.php
Expand Up @@ -34,8 +34,11 @@
final class SandboxNodeVisitor implements NodeVisitorInterface
{
private bool $inAModule = false;
/** @var array<string, int> */
private array $tags;
/** @var array<string, int> */
private array $filters;
/** @var array<string, int> */
private array $functions;
private bool $needsToStringWrap = false;

Expand All @@ -51,22 +54,22 @@ public function enterNode(Node $node, Environment $env): Node
} elseif ($this->inAModule) {
// look for tags
if ($node->getNodeTag() && !isset($this->tags[$node->getNodeTag()])) {
$this->tags[$node->getNodeTag()] = $node;
$this->tags[$node->getNodeTag()] = $node->getTemplateLine();
}

// look for filters
if ($node instanceof FilterExpression && !isset($this->filters[$node->getNode('filter')->getAttribute('value')])) {
$this->filters[$node->getNode('filter')->getAttribute('value')] = $node;
$this->filters[$node->getNode('filter')->getAttribute('value')] = $node->getTemplateLine();
}

// look for functions
if ($node instanceof FunctionExpression && !isset($this->functions[$node->getAttribute('name')])) {
$this->functions[$node->getAttribute('name')] = $node;
$this->functions[$node->getAttribute('name')] = $node->getTemplateLine();
}

// the .. operator is equivalent to the range() function
if ($node instanceof RangeBinary && !isset($this->functions['range'])) {
$this->functions['range'] = $node;
$this->functions['range'] = $node->getTemplateLine();
}

if ($node instanceof PrintNode) {
Expand Down
2 changes: 1 addition & 1 deletion src/Test/IntegrationTestCase.php
Expand Up @@ -189,7 +189,7 @@ protected function doIntegrationTest($file, $message, $condition, $templates, $e
// avoid using the same PHP class name for different cases
$p = new \ReflectionProperty($twig, 'templateClassPrefix');
$p->setAccessible(true);
$p->setValue($twig, '__TwigTemplate_'.hash('xxh128', uniqid(mt_rand(), true), false).'_');
$p->setValue($twig, '__TwigTemplate_'.hash('xxh128', uniqid((string) mt_rand(), true), false).'_');

$deprecations = [];
try {
Expand Down
2 changes: 1 addition & 1 deletion tests/Extension/EscaperTest.php
Expand Up @@ -17,7 +17,7 @@
use Twig\Extension\EscaperExtension;
use Twig\Loader\LoaderInterface;

class Twig_Tests_Extension_EscaperTest extends TestCase
class EscaperTest extends TestCase
{
/**
* All character encodings supported by htmlspecialchars().
Expand Down
2 changes: 1 addition & 1 deletion tests/Fixtures/functions/cycle_empty.test
Expand Up @@ -5,4 +5,4 @@
--DATA--
return []
--EXCEPTION--
Twig\Error\RuntimeError: The "cycle" function does not work on empty arrays in "index.twig" at line 2
Twig\Error\RuntimeError: The "cycle" function does not work on empty arrays in "index.twig" at line 2.
2 changes: 1 addition & 1 deletion tests/Fixtures/functions/include/template_instance.test
@@ -1,5 +1,5 @@
--TEST--
"include" function accepts Twig_Template instance
"include" function accepts Twig\Template instance
--TEMPLATE--
{{ include(foo) }} FOO
--TEMPLATE(foo.twig)--
Expand Down
@@ -1,5 +1,5 @@
--TEST--
"extends" tag with a parent as a Twig_TemplateWrapper instance
"extends" tag with a parent as a Twig\TemplateWrapper instance
--TEMPLATE--
{% extends foo %}

Expand Down
2 changes: 1 addition & 1 deletion tests/Fixtures/tags/inheritance/template_instance.test
@@ -1,5 +1,5 @@
--TEST--
"extends" tag accepts Twig_Template instance
"extends" tag accepts Twig\Template instance
--TEMPLATE--
{% extends foo %}

Expand Down
6 changes: 3 additions & 3 deletions tests/Node/Expression/CallTest.php
Expand Up @@ -112,10 +112,10 @@ public function customFunctionWithArbitraryArguments()
public function testResolveArgumentsWithMissingParameterForArbitraryArgumentsOnFunction()
{
$this->expectException(\LogicException::class);
$this->expectExceptionMessageMatches('#^The last parameter of "Twig\\\\Tests\\\\Node\\\\Expression\\\\custom_Twig_Tests_Node_Expression_CallTest_function" for function "foo" must be an array with default value, eg\\. "array \\$arg \\= \\[\\]"\\.$#');
$this->expectExceptionMessageMatches('#^The last parameter of "Twig\\\\Tests\\\\Node\\\\Expression\\\\custom_call_test_function" for function "foo" must be an array with default value, eg\\. "array \\$arg \\= \\[\\]"\\.$#');

$node = new Node_Expression_Call([], ['type' => 'function', 'name' => 'foo', 'is_variadic' => true]);
$node->getArguments('Twig\Tests\Node\Expression\custom_Twig_Tests_Node_Expression_CallTest_function', []);
$node->getArguments('Twig\Tests\Node\Expression\custom_call_test_function', []);
}

public function testResolveArgumentsWithMissingParameterForArbitraryArgumentsOnObject()
Expand Down Expand Up @@ -143,6 +143,6 @@ public function __invoke($required)
}
}

function custom_Twig_Tests_Node_Expression_CallTest_function($required)
function custom_call_test_function($required)
{
}
4 changes: 2 additions & 2 deletions tests/Util/DeprecationCollectorTest.php
Expand Up @@ -28,7 +28,7 @@ public function testCollect()
$twig->addFunction(new TwigFunction('deprec', [$this, 'deprec'], ['deprecated' => '1.1']));

$collector = new DeprecationCollector($twig);
$deprecations = $collector->collect(new Twig_Tests_Util_Iterator());
$deprecations = $collector->collect(new Iterator());

$this->assertEquals(['Twig Function "deprec" is deprecated since version 1.1 in deprec.twig at line 1.'], $deprecations);
}
Expand All @@ -38,7 +38,7 @@ public function deprec()
}
}

class Twig_Tests_Util_Iterator implements \IteratorAggregate
class Iterator implements \IteratorAggregate
{
public function getIterator(): \Traversable
{
Expand Down

0 comments on commit 5fdb1ee

Please sign in to comment.