Podaljšanje Latte

Latte je zelo prilagodljiv in ga je mogoče razširiti na več načinov: dodajate lahko filtre, funkcije, oznake, nalagalnike itd. Pokazali vam bomo, kako to storiti.

V tem poglavju so opisani različni načini razširitve sistema Latte. Če želite svoje spremembe ponovno uporabiti v različnih projektih ali če jih želite deliti z drugimi, morate nato ustvariti tako imenovano razširitev.

Koliko poti vodi v Rim?

Ker se lahko nekateri načini podaljševanja latte mešajo, poskušajmo najprej razložiti razlike med njimi. Kot primer poskusimo implementirati generator Lorem ipsum, ki mu posredujemo število besed za generiranje.

Glavni konstrukt jezika Latte je oznaka. Generator lahko implementiramo tako, da razširimo program Latte z novo značko:

{lipsum 40}

Oznaka bo delovala odlično. Vendar generator v obliki oznake morda ne bo dovolj prilagodljiv, saj ga ne bo mogoče uporabiti v izrazu. Mimogrede, v praksi redko potrebujete generiranje značk; in to je dobra novica, saj so značke bolj zapleten način razširjanja.

Ok, poskusite ustvariti filter namesto oznake:

{=40|lipsum}

Tudi to je veljavna možnost. Toda filter mora posredovano vrednost spremeniti v nekaj drugega. V tem primeru uporabimo vrednost 40, ki označuje število ustvarjenih besed, kot argument filtra in ne kot vrednost, ki jo želimo pretvoriti.

Zato poskusimo uporabiti funkcijo:

{lipsum(40)}

To je to! Za ta primer je ustvarjanje funkcije idealna razširitvena točka, ki jo lahko uporabimo. Pokličete jo lahko povsod, kjer je na primer sprejet izraz:

{var $text = lipsum(40)}

Filtri

Filter ustvarite tako, da registrirate njegovo ime in katerikoli klic PHP, na primer funkcijo:

$latte = new Latte\Engine;
$latte->addFilter('shortify', fn(string $s) => mb_substr($s, 0, 10)); // skrajša besedilo na 10 znakov

V tem primeru bi bilo bolje, če bi filter dobil dodaten parameter:

$latte->addFilter('shortify', fn(string $s, int $len = 10) => mb_substr($s, 0, $len));

Uporabljamo ga v predlogi, kot je ta:

<p>{$text|shortify}</p>
<p>{$text|shortify:100}</p>

Kot lahko vidite, funkcija kot naslednje argumente prejme levo stran filtra pred cevjo | as the first argument and the arguments passed to the filter after :.

Seveda lahko funkcija, ki predstavlja filter, sprejme poljubno število parametrov, podprti pa so tudi variabilni parametri.

Če filter vrne niz HTML, ga lahko označite, da ga Latte ne bo samodejno (in zato dvojno) eskapiral. S tem se izognete potrebi, da bi v predlogi določili |noescape. Najlažji način je, da niz zapakirate v objekt Latte\Runtime\Html, drugi način pa so kontekstualni filtri.

$latte->addFilter('money', fn(float $amount) => new Latte\Runtime\Html("<i>$amount EUR</i>"));

V tem primeru mora filter zagotoviti pravilno izpisovanje podatkov.

Filtri z uporabo razreda

Drugi način za opredelitev filtra je uporaba razreda. Ustvarimo metodo z atributom TemplateFilter:

class TemplateParameters
{
	public function __construct(
		// parametri
	) {}

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

$params = new TemplateParameters(/* ... */);
$latte->render('template.latte', $params);

Nalaganje filtrov

Namesto registracije posameznih filtrov lahko ustvarite tako imenovani nalagalnik, ki je funkcija, ki se pokliče z imenom filtra kot argumentom in vrne njegov klicni parameter PHP ali nič.

$latte->addFilterLoader([new Filters, 'load']);


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

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

	// ...
}

Kontekstualni filtri

Kontekstualni filter je filter, ki v prvem parametru sprejme predmet Latte\Runtime\FilterInfo, nato pa mu sledijo drugi parametri kot pri klasičnih filtrih. Registrira se na enak način, Latte sam prepozna, da je filter kontekstualen:

use Latte\Runtime\FilterInfo;

$latte->addFilter('foo', function (FilterInfo $info, string $str): string {
	// ...
});

Kontekstni filtri lahko zaznajo in spremenijo vrsto vsebine, ki jo prejmejo v spremenljivki $info->contentType. Če je filter klican klasično nad spremenljivko (npr. {$var|foo}), bo $info->contentType vsebovala ničlo.

