Latte 2.10: A joy to debug

15 days ago by David Grudl     edit

It has become a tradition that once every quarter a new version of Latte with a ton of hot new features is releases. This time, the most important features are related to debugging templates. Come take a look at a quick overview.

Tracy panel

Latte has a completely new panel for Tracy, which in combination with nette/application 3.1 turns on automatically:

In the panel, you can see all templates, starting with the basic presenter template, through layout templates, to all imported, included and embedded templates. You can click through directly into the template, but also into the compiled PHP code. The order of the templates themselves also carries useful information, as it shows the priority of the inheriting block hierarchy, which otherwise may not be obvious at all. If each of the templates defines a block with the same name, the one of the above template will be drawn, and using {include parent} the block of the same name from the following template it will be inserted.

{trace}

Another novelty is closely related to Tracy. This is a {trace} tag that you can place anywhere in the template and it will throw an exception. Special one exceptions, because its callstack is generated from a template perspective. So instead of calling methods, we see blocks and {include}, {extends}, {import} or {embed}. And also the passed arguments.

This tweak is useful, for example, when tuning complex compositions, when you need to find out how the rendering got to a certain point.

By the way Latte compiles templates into clear PHP code, which you can step through in the usual way in the IDE, you can set breakpoints, etc. Version 2.10 adds many more comments /* line 123 */ to the code referring to the line number in the source template, also methods representing blocks have a new explanatory phpDoc comment.

n:tag

The n:tag attribute can dynamically change the name of an HTML element. This can be useful in situations where we change the importance of a header based on the value of a variable:

<h1 n:tag="$heading" class="main">{$title}</h1>

If $heading === null, the tag <h1> is displayed without change. Otherwise, the name of the element is changed to the value of the variable, so for $heading === 'h3' this is written:

<h3 class="main">...</h3>

{iterateWhile}

The tag` {iterateWhile}` is suitable for various tricks in foreach cycles, which are otherwise extremely complicated. It can also replace {first} and {last}. Its description is given in a separate chapter in the documentation.

{embed block}

The previous version of Latte came up with the important tag {embed}, which fundamentally shifts the possibilities of writing components in Latte. In addition to files {embed file.latte}, blocks can now be embedded directly {embed blockname}.

Modern syntax [key: value]

The previous version Latte came with support for named arguments from PHP 8.0 in tags like {include} or {link}:

before
{include 'file.latte' arg1 => 1, arg2 => 2}
now
{include 'file.latte' arg1: 1, arg2: 2}

Now you can also use this modern syntax to write arrays (which is not possible directly in PHP):

before
{[a => active, s => suspended][$user->status]}
now
{[a: active, s: suspended][$user->status]}

New filters and features

In Latte 2.10 you will find a plethora of new functions (even(), odd(), divisibleBy(), first(), last()) and filters (|first, |last, |random, |round, |floor, |ceil, |join, |slice, |explode, |split, |spaceless and |query).

It further extends the filter |sort with the possibility to pass own comparison function as a parameter:

{var $sorted = ($names|sort: fn($a, $b) => $b <=> $a)}

The filter |replace can make several changes at once:

{='hello world'|replace: [h => l, l => h]}  {* prints 'lehho worhd' *}

Engine::addFilterLoader()

The method addFilterLoader($loader) replaces the universal filter addFilter(null, ....), whose API was not very clear. The task of the loader is to return the callback of the filter or null:

class Filters
{
	public function loader(string $filter): ?callable
	{
		if (method_exists($this, $filter)) {
			return [$this, $filter];
		}
		return null;
	}

	public function shortify($s, $len = 10)
	{
		return mb_substr($s, 0, $len);
	}

	...
}

PHP attributes TemplateFunction & TemplateFilter

You can register filters and functions in Latte in another way: by creating methods in the template class and giving them annotations /** @filter */ or /** @function */. You can now replace the annotations with PHP 8.0 attributes:

class HomepageTemplate
{
	#[Latte\Attributes\TemplateFilter]
	public function shortify(string $s, int $len = 10): string
	{
		return mb_substr($s, 0, $len);
	}

	#[Latte\Attributes\TemplateFunction]
	public function random(...$args)
	{
		return $args[array_rand($args)];
	}
}

One more thing

Write, run, test and save templates in the new online playground. Technical curiosity: it all runs purely in the browser.

If you are converting an old project written in pure PHP to Nette, or a project using Twig to Latte, you will appreciate the brand new converter library. You can try their online version at https://php2latte.nette.org and https://twig2latte.nette.org.

It is not about simple regular expressions, on the contrary, PHP or Twig parser is used directly, so the converter can handle any complicated syntax.

The conversion from Twig requires manual adjustment of the result, because the conversion cannot be performed unambiguously. Twig uses a dot syntax, where {{ a.b }} can mean $a->b, $a['b'] or $a->getB(), which can only be distinguished at runtime, not at compile time. The converter therefore converts everything to $a->b. Also, some functions, filters or tags may behave slightly differently in Latte.

Further reading