TYPO3

Fluid 5 in TYPO3 v14: fixing templates after the upgrade

5 min read

Fluid 5 template fixes

TYPO3 v14 comes with Fluid 5. On my v14 project the installed package is typo3fluid/fluid 5.3.2. Most templates render exactly like before, and that is the dangerous part: you click through a few pages, everything looks fine, and then one partial throws an exception on a page nobody tested. Fluid 5 removed things that were deprecated for a long time and made a few rules stricter.

For my own site hoja.in I rebuilt the sitepackage from scratch with PAGEVIEW, so the templates were already written for v14. But on upgrades of older sites, the same three topics come back every time. This post is my checklist for them, with the changelog entry behind each one.

What changed in Fluid 5

The umbrella entry is Breaking-108148-Fluid50.rst in the v14.0 changelog. It says Fluid 5 removes the deprecations from Fluid 2.x and 4.x. For integrators, three more entries with the same issue number matter most.

1. Stricter ViewHelper arguments

Fluid 5 validates ViewHelper arguments against their declared types (Breaking-108148-StrictTypesInFluidViewHelpers.rst). Simple conversions still work, so an integer passed to a string argument is fine. But an array passed where a string is expected is now rejected. Honestly, most of these were bugs before; Fluid just stopped looking away.

A typical case from older templates: somebody passes an array to an attribute that wants a string.

<!-- Before: "classes" is an array assigned by the controller -->
<f:link.page pageUid="{page.uid}" class="{classes}">{page.title}</f:link.page>

<!-- After: build the string where the data is prepared and keep the template simple -->
<f:link.page pageUid="{page.uid}" class="{linkClass}">{page.title}</f:link.page>

The fix belongs in PHP or in a DataProcessor, not in clever template code. When I am not sure which type an argument expects, I look it up in the ViewHelper reference for the exact version instead of guessing.

The same entry has a second change that does not throw but changes HTML. Tag-based ViewHelpers like f:form.textfield, f:link.* or f:image used to render null as an empty attribute. Fluid 5 leaves the attribute out. So placeholder="{maybeNull}" used to give placeholder="" and now gives no placeholder at all. If your CSS or JavaScript selects on an empty attribute, look there.

For custom ViewHelpers, the interface now wants return types: initializeArguments(): void and a declared return type on render() (mixed is allowed). The entry says this is backwards compatible, so you can add the types while still on v13.

2. Variable names starting with an underscore

Names like _temp or _item are reserved for Fluid now (Breaking-108148-FluidUnderscoreVariablePrefix.rst). This hits f:variable, the as of f:for, keys in f:render arguments, and also $view->assign() in PHP. {_all} still works, and so do keys inside arrays like {data._key}.

<!-- Before -->
<f:for each="{items}" as="_item">
    <f:render partial="Teaser" arguments="{_data: _item}" />
</f:for>

<!-- After -->
<f:for each="{items}" as="item">
    <f:render partial="Teaser" arguments="{teaser: item}" />
</f:for>

Remember to rename the variable inside the partial too. I forgot that once and got an empty teaser instead of an exception, which is worse.

3. CDATA is not a comment any more

Old trick: wrap template code in <![CDATA[ ]]> and Fluid removed it completely, so people used it to comment out blocks. Fluid 5 keeps CDATA sections and gives them a new meaning (Breaking-108148-CDATASectionsNoLongerRemoved.rst). Inside CDATA, single braces are ignored and you use three braces to reach Fluid. That is nice for inline CSS or JavaScript, but your “commented out” code suddenly appears in the HTML.

<!-- Before: hidden by Fluid 4, printed by Fluid 5 -->
<![CDATA[
    <f:render partial="OldSlider" arguments="{slides: slides}" />
]]>

<!-- After -->
<f:comment>
    <f:render partial="OldSlider" arguments="{slides: slides}" />
</f:comment>

<!-- And if you really need a value in inline CSS -->
<style>
<![CDATA[
    .hero { background-color: {{{heroColor}}}; }
]]>
</style>

Finding affected templates before the upgrade

This part is the reason I like the v13 to v14 path. Both the underscore variables and the CDATA comments write to the deprecation log since TYPO3 13.4.21, according to their changelog entries. So on the v13 site, before you upgrade, switch the deprecation log on. It is off by default; in config/system/additional.php of your local or staging setup:

$GLOBALS['TYPO3_CONF_VARS']['LOG']['TYPO3']['CMS']['deprecations']['writerConfiguration'][PsrLogLogLevel::NOTICE][TYPO3CMSCoreLogWriterFileWriter::class]['disabled'] = false;

Then crawl the site (a sitemap and a small loop with curl is enough) and read var/log/typo3_deprecations_*.log. Every template that renders on a crawled page and uses the old constructs shows up there. Templates on pages you did not crawl do not, so this is a net, not a proof.

On v14 itself there is a command: vendor/bin/typo3 fluid:analyse (the long name is fluid:analyze). It reports syntax errors and deprecations, but it only scans files with the new *.fluid.html extension. Most project templates are still plain *.html. The command also has a --stdin option, so I feed the old files to it one by one:

find packages/sitepackage/Resources/Private -name '*.html' ! -name '*.fluid.html' |
while read -r file; do
  echo "== $file"
  ddev exec vendor/bin/typo3 fluid:analyse --stdin < "$file"
done

On a test template with <f:variable name="_temp" … />{_temp} it printed: Variable identifiers cannot start with a "_": _temp, with the line and character. That is a parse-level check, so it catches underscore variables everywhere in the file, even on pages you never crawl. What it cannot catch are the runtime type problems from point 1, because those depend on the real values. The changelog says so openly. For those, you still need to render the pages.

The new file extension is optional

Fluid 5 also introduced *.fluid.html (Feature-108166). It is not required: TYPO3 looks for Default.fluid.html first and falls back to Default.html. Do not rename templates in an extension that still supports v13, because older Fluid does not know the new name. For a v14-only sitepackage, renaming lets fluid:analyse check the files without the stdin trick.

My order of work

  1. On v13.4 (latest patch level): enable the deprecation log, crawl, fix underscore variables and CDATA comments.
  2. Add return types to custom ViewHelpers while still on v13.
  3. Upgrade to v14, run fluid:analyse (with --stdin for plain .html).
  4. Render every page type and compare against a baseline from before the upgrade, looking for exceptions and missing attributes.

It is not glamorous work, but it saves the “only the job list page is broken” phone call. Feedbacks are the champion’s breakfast, so tell me what Fluid 5 broke for you.

Further reading

Leave a comment

Your email address will not be published.