Filter mora najprej preveriti, ali je vrsta vsebine vhodnega niza podprta. Lahko ga tudi spremeni. Primer filtra, ki sprejme besedilo (ali ničlo) in vrne HTML:

use Latte\Runtime\FilterInfo;

$latte->addFilter('money', function (FilterInfo $info, float $amount): string {
	// najprej preverimo, ali je tip vsebine vnosa besedilo.
	if (!in_array($info->contentType, [null, ContentType::Text])) {
		throw new Exception("Filter |money used in incompatible content type $info->contentType.");
	}

	// spremenimo tip vsebine v HTML
	$info->contentType = ContentType::Html;
	return "<i>$amount EUR</i>";
});

V tem primeru mora filter zagotoviti pravilno escapiranje podatkov.

Vsi filtri, ki se uporabljajo nad bloki (npr. kot {block|foo}...{/block}) morajo biti kontekstualni.

Funkcije

Privzeto lahko v Latte uporabljate vse izvorne funkcije PHP, razen če je to v peskovniku onemogočeno. Lahko pa določite tudi lastne funkcije. Te lahko prekrijejo izvorne funkcije.

Funkcijo ustvarite tako, da vpišete njeno ime in kateri koli klic PHP:

$latte = new Latte\Engine;
$latte->addFunction('random', function (...$args) {
	return $args[array_rand($args)];
});

Uporaba je nato enaka kot pri klicanju funkcije PHP:

{random(apple, orange, lemon)} // prints for example: apple

Funkcije, ki uporabljajo razred

Drugi način opredelitve funkcije je uporaba razreda. Ustvarimo metodo z atributom TemplateFunction:

class TemplateParameters
{
	public function __construct(
		// parametri
	) {}

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

$params = new TemplateParameters(/* ... */);
$latte->render('template.latte', $params);

Nalagalniki

Nalagalniki so odgovorni za nalaganje predlog iz vira, kot je datotečni sistem. Nastavijo se z metodo setLoader():

$latte->setLoader(new MyLoader);

Vgrajeni nalagalniki so:

FileLoader

Privzeta naprava za nalaganje. Naloži predloge iz datotečnega sistema.

Dostop do datotek lahko omejite z nastavitvijo osnovnega imenika:

$latte->setLoader(new Latte\Loaders\FileLoader($templateDir));
$latte->render('test.latte');

StringLoader

Naloži predloge iz nizov. Ta nalagalnik je zelo uporaben za testiranje enot. Uporablja se lahko tudi pri manjših projektih, kjer je morda smiselno vse predloge shraniti v eno samo datoteko PHP.

$latte->setLoader(new Latte\Loaders\StringLoader([
	'main.file' => '{include other.file}',
	'other.file' => '{if true} {$var} {/if}',
]));

$latte->render('main.file');

Poenostavljena uporaba:

$template = '{if true} {$var} {/if}';
$latte->setLoader(new Latte\Loaders\StringLoader);
$latte->render($template);

Ustvarjanje polnilnika po meri

Loader je razred, ki implementira vmesnik Latte\Loader.

Oznake

Ena od najzanimivejših funkcij mehanizma za oblikovanje predlog je možnost opredelitve novih jezikovnih konstrukcij z uporabo oznak. To je tudi bolj zapletena funkcionalnost, zato morate razumeti, kako Latte notranje deluje.

Vendar pa v večini primerov uporaba značk ni potrebna:

  • Če naj bi ustvarila kakšen izhod, namesto tega uporabite funkcijo
  • če naj bi spremenila nek vhodni podatek in ga vrnila, namesto tega uporabite filter
  • če naj bi urejala območje besedila, ga ovijte z {block} in uporabite filter
  • če ne bi smel ničesar izpisati, ampak samo poklicati funkcijo, jo pokličite z {do}

Če še vedno želite ustvariti oznako, odlično! Vse bistvene informacije so na voljo v poglavju Ustvarjanje razširitve.

Prehodi za prevajalnik

Prehodi za prevajalnik so funkcije, ki spreminjajo AST ali v njih zbirajo informacije. V programu Latte je na primer peskovnik izveden na ta način: prečka vsa vozlišča AST, poišče klice funkcij in metod ter jih nadomesti z nadzorovanimi klici.

Tako kot pri oznakah gre za bolj zapleteno funkcionalnost, zato morate razumeti, kako Latte deluje pod pokrovom. Vse bistvene informacije najdete v poglavju Ustvarjanje razširitve.

različica: 3.0