Latte: як щодо системи типів?

5 років тому від David Grudl  

Система типів. Ключова річ для розробки надійних додатків, у якій PHP значно випередив динамічні мови, такі як Python, Ruby або JavaScript. Фреймворк Nette з самого початку спрямовує програмістів до типізованого та строгого програмування. Latte 2.7 приніс підтримку типів і в шаблони.

Завдяки тому, що ми знаємо, який тип даних або об'єкта міститься в кожній змінній, може

  • IDE правильно підказувати
  • статичний аналіз виявляти помилки

Два пункти, які суттєво підвищують якість та зручність розробки.

Як оголосити типи на стороні PHP?

Дані будемо передавати в шаблон як об'єкт класу, який визначає всі змінні та їхні типи. З використанням нових можливостей PHP 7.4 він міг би виглядати приблизно так:

class MailTemplate
{
	public string $lang = 'cs';
	public Address $address;
	public string $subject;
	public ?float $price = null;
}

Використання:

$template = new MailTemplate;
$template->price = $this->getPrice();
...
$latte->render('mail.latte', $template);

Доповнення: завдяки новим можливостям PHP 8 приклад можна записати ще цікавіше так:

class MailTemplate
{
	public function __construct(
		public string $lang = 'cs',
		public Address $address,
		public string $subject,
		public ?float $price = null,
	) {}
}

$latte->render('mail.latte', new MailTemplate(
	lang: $this->lang,
	subject: $title,
	price: $this->getPrice(),
	address: $userAddress,
));

Як оголосити типи в шаблоні?

Зазначений клас тепер можна пов'язати з шаблоном. Достатньо вказати на початку:

{templateType MailTemplate}

І цим ми визначили, що в шаблоні буде четвірка змінних $lang, $address, $subject та $price включно з відповідними типами.

Альтернативою є визначення типів окремих змінних безпосередньо в шаблоні, тобто без створення класу. Для цього служить тег {varType}:

{varType string $lang}
{varType Address $address}

Звичайно, можна комбінувати обидва підходи. Створити клас, оскільки це забезпечить підказки на стороні presenter'а, пов'язати його з шаблоном за допомогою {templateType} і для інших локальних змінних у блоках тощо використовувати {varType}. Які можна розглядати як аналог /** @var type $variable */, що є коментарем, яким ми іноді інструктуємо IDE або статичний аналізатор у PHP коді.

Тепер тип можна вказати також у тегах {var}, {default} або {define}:

{var Model\Page $page = $items['page']}
{define form, string $name}
	...
{/define}

Примітка: з історичних причин у тегах {var} та {default} можна було писати змінні і без початкового долара (і зі стрілкою замість знака рівності). Оскільки це створює неоднозначність, чи йдеться про тип, чи про змінну, цей синтаксис заборонений, і Latte буде попереджати вас при його використанні. Простий спосіб перевірити свої шаблони на наявність цього старого запису — шукати регулярні вирази /\{(var|default) [^$]/ та /\{(var|default) [^}]*=>/.

Як заощадити собі роботу?

Як найлегше написати клас шаблону або теги {varType}? Дозвольте їм згенеруватися. Саме для цього існують два теги: {templatePrint} та {varPrint}.

Якщо ви розмістите один з цих тегів у шаблоні, замість звичайного відображення з'явиться пропозиція коду класу або список тегів {varPrint}. Потім код достатньо одним кліком виділити та скопіювати до проекту.

Частиною nette/application 3.1 (у бета-версії) є перевантажений тег {templatePrint}, який генерує код, краще пристосований для presenter'ів.

Тег {varPrint} виводить локальні змінні, які не є параметрами шаблону. Якщо ви хочете вивести всі змінні, використовуйте {varPrint all}.

Підказки в IDE

Найновіша версія плагіна Latte для PhpStorm вміє використовувати вищезгадані теги і потім підказувати на основі типів. Плагін також знає типи стандартних змінних, таких як $user, $presenter, $basePath тощо.

Статичний аналіз

Метою є, щоб усі оголошені типи можна було використовувати також для статичного аналізу. Щоб, наприклад, за допомогою PHPStan можна було перевіряти шаблони так само легко, як інші PHP файли.

Ми працюємо над цим, і ви побачите це в одній з наступних версій Latte.