[PHP] Наследование шаблонов в ванильном PHP за 35 строк кода?
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Попал мне как-то под руку проект на 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 - страница сайта, верхний шаблон:
<?php
require_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 раза.Посмотреть исходный код можно тут.
===========
Источник:
habr.com
===========
Похожие новости:
- [PHP, Программирование, Совершенный код, Проектирование и рефакторинг, ООП] Принцип подстановки Барбары Лисков (предусловия и постусловия)
- [PHP, Yii] Велосипед длиной в полжизни
- [PHP, Программирование, Проектирование и рефакторинг, ООП, Go] Prototype Design Pattern в Golang
- [PHP, Профессиональная литература] Книга «Создаем динамические веб-сайты на PHP. 4-е межд. изд.»
- [Perl, Ruby, PHP, Python, JavaScript] Насколько вам наплевать на фичи последней версии языка?
- [CMS, PHP, MySQL, JavaScript] CS Cart или через терни к черной дыре костылей и оптимизаций
- [PHP, Фриланс, Карьера в IT-индустрии, Бизнес-модели] Сайт сына маминой подруги
- [Высокая производительность, Разработка веб-сайтов, PHP, Magento] PHP-SPX простой профайлер трейсер для PHP
- [Разработка веб-сайтов, PHP, Symfony, Конференции] Прямой эфир про тесты, трейты, devops в монолите, переход на Go и KPHP с казанского PHP-митапа
- [PHP] Алгоритм вычисления арифметического выражения в виде строки
Теги для поиска: #_php, #_template, #_inheritance, #_php, #_native, #_plain, #_php
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 05:43
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Попал мне как-то под руку проект на WordPress (WP), где понадобилось сделать кастомную тему. В WP шаблоны нативные, что хорошо, - не надо учить дополнительный язык. Но очень захотелось понаследовать шаблоны как в Twig, а PHP из коробки так не умеет.Осталось решить вопрос наследования. После изучения проблемы, вдохновляться было решено библиотекой phpti, в которой нашлось пару моментов, которые очень захотелось поправить:
// layout.php
<!-- разметка --> <?php startblock('blockName') ?> <?php endblock() ?> <!-- разметка --> // index.php
<?php include 'layout.php' ?> <!-- указываем родительский шаблон --> <?php startblock('blockName') ?> <!-- контент блока --> <?php endblock() ?>
// layout.php
<!-- разметка --> <?php slot('blockName', function(){ ?> <?php }) ?> <!-- разметка --> // index.php
<?php block('blockName', function(){ ?> <!-- контент блока --> <?php }) ?> <?php include 'layout.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> <?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 <?php
require_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>
=========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 05:43
Часовой пояс: UTC + 5