GZip-сжатие

PHP 11.10.21 11.10.21 262
Бесплатные курсына главную сниппетов

Еще одно применение обработчиков — включение GZip-сжатия страниц. Все современные браузеры поддерживают технологию упакованного контента, позволяющую серьезно ускорить загрузку страниц по сети. Как она работает? Все довольно просто:

1. Сервер генерирует некоторый HTML-код страницы, а затем архивирует его стандартным методом GZip.
2. Запакованные данные пересылаются по сети. Естественно, они занимают гораздо меньше места, чем текст неупакованный, а потому налицо экономия.
3. Браузер принимает запакованные данные и по наличию специальных заголовков ответа определяет метод архивации. Затем он распаковывает информацию и отображает страницу в исходном виде.

Как видите, обработчики буфера вывода подходят для реализации этой технологии как нельзя лучше. Действительно, логично поручить архивирование текста страницы как раз такому обработчику.

Он должен:

К счастью, в PHP существует встроенный обработчик, занимающийся как раз GZipсжатием. Его имя — ob_gzhandler(). Итак, достаточно в начале любого скрипта написать всего лишь одну строчку:

ob_start("ob_gzhandler", 9);

и весь текст, который данный скрипт будет генерировать, автоматически отправится в браузер упакованным. Мы указываем последним параметром цифру 9, чтобы сообщить обработчику: необходимо использовать девятый, самый эффективный, уровень компрессии.

ПРИМЕЧАНИЕ
Насколько же эффективно на практике GZip-сжатие? Оказывается, очень эффективно. В большинстве случаев страницы уменьшаются в размере в 4–5 раз, и это далеко не предел! Итак, даже большая страница объемом 100 Кбайт вполне может превратиться в блок данных размером всего 20 Кбайт, который даже по модему загружается очень быстро.

Печать эффективности сжатия

При перехвате выходного потока вы можете указать не один, а сразу несколько обработчиков. Для этого необходимо соответствующее число раз вызвать ob_start(). При этом данные будут передаваться от одного обработчика к другому, как по конвейеру: вначале будет запущена функция, имеющая наибольшую вложенность, затем — поменьше, и так до самой первой.

Технику конвейеризации обработчиков можно применять, если вы хотите вывести на странице, насколько эффективным оказалось GZip-сжатие. Давайте будем отображать две цифры: первая показывает, сколько страница занимала до архивации, а вторая — сколько она занимает после. Главная проблема заключается в том, что, сжав данные однажды, мы уже не можем ничего в них добавить (в частности, эти две цифры). Но как же тогда передать информацию браузеру? Например, через cookies.

В листинге проиллюстрирован данный подход. В нем приведен скрипт, который отображает в верхней части страницы информацию о GZip-сжатии, а в нижней — некоторый объемистый текст.

Отображение параметров GZip-сжатия. Файл gz.php

<?php ## Отображение параметров GZip-сжатия 
  // Функция только устанавливает значение cookie page_size_after 
  function obSaveCookieAfter($s) 
  { 
    setcookie("page_size_after", strlen($s)); 
    return $s; 
  } 
  // Аналогично, но для cookie page_size_before 
  function obSaveCookieBefore($s) 
  { 
    setcookie("page_size_before", strlen($s)); 
    return $s; 
  } 
  // Устанавливаем конвейер обработчиков 
  ob_start("obSaveCookieAfter"); 
  ob_start("ob_gzhandler", 9); 
  ob_start("obSaveCookieBefore"); 
  // Дальше можно выводить любой текст - он будет сжат 
?> 
<!-- Выводим информацию о сжатии (в отдельном шаблоне). --> 
<b><?php include "gz.htm"?></b><hr /> 
<!-- Выводим текст страницы. --> 
<pre> 
<?=file_get_contents("../preg/largetextfile.txt")?> 
</pre> 

Мы определяем две функции, каждая из которых устанавливает собственный cookie, не модифицируя при этом данные в буфере. В соответствии с порядком вызовов ob_start() конвейер обработчиков выглядит так:

ob_saveCookieBefore() -> ob_gzhandler() -> ob_saveCookieAfter()

