W3docs

Функции управления выводом 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() возвращает буфер и останавливает буферизацию за один вызов:

php— editable, runs on the server

Ничего не попадает в браузер до финального 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(), чтобы отправить всё сразу:

php— editable, runs on the server

Пока буфер открыт, можно также проверять его с помощью 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.

Практика

Практика
Что из перечисленного верно в отношении управления выводом PHP?
Что из перечисленного верно в отношении управления выводом PHP?
Was this page helpful?