Русский
Русский
English
Статистика
Реклама

Plain

Наследование шаблонов в ванильном PHP за 35 строк кода?

29.05.2021 12:13:17 | Автор: admin

Попал мне как-то под руку проект на WordPress (WP), где понадобилось сделать кастомную тему. В WP шаблоны нативные, что хорошо, - не надо учить дополнительный язык. Но очень захотелось понаследовать шаблоны как в Twig, а PHP из коробки так не умеет.

Осталось решить вопрос наследования. После изучения проблемы, вдохновляться было решено библиотекой phpti, в которой нашлось пару моментов, которые очень захотелось поправить:

  • Автор библиотеки большими буквами написал Every Block is Always Executed!, т. е. все блоки выполняются, даже если переопределены, и никогда не будут выведены.

  • Второй момент - библиотека под капотом использует анализ стека вызовов, что, как мне кажется, чуть-чуть читерство.

  • Третий момент - хочу ещё быстрее. В библиотеке активно используется ob_start на чём и попробуем сэкономить пару спичек.

Библиотека phpti построена вокруг основной конструкции startblock/endblock и наследования при помощи import в начале файла:

// layout.php<!-- разметка --><?php startblock('blockName') ?><?php endblock() ?><!-- разметка -->
// index.php<?php include 'layout.php' ?> <!-- указываем родительский шаблон --><?php startblock('blockName') ?>    <!-- контент блока --><?php endblock() ?>

Некоторые наблюдения:

  • Вызовы вроде start/end можно заменить на анонимную функцию. Это избавит от необходимости сопоставлять вложенные старты и энды, а также сделает код контента ленивым.

  • Родительский шаблон указывается сверху файла. Это значит, что придётся сначала отрабатывать родительский, а потом дочерний шаблон. А значит придётся много чего буферизировать. А почему бы родителя не указать в конце?

  • Можно заметить, что на самом деле есть два разных типа блоков: одни определяются внутри шаблонов куда и выводят результат по месту, а другие по месту определения ничего не выводят, а только переопределяют родительские блоки.

С учётом вышесказанного, переделаем конструкцию на следующую:

// layout.php<!-- разметка --><?php slot('blockName', function(){ ?><?php }) ?><!-- разметка -->
// index.php<?php block('blockName', function(){ ?>    <!-- контент блока --><?php }) ?><?php include 'layout.php' ?> <!-- указываем родительский шаблон -->

Разделив блоки на slot и block, библиотеке больше не нужно пытаться понять, когда нужно выводить результат, а когда нужно только переопределить блок.

Вроде выглядит не слишком монструозно по сравнению с оригинальной структурой. Посмотрим, покроет ли такой подход все хотелки.

root.php - базовый шаблон, внизу иерархии:

<!DOCTYPE html><html>  <head>    <title><?php slot('title') /* слот - место в шаблоне */ ?></title>  </head>  <body>    <div id="root">      <?php slot('body', function () { /* слот с контентом по дефолту */?>        <p>'body' :: root.php</p>      <?php }) ?>    </div>  </body></html>

two-columns.php - промежуточный шаблон:

<?php block('title', function () { /* блок - контент для вставки в слот */?>  Title :: two-columns.php<?php });block('body', function () { ?>  <div id="two-columnts">    <div id="main">      <?php slot('main', function () { /* слот внутри блока */?>    <p>'main' :: two-columns.php</p>  <?php }) ?></div><div id="side">  <?php slot('side', function () { ?><p>'side' :: two-columns.php</p>  <?php }) ?></div>  </div>  <div id="footer">    <?php slot('footer', function () { ?>  <p>'footer' :: two-columns.php</p><?php }) ?>  </div><?php });include './root.php'; // наследуем от root.php

index.php - страница сайта, верхний шаблон:

<?phprequire_once '../src/InheritTpl.php'; block('title', function () { ?> 'title' :: index.php <?php });block('side', function () { ?>  <p>'side' :: index.php</p><?php }); block('main', function () { ?>  <div id="main-index"> <!-- Обернём содержимое от родителя -->    <?php super() /* тут выводим контент из родительского блока */?>  </div><?php });block('main', function () { /* Ещё раз тот же блок, почему бы и нет? */?>  <div id="main-index"> <!-- И ещё раз обернём содержимое -->    <?php super() ?>  </div><?php });// А 'footer' пусть остаётся как былinclude './two-columns.php';

Результат рендеринга (отформатирован для читабельности):

<!DOCTYPE html><html>  <head>    <title> 'title' :: index.php </title>  </head>  <body>    <div id="root">      <div id="two-columnts">        <div id="main">          <div id="main-index"> <!-- Обернём содержимое от родителя -->            <div id="main-index"> <!-- И ещё раз обернём содержимое -->              <p>'main' :: two-columns.php</p>            </div>          </div>        </div>        <div id="side">          <p>'side' :: index.php</p>        </div>      </div>      <div id="footer">        <p>'footer' :: two-columns.php</p>      </div>    </div>  </body></html>

Хотелки наследования удовлетворены. Но вот интересно, удалось ли сделать эту штуку быстрее?

Перепишем пример выше под библиотеку phpti. Дадим ей небольшую фору, т.к. в примере нет тяжеловесных переопределяемых блоков.

Сравнивать будем время 10,000 рендеров на PHP 8.0.2 и процессоре 3.6ГГц.

  • phpti: 0.831 секунд

  • сабж: 0.353 секунд

В качестве вывода можно сказать что размер библиотеки удалось сократить в 10 раз, при этом скорость работы механизма наследования увеличилась как минимум в 2 раза.

Посмотреть исходный код можно тут.

Подробнее..
Категории: Php , Template , Inheritance , Native , Plain

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru