twigphp / twigphp/Twig

Add `block_data()` and `block_export()` functions

Open
#4,919 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
PHP
Stars
8.4k
Forks
1.3k
Avg merge
1d 13h
Merged PRs (30d)
37

Description

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 a parser_callable (like block()) to compile to a BlockDataExpression node
  • BlockDataExpression compiles to $this->unwrap()->renderBlockData(...)
  • Template::renderBlockData() calls renderBlock() then returns the exported data from CoreExtension::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 $context passed 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

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start from the existing block() parser_callable and compare its behavior with the proposed BlockDataExpression. Trace Template::renderBlockData(), CoreExtension::getExportedBlockData(), and the export-consumption flow. Done means block_data() returns exported data or [] while discarding HTML, block_export() preserves normal rendering, and existing block behavior remains unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
backend
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.