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.
{% 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.
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):
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
version_compare as a Twig function (line 131) — straight PHP version_compare, useful for templates that adapt to plugin or Shopware versions.
array({'key': 'value'}) (line 130) wraps the array in an ArrayFacade, giving you object-style access to array data.
md5 filter — but note it routes through Shopware’s Hasher::hash() (line 119), not raw md5(). Fine for cache keys and DOM ids; know what it is before using it for anything else.
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.