W3docs

Функция PHP ob_gzhandler(): всё, что нужно знать

Функция ob_gzhandler() в PHP позволяет сжимать вывод скрипта с помощью gzip, снижая трафик и ускоряя загрузку страниц.

Сжатие HTML, CSS или JSON перед отправкой с сервера снижает потребление трафика и ускоряет загрузку страниц. Встроенная функция PHP ob_gzhandler() — готовый способ сделать это прямо в скрипте: вы передаёте её в буфер вывода, и она gzip-сжимает всё, что выводит скрипт, — но только если браузер сообщает, что умеет распаковывать данные. В этой статье рассматриваются синтаксис, полный пример, принцип согласования с клиентом, типичные ошибки и ситуации, когда стоит использовать функцию, а когда лучше доверить сжатие серверу.

Что делает функция ob_gzhandler()

ob_gzhandler() — это callback-функция, предназначенная для передачи в ob_start(). Вы никогда не вызываете её напрямую — система буферизации вывода вызывает её сама, передавая буферизованный контент в качестве аргумента, и функция возвращает сжатые (или, если сжатие невозможно, неизменённые) байты.

Перед сжатием функция проверяет заголовок Accept-Encoding запроса и выбирает наиболее подходящую схему:

  • Если клиент поддерживает gzip, данные сжимаются с помощью gzip и устанавливается заголовок Content-Encoding: gzip.
  • Если клиент поддерживает только deflate, используется deflate.
  • Если клиент не поддерживает ни один из форматов, контент возвращается без изменений, а ob_start() завершается с ошибкой (возвращает false), и ответ отправляется без сжатия.

Поскольку функция автоматически устанавливает заголовки Content-Encoding и Vary, её необходимо зарегистрировать до отправки любого вывода — если вы столкнулись с ошибкой «headers already sent», см. headers_sent().

Синтаксис

ob_start("ob_gzhandler");

Внутри ob_gzhandler() принимает два параметра ($buffer и $mode), но вы их не передаёте — это делает движок буферизации. Вы просто регистрируете строку "ob_gzhandler" как имя callback-функции.

Полный пример

<?php

ob_start("ob_gzhandler");
echo "This will be compressed using gzip compression";
ob_end_flush();
?>

Здесь ob_start() открывает буфер вывода с ob_gzhandler() в качестве обработчика, echo записывает данные в этот буфер вместо прямой отправки клиенту, а ob_end_flush() закрывает буфер и отправляет его (теперь сжатое) содержимое. Для посетителя ничего не меняется — браузер прозрачно распаковывает ответ, — однако по сети передаётся меньше байт.

Запасной вариант для клиентов без поддержки gzip

ob_start("ob_gzhandler") возвращает false, если клиент не объявляет поддержку gzip или deflate. Если проигнорировать это, буфер не будет создан, и последующий вызов ob_end_flush() выдаст предупреждение. Проверяйте возвращаемое значение и используйте обычный буфер как запасной вариант:

<?php
if (!ob_start("ob_gzhandler")) {
    ob_start(); // plain buffer, no compression
}
echo "Served either compressed or uncompressed, but always buffered.";
ob_end_flush();
?>

Настройка уровня сжатия

ob_start() не принимает уровень сжатия — ob_gzhandler() использует значение zlib по умолчанию (определяется параметром INI zlib.output_compression_level, значение по умолчанию -1). Чтобы задать конкретный уровень от 1 (быстрейший, минимальное сжатие) до 9 (медленнейший, максимальное сжатие), откажитесь от ob_gzhandler() и используйте собственный callback с gzencode():

<?php
ob_start(function ($buffer) {
    return gzencode($buffer, 9);
});
echo "Compressed at the maximum level.";
ob_end_flush();
?>

Обратите внимание: этот пользовательский callback не проверяет Accept-Encoding и не устанавливает заголовок Content-Encoding: gzip автоматически — ob_gzhandler() делает всё это за вас. Если вы пишете собственный обработчик, заголовки придётся отправлять самостоятельно, именно поэтому ob_gzhandler() остаётся удобным решением для большинства случаев.

ob_gzhandler() и zlib.output_compression

В PHP есть второй, ещё более простой способ сжать вывод: директива INI zlib.output_compression. Установите её в On (или задайте пороговое значение в байтах), и PHP будет gzip-сжимать весь ответ без какого-либо кода:

zlib.output_compression = On

Оба подхода несовместимы — если zlib.output_compression включена и при этом вызывается ob_start("ob_gzhandler"), возникнет предупреждение, а двойное сжатие никакого смысла не даст. Предпочитайте zlib.output_compression, если у вас есть доступ к php.ini или можно использовать ini_set(), и прибегайте к ob_gzhandler() в тех случаях, когда нужно управлять сжатием внутри скрипта без доступа к конфигурации.

Типичные ошибки

  • Не создавайте вложенные уровни gzip. Совместное использование ob_gzhandler() со сжатием на уровне сервера (Nginx/Apache gzip) или с zlib.output_compression может привести к повреждённым, дважды закодированным ответам.
  • Регистрируйте обработчик первым. Любой предшествующий echo, пробел перед <?php или BOM в файле отправит заголовки раньше времени и нарушит сжатие.
  • Не используйте для уже сжатых бинарных данных. Gzip-сжатие JPEG, PNG или ZIP-файлов тратит процессорное время практически без выигрыша в размере.
  • Требуется расширение zlib. ob_gzhandler() требует, чтобы PHP был собран с поддержкой zlib (в большинстве случаев так и есть, но стоит учитывать на минимальных сборках).

Заключение

Функция ob_gzhandler() предоставляет простой самодостаточный способ gzip-сжатия вывода PHP: зарегистрируйте её как callback в ob_start(), и она сама согласует кодирование и установит заголовки. Однако в современных конфигурациях, как правило, предпочтительнее сжатие на уровне сервера (Nginx, Apache) или CDN с поддержкой gzip/brotli — это снимает нагрузку с PHP и охватывает также статические ресурсы. Знание ob_gzhandler() по-прежнему важно для устаревших кодовых баз и скриптов, где изменить конфигурацию сервера невозможно. Общее представление о работе с буферами даёт обзор PHP Output Control.

Практика

Практика
Какова цель ob_gzhandler в PHP?
Какова цель ob_gzhandler в PHP?
Was this page helpful?