Шаблонизатор на PHP. Часть 1

Шаблонизатор на PHP. Часть 1

Coding

Мне в голову пришла идея написать простой шаблонизатор PHP, в котором файл шаблона открывается в потоке, изменяется и преобразуется в корректный PHP-код. А затем другой поток используется, чтобы смоделировать файл, который отображается через буфер вывода и функцию include. Таким образом исключаются временные файлы и легко могут быть созданы фильтры, делая синтаксис шаблона простым для изменения.

Синтаксис

Пример реализации механизма шаблонизатора находится здесь —Affinity4/Template. Он поддерживает PHP, поэтому синтаксис шаблонов не является обязательным. На это меня вдохновил Sass.

Вторая ключевая особенность – это то, что синтаксис шаблона не будет отображаться без применения механизма шаблонов или в случае ошибки компиляции потока в PHP. Это означает, что если вы решите использовать другой язык шаблонов или включите файлы напрямую, то при просмотре страницы вы не увидите блоков {{ var }} или @foreach(thingy in thingys), оставленных в коде.

Третьей особенностью является то, что синтаксис по умолчанию должен работать во всех IDE, текстовых редакторах и быть понятен всем, знающим HTML.

Чтобы шаблонизатор соответствовал второму и третьему требованию, нужно использовать комментарии HTML.

Вот пример:

<h1><!-- :title --></h1>


 <!-- @if :show_list is true -->


 <ul>


 <!-- @each :item in :items -->


 <li><!-- :item --></li>


 <!-- @/each -->


 <ul>


 <!-- @/if -->


Недостаток заключается в том, что здесь больше кода. Но он может быть быстро написан, используя горячие клавиши комментариев в редакторе или IDE (обычно Ctrl + / или Cmd + /).

Достоинства шаблонизатора:

  1. Поддержку IDE/редакторов по умолчанию.
  2. Возможность использования чистого PHP-код. Это позволяет быстро усвоить механизм.
  3. Специфический язык шаблона может быть полностью проигнорирован любым, кто хочет работать только над разметкой.

Наш механизм шаблонов не будет включать в себя инструменты макетов или блоков, которые есть в Twig, Blade, Latte и т.д. Это важные средства, и я планирую реализовать их в будущем. Но нам они не нужны.

Структура проекта

Мы поместим всё в два класса: один для обработки правил синтаксиса (регулярные выражения), другой — для открытия файлов исходного кода, их компиляции в PHP, выделения переменных и вывода.

Звучит запутанно? Но с помощью vfsStream это становится тривиальной задачей по сравнению с традиционным методом создания лексеров/парсеров, токенизаторов и разработкой некоторой формы AST(абстрактного синтаксического дерева).

Мы используем менеджер зависимостей Composer, чтобы подключать vfsStream и загружать наши собственные классы.

composer require mikey179/vfsStream

Далее создадим в корне проекта папку под названием src. Внутри нее разместим два файла классов (Engine.php и Syntax.php). Структура папок должна выглядеть следующим образом:

template


 |-- composer.json


 |-- src


 |-- Engine.php


 |-- Syntax.php


 |-- views


 |-- home.php

Затем я записываю всё в файл index.php, чтобы увидеть правильность реализации. Для механизма шаблонов это достаточно просто. Поэтому можно сделать следующее:

Файл: index.php

<?php



 use TemplateEngine;
require_once __DIR__ . '/vendor/autoload.php';
$template = new Engine;


 $template->render('views/home.php', [


 'show_title' => true,


 'title' => 'Home'


 ]);?>

Чтобы получить рабочую версию шаблонизатора, нужно следующее:

  1. Метод прорисовки шаблона (render).
  2. Метод render должен получать путь к представлению в качестве первого аргумента.
  3. Необязательный массив параметров может передаваться в представление.
  4. Параметры должны быть выделены в переменные, если второй аргумент – не пустой массив.
  5. Загрузка представления в vfsStream для дальнейшей обработки.
  6. Включение обработанного (скомпилированного) «файла» в буфер вывода для отображения.
  7. Файл представления: views/home.php.

Сначала создадим наше представление с простым PHP кодом, чтобы убедиться, что переменные передаются в представление:

Файл: views/home.php

<?php if ($show_title) : ?>


 <h1><?php echo $title; undefined?></h1>


 <?php endif; ?>

Теперь можно сфокусироваться на простейшей реализации класса шаблонизатора на PHP(Engine.php).

Файл: src/Engine.php


 namespace TemplateEngine;
use orgbovigovfsvfsStream;
class Engine


 {


 public function render($view, $params = [])


 {


 // 1. Выделить параметры при непустом массиве params



 // 2. Если представление существует, включить его в буфер вывода


 // 3. Если файл представления не найден, вывести исключение


 }


 }?>

Так мы получим рабочую версию метода render, хотя и без желаемой функциональности.

Перед тем, как двигаться дальше, нужно добавить папку src в автозагрузчик PSR-4, расположенный в файле composer.json:

Файл: composer.json 

{


 <span class="strong">undefinedundefinedundefined "autoload": {</span>


 <span class="strong">undefinedundefinedundefinedundefinedundefinedundefinedundefined "psr-4": {</span>


 <span class="strong">undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefined "Template": "src/"</span>


 <span class="strong">undefinedundefinedundefinedundefinedundefinedundefinedundefined }</span>


 <span class="strong">undefinedundefinedundefined },</span>


 "require": {


 "mikey179/vfsStream": "^1.6"


 }


 }

Теперь требуется повторно сгенерировать файлы автозагрузки и файл composer.lock:

composer dumpautoload

Реализуем логику получения представления для отображения с параметрами, которые мы передали.

Файл: src/Engine.php

<?php


 ...


 class Engine



 {


 public function render($view, $params = [])


 {


 // 1. Выделить параметры при непустом массиве params


 <span class="strong">if (!empty($params)) extract($params);</span>
// 2. Если наше представление существует, включить его в буфер вывода


 <span class="strong">undefinedundefinedundefinedundefinedundefinedundefinedundefined if (file_exists($view)) {</span>


 <span class="strong">undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefined ob_start();</span>


 <span class="strong">undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefined include $view;</span>


 <span class="strong">undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefined ob_get_flush();</span>


 <span class="strong">undefinedundefinedundefinedundefinedundefinedundefinedundefined } else {</span>


 // 3. Если файл представления не найден, вывести исключение.


 <span class="strong">undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefined throw new Exception(sprintf('Файл %s не найден.', $view));</span>


 <span class="strong">undefinedundefinedundefinedundefinedundefinedundefinedundefined }</span>


 }


 }


 ?>

Запустите локальный сервер для PHP. Перейдите в корень проекта и откройте командную строку. В командной строке наберите:

php -S localhost:80

Вы увидите сообщение о том, что сервер запущен. Откройте браузер, перейдите по адресу http://localhost/ и вы должны увидеть:

Теперь мы знаем, что метод render будет работать, так как ожидается. В следующей части мы подключим vfsStream.


Не забывайте ставить 👍 если вам понравилась и подписаться на канал

 

Report Page