Skip to content

Commit

Permalink
added the apply tag
Browse files Browse the repository at this point in the history
  • Loading branch information
fabpot committed Apr 24, 2019
1 parent f497913 commit 619bc12
Show file tree
Hide file tree
Showing 17 changed files with 231 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
@@ -1,5 +1,6 @@
* 1.40.0 (2019-XX-XX)

* added the "apply" tag as a replacement for the "filter" tag
* allowed Twig\Loader\FilesystemLoader::findTemplate() to return "null" instead of "false" (same meaning)
* added support for "Twig\Markup" instances in the "in" test
* fixed Lexer when using custom options containing the # char
Expand Down
11 changes: 8 additions & 3 deletions doc/advanced.rst
Expand Up @@ -400,16 +400,21 @@ Most of the time though, a tag is not needed:
{{ '**markdown** text'|markdown }}
If you want use this filter on large amounts of text, wrap it with the
:doc:`filter <tags/filter>` tag:
:doc:`apply <tags/apply>` tag:

.. code-block:: jinja
{% filter markdown %}
{% apply markdown %}
Title
=====
Much better than creating a tag as you can **compose** filters.
{% endfilter %}
{% endapply %}
.. note::

The ``apply`` tag was introduced in Twig 1.40; use the ``filter`` tag with
previous versions.