Итак, теперь при выдаче страницы браузеру ему также передаются два cookies, хранящие сведения о степени сжатия страницы. Как же нам их отобразить? Для этого есть только один способ — код на JavaScript. В листинге приведен HTML-код файла gz.htm, который мы включаем в главном сценарии по команде include. Он содержит
вставки на JavaScript, предназначенные для отображения значений наших cookies в браузере.

ВНИМАНИЕ!
Помните: язык JavaScript браузерный, а потому код на нем выполняется не на сервере, а в браузере, уже после загрузки страницы по сети.

JavaScript-код, отображающий параметры GZip-сжатия Файл gz.htm

<!-- Код на JavaScript, отображающий параметры GZip-сжатия. --> 
<script language="JavaScript"><!-- 
// Возвращает cookie с указанным именем 
function getCookie(name) { 
  var p = name + "="; 
  var si = document.cookie.indexOf(p); 
  if (si == -1) return null; 
  var ei = document.cookie.indexOf(";", si + p.length); 
  if (ei == -1) ei = document.cookie.length; 
  return unescape(document.cookie.substring(si + p.length, ei)); 
} 
var b = getCookie("page_size_before"); 
var a = getCookie("page_size_after"); 
if (a && b) { 
  document.write( 
    "[GZip: " + 
    "<span title='стало'>"+a+"</span>/" + 
    "<span title='было'>"+b+"</span> " + 
    "<span title='откусили'>("+(100-Math.round(a/b*100))+"%)</span>" + 
    "]" 
 ) 
} else { 
  document.write("[GZip выключен]"); 
} 
//--></script> 
ПРИМЕЧАНИЕ
Можно заметить, что JavaScript не поддерживается старыми версиями браузеров, либо же он может быть выключен пользователем. На практике такие ситуации весьма редки. Но даже если JavaScript-код и не сработает, ничего страшного не случится — просто информация о GZip-сжатии не будет отображена.

////////////

*Функции перехвата

void ob_start() 

Вызов данной функции говорит PHP, что необходимо начать “перехват” стандартного выходного потока программы. Иными словами, весь текст, который выводится операторами echo или расположен вне участков кода PHP, будет накапливаться в специальном буфере, а не отправится в браузер. В любой момент времени мы можем получить все содержимое этого буфера, вызвав функцию ob_get_contents().

string ob_get_contents() 

Функция возвращает текущее содержимое буфера, который заполняется операторами вывода при включенном режиме буферизации. Именно ob_get_contents() обеспечивает возможность накопления текста, выводимого операторами echo.

ПРИМЕЧАНИЕ
Если буферизация выходного потока не была включена, функция возвращает false. Это свойство можно использовать для проверки, установлен ли буфер вывода, или же данные сразу направляются в браузер.

void ob_clean() 

Данную функцию можно вызывать для немедленной очистки текущего выходного потока.

void ob_end_clean()

Вызов данной функции завершает буферизацию выходного потока. При этом все содержимое буфера, которое было накоплено с момента последнего вызова ob_start(), теряется (не попадает в браузер). Конечно, если текст вывода нужен, следует сначала получить его при помощи функции ob_get_contents().

void ob_end_flush() 

Эта функция практически полностью эквивалентна ob_end_clean(), за исключением того, что данные, накопленные в буфере, немедленно выводятся в браузер пользователя. Ее применение оправдано, если мы хотим отправлять данные страницы клиенту, параллельно записывая их в переменную для дальнейшей обработки.

int ob_get_level() 

Перехватывать выходной поток скрипта можно вложенным образом. Иными словами, можно вызывать функцию ob_start() несколько раз, при этом последующий вызов ob_end_clean() будет не просто уничтожать текущий буфер перехвата, но и возвращаться к предыдущему установленному. Функция ob_get_level() возвращает информацию о “глубине” вложенности текущего перехвата.

ПРИМЕЧАНИЕ
Если поток вовсе не был перехвачен, функция выдает 0.

Давайте зададимся вопросом: что получится, если вызвать функцию ob_start() больше одного раза подряд? В общем-то, ничего нежелательного не произойдет. Последующие операторы вывода будут работать с тем буфером, который был установлен самым последним вызовом. При этом функция ob_end_clean() не завершит буферизацию, а просто установит в активное состояние “предыдущий” буфер (разумеется, сохранив его предыдущее содержимое).

 

на главную сниппетов
Курсы