Разширяване на Latte

Latte е проектиран с мисъл за разширяемост. Въпреки че стандартният му набор от тагове, филтри и функции покрива много случаи на употреба, често се налага да добавяте собствена специфична логика или помощни инструменти. Тази страница предоставя преглед на начините за разширяване на Latte, така че да отговаря перфектно на изискванията на вашия проект – от прости помощници до сложен нов синтаксис.

Начини за разширяване на Latte

Ето бърз преглед на основните начини, по които можете да персонализирате и разширите Latte:

  • Потребителски филтри: За форматиране или трансформиране на данни директно в изхода на шаблона (напр. {$var|myFilter}). Идеални за задачи като форматиране на дати, редактиране на текст или прилагане на специфично екраниране. Можете също да ги използвате за модифициране на по-големи блокове HTML съдържание, като обвиете съдържанието в анонимен {block} и приложите към него потребителски филтър.
  • Потребителски функции: За добавяне на преизползваема логика, която може да бъде извикана в рамките на изрази в шаблона (напр. {myFunction($arg1, $arg2)}). Полезни за изчисления, достъп до помощни функции на приложението или генериране на малки части от съдържанието.
  • Потребителски тагове: За създаване на напълно нови езикови конструкции ({mytag}...{/mytag} или n:mytag). Таговете предлагат най-много възможности, позволяват дефиниране на собствени структури, контрол върху парсването на шаблона и имплементиране на сложна логика за рендиране.
  • Компилационни преминавания: Функции, които модифицират абстрактното синтактично дърво (AST) на шаблона след парсване, но преди генериране на PHP код. Използват се за напреднали оптимизации, проверки за сигурност (като Sandbox) или автоматични модификации на кода.
  • Потребителски зареждащи устройства: За промяна на начина, по който Latte търси и зарежда файлове с шаблони (напр. зареждане от база данни, криптирано хранилище и т.н.).

Изборът на правилния метод за разширение е ключов. Преди да създадете сложен таг, помислете дали по-прост филтър или функция не биха били достатъчни. Нека го илюстрираме с пример: имплементиране на генератор Lorem ipsum, който приема като аргумент броя на думите за генериране.

  • Като таг? {lipsum 40} – Възможно е, но таговете са по-подходящи за контролни структури или генериране на сложни тагове. Таговете не могат да се използват директно в изрази.
  • Като филтър? {=40|lipsum} – Технически работи, но филтрите са предназначени за трансформиране на входната стойност. Тук 40 е аргумент, а не стойност, която се трансформира. Това изглежда семантично неправилно.
  • Като функция? {lipsum(40)} – Това е най-естественото решение! Функциите приемат аргументи и връщат стойности, което е идеално за използване във всеки израз: {var $text = lipsum(40)}.

Обща препоръка: Използвайте функции за изчисления/генериране, филтри за трансформация и тагове за нови езикови конструкции или сложни тагове. Използвайте преминавания за манипулиране на AST и зареждащи устройства за извличане на шаблони.

Директна регистрация

За помощни инструменти, специфични за проекта, или бързи разширения, Latte позволява директна регистрация на филтри и функции в обекта Latte\Engine.

За да регистрирате филтър, използвайте метода addFilter(). Първият аргумент на вашата филтърна функция ще бъде стойността преди знака |, а следващите аргументи са тези, които се предават след двоеточието :.

$latte = new Latte\Engine;

// Дефиниция на филтъра (извикващ се обект: функция, статичен метод и т.н.)
$myTruncate = fn(string $s, int $length = 50) => mb_substr($s, 0, $length);

// Регистрация
$latte->addFilter('truncate', $myTruncate);

// Използване в шаблона: {$text|truncate} или {$text|truncate:100}

Можете също да регистрирате Filter Loader, функция, която динамично предоставя извикващи се обекти на филтри според изискваното име:

$latte->addFilterLoader(fn(string $name) => /* връща извикващ се обект или null */);

За да регистрирате функция, използваема в изрази на шаблона, използвайте addFunction().

$latte = new Latte\Engine;

// Дефиниция на функцията
$isWeekend = fn(DateTimeInterface $date) => $date->format('N') >= 6;

// Регистрация
$latte->addFunction('isWeekend', $isWeekend);

// Използване в шаблона: {if isWeekend($myDate)}Уикенд!{/if}

Повече информация ще намерите в секциите Създаване на потребителски филтри и Функции.

Надежден начин: Latte Extension

Докато директната регистрация е проста, стандартният и препоръчителен начин за пакетиране и разпространение на разширения на Latte е чрез класове Extension. Extension служи като централна конфигурационна точка за регистрация на множество тагове, филтри, функции, компилационни преминавания и други елементи.

