diff --git a/.gitattributes b/.gitattributes index c07b0dfb5..c9d423680 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8,3 +8,5 @@ /.gitignore export-ignore /.php-cs-fixer.dist.php export-ignore /phpunit.xml.dist export-ignore +/phpstan.neon.dist export-ignore +/phpstan-baseline.neon export-ignore diff --git a/doc/filters/index.rst b/doc/filters/index.rst index 3469febab..cba54078c 100644 --- a/doc/filters/index.rst +++ b/doc/filters/index.rst @@ -29,6 +29,7 @@ Filters html_to_markdown inline_css inky_to_html + invoke join json_encode keys diff --git a/doc/functions/random.rst b/doc/functions/random.rst index aac2986c3..3ebf40cb1 100644 --- a/doc/functions/random.rst +++ b/doc/functions/random.rst @@ -10,6 +10,11 @@ parameter type: * a random integer between the integer parameter (when negative) and 0 (inclusive). * a random integer between the first integer and the second integer parameter (inclusive). +.. caution:: + + The ``random`` function does not produce cryptographically secure random numbers. + Do not use them for purposes that require returned values to be unguessable. + .. code-block:: twig {{ random(['apple', 'orange', 'citrus']) }} {# example output: orange #} diff --git a/src/ExpressionParser/InfixExpressionParserInterface.php b/src/ExpressionParser/InfixExpressionParserInterface.php index 8d0ac674c..4641ef815 100644 --- a/src/ExpressionParser/InfixExpressionParserInterface.php +++ b/src/ExpressionParser/InfixExpressionParserInterface.php @@ -11,12 +11,16 @@ namespace Twig\ExpressionParser; +use Twig\Error\SyntaxError; use Twig\Node\Expression\AbstractExpression; use Twig\Parser; use Twig\Token; interface InfixExpressionParserInterface extends ExpressionParserInterface { + /** + * @throws SyntaxError + */ public function parse(Parser $parser, AbstractExpression $left, Token $token): AbstractExpression; public function getAssociativity(): InfixAssociativity; diff --git a/src/ExpressionParser/PrefixExpressionParserInterface.php b/src/ExpressionParser/PrefixExpressionParserInterface.php index 587997c51..8e39dad37 100644 --- a/src/ExpressionParser/PrefixExpressionParserInterface.php +++ b/src/ExpressionParser/PrefixExpressionParserInterface.php @@ -11,11 +11,15 @@ namespace Twig\ExpressionParser; +use Twig\Error\SyntaxError; use Twig\Node\Expression\AbstractExpression; use Twig\Parser; use Twig\Token; interface PrefixExpressionParserInterface extends ExpressionParserInterface { + /** + * @throws SyntaxError + */ public function parse(Parser $parser, Token $token): AbstractExpression; } diff --git a/src/Extension/CoreExtension.php b/src/Extension/CoreExtension.php index 246a9ced4..4c48c14d3 100644 --- a/src/Extension/CoreExtension.php +++ b/src/Extension/CoreExtension.php @@ -415,9 +415,9 @@ final class CoreExtension extends AbstractExtension if (!is_countable($values)) { throw new RuntimeError('The "cycle" function expects a countable sequence as first argument.'); - } - $values = self::toArray($values, false); + $values = self::toArray($values, false); + } } if (!$count = \count($values)) { @@ -1817,7 +1817,7 @@ final class CoreExtension extends AbstractExtension 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); + 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) { diff --git a/src/Parser.php b/src/Parser.php index 56b72b880..2b31826fb 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -86,6 +86,9 @@ class Parser return $this->env; } + /** + * @throws SyntaxError + */ public function parse(TokenStream $stream, $test = null, bool $dropNeedle = false): ModuleNode { $vars = get_object_vars($this); @@ -170,6 +173,8 @@ class Parser /** * @phpstan-impure + * + * @throws SyntaxError */ public function subparse($test, bool $dropNeedle = false): Node { diff --git a/tests/Extension/CoreTest.php b/tests/Extension/CoreTest.php index 34058ee6a..73e31b1f8 100644 --- a/tests/Extension/CoreTest.php +++ b/tests/Extension/CoreTest.php @@ -22,6 +22,7 @@ namespace Twig\Tests\Extension; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Twig\Environment; use Twig\Error\RuntimeError; use Twig\Extension\CoreExtension; diff --git a/tests/TemplateTest.php b/tests/TemplateTest.php index ad59b0237..b777234c9 100644 --- a/tests/TemplateTest.php +++ b/tests/TemplateTest.php @@ -85,8 +85,8 @@ class TemplateTest extends TestCase ['{{ empty_array.a }}', 'Key "a" does not exist as the sequence/mapping is empty in "%s" at line 1.'], ['{{ array.a }}', 'Key "a" for sequence/mapping with keys "foo" does not exist in "%s" at line 1.'], ['{{ array.(-10) }}', 'Key "-10" for sequence/mapping with keys "foo" does not exist in "%s" at line 1.'], - ['{{ array_access.a }}', 'Neither the property "a" nor one of the methods "a()", "geta()"/"isa()"/"hasa()" or "__call()" exist and have public access in class "Twig\Tests\TemplateArrayAccessObject" in "%s" at line 1.'], - ['{% from _self import foo %}{% macro foo(obj) %}{{ obj.missing_method() }}{% endmacro %}{{ foo(array_access) }}', 'Neither the property "missing_method" nor one of the methods "missing_method()", "getmissing_method()"/"ismissing_method()"/"hasmissing_method()" or "__call()" exist and have public access in class "Twig\Tests\TemplateArrayAccessObject" in "%s" at line 1.'], + ['{{ array_access.a }}', 'Neither the property "a" nor one of the methods "a()", "geta()", "isa()", "hasa()" or "__call()" exist and have public access in class "Twig\Tests\TemplateArrayAccessObject" in "%s" at line 1.'], + ['{% from _self import foo %}{% macro foo(obj) %}{{ obj.missing_method() }}{% endmacro %}{{ foo(array_access) }}', 'Neither the property "missing_method" nor one of the methods "missing_method()", "getmissing_method()", "ismissing_method()", "hasmissing_method()" or "__call()" exist and have public access in class "Twig\Tests\TemplateArrayAccessObject" in "%s" at line 1.'], ['{{ magic_exception.test }}', 'An exception has been thrown during the rendering of a template ("Hey! Don\'t try to isset me!") in "%s" at line 1.'], ['{{ object["a"] }}', 'Impossible to access a key "a" on an object of class "stdClass" that does not implement ArrayAccess interface in "%s" at line 1.'], ];