Add `block_data()` and `block_export()` functions
Description
Currently, Twig's block() function renders a block and returns the result as an HTML string. There is no way to extract structured data from a block.
This becomes a limitation when using Twig Components (<twig:> syntax), where the spread syntax ({{ ...hash }}) requires an associative array, not an HTML string.
Use case: Symfony Form themes with Twig Components
In Symfony's form themes, blocks like widget_attributes and attributes render HTML attribute strings:
<input type="text" {{ block('widget_attributes') }} />
{# outputs: id="name" name="form[name]" required="required" #}A Twig Component form theme needs the same data as a hash for spread syntax:
<twig:Input type="text" {{ ...block_data('widget_attributes') }} />Proposal
Add two new Twig functions:
block_export(data)
Called inside a block to export structured data via a side-channel (stored on CoreExtension). The block continues to render HTML normally.
{% block attributes %}
{%- set attrs = {} -%}
{%- for attrname, attrvalue in attr -%}
{%- if attrvalue is same as(true) -%}
{%- set attrs = attrs|merge({(attrname): attrname}) -%}
{%- elseif attrvalue is not same as(false) -%}
{%- set attrs = attrs|merge({(attrname): attrvalue}) -%}
{%- endif -%}
{%- endfor -%}
{%- do block_export(attrs) -%}
{%- for attrname, attrvalue in attrs -%}
{{- ' ' ~ attrname }}="{{ attrvalue }}"
{%- endfor -%}
{% endblock %}block_data(name)
Renders the block (HTML output is discarded) and returns the data exported via block_export(). Returns [] if the block does not call block_export().
{{ block_data('attributes').id }}
{# returns the structured hash instead of the HTML string #}Implementation
block_data()uses aparser_callable(likeblock()) to compile to aBlockDataExpressionnodeBlockDataExpressioncompiles to$this->unwrap()->renderBlockData(...)Template::renderBlockData()callsrenderBlock()then returns the exported data fromCoreExtension::getExportedBlockData()- The exported data is consumed on read (reset to
null), preventing leaks between calls
Why not a different approach?
- Returning data from blocks directly: Twig blocks compile to PHP methods with
array $contextpassed by value (variables set inside don't propagate back) - New PHP Twig functions (e.g. in Symfony): would duplicate the logic already in the Twig blocks (translations, boolean normalization), and wouldn't benefit from block inheritance/override
block_data()/block_export(): keeps the logic in one place (the block), works with block inheritance, and is fully backward-compatible (existing blocks continue to render HTML as before)
Related: symfony/symfony#65713
Source: twigphp/Twig