Releases: nette/latte
Release list
Released version 3.1.5
Template inheritance just got a lot more flexible: layouts can now receive explicit variables, {embed} supports an implicit default block, and blocks finally behave intuitively in places where they used to fail. On top of that, the whitespace minifier behind {spaceless} has been rewritten from scratch to actually understand HTML.
✨ New Features
- {spaceless} rewritten from the ground up – the minifier now truly understands HTML, so it squeezes out much more whitespace without ever breaking your page: it strips whitespace completely around block elements, keeps proper word spacing around inline tags like
<i>or<a>, and leavespre,textarea,script,styleand attribute values untouched. And it streams, so even huge templates minify with no memory overhead. - Implicit default block for {embed} – content written directly inside
{embed}without a{block}wrapper now overrides{block default}of the embedded template, while an empty{embed}keeps the fallback (#419) - {layout file, vars} – pass explicit variables straight to the parent template:
{layout 'parent.latte', title: 'Hello'}. They reach the layout only (child blocks don't see them) and override same-named parameters. Works with{extends}too; note the comma before the arguments is required. - |json attribute modifier – JSON-encode a dynamic HTML attribute value with smart quoting:
<div data-config={$config|json}>
🐛 Bug Fixes
{include parent}and{include this}now work inside dynamically named blocks – previously this was a compile error, or worse, silently bound to an outer static block{contentType}inside<script>now really switches the escaping context – previously it was a silent no-op unless the script element had a matchingtypeattribute
🛠 For Extension Developers
- Every AST node now carries its full source extent:
Node::$positionmarks the start and the newNode::$endthe exclusive end;Rangeis a standalone start/end value object again (this reverts the shape shipped in 3.1.4)
Released version 3.1.4
- Range – a new
Latte\Compiler\Rangeclass (extendingPositionwith alength) gives every token and parser node a precise source span instead of just a starting position, unlocking richer tooling and editor integrations - StatementNode::$tagRanges – statement nodes now expose exact byte ranges for their opening and closing tags, making it straightforward to map compiled output back to the original template
|limitaccepts strings – the|limitfilter now works on strings too, so you can trim text the same way you slice arrays{dedent}– fixed inline content handling andatLineStarttracking; the logic moved into a dedicatedDedentclass with considerably more test coverage (#412, #413, #414)PhpHelpers::checkCode()–proc_opennow uses array syntax for the PHP binary path, so installations where the interpreter lives in a directory with spaces no longer break (#415)- TemplateGenerator – stopped emitting the
/** source */comment for multi-line template names, which could produce broken PHP {switch}– a{case}with a single value now compiles to===instead ofin_array(), producing faster and cleaner generated PHP
Released version 3.1.3
Latte 3.1.3 brings two powerful new engine features – automatic dedentation and scoped loop variables – alongside a fresh batch of filters that make everyday template work smoother.
✨ New Features
- Feature::Dedent – removes structural indentation from paired tag content, so your generated HTML stays clean regardless of how deeply you nest your Latte tags
- Feature::ScopedLoopVariables – variables introduced by
{foreach}now live only inside the loop body, preventing accidental leaks into the surrounding scope (supports destructuring viaListNodetoo) - |commas filter – joins array elements with
,and supports a custom last-item glue (e.g.{$items|commas:' and '}→a, b and c) - |column filter – extracts values from a single column of a multidimensional array or iterable, mirroring PHP's
array_column() - |limit filter – a friendlier wrapper around
|slicewith swapped arguments: limit first, offset second, and keys preserved by default - |slice – now works with iterators, not just arrays and strings
- |padLeft, |padRight – now accept
int|floatvalues in addition to strings (#408)
♻️ Code Refactoring
- Helpers::sortBeforeAfter() – reimplemented using Kahn's algorithm for topological sorting, making extension ordering both faster and more correct
- Engine::addDefaultExtensions() – extracted into its own method for better extensibility
Released version 3.1.2
Latte 3.1.2 introduces a clean feature flag system, tightens up the compiler API with two breaking changes, and brings the codebase under stricter static analysis. If you're extending the compiler, pay attention to the TokenStream and custom function changes below.
- Feature enum with setFeature()/hasFeature() – a unified way to toggle engine capabilities like
StrictTypes,StrictParsing, andMigrationWarnings. The old methods (setStrictTypes(),setStrictParsing(),setMigrationWarnings()) still work but are now deprecated in favor of the enum-based API. - setCacheDirectory() replaces the old
setTempDirectory()– because "temp" was way too easily confused with "template". The old method still works but is deprecated. - LinterExtension now correctly validates
::classexpressions – previously{=UnknownClass::class}slipped through validation because it was handled as a class constant instead of a class type check. - Added missing native type declarations across the codebase.
- Improved phpDoc types and descriptions.
- Unified
declare(strict_types=1)to single-line format.
Released version 3.0.26
- Feature class with setFeature()/hasFeature() – a unified way to toggle engine capabilities. Existing methods like
setStrictTypes()andsetStrictParsing()now delegate to this system, so you can use either style. New features can be added without cluttering the Engine API. - setCacheDirectory() replaces the old
setTempDirectory()– because "temp" was too easily confused with "template". The old method still works but consider it deprecated. - Fixed
!operator priority – the logical NOT operator was incorrectly placed at the same precedence level as bitwise NOT and type casts, aboveinstanceof. It now correctly sits belowinstanceof, matching PHP's actual operator precedence.
Released version 3.1.1
- added LinterExtension for validating filters, functions, classes and methods
- linter: improved finding files, excludes temp, vendor, node_modules
- support every argument syntax for
clone()in PHP 8.5 - added CustomFunctionCallNode for custom functions
- refactor: decentralize operator precedence to individual nodes
- *CallableNode replaced with solution with VariadicPlaceholder (BC break)
- ErrorSuppressNode & NotNode merged into UnaryOpNode (BC break)
- deprecated
PrintContext::infixOp(),prefixOp(),postfixOp()(BC break) - fixed operator ! priority
Release version 3.0.25
- Escaper: supports converting HTML => TEXT
- Escaper: fixed converting between HTML/ATTR
<=>HTML - added
ModifierNode::removeFilter() - linter: added assets extension
Released version 3.1.0
This major release introduces Smart HTML Attributes, strict_types by default, and several syntax improvements. It requires PHP 8.2+.
Smart HTML Attributes
- Null values: Attributes with
nullvalues are now dropped instead of rendering as empty strings. - Boolean attributes: Attributes like
checkedordisabledrender conditionally based on truthy/falsey values. - Array support:
classandstyleattributes now accept arrays (e.g.,class="{[active: $cond]}"). - Data attributes:
data-*attributes automatically JSON-serialize non-string values (arrays, objects, booleans). - Aria attributes:
aria-*attributes render boolean values as"true"/"false". - Type checking: Passing invalid types (arrays, objects) to standard attributes now triggers a warning and omits the attribute.
New Features
- Added
n:elseifsupport. - Added nullsafe filter operator
?|(e.g.,{$var?|upper}). - Added
|togglefilter for manual boolean attribute control. - Support for unquoted n-attribute syntax:
n:if={$cond}.
Breaking Changes & Deprecations
strictTypesis now enabled by default.- Using
$thisand$__*variables in templates is deprecated. - The undefined unsafe operator
??->is deprecated (use?->). - Global constants should now be prefixed with a slash (e.g.,
\PHP_VERSION). Engine::addFilterLoader()is deprecated.
Released version 3.0.24
- Released version 3.0.24
- support for PHP 8.5
- added support for pipe operator in PHP 8.5
- added support for new deref without parens in PHP 8.4
- added
Engine::setSyntax()to set default syntax (#359) - refactor: moved script tag quote validation from PrintNode to compiler pass
- Tag parser: checks octal sequence overflow
Engine::getCacheKey()reflects strictTypes & strictParsing, ignores filemtime of extensionsElementNode::getAttribute()uses case-insensitive comparison in XML- NAttrNode: renamed temporary variables not to collide with n:snippet
Released version 3.0.22
- TagParser: allowed |
filter()and bitwise OR PrintContext::format()parenthesizes %node if it has lower precedence than the assignment operator- PrintContent: refactoring and updated operator precedence and associativity table
Cache::generateFileName()file name always contains 'latte--'- added /** Generated by Latte */ comment
- n:else: fixed when there are multiple TextNode
- PrintNode moved to Latte\Compiler\Nodes
- Nodes: removes null nodes after traversal