Функция 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/Apachegzip) или с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.