From 4f013e09ea84cea7a2722a635621f5c72590c391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Haso=C5=88?= Date: Tue, 15 Sep 2015 23:45:28 +0200 Subject: [PATCH 1/2] Add test to check if a block exists --- doc/tests/block_name.rst | 13 +++++++ doc/tests/index.rst | 1 + lib/Twig/Extension/Core.php | 1 + lib/Twig/Node/Expression/Test/BlockName.php | 33 ++++++++++++++++ lib/Twig/Template.php | 35 +++++++++++++++++ .../Twig/Tests/Fixtures/tests/block_name.test | 38 +++++++++++++++++++ 6 files changed, 121 insertions(+) create mode 100644 doc/tests/block_name.rst create mode 100644 lib/Twig/Node/Expression/Test/BlockName.php create mode 100644 test/Twig/Tests/Fixtures/tests/block_name.test diff --git a/doc/tests/block_name.rst b/doc/tests/block_name.rst new file mode 100644 index 000000000..8cba0cee7 --- /dev/null +++ b/doc/tests/block_name.rst @@ -0,0 +1,13 @@ +``block name`` +============== + +.. versionadded:: 1.28 + The ``block name`` test was added in Twig 1.28. + +``block name`` checks if a block is defined in the current context of the template. + +.. code-block:: jinja + + {% if 'title' is block name %} + {{ block('title') }}<title> + {% endif %} diff --git a/doc/tests/index.rst b/doc/tests/index.rst index c63208ee7..42655fda9 100644 --- a/doc/tests/index.rst +++ b/doc/tests/index.rst @@ -4,6 +4,7 @@ Tests .. toctree:: :maxdepth: 1 + block_name constant defined divisibleby diff --git a/lib/Twig/Extension/Core.php b/lib/Twig/Extension/Core.php index 901a3058f..ebaf8bfc8 100644 --- a/lib/Twig/Extension/Core.php +++ b/lib/Twig/Extension/Core.php @@ -222,6 +222,7 @@ class Twig_Extension_Core extends Twig_Extension new Twig_SimpleTest('constant', null, array('node_class' => 'Twig_Node_Expression_Test_Constant')), new Twig_SimpleTest('empty', 'twig_test_empty'), new Twig_SimpleTest('iterable', 'twig_test_iterable'), + new Twig_SimpleTest('block name', null, array('node_class' => 'Twig_Node_Expression_Test_BlockName')), ); } diff --git a/lib/Twig/Node/Expression/Test/BlockName.php b/lib/Twig/Node/Expression/Test/BlockName.php new file mode 100644 index 000000000..8261d0ddf --- /dev/null +++ b/lib/Twig/Node/Expression/Test/BlockName.php @@ -0,0 +1,33 @@ +<?php + +/* + * This file is part of Twig. + * + * (c) 2016 Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Checks whether a block is defined. + * + * <pre> + * {% if 'title' is block name %} + * <title>{{ block('title') }} + * {% endif %} + * + * + * @author Martin Hasoň + */ +class Twig_Node_Expression_Test_BlockName extends Twig_Node_Expression_Test +{ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->raw('$this->blockExists(') + ->subcompile($this->getNode('node')) + ->raw(', $context, $blocks)') + ; + } +} diff --git a/lib/Twig/Template.php b/lib/Twig/Template.php index b1865b2ee..01e80716f 100644 --- a/lib/Twig/Template.php +++ b/lib/Twig/Template.php @@ -286,6 +286,8 @@ abstract class Twig_Template implements Twig_TemplateInterface * * @return bool true if the block exists, false otherwise * + * @see blockExists + * * @internal */ public function hasBlock($name) @@ -653,4 +655,37 @@ abstract class Twig_Template implements Twig_TemplateInterface return $ret; } + + /** + * Returns whether a block exists or not in the current context of the template. + * + * This method checks blocks defined in the current template + * or defined in "used" traits or defined in parent templates. + * + * @param string $name The block name + * @param array $context The context + * @param array $blocks The current set of blocks + * + * @return bool true if the block exists, false otherwise + * + * @see hasBlock + * + * @internal + */ + protected function blockExists($name, array $context, array $blocks = array()) + { + if (isset($blocks[$name])) { + return $blocks[$name][0] instanceof self; + } + + if (isset($this->blocks[$name])) { + return true; + } + + if (false !== $parent = $this->getParent($context)) { + return $parent->blockExists($name, $context); + } + + return false; + } } diff --git a/test/Twig/Tests/Fixtures/tests/block_name.test b/test/Twig/Tests/Fixtures/tests/block_name.test new file mode 100644 index 000000000..690f3fa69 --- /dev/null +++ b/test/Twig/Tests/Fixtures/tests/block_name.test @@ -0,0 +1,38 @@ +--TEST-- +"block name" test +--TEMPLATE-- +{% extends 'parent' %} +{% block icon %}icon{% endblock %} +{% block body %} + {{ parent() }} + {{ 'foo' is block name ? 'ok' : 'ko' }} + {{ 'footer' is block name ? 'ok' : 'ko' }} + {{ 'icon' is block name ? 'ok' : 'ko' }} + {{ 'block1' is block name ? 'ok' : 'ko' }} + {%- embed 'embed' %} + {% block content %}content{% endblock %} + {% endembed %} +{% endblock %} +{% use 'blocks' %} +--TEMPLATE(parent)-- +{% block body %} + {{ 'icon' is block name ? 'ok' : 'ko' -}} +{% endblock %} +{% block footer %}{% endblock %} +--TEMPLATE(embed)-- +{{ 'icon' is block name ? 'ok' : 'ko' }} +{{ 'content' is block name ? 'ok' : 'ko' }} +{{ 'block1' is block name ? 'ok' : 'ko' }} +--TEMPLATE(blocks)-- +{% block block1 %}{%endblock %} +--DATA-- +return array() +--EXPECT-- +ok + ko + ok + ok + ok +ko +ok +ko From 3ce06af6b4ca335f889da3f9fff8f3a372005759 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 11 Nov 2016 09:04:38 -0800 Subject: [PATCH 2/2] added 'is defined' support for block() --- CHANGELOG | 1 + doc/functions/block.rst | 9 +++++ doc/tests/block_name.rst | 13 -------- doc/tests/index.rst | 1 - lib/Twig/Extension/Core.php | 1 - lib/Twig/Node/Expression/BlockReference.php | 28 ++++++++++------ lib/Twig/Node/Expression/Test/BlockName.php | 33 ------------------- lib/Twig/Node/Expression/Test/Defined.php | 3 +- ...lock_name.test => defined_for_blocks.test} | 18 +++++----- 9 files changed, 39 insertions(+), 68 deletions(-) delete mode 100644 doc/tests/block_name.rst delete mode 100644 lib/Twig/Node/Expression/Test/BlockName.php rename test/Twig/Tests/Fixtures/tests/{block_name.test => defined_for_blocks.test} (53%) diff --git a/CHANGELOG b/CHANGELOG index 6920bd1fb..1a0807c9f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ * 1.28.0 (2016-XX-XX) + * added "is defined" support for block() * optimized the way attributes are fetched * 1.27.0 (2016-10-25) diff --git a/doc/functions/block.rst b/doc/functions/block.rst index fd571efbc..2f3de2456 100644 --- a/doc/functions/block.rst +++ b/doc/functions/block.rst @@ -12,4 +12,13 @@ times, use the ``block`` function: {% block body %}{% endblock %} +Use the ``defined`` test to check if a block exists in the context of the +current template: + +.. code-block:: jinja + + {% if block("footer") is defined %} + ... + {% endif %} + .. seealso:: :doc:`extends<../tags/extends>`, :doc:`parent<../functions/parent>` diff --git a/doc/tests/block_name.rst b/doc/tests/block_name.rst deleted file mode 100644 index 8cba0cee7..000000000 --- a/doc/tests/block_name.rst +++ /dev/null @@ -1,13 +0,0 @@ -``block name`` -============== - -.. versionadded:: 1.28 - The ``block name`` test was added in Twig 1.28. - -``block name`` checks if a block is defined in the current context of the template. - -.. code-block:: jinja - - {% if 'title' is block name %} - {{ block('title') }}<title> - {% endif %} diff --git a/doc/tests/index.rst b/doc/tests/index.rst index 42655fda9..c63208ee7 100644 --- a/doc/tests/index.rst +++ b/doc/tests/index.rst @@ -4,7 +4,6 @@ Tests .. toctree:: :maxdepth: 1 - block_name constant defined divisibleby diff --git a/lib/Twig/Extension/Core.php b/lib/Twig/Extension/Core.php index ebaf8bfc8..901a3058f 100644 --- a/lib/Twig/Extension/Core.php +++ b/lib/Twig/Extension/Core.php @@ -222,7 +222,6 @@ class Twig_Extension_Core extends Twig_Extension new Twig_SimpleTest('constant', null, array('node_class' => 'Twig_Node_Expression_Test_Constant')), new Twig_SimpleTest('empty', 'twig_test_empty'), new Twig_SimpleTest('iterable', 'twig_test_iterable'), - new Twig_SimpleTest('block name', null, array('node_class' => 'Twig_Node_Expression_Test_BlockName')), ); } diff --git a/lib/Twig/Node/Expression/BlockReference.php b/lib/Twig/Node/Expression/BlockReference.php index 3fe7d0580..d22dd57cc 100644 --- a/lib/Twig/Node/Expression/BlockReference.php +++ b/lib/Twig/Node/Expression/BlockReference.php @@ -26,24 +26,32 @@ class Twig_Node_Expression_BlockReference extends Twig_Node_Expression $tag = func_num_args() > 3 ? func_get_arg(3) : null; } - parent::__construct(array('name' => $name), array('output' => false), $lineno, $tag); + parent::__construct(array('name' => $name), array('is_defined_test' => false, 'output' => false), $lineno, $tag); } public function compile(Twig_Compiler $compiler) { - if ($this->getAttribute('output')) { + if ($this->getAttribute('is_defined_test')) { $compiler - ->addDebugInfo($this) - ->write('$this->displayBlock(') - ->subcompile($this->getNode('name')) - ->raw(", \$context, \$blocks);\n") - ; - } else { - $compiler - ->raw('$this->renderBlock(') + ->raw('$this->blockExists(') ->subcompile($this->getNode('name')) ->raw(', $context, $blocks)') ; + } else { + if ($this->getAttribute('output')) { + $compiler + ->addDebugInfo($this) + ->write('$this->displayBlock(') + ->subcompile($this->getNode('name')) + ->raw(", \$context, \$blocks);\n") + ; + } else { + $compiler + ->raw('$this->renderBlock(') + ->subcompile($this->getNode('name')) + ->raw(', $context, $blocks)') + ; + } } } } diff --git a/lib/Twig/Node/Expression/Test/BlockName.php b/lib/Twig/Node/Expression/Test/BlockName.php deleted file mode 100644 index 8261d0ddf..000000000 --- a/lib/Twig/Node/Expression/Test/BlockName.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php - -/* - * This file is part of Twig. - * - * (c) 2016 Fabien Potencier - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -/** - * Checks whether a block is defined. - * - * <pre> - * {% if 'title' is block name %} - * <title>{{ block('title') }} - * {% endif %} - * - * - * @author Martin Hasoň - */ -class Twig_Node_Expression_Test_BlockName extends Twig_Node_Expression_Test -{ - public function compile(Twig_Compiler $compiler) - { - $compiler - ->raw('$this->blockExists(') - ->subcompile($this->getNode('node')) - ->raw(', $context, $blocks)') - ; - } -} diff --git a/lib/Twig/Node/Expression/Test/Defined.php b/lib/Twig/Node/Expression/Test/Defined.php index c57fe09eb..a8fd74db9 100644 --- a/lib/Twig/Node/Expression/Test/Defined.php +++ b/lib/Twig/Node/Expression/Test/Defined.php @@ -29,8 +29,9 @@ class Twig_Node_Expression_Test_Defined extends Twig_Node_Expression_Test $node->setAttribute('is_defined_test', true); } elseif ($node instanceof Twig_Node_Expression_GetAttr) { $node->setAttribute('is_defined_test', true); - $this->changeIgnoreStrictCheck($node); + } elseif ($node instanceof Twig_Node_Expression_BlockReference) { + $node->setAttribute('is_defined_test', true); } elseif ($node instanceof Twig_Node_Expression_Constant || $node instanceof Twig_Node_Expression_Array) { $node = new Twig_Node_Expression_Constant(true, $node->getTemplateLine()); } else { diff --git a/test/Twig/Tests/Fixtures/tests/block_name.test b/test/Twig/Tests/Fixtures/tests/defined_for_blocks.test similarity index 53% rename from test/Twig/Tests/Fixtures/tests/block_name.test rename to test/Twig/Tests/Fixtures/tests/defined_for_blocks.test index 690f3fa69..64d7d0431 100644 --- a/test/Twig/Tests/Fixtures/tests/block_name.test +++ b/test/Twig/Tests/Fixtures/tests/defined_for_blocks.test @@ -1,14 +1,14 @@ --TEST-- -"block name" test +"defined" support for blocks --TEMPLATE-- {% extends 'parent' %} {% block icon %}icon{% endblock %} {% block body %} {{ parent() }} - {{ 'foo' is block name ? 'ok' : 'ko' }} - {{ 'footer' is block name ? 'ok' : 'ko' }} - {{ 'icon' is block name ? 'ok' : 'ko' }} - {{ 'block1' is block name ? 'ok' : 'ko' }} + {{ block('foo') is defined ? 'ok' : 'ko' }} + {{ block('footer') is defined ? 'ok' : 'ko' }} + {{ block('icon') is defined ? 'ok' : 'ko' }} + {{ block('block1') is defined ? 'ok' : 'ko' }} {%- embed 'embed' %} {% block content %}content{% endblock %} {% endembed %} @@ -16,13 +16,13 @@ {% use 'blocks' %} --TEMPLATE(parent)-- {% block body %} - {{ 'icon' is block name ? 'ok' : 'ko' -}} + {{ block('icon') is defined ? 'ok' : 'ko' -}} {% endblock %} {% block footer %}{% endblock %} --TEMPLATE(embed)-- -{{ 'icon' is block name ? 'ok' : 'ko' }} -{{ 'content' is block name ? 'ok' : 'ko' }} -{{ 'block1' is block name ? 'ok' : 'ko' }} +{{ block('icon') is defined ? 'ok' : 'ko' }} +{{ block('content') is defined ? 'ok' : 'ko' }} +{{ block('block1') is defined ? 'ok' : 'ko' }} --TEMPLATE(blocks)-- {% block block1 %}{%endblock %} --DATA--