Skip to content

[6.x] Keep augmentable tag results as objects inside Antlers interpolations - #15424

Open
duncanmcclean wants to merge 3 commits into
6.xfrom
asset-tag-interpolation
Open

[6.x] Keep augmentable tag results as objects inside Antlers interpolations#15424
duncanmcclean wants to merge 3 commits into
6.xfrom
asset-tag-interpolation

Conversation

@duncanmcclean

@duncanmcclean duncanmcclean commented Sep 8, 2026

Copy link
Copy Markdown
Member

This pull request fixes an issue where assigning the asset tag's result to a variable, like {{ img = { asset url="/img/logo.png" } }}, gave you the URL string rather than the asset, so {{ img.alt }}, {{ img.width }} and friends were empty.

This was happening because tag output inside an interpolation was reduced as if it were being rendered directly, and that path casts any object with __toString (like an Asset) to a string before the value is handed back.

This PR fixes it by keeping augmentable objects intact only when the interpolation is the right-hand side of an assignment. Interpolations used anywhere else, like dynamic keys ({{ items.{tag} }}), condition operands and expressions, are reduced to strings exactly as before. The Antlers runtime already knows how to work with an Asset held in a variable, so {{ img }} still outputs the URL, while {{ img.alt }}, {{ img:width }} and looping over {{ img }} now work too.

Since img now holds the asset rather than a string, {{ if img === "/img/logo.png" }} is false, the same as it is for an asset from a field. Loose == still matches.

Fixes #11257

@jasonvarga jasonvarga left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for digging into this one — the diagnosis is right, and the fix does resolve #11257. {{ img = { asset url="..." } }} keeps the Asset now, and the paired form starts working as a bonus.

The problem is scope. The guard keys off $this->isInterpolationProcessor, which is shared by three consumers, and two of them require a scalar. That leaves two regressions, one of which is a hard error:

template 6.x (e76bf9c) this branch
{{ img = { asset } }}{{ img }}|{{ img.alt }} /assets/a.jpg| /assets/a.jpg|Alpha ✅ fixed
{{ img = { asset } }}{{ img }}[{{ alt }}]{{ /img }} (empty) [Alpha] ✅ improved
{{ if {asset} == "/assets/a.jpg" }} yes yes
{{ if {asset} === "/assets/a.jpg" }} yes no ⚠️
{{ items[{asset}] }} matched empty ⚠️
{{ items.{asset} }} matched TypeError

Measured on PHP 8.5 against a tag returning an Asset whose URL is /assets/a.jpg. CI is green because no test covers either of the broken paths.

Suggested direction: keep the object only for the assignment consumer, and coerce Stringable results to string at the key-consuming boundaries in PathDataManager, rather than gating inside NodeProcessor::reduce() where all three consumers share the flag. Details inline.

Comment thread src/View/Antlers/Language/Runtime/NodeProcessor.php Outdated
Comment thread src/View/Antlers/Language/Runtime/NodeProcessor.php Outdated
Comment thread tests/Antlers/Runtime/TagsTest.php
duncanmcclean and others added 2 commits September 14, 2026 08:45
`isInterpolationProcessor` is shared by every interpolation consumer, so keeping objects behind it leaked an `Asset` into dynamic key lookups, condition operands and expressions. the object now survives only when the interpolation is the right-hand side of an assignment, via `Environment::getAssignedValue()` and `NodeProcessor::reduceAssignedInterpolatedVariable()`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017G87jCB5KrA8cTTfmrkAWM
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

asset tag does not return asset object when used as "inline mode"

2 participants