Обычно за шаблоны в PHP платят тремя вещами: композером с десятком пакетов, отдельным кэшем, который надо чистить, и заменой <?= $var ?> на что-то, что экранирует само. HTML.PHP 6.0.0 пытается обойтись одним файлом: шаблонизатор PHP без зависимостей, с экранированием по умолчанию, полным набором директив Apache SSI и gettext-совместимой локализацией. Ниже — что внутри, как это проверено на живом PHP 8.5, где проходит граница разумного применения и на чём спотыкаются чаще всего.

TL;DR: что это и как проверить за десять минут
- Что это. Библиотека обработки шаблонов: подстановки с фильтрами, циклы, условия, наследование макетов, частичные шаблоны с областью видимости.
- Что ещё. Полный процессор Apache SSI (
#if,#exec,#printenv,#block) и слой i18n с каталогами PO/MO/JSON/PHP. - Зависимостей нет. Один файл:
php83/html.phpдля PHP 8.3–8.5 илиphp85/html.phpтолько для 8.5. - Лицензия MIT — в этой версии она прописана во всех манифестах и проверяется тестом.
- Движок тот же, что в 5.0.0. Ни одна конструкция шаблонов, опция или метод API не изменились.
curl -fsSL -o html.php \
https://github.com/paulmann/HTML.PHP/releases/download/v6.0.0/html-php-6.0.0-php83.php
sha256sum html.php # 7df928018f8f43f7e9d402e7a5ff40fed73f490f549b5a69e8e07afa1f1477b6
php -r "require 'html.php'; echo HtmlPhp\Engine::create()->renderString('Hello %{name}!', ['name' => '<World>']);"
# Hello <World>
Проблема, которую это решает
Шаблоны в PHP-проекте почти всегда начинаются одинаково: <?= htmlspecialchars($title) ?>. Через месяц кто-то забывает htmlspecialchars, и в разметку приезжает пользовательский текст. Через два — появляется «свой» мини-шаблонизатор с регулярками. Через три — в проект добавляется Twig, а вместе с ним композер, кэш шаблонов, отдельный синтаксис, и вопрос «где это компилируется» на каждом новом сервере.
Второй пласт — локализация. Gettext в PHP работает, но обвязка вокруг него (каталоги, множественное число, извлечение строк, аудит непереведённого) обычно пишется руками и живёт в каждом проекте по-своему.
Третий — SSI. Классические .shtml с <!--#include virtual=...--> обрабатывает сам веб-сервер, и это привязывает шаблоны к Apache с mod_include. На nginx такой страницы не будет, а перенести сайт — значит переписать вставки.
HTML.PHP собирает все три истории в одну библиотеку: шаблон обрабатывается в PHP, SSI разбирается в PHP, каталоги перевода читаются в PHP. От веб-сервера не зависит ничего.
Что такое HTML.PHP
flowchart TB APP["приложение на PHP"] --> ENG["Engine: единая точка входа"] ENG --> TPL["шаблоны: %-подстановки, фильтры, выражения"] ENG --> SSI["слой Apache SSI: if, exec, block, printenv"] ENG --> I18N["i18n: каталоги PO, MO, JSON, PHP"] ENG --> CACHE["кэш рендера на файлах"] TPL --> LINT["линтер шаблонов для CI"] I18N --> PLUR["правила множественного числа для 60+ языков"]
| Свойство | Значение |
|---|---|
| Тип | библиотека обработки шаблонов, не приложение |
| Рантайм | PHP 8.3+ (php83/html.php, CI: 8.3, 8.4, 8.5) или PHP 8.5+ (php85/html.php) |
| Платформа | Windows, Linux, macOS — где есть PHP |
| Точки входа | единый файл, src/ через autoload.php, Composer PSR-4, CLI bin/htmlphp |
| Зависимости | нет |
| Сеть | нет: ни телеметрии, ни исходящих запросов |
| Запись на диск | только если включён кэш рендера, файловый лог или экспорт POT |
| Лицензия | MIT, включая исходники, оба файла и плагин WordPress |
Это библиотека, а не фреймворк: нет роутера, базы, демона и веб-интерфейса. Её место — внутри существующего приложения, между контроллером и выводом.
Три части, которые обычно живут раздельно
| Часть | Что делает |
|---|---|
| Шаблоны | подстановки %{}, #{}, @{} с фильтрами, циклы, условия, макеты, частичные шаблоны |
| SSI | разбор директив mod_include в PHP: #include, #if, #exec, #printenv, #block/#stub |
| i18n | gettext-совместимые каталоги PO, MO, JSON, PHP; правила множественного числа для 60+ языков |
Каждая часть работает и сама по себе: можно взять только шаблоны, можно прогнать SSI по готовому HTML из базы, можно использовать только перевод.
Возможности
Шаблоны
| Что | Как выглядит |
|---|---|
| Подстановка | %{title} — с автоэкранированием; #{...} и @{} для других режимов |
| Фильтры | %{price|number:2}, %{name|upper} и десятки других |
| Выражения | %{= done / total * 100 | round:1 } — арифметика прямо в подстановке |
| Динамические ключи | %{= prices[currency]}, %{= rows[i].name} |
| Косвенность | %{= get(fieldName)} — имя поля берётся из данных |
| Циклы | <!--@Loop rows--> … <!--@Else--> … <!--@EndLoop-->, %{_position} |
| Условия | <!--@If total > 100 --> … <!--@Else--> … <!--@EndIf--> |
| Вычисления | <!--@Set m="price * 2"--> |
| Наследование | <!--@Extends "base.html"--> для макетов |
| Частичные шаблоны | с ограниченной областью видимости |
Фильтры арифметики: add sub mul div mod pow sqrt pct sum avg clamp. Диапазоны a..b в выражениях работают и индексируются: %{= (1..3)[1]} даёт второй элемент.
Локализация
| Что | Как работает |
|---|---|
| Каталоги | PO, MO, JSON, PHP — что удобнее в проекте |
| Множественное число | правила для 60+ языков, включая русские формы |
| Извлечение строк | i18n:extract собирает POT из шаблонов |
| Аудит | отчёт о непереведённых строках |
| В шаблоне | @{list.empty} — термин переводится, если список пуст |
SSI
| Директива | Назначение |
|---|---|
#include |
вставка файла, как в mod_include |
#if / #elif / #else |
условия с выражениями |
#exec |
выполнение команды — по умолчанию запрещено (ssi.exec_mode: deny) |
#printenv |
вывод переменных окружения |
#block / #stub |
блоки с подстановкой заглушек |
processSsi() работает и над произвольной строкой — например, над содержимым записи WordPress, пришедшим из базы.
Архитектура внутри
flowchart LR F["шаблон .html"] --> PARSE["разбор: подстановки и директива @Loop/@If"] PARSE --> EXPR["выражения в песочнице: без eval, только белый список"] EXPR --> ESC["экранирование по умолчанию"] ESC --> TR["перевод через каталог i18n"] TR --> OUT["готовый HTML"] CACHE["кэш рендера"] -.-> OUT
Устройство слоёное, и это видно по структуре: фасад Engine, контейнер, рендерер с конвейером «разбор → макеты → дерево → SSI → кэш», процессор SSI, переводчик, интерфейсы кэша и логгера, диспетчер событий, диагностика и адаптеры WordPress.
| Слой | Ответственность |
|---|---|
Engine |
фасад: render, renderFile, renderString, lint, t, processSsi, stats |
Template\Renderer |
конвейер разбора и вывода |
Expression |
безопасный вычислитель выражений |
Ssi\Processor |
сканер директив и реестр политик |
I18n\Translator |
каталоги, формы множественного числа, делегирование WordPress |
CacheInterface |
драйверы file, apcu, array, null, wp, psr16 |
Log\LoggerInterface |
null, array, file, errorlog, syslog, wp, psr3 |
Жёсткое правило, которое проверяется тестом на уровне токенов: ядро никогда не вызывает eval() и не читает $GLOBALS и суперглобальные массивы. Единственные разрешённые точки соприкосновения с внешним миром — явный помощник ввода и адаптеры WordPress. Для библиотеки, которая работает с пользовательским контентом, это правильная граница.
Замеры и цифры
Проверено 09.11.2026: скачаны оба файла релиза, сверены контрольные суммы, затем библиотека запущена на живом PHP.
| Что измерено | Значение | Как |
|---|---|---|
html-php-6.0.0-php83.php |
530 172 байта, SHA-256 7df928…47b6 |
gh release download + Get-FileHash |
html-php-6.0.0-php85.php |
531 994 байта, SHA-256 19c4e4…0274 |
там же |
| Совпадение с хешами из заметок к релизу | оба совпали | сверка вручную |
| Объём файлов | 16 269 и 16 316 строк | подсчёт строк |
| Проверка синтаксиса | php -l — ошибок нет |
CLI |
| Версия PHP на машине | 8.5.0 (cli, NTS, Visual C++ 2022 x64) | php -v |
| Тесты по бейджу README | 391 тест, 389 пройдено, 2 пропущено | README.md |
| PHPStan | уровень 6, чисто | бейдж и заметки |
| Оформление кода | php-cs-fixer чист на 143 файлах | заметки к релизу |

Прогон в один заход, восемь проверок:
| Проверка | Код | Результат |
|---|---|---|
| Экранирование | renderString('%{title}') |
Hello <World> — теги обезврежены |
| Арифметика | %{= done / total * 100 \| round:1 }% |
14.8% |
| Формат числа | %{price\|number:2} при локали ru |
1 234,50 |
| Цикл | @Loop с %{_position} и upper |
[1:ADA][2:GRACE] |
| Условие | @If total > 100 |
много |
| Вычисление | @Set m="price * 2" |
2469.0 |
| Диапазон | %{= (1..3)[1]} |
2 |
| Перевод | $engine->t('{0} files', [42]) |
42 files |
Отдельно проверил stats(): возвращает template, startedAt, durationMs, cacheHit, substitutions, loops — этого достаточно, чтобы однажды выяснить, почему страница стала медленной, без профайлера.
Что замеры не показывают: производительность на реальном сайте, поведение под нагрузкой и работу SSI-директив #exec/#block — я их не запускал. Первое требует стенда с трафиком, второе — стенда с Apache-подобным окружением.
Сильные стороны продукта
- Ноль зависимостей. Один файл, никакого композера — и никаких «а почему у вас не установился пакет».
- Экранирование по умолчанию. Забыть
htmlspecialcharsтехнически невозможно: вывод экранируется, пока не попросишь обратного. - Нет
evalи суперглобальных массивов в ядре. Проверяется тестом на уровне токенов, а не обещанием в документации. - SSI без Apache. Директивы разбираются в PHP, поэтому шаблоны переносятся на nginx и FPM без переписывания.
- Локализация в комплекте. Каталоги, множественное число и извлечение строк — не отдельный проект.
- Понятная конфигурация. Неизвестный ключ даёт исключение, а не молча игнорируется.
- Диагностика из коробки.
stats()и структурированный сборщик диагностик с устойчивыми кодами. - MIT. Коммерческое использование и закрытая сборка разрешены при сохранении уведомления.
Установка
Три пути, и первый — самый короткий.
# 1. из релиза: один файл, без Composer
curl -fsSL -o html.php \
https://github.com/paulmann/HTML.PHP/releases/download/v6.0.0/html-php-6.0.0-php83.php
sha256sum html.php
# 2. Composer
composer require paulmann/html-php # PHP 8.3/8.4
composer require paulmann/html-php85 # PHP 8.5
# 3. WordPress: плагин из каталога wordpress/html-php/
| Требование | Зачем | Проверка |
|---|---|---|
| PHP 8.3 или новее | оба файла отказываются работать на старом рантайме | php -v |
| PHP 8.5+ для сборки 8.5 | её манифест требует >=8.5 |
php -v |
| Расширения не обязательны | mbstring, intl, apcu только улучшают поведение |
php -m |
| Записываемый каталог | нужен, только если включён файловый кэш | выбрать путь в cache.directory |
Запуск и первый шаблон
require_once __DIR__ . '/html.php';
use HtmlPhp\Engine;
$engine = Engine::create([
'templates' => ['paths' => [__DIR__ . '/templates']],
'i18n' => ['locale' => 'de', 'paths' => [__DIR__ . '/languages']],
'cache' => ['enabled' => true, 'driver' => 'file',
'directory' => sys_get_temp_dir() . '/htmlphp'],
]);
echo $engine->render('page.html', [
'title' => 'Hello <World>',
'rows' => [['name' => 'Ada'], ['name' => 'Grace']],
]);
<h1>%{title}</h1> <!-- Hello <World> -->
<ul>
<!--@Loop rows-->
<li>%{_position}. %{name|upper}</li>
<!--@Else-->
<li>@{list.empty}</li>
<!--@EndLoop-->
</ul>
<!--#include virtual="/partials/footer.shtml" --> <!-- Apache SSI -->
Проверка версии и работоспособности — две команды:
php bin/htmlphp version
php -r "require 'html.php'; echo HtmlPhp\Engine::create()->renderString('Hello %{name}!', ['name' => '<World>']);"
Если вторая строка вывела Hello <World>!, экранирование работает. Если вывела <World> — вы подключили не тот файл.
Настройка
flowchart TD
CFG["Engine::create: массив опций"] --> TP["templates.paths"]
CFG --> I18["i18n.locale и i18n.paths"]
CFG --> CA["cache.enabled, driver, directory"]
CFG --> SYN["syntax.expressions: выключатель выражений"]
TP --> R["render ('page.html')"]
I18 --> R
CA --> R
SYN --> RКонфигурация — обычный массив в Engine::create(). CLI принимает тот же массив из PHP-файла: --config=FILE, файл обязан вернуть массив, иначе команда завершится кодом 2.
| Ключ | По умолчанию | Что значит |
|---|---|---|
syntax.auto_escape |
true |
экранировать вывод по умолчанию |
syntax.expressions |
включены | выключатель выражений в подстановках |
syntax.allow_raw_filter |
— | разрешать фильтр «вывести как есть» |
ssi.enabled |
true |
обрабатывать SSI-директивы |
ssi.exec_mode |
deny |
политика выполнения команд |
cache.enabled |
false |
кэш рендера выключен |
cache.driver |
file |
драйвер кэша |
log.driver |
null |
логирование выключено |
i18n.locale |
en |
язык по умолчанию |
Два правила, которые стоит запомнить. Неизвестный ключ на любом уровне вложенности даёт ConfigException — опечатка не отключит защиту молча. И для шаблонов, которые правят пользователи, документация советует выключить syntax.allow_raw_filter и syntax.expressions: тогда из шаблона нельзя ни вывести сырой HTML, ни заставить библиотеку считать выражение.
Когда SSI в PHP, а не в Apache
Разница практическая. Классический mod_include обрабатывает вставки на уровне веб-сервера: страница .shtml уходит клиенту уже собранной, но собрать её может только Apache с включённым модулем. Стоит перенести сайт на nginx — вставки перестают работать, и в браузере видно <!--#include ... --> как текст.
Здесь разбор идёт в PHP: страница собирается в приложении, поэтому одинаково работает на Apache, на nginx с FPM, на IIS и в консоли. Плата — время PHP на разбор директив вместо C-модуля сервера; выигрыш — переносимость и то, что SSI можно применить к содержимому из базы.
| Критерий | mod_include |
SSI в PHP |
|---|---|---|
| Где работает | только Apache с модулем | везде, где есть PHP |
| Что обрабатывает | только файлы на диске | файлы и строки из базы |
Политика #exec |
настройками сервера | явная: по умолчанию deny |
| Стоимость | C-модуль | время PHP |
Локализация
| Сценарий | Как решается |
|---|---|
| Строки в шаблонах | @{key} — термин подставляется из каталога |
| Строки в коде | $engine->t('{0} files', [42]) |
| Множественное число | правила для 60+ языков, включая русские формы |
Русские формы — отдельная проверка: три разные строки для одного ключа (1 файл, 2 файла, 5 файлов), и правило выбирает нужную по числу. Без такого механизма шаблон пришлось бы обвешивать условиями, а условия в шаблоне — прямой путь к ошибкам.
| Извлечение строк | i18n:extract → файл POT |
| Аудит | отчёт о непереведённых ключах |
| Каталог | PO, MO, JSON или PHP — на выбор проекта |
JSON и PHP-каталоги здесь не экзотика: они удобны, когда переводы правят не переводчики с Poedit, а разработчики в репозитории — diff читаемый, конфликтов меньше.
WordPress-мост
В комплекте — необязательный мост для WordPress: адаптер кэша на транзиентах, логгер, загрузчик шаблонов, шорткоды, страница настроек, CLI и почтовый адаптер. Плюс набор обслуживания.
| Что даёт | Зачем нужно |
|---|---|
| Кэш на транзиентах | не заводить отдельный каталог кэша на хостинге |
| Загрузчик шаблонов | темы и плагины находят шаблоны в своих каталогах |
| Шорткоды | вывод шаблона из содержимого записи |
processSsi для контента |
SSI-директивы прямо в записях |
| CLI-команды | обслуживание без админки |
Для сайта на WordPress это тот случай, когда библиотека не спорит с платформой, а встаёт рядом: свои кэши, свои логи, свои шаблоны.
Модель безопасности: что именно проверяется тестом
Про «безопасный шаблонизатор» обычно говорят в маркетинговом смысле. Здесь это проверяемое утверждение, и его стоит разобрать по частям.
| Утверждение | Как проверяется |
|---|---|
Ядро не вызывает eval() |
тест на уровне токенов сканирует исходники |
Ядро не читает $GLOBALS и суперглобальные |
то же сканирование |
| Вывод экранируется по умолчанию | syntax.auto_escape: true, проверено прогоном |
| Выражения не дают новый вектор | переиспользуется тот же вычислитель, что с версии 4 |
| Динамические ключи не доходят до объектов | ключи ограничены массивами и свойствами данных |
get() резолвится только по стеку областей |
не выходит за пределы переданных данных |
Арифметика без INF и NAN |
результат ограничен, деление на ноль не даёт бесконечности |
| Нет исходящих запросов | сетевого слоя нет; SSI-подзапросы -A/-U отклоняются с диагностикой |
Два пункта из этого списка важны на практике. Первый — про выражения: арифметика в подстановках появилась в 5.0 и могла бы стать дырой, если бы её сделали через eval; здесь её отдали тому же песочничному вычислителю, что и условия в шаблонах, с белым списком функций. Второй — про ограничения итераций: на каждом обходе хостового итерируемого объекта стоит бюджет в 100 000 шагов, а кэш разобранных выражений ограничен 4096 записями в порядке FIFO. Иначе шаблон с большими данными превращается в способ съесть память.
Отдельно — #exec. В Apache эта директива выполняется процессом сервера, и её ограничивают настройками mod_include. Здесь политика явная и по умолчанию deny: пока её не включишь, #exec в шаблоне ничего не делает. Это правильный порядок вещей — сначала безопасно, потом удобно.
Диагностика и обслуживание
| Инструмент | Что даёт |
|---|---|
$engine->stats() |
время, число подстановок, циклов, попаданий в кэш |
lintDirectory() |
проверка шаблонов в CI тем же разборщиком, что и рендер |
| Сборщик диагностик | структурированные сообщения с устойчивыми кодами с 3.x |
bin/htmlphp |
CLI: версия, сборка, проверка каталога, извлечение строк |
tools/build.php --check |
расхождение src/ и файлов сборки |
Самое полезное здесь — lintDirectory(). Опечатка в шаблоне обычно обнаруживается в момент, когда страницу открыл читатель; линтер переносит эту ошибку в сборку. Второе по полезности — stats(): он показывает попадания в кэш и число подстановок, то есть отвечает на вопрос «почему эта страница рендерится 80 мс», не требуя профайлера.
| Что смотреть | На что указывает |
|---|---|
cacheHit низкий |
включённый кэш работает впустую: шаблон меняется часто |
substitutions растёт, durationMs тоже |
тяжёлые фильтры или большие циклы |
loops десятки на страницу |
шаблон собирает данные, которые должен был собрать контроллер |
Сравнение с альтернативами
| Вариант | Плюсы | Минусы |
|---|---|---|
| Twig | зрелость, экосистема, строгий синтаксис | зависимость, свой кэш, отдельный язык шаблонов |
| Blade | привычен для Laravel | вне Laravel требует обвязки |
| Чистый PHP в шаблонах | ничего не ставить | экранирование вручную, дисциплина на совести разработчика |
| HTML.PHP | один файл, автэкранирование, SSI и i18n рядом | свой синтаксис, экосистема маленькая |
Выбор честно сводится к вопросу «нужен ли отдельный слой между контроллером и HTML». Если команда уже умеет Twig — менять смысла мало. Если проект небольшой, а зависимости нежелательны, один файл решает задачу без инфраструктуры.
Подводные камни
- Версия файла важна.
php85/html.phpтребует PHP 8.5 и откажется работать на 8.3; универсальный вариант —php83/html.php. - Смена мажорной версии ничего не ломает, но проверять надо. 6.0.0 — это 5.0.0 без изменений в движке; обновление сводится к замене файла и сверке контрольной суммы.
- Кэш рендера по умолчанию выключен. Включённый кэш с файловым драйвером требует записываемого каталога и, при
cache.harden_directory, кладёт рядом.htaccessиindex.html. #execпо умолчанию запрещён. Это не недоработка: политикаdeny— сознательное решение. Включать её стоит только осознанно и с пониманием, кто пишет шаблоны.- Шаблоны, редактируемые пользователями, требуют урезанного режима.
syntax.allow_raw_filter: falseиsyntax.expressions: false— не рекомендация, а необходимость. - Опечатка в конфигурации падает исключением. Это плюс, но при миграции со старой версии вылеты будут, пока ключи не приведены в порядок.
bin/htmlphpв чекауте подключаетsrc/, а не файл сборки. Баннер версии напишет8.3 editionдаже на PHP 8.5 — это не ошибка.- Bundles нужно пересобирать. Если правится
src/, файлы сборки отстают; в CI за этим следитtools/build.php --check. - Мимо каталога Packagist. Наличие пакета в репозитории не подтверждено, а команды
composer requireвзяты из README. - Один файл — не значит «ничего не читать». 16 тысяч строк в одном файле удобно подключать, но неудобно ревьюить в диффе.
Чек-лист
- [ ] Проверить версию PHP: 8.3+ для универсального файла, 8.5 для сборки 8.5.
- [ ] Скачать файл релиза и сверить SHA-256 с заметками к релизу.
- [ ] Запустить однострочную проверку экранирования.
- [ ] Задать
templates.pathsи путь к каталогам перевода. - [ ] Решить, нужен ли кэш рендера, и если да — подготовить каталог.
- [ ] Выключить
syntax.expressionsиallow_raw_filter, если шаблоны правят пользователи. - [ ] Проверить, что
ssi.exec_modeосталсяdeny. - [ ] Прогнать линтер шаблонов в CI:
lintDirectory(). - [ ] Записать версию и хеш файла в паспорт проекта.
FAQ
Нужен ли Composer?
Нет. Основной способ — скачать один файл и подключить его через require. Composer — вариант для проектов, где уже есть автозагрузка PSR-4.
Чем php83/html.php отличается от php85/html.php?
Первый работает на PHP 8.3, 8.4 и 8.5, второй требует 8.5 и использует его возможности. Размер отличается на 1 822 байта, набор возможностей — нет.
Это замена Twig?
Не замена, а альтернатива для случая «нужен минимальный слой без зависимостей». Twig даёт больше, но требует композера, своего кэша и отдельного языка шаблонов.
Безопасно ли отдавать шаблоны пользователям?
Только с syntax.allow_raw_filter: false и syntax.expressions: false. Ядро не вызывает eval() и не читает суперглобальные массивы, но выражения и сырой фильтр — это ровно те возможности, которые не нужны пользовательским шаблонам.
Что даёт версия 6.0.0, если движок не менялся?
Лицензионную ясность: MIT прописан в LICENSE, корневом composer.json, манифестах редакций, шапке плагина и всех README, а тест 16_release_contract падает, если они разойдутся.
Можно ли применить SSI к содержимому из базы?
Да, для этого есть processSsi($content). Директивы разбираются в PHP, поэтому веб-сервер роли не играет.
Итог
HTML.PHP 6.0.0 — библиотека с ясной границей: шаблоны, SSI и локализация в одном файле, без зависимостей и без выхода в сеть. Проверка на живом PHP 8.5 подтвердила главное, что здесь можно проверить быстро: оба файла релиза совпадают по контрольным суммам с заявленными, автэкранирование работает по умолчанию, арифметика в подстановках считает, фильтр чисел уважает локаль, а циклы и условия выдают ожидаемое. Ограничения тоже честные: свой синтаксис, маленькая экосистема, 16 тысяч строк в одном файле и набор директив SSI, который надо включать с головой.
Цитата: Хороший шаблонизатор не тот, который умеет больше, а тот, после которого не приходится объяснять, почему в HTML приехал пользовательский тег.
Связанные материалы
- MD Viewer: просмотрщик Markdown без браузерного движка — ещё один продукт, где отказ от внешнего движка дал свои плюсы и свои ограничения.
- Multilingual Engine: многоязычный WordPress без WPML и Polylang — второй подход к локализации, на стороне данных.
- PHP Shield: файрвол для PHP — защита приложения на уровне запросов.
- Bash WP-CLI Update 8.1.0 — обслуживание парка WordPress, где такие библиотеки часто и живут.
- github-agent 1.3.0 — проверка репозиториев и документации перед публикацией.
LLM Summary
- Тема:
HTML.PHP6.0.0 — библиотека обработки шаблонов для PHP: шаблоны, Apache SSI и gettext-совместимая локализация без зависимостей. - Лицензия: MIT, прописана в
LICENSE, корневомcomposer.json, манифестах, шапке плагина WordPress и всех шести README; расхождение ловитtests/cases/16_release_contract.test.php. - Движок: без изменений относительно 5.0.0 — ни одна конструкция шаблонов, ключ конфигурации, фильтр, команда CLI или метод API не менялись; переход — замена файла.
- Файлы релиза (замерено 09.11.2026):
html-php-6.0.0-php83.php530 172 байта, SHA-2567df928018f8f43f7e9d402e7a5ff40fed73f490f549b5a69e8e07afa1f1477b6;html-php-6.0.0-php85.php531 994 байта, SHA-25619c4e456842d9a34ab70e70a71b4dddd19b8449d459daa288a4c18f6da4d0274; оба совпали с заметками к релизу. - Прогон на PHP 8.5.0: экранирование
Hello %{name}→Hello <World>;%{= done / total * 100 | round:1 }%→14.8%;%{price|number:2}при локалиru→1 234,50;_positionв цикле;@If,@Set, диапазон(1..3)[1];t('{0} files', [42])→42 files. - Безопасность: ядро не вызывает
eval()и не читает$GLOBALSи суперглобальные массивы (проверяется тестом на уровне токенов);syntax.auto_escape: trueиssi.exec_mode: denyпо умолчанию; для пользовательских шаблонов —syntax.allow_raw_filter: false,syntax.expressions: false. - Тесты по бейджу: 391 тест, 389 пройдено, 2 пропущено; PHPStan уровень 6; php-cs-fixer чист на 143 файлах.
- Чего нет: нет сети и телеметрии, нет записи на диск без явной настройки, нет собственного кэша по умолчанию (
cache.enabled: false). - Не проверялось: производительность под нагрузкой, работа
#execи#block, поведение WordPress-моста в живом сайте. - Ссылки: https://github.com/paulmann/HTML.PHP, https://github.com/paulmann/HTML.PHP/releases/tag/v6.0.0
Промт для агента (установка и настройка)
Готовый промт: скопируйте блок целиком. Шаги и команды взяты из AGENTS.md репозитория (он есть, проверено HTTP 200) и из заметок к релизу 6.0.0.
Задача: поставить HTML.PHP 6.0.0 в PHP-проект, включить экранирование и кэш, проверить работу шаблонов и локализации.
Материалы:
- репозиторий: https://github.com/paulmann/HTML.PHP
- AGENTS.md: https://github.com/paulmann/HTML.PHP/blob/main/AGENTS.md
- README: https://github.com/paulmann/HTML.PHP/blob/main/README.md
- релиз: https://github.com/paulmann/HTML.PHP/releases/tag/v6.0.0
Шаг 1. Проверить предусловия: php -v (нужно 8.3+; для сборки 8.5 — 8.5+), php -m
Шаг 2. Скачать файл релиза и сверить контрольную сумму:
curl -fsSL -o html.php https://github.com/paulmann/HTML.PHP/releases/download/v6.0.0/html-php-6.0.0-php83.php
sha256sum html.php # 7df928018f8f43f7e9d402e7a5ff40fed73f490f549b5a69e8e07afa1f1477b6
Шаг 3. Проверить, что библиотека загружается и экранирует вывод:
php -r "require 'html.php'; echo HtmlPhp\Engine::create()->renderString('Hello %{name}!', ['name' => '<World>']), PHP_EOL;"
Ожидается: Hello <World>!
Шаг 4. Создать конфигурацию (массив в Engine::create или файл для CLI, который возвращает массив):
templates.paths, i18n.locale и i18n.paths, cache.enabled и cache.directory
Шаг 5. Убедиться, что защита на месте: syntax.auto_escape = true, ssi.exec_mode = deny; для шаблонов, которые правят пользователи, добавить syntax.allow_raw_filter = false и syntax.expressions = false
Шаг 6. Прогнать линтер шаблонов в CI: $engine->lintDirectory(<каталог шаблонов>)
Шаг 7. Снять stats() после первых запросов и записать значения для сравнения в будущем
Запрещено:
- включать ssi.exec_mode = allow без явного решения владельца проекта;
- подключать php85/html.php на PHP ниже 8.5: файл откажется работать;
- полагаться на старый файл 4.x без чтения docs/MIGRATION-4.x-TO-5.0.md;
- считать, что composer require paulmann/html-php работает всегда: наличие пакета на Packagist в репозитории не подтверждено, проверять;
- править src/ без пересборки файлов: расхождение ловит tools/build.php --check.
Что считать успехом: однострочная проверка выводит Hello <World>!, шаблон с циклом и условием рендерится, перевод через t() работает, stats() отдаёт значения, линтер шаблонов чист.