* If your tag does not output anything, but only exists because of a side
effect, create a **function** that returns nothing and call it via the
Expand Down
13 changes: 9 additions & 4 deletions doc/filters/spaceless.rst
Expand Up @@ -18,19 +18,24 @@ whitespace within HTML tags or whitespace in plain text:
{# output will be <div><strong>foo</strong></div> #}
You can combine ``spaceless`` with the ``filter`` tag to apply the
transformation on large amounts of HTML:
You can combine ``spaceless`` with the ``apply`` tag to apply the transformation
on large amounts of HTML:

.. code-block:: jinja
{% filter spaceless %}
{% apply spaceless %}
<div>
<strong>foo</strong>
</div>
{% endfilter %}
{% endapply %}
{# output will be <div><strong>foo</strong></div> #}
.. note::

The ``apply`` tag was introduced in Twig 1.40; use the ``filter`` tag with
previous versions.

This tag is not meant to "optimize" the size of the generated HTML content but
merely to avoid extra whitespace between HTML tags to avoid browser rendering
quirks under some circumstances.
Expand Down
20 changes: 20 additions & 0 deletions doc/tags/apply.rst
@@ -0,0 +1,20 @@
``apply``
=========

The ``apply`` tag allows you to apply Twig filters on a block of template data:

.. code-block:: jinja
{% apply upper %}
This text becomes uppercase
{% endapply %}
You can also chain filters and pass arguments to them:

.. code-block:: jinja
{% apply lower|escape('html') %}
<strong>SOME TEXT</strong>
{% endapply %}
{# outputs "&lt;strong&gt;some text&lt;/strong&gt;" #}
5 changes: 5 additions & 0 deletions doc/tags/filter.rst
@@ -1,6 +1,11 @@
``filter``
==========

.. note::

As of Twig 1.40, you should use the ``apply`` tag instead which does the
same thing except that the wrapped template data is not scoped.

Filter sections allow you to apply regular Twig filters on a block of template
data. Just wrap the code in the special ``filter`` section:

Expand Down
20 changes: 15 additions & 5 deletions doc/templates.rst
Expand Up @@ -167,17 +167,22 @@ example will join a list by commas:
{{ list|join(', ') }}
To apply a filter on a section of code, wrap it in the
:doc:`filter<tags/filter>` tag:
:doc:`apply<tags/apply>` tag:

.. code-block:: jinja
{% filter upper %}
{% apply upper %}
This text becomes uppercase
{% endfilter %}
{% endapply %}
Go to the :doc:`filters<filters/index>` page to learn more about built-in
filters.

.. note::

The ``apply`` tag was introduced in Twig 1.40; use the ``filter`` tag with
previous versions.

Functions
---------

Expand Down Expand Up @@ -895,14 +900,19 @@ the modifiers on one side of a tag or on both sides:

.. code-block:: jinja
{% filter spaceless %}
{% apply spaceless %}
<div>
<strong>foo bar</strong>
</div>
{% endfilter %}
{% endapply %}
{# output will be <div><strong>foo bar</strong></div> #}
.. note::

The ``apply`` tag was introduced in Twig 1.40; use the ``filter`` tag with
previous versions.

Extensions
----------

Expand Down
2 changes: 2 additions & 0 deletions src/Extension/CoreExtension.php
Expand Up @@ -11,6 +11,7 @@

namespace Twig\Extension {
use Twig\ExpressionParser;
use Twig\TokenParser\ApplyTokenParser;
use Twig\TokenParser\BlockTokenParser;
use Twig\TokenParser\DeprecatedTokenParser;
use Twig\TokenParser\DoTokenParser;
Expand Down Expand Up @@ -139,6 +140,7 @@ public function getNumberFormat()
public function getTokenParsers()
{
return [
new ApplyTokenParser(),
new ForTokenParser(),
new IfTokenParser(),
new ExtendsTokenParser(),
Expand Down
1 change: 1 addition & 0 deletions src/NodeVisitor/OptimizerNodeVisitor.php
Expand Up @@ -111,6 +111,7 @@ protected function doLeaveNode(Node $node, Environment $env)
if (!$expression && 'Twig_Node' !== \get_class($node) && $prependedNodes = array_shift($this->prependedNodes)) {
$nodes = [];
foreach (array_unique($prependedNodes) as $name) {
die();
$nodes[] = new SetTempNode($name, $node->getTemplateLine());
}

Expand Down
58 changes: 58 additions & 0 deletions src/TokenParser/ApplyTokenParser.php
@@ -0,0 +1,58 @@
<?php

/*
* This file is part of Twig.
*
* (c) Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Twig\TokenParser;

use Twig\Node\Expression\TempNameExpression;
use Twig\Node\Node;
use Twig\Node\PrintNode;
use Twig\Node\SetNode;
use Twig\Token;

/**
* Applies filters on a section of a template.
*
* {% apply upper %}
* This text becomes uppercase
* {% endapplys %}
*/
final class ApplyTokenParser extends AbstractTokenParser
{
public function parse(Token $token)
{
$lineno = $token->getLine();
$name = $this->parser->getVarName();

$ref = new TempNameExpression($name, $lineno);
$ref->setAttribute('always_defined', true);

$filter = $this->parser->getExpressionParser()->parseFilterExpressionRaw($ref, $this->getTag());

$this->parser->getStream()->expect(Token::BLOCK_END_TYPE);
$body = $this->parser->subparse([$this, 'decideApplyEnd'], true);
$this->parser->getStream()->expect(Token::BLOCK_END_TYPE);

return new Node([
new SetNode(true, $ref, $body, $lineno, $this->getTag()),
new PrintNode($filter, $lineno, $this->getTag()),
]);
}

public function decideApplyEnd(Token $token)
{
return $token->test('endapply');
}

public function getTag()
{
return 'apply';
}
}
10 changes: 10 additions & 0 deletions test/Twig/Tests/Fixtures/tags/apply/basic.test
@@ -0,0 +1,10 @@
--TEST--
"apply" tag applies a filter on its children
--TEMPLATE--
{% apply upper %}
Some text with a {{ var }}
{% endapply %}
--DATA--
return ['var' => 'var']
--EXPECT--
SOME TEXT WITH A VAR
8 changes: 8 additions & 0 deletions test/Twig/Tests/Fixtures/tags/apply/json_encode.test
@@ -0,0 +1,8 @@
--TEST--
"apply" tag applies a filter on its children
--TEMPLATE--
{% apply json_encode|raw %}test{% endapply %}
--DATA--
return []
--EXPECT--
"test"
10 changes: 10 additions & 0 deletions test/Twig/Tests/Fixtures/tags/apply/multiple.test
@@ -0,0 +1,10 @@
--TEST--
"apply" tags accept multiple chained filters
--TEMPLATE--
{% apply lower|title %}
{{ var }}
{% endapply %}
--DATA--
return ['var' => 'VAR']
--EXPECT--
Var
16 changes: 16 additions & 0 deletions test/Twig/Tests/Fixtures/tags/apply/nested.test
@@ -0,0 +1,16 @@
--TEST--
"apply" tags can be nested at will
--TEMPLATE--
{% apply lower|title %}
{{ var }}
{% apply upper %}
{{ var }}
{% endapply %}
{{ var }}
{% endapply %}
--DATA--
return ['var' => 'var']
--EXPECT--
Var
Var
Var
15 changes: 15 additions & 0 deletions test/Twig/Tests/Fixtures/tags/apply/scope.test
@@ -0,0 +1,15 @@
--TEST--
"apply" tag does not create a new scope
--TEMPLATE--
{% set foo = 'baz' %}
{% apply spaceless %}
{% set foo = 'foo' %}
{% set bar = 'bar' %}
{% endapply %}
{{ 'foo' == foo ? 'OK ' ~ foo : 'KO' }}
{{ 'bar' == bar ? 'OK ' ~ bar : 'KO' }}
--DATA--
return []
--EXPECT--
OK foo
OK bar
13 changes: 13 additions & 0 deletions test/Twig/Tests/Fixtures/tags/apply/with_for_tag.test
@@ -0,0 +1,13 @@
--TEST--
"apply" tag applies the filter on "for" tags
--TEMPLATE--
{% apply upper %}
{% for item in items %}
{{ item }}
{% endfor %}
{% endapply %}
--DATA--
return ['items' => ['a', 'b']]
--EXPECT--
A
B
29 changes: 29 additions & 0 deletions test/Twig/Tests/Fixtures/tags/apply/with_if_tag.test
@@ -0,0 +1,29 @@
--TEST--
"apply" tag applies the filter on "if" tags
--TEMPLATE--
{% apply upper %}
{% if items %}
{{ items|join(', ') }}
{% endif %}

{% if items.3 is defined %}
FOO
{% else %}
{{ items.1 }}
{% endif %}

{% if items.3 is defined %}
FOO
{% elseif items.1 %}
{{ items.0 }}
{% endif %}

{% endapply %}
--DATA--
return ['items' => ['a', 'b']]
--EXPECT--
A, B

B

A
11 changes: 11 additions & 0 deletions test/Twig/Tests/Fixtures/tags/filter/scope.test
@@ -0,0 +1,11 @@
--TEST--
"scope" tag creates a new scope
--TEMPLATE--
{% filter spaceless %}
{% set item = 'foo' %}
{% endfilter %}
{{ item }}
--DATA--
return []
--EXCEPTION--
Twig\Error\RuntimeError: Variable "item" does not exist in "index.twig" at line 5.

0 comments on commit 619bc12

Please sign in to comment.