Latte 3.1: When a templating system truly understands HTML
Latte has long held the position of the safest templating system for PHP. It's not just a marketing phrase – it's a consequence of the fact that Latte (unlike Twig or Blade) understands context. It doesn't just distinguish between “HTML” and “PHP code”. It sees tags, attributes, values. And in version 3.1, we pushed this understanding to a level that will radically improve your DX.
While version 3.0 brought a completely new compiler and parser, version 3.1 focuses on semantics and code cleanliness. It comes with the concept of Smart HTML Attributes, which eliminates dozens of lines of unnecessary conditions and helpers.
Let's look at what Latte 3.1 does under the hood and why you'll want to use it.
Native mapping of PHP types to HTML attributes
Until now, the template was a passive string generator. If you sent
null to <span title="{$title}">, you got an
empty string title="". If you sent an array, you got
title="Array". Latte 3.1 changes the rules of the game and
introduces strict rendering logic based on data types.
Null means “attribute does not exist”
In the DOM, there is a difference between <div title="">
(empty title) and <div> (no title). Latte 3.1 respects this
difference.
- Behavior: A
nullvalue in any HTML attribute results in its complete removal from the output. - Impact: The end of
n:attror{if}conditions inside tags. You just pass data.
Boolean attributes without magic
Attributes like disabled, checked or
required are binary. Either they are there, or not. Latte now
accepts an expression directly in the attribute value.
- Behavior: Truthy expression ⇒ attribute is rendered. Falsey expression ⇒ attribute disappears.
- Advantage:
n:attris a great tool, but for this common use-case it was “overkill”. Now the syntax is clean and declarative:
<input disabled={$isDisabled}>
Arrays in class and style
Attributes that expect a list of values (class,
rel, sandbox…) now natively accept arrays. Latte
handles the intelligent rendering.
<div class={[
btn,
btn-primary => $isPrimary, // added only if true
]}></div>
<div style={[
background => lightblue,
display => $isVisible ? block : null,
font-size => '16px',
]}></div>
Automatic JSON serialization
You can pass an array or object (stdClass) to
data-* attributes. Latte detects the context and automatically
performs json_encode.
WAI-ARIA compliance without effort
Accessibility is important, but the WAI-ARIA specification has its specifics.
Unlike standard HTML boolean attributes, where mere presence decides, ARIA
requires explicit text values "true" and "false".
Latte 3.1 solves this problem for you. It detects the aria-
prefix and automatically ensures correct stringification.
<button aria-expanded={$isExpanded} aria-hidden={=false}>
{* <button aria-expanded="true" aria-hidden="false"> *}
Advantage: No more manual writing of ternary operators like
{$val ? 'true' : 'false'}. Latte guarantees valid output.
Type safety in templates
Latte 3.1 is stricter. And that is good. Common templating systems allow you
to print an array into href, generating a broken link
<a href="Array">.
Latte 3.1 introduces Runtime Type Checking for attributes.
- Trying to print a boolean into
href? Warning. - Trying to print an object without
__toStringintotitle? Warning.
Thanks to this, you detect errors in presentation logic immediately during development, not when a user complains about broken UI. Moreover, the warning contains the exact line and column in the template.
Strict Types by Default
We are keeping up with the times. The PHP ecosystem has matured to
strict_types=1 and Latte is not lagging behind. All templates in
Latte 3.1 are compiled with this directive by default. This ensures consistent
behavior with your backend code and prevents unwanted type casting in critical
template logic.
Nullsafe filters: The Holy Grail for Strict Types
You might be wondering how filters, strict types, and HTML attributes are related. In Latte 3.1, very closely.
Imagine a situation where you want to print a title in uppercase, but the variable might be null.
<div title={$title|upper}>
If $title === null, you encounter two problems:
- Strict types: If the
upperfilter expects astringbut receivesnull, a silent conversion to an empty string would have occurred previously, but with active strict types, aTypeErroroccurs. - Loss of “Smart” behavior: Even if the filter accepts
null, it returns an empty string"", which for Latte is no longernull. The result is<div title="">. The attribute does not disappear, it is just empty.
The solution is the new Nullsafe filter operator ?|.
It works exactly like ?-> in PHP. If the input value is
null, the filter is not called at all (nor following filters) and
the expression returns null.
<div title={$title?|upper}>
Result? No TypeError, because the filter is not called on null.
And the Smart Attribute works, so title completely disappears from
the HTML.
Syntactic sugar for cleaner code
We heard the community's call and added missing pieces of syntax:
n:elseif: The missing link in chaining conditions using n:attributes is finally here.- n:attributes: Alternative syntax
<div n:if={$cond}>, thanks to which you can freely use single and double quotes inside{...}.
How to upgrade safely?
Changing the behavior of null and boolean attributes is a BC
break, but we thought about that. Latte 3.1 includes a Migration
Mode.
- Call
$latte->setMigrationWarnings(). - Click through the application. Latte will print exactly those places where the output differs from version 3.0 to Tracy/log.
- Use the temporary
|acceptfilter to confirm that the new behavior (e.g. disappearance of an empty attribute) is desired, or modify the code.
Latte 3.1 is not just “another version”. It is a shift towards modern, type-safe, and semantic HTML generation. Try it out and see how much cleaner your templates can be.
Sign in to submit a comment