rybakit / twig-deferred-extension Goto Github PK
View Code? Open in Web Editor NEWAn extension for Twig that allows to defer block rendering.
License: MIT License
An extension for Twig that allows to defer block rendering.
License: MIT License
If a template contains a deferred block named foo
and another block named foo_deferred
, the compiled template will contain 2 methods block_foo_deferred
(corresponding to different cases).
The method names for block_*_deferred
should probably be changed to deferred_block_*
to avoid conflicts with the block_*
pattern of block methods.
Redefining a deferred block that is not the first deferred block of the parent template removes other blocks from rendering.
Here is a test to reproduce the issue :
--TEST--
Failed parents override
--TEMPLATE--
{% extends "level1.twig" %}
{% block overrided deferred %}{{ parent() }}:level2({{data|join(', ')}}){% endblock %}
{% do data.append('lazy2') %}
--TEMPLATE(level1.twig)--
{% block bar '[bar]' %}
{% block foo deferred %}[foo]{% endblock %}
{% block baz '[baz]' %}
{% block overrided deferred %}[overrided]{% endblock %}
{% block zoo '[zoo]' %}
{% do data.append('lazy1') %}
--DATA--
return []
--EXPECT--
[bar][foo][baz][overrided]:level2(lazy2, lazy1)[zoo]
This test currently produces this output : [baz][foo][zoo]
This test deliberately adds intermediate blocks to clearly see the abnormal behavior.
Here is a minimal version to reproduce the issue :
--TEST--
Failed parents override simple
--TEMPLATE--
{% extends "level1.twig" %}
{% block overrided deferred %}{{ parent() }}:level2{% endblock %}
--TEMPLATE(level1.twig)--
{% block foo deferred %}[foo]{% endblock %}
{% block overrided deferred %}[overrided]{% endblock %}
--DATA--
return []
--EXPECT--
[foo][overrided]:level2
This test currently produces : [foo]
In both cases, if we swap the foo and overrided blocks in level1.twig, then the expected rendering is correct.
--TEST--
Passed parents override
--TEMPLATE--
{% extends "level1.twig" %}
{% block overrided deferred %}{{ parent() }}:level2({{data|join(', ')}}){% endblock %}
{% do data.append('lazy2') %}
--TEMPLATE(level1.twig)--
{% block bar '[bar]' %}
{% block overrided deferred %}[overrided]{% endblock %}
{% block baz '[baz]' %}
{% block foo deferred %}[foo]{% endblock %}
{% block zoo '[zoo]' %}
{% do data.append('lazy1') %}
--DATA--
return []
--EXPECT--
[bar][overrided]:level2(lazy2, lazy1)[baz][foo][zoo]
--TEST--
Passed parents override simple
--TEMPLATE--
{% extends "level1.twig" %}
{% block overrided deferred %}{{ parent() }}:level2{% endblock %}
--TEMPLATE(level1.twig)--
{% block overrided deferred %}[overrided]{% endblock %}
{% block foo deferred %}[foo]{% endblock %}
--DATA--
return []
--EXPECT--
[overrided]:level2[foo]
For my part, I have not yet managed to find the cause of the bug.
You will undoubtedly be more efficient.
<html>
<body>
{% block content deferred %}
<h1>hello world</h1>
{% endblock %}
</body>
</html>
example works with twig/twig < 3.9.0
see also twigphp/Twig#4026
Check if it's possible to replace
{% deferred foo %}
with
{% block foo deferred %}
Phive\Twig\Extensions\Deferred\
-> Rybakit\Twig\DeferredExtension\
.
DeferredBlockNode
-> BlockNode
DeferredExtension
-> Extension
etc.
It looks like deferred acts erratically if an exception is thrown during rendering.
My proposed fix is something like this (against the legacy version, but should apply both):
DeferredExtension
public function defer(\Twig_Template $template, $blockName)
{
ob_start();
$templateName = $template->getTemplateName();
$this->blocks[$templateName][] = [ob_get_level(), $blockName];
}
public function resolve(\Twig_Template $template, array $context, array $blocks)
{
$templateName = $template->getTemplateName();
if (empty($this->blocks[$templateName])) {
return;
}
while ($block = array_pop($this->blocks[$templateName])) {
[$level, $blockName] = $block;
if (ob_get_level() !== $level) {
continue;
}
$buffer = ob_get_clean();
$blocks[$blockName] = array($template, 'block_'.$blockName.'_deferred');
$template->displayBlock($blockName, $context, $blocks);
echo $buffer;
}
if ($parent = $template->getParent($context)) {
$this->resolve($parent, $context, $blocks);
}
}
Basically, I store information about the ob level and ignore the buffer if the level doesn't match. I am not sure if this is the right approach, but at least it prevents too many ob_cleans from happening.
I just wanted to say thank you for this wonderful twig extension.
It saved me a minimum of 1-2 weeks of work.
Came like a gift from heaven - well tested, well maintained, well written.
Thank you.
An error is thrown when using filter
in a deferred
block
.
No error is thrown.
Add the following snippet in your template:
{%- block my_block deferred -%}
{% for test in test_array|default([])|filter(test => true) %}
{% endfor %}
{%- endblock -%}
Is it possible to set data from a string template and include it after ?
{# page.html.twig #}
{% extends "layout.html.twig" %}
{% block content %}
{{ data.append('/js/page-header.js') }}
{{ include(template_from_string(my_template)) }}
{{ data.append('/js/page-footer.js') }}
{% endblock %}
{# my_template #}
{{ data.append('/js/subpage1.js') }}
This is somewhat strange.
When I run the tests in your project, it seems to work fine against 1.18.2
. However, in my own project when I try to use 1.18.2
, the deferred blocks render nothing. When I use twig 1.17.0
, everything works as expected.
I tried to render the demos on README
to no avail.
By hijacking the core concept of blocks and changing entirely the behavior of rendering a block, this extension breaks the usage of blocks through the renderBlock
API of Twig.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.