Событие OnEndBufferContent перехватывает полностью собранный HTML страницы прямо перед отправкой браузеру. Это последний шанс глобально изменить вывод: вставить скрипты, заменить плейсхолдеры или почистить разметку без правки шаблонов и компонентов.
Что делает OnEndBufferContent
Событие относится к модулю main и вызывается на завершающем этапе работы буфера вывода, когда пролог, рабочая область и эпилог уже собраны в единую строку. Обработчик получает весь HTML страницы по ссылке и может изменить его перед отдачей клиенту.
В отличие от точечных событий компонентов, OnEndBufferContent работает с финальным результатом целиком. Это удобно для сквозных задач:
- вставка счётчиков, пикселей и системных скриптов перед
</body>; - замена глобальных плейсхолдеров вида
#PHONE#на актуальные значения; - удаление HTML-комментариев и лишних пробелов (лёгкая минификация);
- добавление или правка мета-тегов в
<head>.
Регистрация обработчика
Подключаем обработчик в /local/php_interface/init.php. Событие «старого» стиля — контент передаётся по ссылке:
use Bitrix\Main\EventManager;
EventManager::getInstance()->addEventHandler(
'main',
'OnEndBufferContent',
['\Vendor\Front\BufferHandler', 'onEndBuffer']
);
Сигнатура обработчика проста — один аргумент со всем HTML:
public static function onEndBuffer(&$content)
{
if (!is_string($content) || $content === '') { return; }
$content = str_replace('#SUPPORT_PHONE#', '8 800 000-00-00', $content);
}
Вставка скриптов и мета-тегов
Классический приём — добавить блок кода перед закрывающим тегом. Чтобы не сломать вёрстку, ищите последнее вхождение тега:
$snippet = '<script>/* аналитика */</script>';
$pos = strripos($content, '</body>');
if ($pos !== false) {
$content = substr($content, 0, $pos)
. $snippet
. substr($content, $pos);
}
</body> надёжна, но попытки регуляркой «понять» структуру документа хрупки. Для системных скриптов правильнее использовать API — Asset::getInstance()->addString() в прологе, а OnEndBufferContent оставить для того, что иначе не сделать.Помните: событие затрагивает и служебные ответы. Проверяйте контекст, чтобы не вставить скрипт в JSON-ответ AJAX-контроллера или в файл, отдаваемый под видом страницы.
Замена плейсхолдеров и отложенных функций
OnEndBufferContent часто используют вместе с механизмом отложенных функций Битрикса. Значения, которые становятся известны только в конце страницы (например, набор товаров в «просмотренных»), помечают плейсхолдером в прологе, а подставляют в этом событии.
Пример замены нескольких меток за один проход:
$replace = [
'#CITY_NAME#' => $cityName,
'#CART_COUNT#' => (string)$cartCount,
];
$content = strtr($content, $replace);
Функция strtr() с массивом заменяет все метки за один проход и работает быстрее череды str_replace(). Метки выбирайте маловероятные в обычном тексте, чтобы избежать случайных замен.
Кэширование и композитный сайт
Взаимодействие с кэшем — самый тонкий момент. OnEndBufferContent срабатывает после кэша страницы, но нужно учитывать композитный режим:
| Режим | Поведение |
|---|---|
| Обычная страница | Событие срабатывает на каждый хит |
| Композит (первый показ) | Обрабатывается полный HTML, затем сохраняется в композит |
| Композит (из кэша) | Статика может отдаваться минуя PHP — событие не вызовется |
Производительность и предостережения
Поскольку обработчик выполняется на каждый хит и работает со всей строкой HTML, следуйте нескольким правилам:
- минимизируйте число проходов по строке — объединяйте замены в один
strtr()илиpreg_replace_callback(); - раньше выходите из обработчика, если контент не подлежит обработке (пустой, AJAX, служебный);
- не выполняйте запросов к БД и внешним сервисам внутри события — все данные готовьте заранее;
- тестируйте на страницах с большим DOM: тяжёлые регулярки на длинном HTML заметно бьют по TTFB.
Если задача — только зарегистрировать ресурсы (CSS/JS), почти всегда правильнее \Bitrix\Main\Page\Asset, а OnEndBufferContent приберечь для случаев, когда нужно вмешаться в уже готовый HTML. Отдельно отметьте порядок обработчиков: если на событие подписано несколько классов, каждый получает результат правок предыдущего, поэтому важен параметр сортировки при регистрации. И не забывайте про кодировку — работайте со строкой в той же кодировке, что и сайт (обычно UTF-8), иначе функции поиска подстроки могут разрезать многобайтовые символы и сломать вывод.
Итог
OnEndBufferContent даёт полный контроль над итоговым HTML: глобальные замены, вставка скриптов и лёгкая минификация без правки шаблонов. Взамен требует аккуратности — быстрая логика, один проход по строке, учёт композита и служебных ответов. Для регистрации ресурсов предпочтителен Asset, а событие — для того, что иначе не сделать.
Когда фронтенд обрастает сквозными правилами вывода, персонализацией и требованиями к скорости, мы в B2Bsite выстраиваем это как продуманную архитектуру с учётом кэша и композита, чтобы производительность не страдала от «магии» в буфере.
Частые вопросы
Что получает обработчик OnEndBufferContent?
Единственный аргумент — полностью собранный HTML страницы, переданный по ссылке. Изменения строки $content напрямую попадают в ответ браузеру.
Чем OnEndBufferContent лучше правки шаблона?
Оно работает глобально для всех страниц и применяется к финальному HTML целиком. Это удобно для сквозных задач, но не заменяет точечные правки, которые логичнее делать в шаблоне или компоненте.
Можно ли вставлять скрипты через это событие?
Да, обычно перед закрывающим тегом body через поиск позиции и вставку. Но для регистрации системных CSS/JS правильнее использовать Asset::getInstance()->addString в прологе.
Почему событие не срабатывает на композитном сайте?
При отдаче страницы из композитного кэша статика может возвращаться минуя PHP, поэтому обработчик не вызывается. Динамику нужно выводить через штатные динамические области композита.
Безопасно ли подставлять персональные данные глобальной заменой?
Нет, при включённом кэше или композите HTML одинаков для всех пользователей. Персонализацию выводите динамическими областями, а не заменой в буфере.
Как ускорить обработчик?
Делайте один проход по строке через strtr с массивом замен, рано выходите для неподходящего контента и не обращайтесь к БД внутри события — данные готовьте заранее.
Событие затрагивает AJAX-ответы?
Оно может срабатывать и для служебных ответов, идущих через буфер. Проверяйте контекст, чтобы не испортить JSON или файл, случайно вставив в него HTML.
Чем заменять несколько плейсхолдеров за раз?
Функцией strtr с массивом соответствий: она выполняет все замены за один проход и работает быстрее последовательных вызовов str_replace.
Поможем с настройкой и поддержкой Для разработчиков
Поможем с настройкой, доработкой и поддержкой Для разработчиков.