💥 Breaking Changes
- contenteditable, draggable, spellcheck, hidden are no longer treated as plain boolean attributes, because in HTML they are not.
contenteditable={$editable}now renderscontenteditable="true"orcontenteditable="false", sofalsereally turns editing off instead of silently inheriting it from the parent, anddraggablecan be set totrueat all.hiddenandpopoverstay flags but keep a value, sohidden="until-found"survives. With migration warnings on, Latte reports every place where the output changes; use|toggleto keep the old bare-name rendering - Sandbox – property names in a policy are case-sensitive, as they are in PHP: allowing
visibleno longer allowsVisible - isPartialFunction() of the call nodes is renamed to isPartialApplication() – only matters for extensions working with the AST
✨ New Features
- PHP 8.6 support
- NodeHelpers::find() and findFirst() accept a node class as the filter:
find($node, TextNode::class)returns alist<TextNode>, no type annotation needed - ExpressionNode::toValue() and hasValue() tell an extension whether an expression is a literal value and which;
NodeHelpers::toValue()is deprecated in their favor
🔒 Security
- The sandbox got a thorough review of its runtime checks: a dynamic property name is evaluated just once, so it cannot be checked with one value and accessed with another;
(array)cast of an object is forbidden andforeachover an object checks its properties against the policy;$iteratorno longer passes calls of unknown methods to the iterated object; and->/?->method calls behave exactly as in PHP - A
<script>whosetypeis anything the browser might execute (e.g.text/javascript1.5) is escaped as JavaScript; only a plain data type such astext/csvis escaped as text, andtext/htmlortext/x-templateas HTML - A custom sandbox policy without
getCacheSignature()triggers a deprecation, because its instances with different rules would share compiled templates
🐛 Bug Fixes
{include parent}restores the block level after an exception and works in a block that includes itself{capture $a[$i++]|filter}evaluates its target once|slicewith a negative start or length works on iterators the same way as on arrays, and|batchkeeps every item of an iterator with repeated keys- An arrow function body swallows
and,orandxor, as in PHP - The deprecation of an unqualified constant is no longer raised for class names written in capitals such as
FOO::bar()ornew FOO
🛡️ Robustness
- Writing through
?->anywhere in a chain ($a?->b->c = 1) is a compile error instead of a PHP error later - The linter writes errors to stderr outside CLI too and restores its error handler for an unreadable file