pbFenom
Шаблоны рендерит pbFenom — наш форк Fenom, подключённый как пакет Composer pageblocks/fenom. Это обязательная зависимость, а не опция: View типизирован по \pbFenom и поднимает движок через \pbFenom::factory().
"require": {
"pageblocks/fenom": "^1.0"
}Сам язык шаблонов не менялся. Всё, что вы знаете про синтаксис Fenom — {if}, {foreach}, модификаторы, {extends}, {include} — работает так же. Поменялось то, что под ним.
Заменить оригиналом нельзя
Форк живёт в своём пространстве имён: класс называется pbFenom, интерфейсы — pbFenom\ProviderInterface и прочие. Подставить fenom/fenom не выйдет, и поставить оба в надежде, что это один движок, — тоже.
Изоляция здесь и есть смысл: компонент фиксирует поведение, на которое опирается, вместо того чтобы следовать за меняющимся upstream.
Зачем форк
eval() больше нет
Upstream в нескольких режимах рендерил через eval() — а FORCE_COMPILE вообще писал скомпилированный файл на диск и потом его игнорировал. Форк подключает тот артефакт, который только что записал, поэтому opcache может держать опкоды; два режима, у которых файла нет по замыслу, рендерятся через приватную обёртку потока.
Практический эффект: ошибка времени выполнения называет шаблон, а не сообщает eval()'d code on line N.
Модификаторы снова принимают null
В upstream 3.0.0 модификаторам добавили типы параметров, и обычная пустая переменная шаблона стала фаталом:
{$x} {* нормально - htmlspecialchars(null) только предупреждает *}
{$x|escape} {* upstream 3.0.0: фатал *}В CMS пустое поле — норма, а не исключение. escape, unescape, truncate, strip, replace, ereplace, match, ematch и date считают null пустой строкой, как это было до 3.0.0.
Рекурсия ограничена
Циклический {include} раньше уходил в рекурсию, пока PHP не исчерпает стек вызовов, а циклический {insert} — пока не кончится память. И то и другое фатал, то есть непойманное: посетитель получал пустой 500, а в логе не было ничего полезного.
Теперь оба поднимают обычное исключение за миллисекунды и называют шаблон. Предел — pbFenom::$max_template_depth (32). Цена на горячем пути: 1,8% на цикле из тысячи {include}.
Вложенные ошибки рендера больше не заворачиваются повторно на каждом уровне — сообщения раньше читались как «unhandled exception in a: unhandled exception in b: ...».
addFunction() сохраняет свой контракт
Колбэк получает ($params, $tpl, $var), как и всегда. Если хочется, чтобы сигнатура самого колбэка стала API шаблона, регистрируйте через addFunctionSmart() — который в форке наконец принимает замыкания, массивы-колбэки и объекты с __invoke, а не только строки.
Пропущенный обязательный аргумент smart-функции теперь сообщается на этапе компиляции («Function excerpt requires the 'text' argument»), а не взрывается посреди рендера.
Подпись кэша учитывает конфигурацию
Регистрация функций не сбрасывала подпись кэша компиляции, поэтому два экземпляра движка, отличающиеся только зарегистрированной функцией, могли подсовывать друг другу чужие артефакты. Теперь подпись это покрывает, плюс появилась константа CACHE_FORMAT, чтобы артефакты от старого генератора кода не переиспользовались.
Строгие типы
Во всех 17 файлах исходников объявлен strict_types=1, и это вскрыло четыре скрытых бага, которые слабый режим тихо замазывал. Среди них — getProvider(), получавший булево значение при каждой загрузке шаблона: strstr() возвращает false, а не null, когда в имени шаблона нет схемы.
Скомпилированные артефакты строгие типы намеренно не объявляют: данные шаблона произвольны, и приведение целого, попавшего в модификатор со строковым типом, — это документированное поведение.
Удалено
auto_trim / pbFenom::AUTO_TRIM — зарезервирован и бездействовал с 2013 года. Теперь его передача бросает Undefined option 'auto_trim', а не принимается молча и игнорируется. {autotrim} и опции тегов :trim / :ltrim / :rtrim были задокументированы десять лет и никогда не реализованы; их страницу убрали.
Версия
pbFenom::VERSION — 3.1, по поколению upstream, от которого форк отошёл. Версия пакета считается отдельно (pageblocks/fenom 1.x).
Где настраивается
Движок поднимается в View::init(): каталог кэша, auto_reload из настройки pageblocks_fenom_auto_reload и провайдеры шаблонов — см. View.