diff --git a/CHANGELOG.md b/CHANGELOG.md index c8b71e2..c8505a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Twig Perversion Changelog +## 5.0.1 - 2024.11.28 + +### + +- Merged in Brandon's fix for twig 3.12 breaking `{% return %}` + ## 5.0.0 - 2024.04.29 ### Changed diff --git a/README.md b/README.md index 780cccd..05a0c94 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,5 @@ # Twig Perversion plugin for Craft CMS -## Obituary - -Twig Perversions tags no longer work as of twig 3.12 (Craft version 5.4). Twig used to compile its macros into fairly straightforward functions, which Twig Perversion was able to hack. They are now using generator functions, and the old hacks no longer work. - -## Intro - Making twig do things it really shouldn't. Twig is not intended to be a general purpose programming language, and there are some things that really don't belong in the language. This plugin adds a few of those things anyway. - `{% while %}`, `{% break %}`, `{% continue %}`, and `{% return %}` tags @@ -15,7 +9,7 @@ Making twig do things it really shouldn't. Twig is not intended to be a gene ## Requirements -This plugin requires Craft CMS 3.1.29 or later. +This plugin requires Craft CMS 4.12 or later. ## Installation diff --git a/composer.json b/composer.json index 4d850ea..347fbd6 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "marionnewlevant/twig-perversion", "description": "Making twig do things it really shouldn't", - "version": "5.0.0", + "version": "5.0.1", "type": "craft-plugin", "keywords": [ "craft", @@ -23,7 +23,7 @@ }, "require": { "php": ">=8.2", - "craftcms/cms": "5.0 - 5.3" + "craftcms/cms": "^5.0" }, "autoload": { "psr-4": { diff --git a/src/Plugin.php b/src/Plugin.php index 8f18bd3..f4d2736 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -1,8 +1,41 @@ raw("\n") + ->indent() + ->write(sprintf("%s::processMacroResponse(\n", Plugin::class)) + ->indent() + ->write('') + ->subcompile($this->getNode('methodCallExpression')) + ->raw("\n") + ->outdent() + ->write(")\n") + ->outdent() + ->write(''); + } +} diff --git a/src/twigextensions/Return_Node.php b/src/twigextensions/Return_Node.php index 5475848..c6bea74 100644 --- a/src/twigextensions/Return_Node.php +++ b/src/twigextensions/Return_Node.php @@ -1,6 +1,8 @@ addDebugInfo($this) - ->write('return '); + ->addDebugInfo($this) + ->write("if (!isset(\$returnedMarker)) {\n") + ->indent() + ->write("\$returnedMarker = true;\n") + ->write("\$marker = sprintf('[RETURN_MARKER:%s]', mt_rand());\n") + ->write(sprintf("%s::\$returnValues[\$marker] = ", Plugin::class)); + // check for an expression to return. if ($this->hasNode('expr')) { $compiler->subcompile($this->getNode('expr')); @@ -31,6 +38,10 @@ public function compile(\Twig\Compiler $compiler) $compiler->raw('""'); } - $compiler->raw(";\n"); + $compiler + ->raw(";\n") + ->write("yield \$marker;\n") + ->outdent() + ->write("}\n"); } } diff --git a/src/twigextensions/Return_NodeVisitor.php b/src/twigextensions/Return_NodeVisitor.php new file mode 100644 index 0000000..2a35c43 --- /dev/null +++ b/src/twigextensions/Return_NodeVisitor.php @@ -0,0 +1,42 @@ +getAttribute('is_defined_test')) { + return new MacroProcessor_Node([ + 'methodCallExpression' => $node, + ], [ + 'is_generator' => $node->hasAttribute('is_generator') ? $node->getAttribute('is_generator') : false, + ]); + } + return $node; + } + + public function getPriority() + { + return 0; + } +} diff --git a/src/twigextensions/TwigPerversionTwigExtension.php b/src/twigextensions/TwigPerversionTwigExtension.php index d073cb2..b70325b 100644 --- a/src/twigextensions/TwigPerversionTwigExtension.php +++ b/src/twigextensions/TwigPerversionTwigExtension.php @@ -97,7 +97,14 @@ public function getOperators() ]; } - /** + public function getNodeVisitors() + { + return [ + new Return_NodeVisitor(), + ]; + } + + /** * Cast value as a string. * * @param mixed $subject Value to be cast. diff --git a/twig-perversion-tests.twig b/twig-perversion-tests.twig new file mode 100644 index 0000000..a9db181 --- /dev/null +++ b/twig-perversion-tests.twig @@ -0,0 +1,137 @@ +

Twig-perversion tests

+

twig code for testing twig-perversion

+ +

return

+{% macro testMacro() %} + {% return "test" %} + ignore me... +{% endmacro %} + +{% set x = _self.testMacro() %} + +x = {{x}} +{# x = test #} +
+{% macro t2(v) %} + {% if v < 0 %} + {% return "negative" %} + ignore me + {% else %} + {% return "positive" %} + {% endif %} + ignore me +{% endmacro %} + +negative: {{ _self.t2(-4)}} +positive: {{ _self.t2(4)}} + +
+{% macro double(n) %} + {% return n * 2 %} + ignore me +{% endmacro %} +double(4): {{_self.double(4)}} + +{% macro triple(n) %} + {% return n + _self.double(n) %} + ignore me +{% endmacro %} +triple(4): {{_self.triple(4)}} +
+{% macro recur(n) %} + {{n}} + {% if n > 1 %} + {{ _self.recur(n-1)}} + {% endif %} +{% endmacro %} + +{{ _self.recur(5)}} +
+{% macro fact(n) %} + {% if n == 1 %} + {% return 1 %} + {% endif %} + {% return n * _self.fact(n-1) %} +{% endmacro %} + +1!: {{ _self.fact(1)}} +{% set fact5 = _self.fact(5) %} +5! = {{fact5}} + +

while loops

+while loop indexes
+{% set x = 0 %} +{% while x < 5 %} + {{x}} loop: {{loop.index}} {{loop.index0}} {{loop.first ? 'first' : 'not first'}}
+ {% set x = x + 1 %} +{% endwhile %} +
+while with loop and loop.parent indexes
+{% set x = 0 %} +{% while x < 5 %} + {% set y = 0 %} + {% while y < 3 %} + loop: {{loop.index}} {{loop.index0}} {{loop.first ? 'first' : 'not first'}} + parent: {{loop.parent.loop.index}} {{loop.parent.loop.index0}} {{loop.parent.loop.first ? 'first': 'not'}}
+ {% set y = y + 1 %} + {% endwhile %} + {% set x = x + 1 %} +{% endwhile %} +
+while w/ continue on even: +{% set x = 0 %} +{% while x < 5 %} + {% set x = x + 1 %} + {{x}} + {% if x % 2 == 0 %} {% continue %} {% endif %} + odd +{% endwhile %} +
+while w/ break on 3: +{% set x = 0 %} +{% while x < 5 %} + {{x}} + {% set x = x + 1 %} + {% if x == 3 %} {% break %} {% endif %} + .. +{% endwhile %} +
+ +

numeric

+12 ? {{ 12 is numeric ? 'Yes' : 'No' }} +{# Yes #} + +-1.3 ? {{ '-1.3' is numeric ? 'Yes' : 'No' }} +{# Yes #} + +0x539 ? {{ '0x539' is numeric ? 'Yes' : 'No'}} +{# No #} + +

return types

+{% macro foo() %} + {# ... calculate someValue ... #} + {% set someValue = 'our return value' %} + {% return someValue %} +{% endmacro %} + +{% macro bar() %} + {% set x = 0 %} + {% while x < 5 %} + x = {{x}} + {% set x = x + 1 %} + {% endwhile %} + {% return %} +{% endmacro %} + +{% macro baz() %} + {% return 23 %} +{% endmacro %} + +{{ _self.foo() }} +{{ _self.foo() is string ? 'foo is string' : 'foo is not string' }}
+ +{{ _self.bar() }} +{{ _self.bar() is string ? 'bar is string' : 'bar is not string' }}
+ +{{ _self.baz() }} +{{ _self.baz() is string ? 'baz is string' : 'baz is not string' }}
\ No newline at end of file