Функции управления выводом PHP: всё, что нужно знать
Буферизация вывода PHP и функции ob_*: ob_start, ob_get_clean, ob_end_flush, коллбэки и типичные ошибки — с примерами.
В обычном режиме каждый echo, print или фрагмент HTML в PHP-скрипте отправляется в браузер в момент выполнения. Буферизация вывода позволяет перехватить этот вывод и удерживать его в памяти (буфере), чтобы впоследствии проверить, изменить, отбросить или отправить его. Функции управления выводом PHP — это встроенные инструменты для работы с этим буфером.
В этой главе объясняется, что такое буферизация вывода, какие реальные задачи она решает и как работает каждая функция ob_* — с запускаемыми примерами.
Зачем нужна буферизация вывода
Буферизация вывода — не просто любопытная возможность. Она решает ряд повседневных задач PHP:
- Избавление от ошибок «headers already sent». В PHP функции
header()иsetcookie()должны вызываться до того, как какой-либо вывод достигнет браузера. Буферизация позволяет отправлять заголовки даже после выполнения шаблонов, поскольку ничего ещё не было сброшено. См. headers_sent(). - Захват вывода в строку. Можно отрендерить шаблон или подключить файл и сохранить результат в переменную, не выводя его на страницу — именно на этом основаны большинство простых шаблонизаторов.
- Постобработка всей страницы. Минификация HTML, замена плейсхолдеров или сжатие вывода (gzip) в одном месте перед отправкой.
- Отбрасывание нежелательного вывода. Избавление от шума, порождаемого сторонней библиотекой или отладочным оператором, который пользователь не должен видеть.
Функции управления выводом
Ниже перечислены наиболее часто используемые функции. Все они работают с буфером, запущенным с помощью ob_start().
| Функция | Что делает |
|---|---|
ob_start() | Запускает новый буфер вывода. Захват начинается с этого момента. |
ob_get_contents() | Возвращает текущее содержимое буфера без остановки буферизации. |
ob_get_length() | Возвращает количество байт, находящихся в буфере. |
ob_get_level() | Возвращает уровень вложенности (количество активных буферов). |
ob_clean() | Очищает буфер, не останавливая буферизацию. |
ob_get_clean() | Возвращает содержимое и отключает буфер — распространённая комбинация. |
ob_end_clean() | Отбрасывает буфер и отключает буферизацию (ничего не возвращает). |
ob_flush() / ob_end_flush() | Отправляет буфер в браузер; ob_end_flush() также останавливает буферизацию. |
Захват вывода в строку
Наиболее распространённое применение буферизации — перехват всего, что выводит блок кода, и сохранение этого в переменную. ob_get_clean() возвращает буфер и останавливает буферизацию за один вызов:
Ничего не попадает в браузер до финального echo, которое выводит HELLO, WORLD!. Мы захватили текст, преобразовали его и только потом отправили. Именно так работает простейшая шаблонная функция:
<?php
function renderTemplate(string $name): string {
ob_start();
echo "Hello, $name!";
return ob_get_clean(); // contents + stop buffering
}
echo renderTemplate("Ada");Выводит Hello, Ada!. В реальном проекте буферизуемый блок представлял бы собой полноценный HTML-шаблон с встроенными тегами <?= $name ?>.
Сброс буфера в браузер
Если нужно лишь отложить вывод (не захватывать его), используйте ob_end_flush(), чтобы отправить всё сразу:
Пока буфер открыт, можно также проверять его с помощью ob_get_length() и ob_get_level():
<?php
ob_start();
echo "buffered text";
echo "\nLevel: " . ob_get_level(); // 1 — one buffer is active
echo "\nLength: " . ob_get_length(); // bytes captured so far
ob_end_flush();ob_get_level() возвращает 1, поскольку активен один буфер; если вызвать ob_start() внутри него ещё раз, уровень станет 2 (буферы вкладываются как стек).
Преобразование вывода с помощью коллбэка
ob_start() принимает коллбэк, который получает весь буфер и возвращает изменённую версию. Именно так работают минификаторы вывода и фильтры поиска-замены:
<?php
ob_start(function (string $buffer): string {
return str_replace("cat", "dog", $buffer);
});
echo "I have a cat.";
ob_end_flush();Коллбэк срабатывает при сбросе буфера, поэтому страница выводит I have a dog.. Тот же паттерн лежит в основе ob_gzhandler — встроенного PHP-коллбэка для gzip-сжатия вывода.
Типичные ошибки
- Всегда закрывайте то, что открыли. Каждый
ob_start()должен быть завершён вызовом flush или clean. Незакрытый буфер удерживает вывод в памяти, и он никогда не дойдёт до пользователя. - Буферы вкладываются. Каждый
ob_start()увеличивает уровень вложенности.ob_get_clean()закрывает только самый внутренний буфер; используйтеob_get_level()в цикле, если нужно размотать несколько уровней. ob_get_contents()не останавливает буферизацию — это делают только функции*_cleanи*_end_*. Путаница между ними — частая причина дублирующегося вывода.- Буфер имеет конечный размер. По умолчанию PHP автоматически сбрасывает буфер, когда его объём достигает значения
output_bufferingбайт (php.ini), поэтому не рассчитывайте на хранение неограниченного объёма данных.
Заключение
Буферизация вывода даёт контроль над тем, когда и как отправляется вывод скрипта. Используйте её для захвата отрендеренного содержимого в строку, установки заголовков после генерации страницы, отбрасывания нежелательного вывода или постобработки всего ответа в одном месте. Как только вы поймёте цикл start/get/clean/flush, семейство функций ob_* окажется небольшим и предсказуемым.
Дополнительное чтение: echo and print, PHP header(), PHP Sessions и PHP Cookies.