14.08.2026・TechStuff
14.08.2026・TechStuff

Shopware 6 Hidden Gems #10: The Twig you didn’t know Shopware speaks

Fabian Blechschmidt

During a code review I once flagged a colleague’s template: „{% break %}? That’s not Twig, this can’t work.“ It worked. I lost a small bet and gained a grep target.

The answer lives in Framework/Adapter/Twig/Extension/PhpSyntaxExtension.php. Shopware quietly extends Twig with a whole layer of PHP-isms — control structures, operators, type tests, cast filters. It was built for app scripts (that’s the only place the docs mention any of it), but the extension is registered globally in the core DI (services.xml:368). It works in every storefront template, every mail template, every CMS block you render.

Here’s the full inventory, because I couldn’t find one anywhere else.

Control structures

{% foreach products as product %}
    {% if product.stock <= 0 %}
        {% continue %}
    {% endif %}
    {% if loop.index > 8 %}
        {% break %}
    {% endif %}
    ...
{% endforeach %}

{% foreach %}, {% break %}, {% continue %} and {% return %} are real token parsers (PhpSyntaxExtension.php:40-48). break and continue are the headline: vanilla Twig famously has neither, and the workarounds (nested ifs, slicing before the loop) are exactly the template noise these two remove.

Operators: ===, !==, &&, ||

{% if product.type === 'digital' && customer !== null %}

Twig’s is same as(...) never stopped feeling like typing with mittens on. Shopware maps the PHP operators directly (getOperators(), lines 180-188): === is is same as, !== is is not same as, &&/|| are and/or. Nothing new semantically — just templates PHP developers can read without switching dialects.

Type tests

{% if value is string %} ... {% endif %}
{% if config is array %} ... {% endif %}
{% if callback is callable %} ... {% endif %}

Eleven of them (getTests(), lines 143-169): true, false, array, bool/boolean, callable, float, int/integer, object, scalar, string. Handy in templates that receive loosely-typed config — theme settings, CMS element config, custom fields — where „is this a string or already an array?“ is a real question.

Cast filters — with a safety net

{{ someConfigValue|intval }}
{{ "19.99"|floatval }}

intval, floatval, strval, boolval (lines 57-92). They’re not blind casts: objects with __toString are converted, and non-scalar input throws a proper AdapterException instead of Twig’s usual silent weirdness (validateType(), line 193).

json_encode that respects your prices

My favorite detail in the whole file (lines 93-107):

return json_encode($var, $options | \JSON_PRESERVE_ZERO_FRACTION, $depth);

Shopware’s json_encode filter always sets JSON_PRESERVE_ZERO_FRACTION. Why should you care? Because plain PHP json_encode(19.00) produces 19 — the float loses its decimal identity. In a price payload for a JS component or a structured-data snippet, 19 vs 19.0 can be the difference between „works“ and „why does the comparison fail for round prices only“. Shopware made the filter price-safe by default; if you’re generating JSON in templates any other way, you now know the flag to set.

The filter also flips FieldVisibility::$isInTwigRenderingContext during encoding, so internal fields honor their API visibility rules — encoding an entity in a template won’t leak fields that the API would hide.

The rest of the drawer

Should you actually use this?

The purist argument: templates shouldn’t need break, heavy logic belongs in the page loader or a subscriber, and non-standard Twig makes templates less portable. All true.

The pragmatic argument: you inherit real-world templates, and a {% break %} beats a triple-nested if every time someone has to read it. My rule of thumb — type tests, cast filters and json_encode freely; foreach/break/continue when they remove complexity; and if you find yourself needing {% return %} in a storefront template, something upstream went wrong.

Either way you should know it exists — if only so you don’t lose a bet in code review like I did.

TL;DR

PhpSyntaxExtension teaches Twig PHP tricks, globally, in every Shopware template: {% foreach %} with {% break %}/{% continue %}, ===/!==/&&/||, type tests like is string, safe cast filters, version_compare, and a json_encode that preserves 19.00 as a float and respects API field visibility. Documented only for app scripts — works everywhere.

Next up: sw_icon_cache — the Twig filter that deduplicates SVG icons and shrinks your listing HTML.

Know more undocumented Twig goodies? Drop me an email!


Found in Shopware 6.7.0.0: Framework/Adapter/Twig/Extension/PhpSyntaxExtension.php — token parsers (lines 40-48), filters (57-121), functions (127-132), tests (143-169), operators (176-190); registered globally in Framework/DependencyInjection/services.xml:368.