Защо да използвате Extensions?

  • Организация: Поддържа свързаните разширения (тагове, филтри и т.н. за конкретна функция) заедно в един клас.
  • Преизползваемост и споделяне: Лесно пакетирайте вашите разширения за използване в други проекти или за споделяне с общността (напр. чрез Composer).
  • Пълна мощ: Потребителските тагове и компилационните преминавания могат да се регистрират само чрез Extensions.

Регистрация на Extension

Extension се регистрира в Latte с помощта на метода addExtension() (или чрез конфигурационен файл):

$latte = new Latte\Engine;
$latte->addExtension(new MyProjectExtension);

Ако регистрирате множество разширения и те дефинират тагове, филтри или функции с еднакви имена, предимство има последно добавеното разширение. Това също означава, че вашите разширения могат да презапишат нативните тагове/филтри/функции.

Всеки път, когато направите промяна в класа и автоматичното обновяване не е изключено, Latte автоматично ще прекомпилира вашите шаблони.

Създаване на Extension

За да създадете собствено разширение, трябва да създадете клас, който наследява от Latte\Extension. За да добиете представа как изглежда такова разширение, разгледайте вграденото CoreExtension.

Нека разгледаме методите, които можете да имплементирате:

beforeCompile (Latte\Engine $engine)void

Извиква се преди компилацията на шаблона. Методът може да се използва например за инициализации, свързани с компилацията.

getTags(): array

Извиква се при компилация на шаблона. Връща асоциативен масив име на таг ⇒ извикващ се обект, което са функции за парсване на тагове. Повече информация.

public function getTags(): array
{
	return [
		'foo' => FooNode::create(...),
		'bar' => BarNode::create(...),
		'n:baz' => NBazNode::create(...),
		// ...
	];
}

Тагът n:baz представлява чист n:атрибут, т.е. таг, който може да бъде записан само като атрибут.

При таговете foo и bar, Latte автоматично разпознава дали са двойни тагове и ако да, могат автоматично да се записват с помощта на n:атрибути, включително варианти с префикси n:inner-foo и n:tag-foo.

Редът на изпълнение на такива n:атрибути се определя от реда им в масива, върнат от метода getTags(). Така n:foo винаги се изпълнява преди n:bar, дори ако атрибутите в HTML тага са изброени в обратен ред като <div n:bar="..." n:foo="...">.

Ако трябва да определите реда на n:атрибутите между няколко разширения, използвайте помощния метод order(), където параметърът before xor after определя кои тагове се сортират преди или след тага.

public function getTags(): array
{
	return [
		'foo' => self::order(FooNode::create(...), before: 'bar')]
		'bar' => self::order(BarNode::create(...), after: ['block', 'snippet'])]
	];
}

getPasses(): array

Извиква се при компилация на шаблона. Връща асоциативен масив име на преминаване ⇒ извикващ се обект, което са функции, представляващи т.нар. компилационни преминавания, които преминават и модифицират AST.

Тук също може да се използва помощният метод order(). Стойността на параметрите before или after може да бъде * със значение преди/след всички.

public function getPasses(): array
{
	return [
		'optimize' => Passes::optimizePass(...),
		'sandbox' => self::order($this->sandboxPass(...), before: '*'),
		// ...
	];
}

beforeRender (Latte\Engine $engine)void

Извиква се преди всяко рендиране на шаблона. Методът може да се използва например за инициализиране на променливи, използвани по време на рендирането.

getFilters(): array

Извиква се преди рендиране на шаблона. Връща филтри като асоциативен масив име на филтър ⇒ извикващ се обект. Повече информация.

public function getFilters(): array
{
	return [
		'batch' => $this->batchFilter(...),
		'trim' => $this->trimFilter(...),
		// ...
	];
}

getFunctions(): array

Извиква се преди рендиране на шаблона. Връща функции като асоциативен масив име на функция ⇒ извикващ се обект. Повече информация.

public function getFunctions(): array
{
	return [
		'clamp' => $this->clampFunction(...),
		'divisibleBy' => $this->divisibleByFunction(...),
		// ...
	];
}

getProviders(): array

Извиква се преди рендиране на шаблона. Връща масив от providers, които обикновено са обекти, използвани от таговете по време на изпълнение. Достъпват се чрез $this->global->.... Повече информация.

public function getProviders(): array
{
	return [
		'myFoo' => $this->foo,
		'myBar' => $this->bar,
		// ...
	];
}

getCacheKey (Latte\Engine $engine)mixed

Извиква се преди рендиране на шаблона. Върнатата стойност става част от ключа, чийто хеш се съдържа в името на файла на компилирания шаблон. Следователно за различни върнати стойности Latte ще генерира различни кеш файлове.

версия: 3.0