Управление Выводом: Caching и сжатие динамических страниц
http://php.spiker.ru
Mod_gzip - Apache модуль, который сжимает статические страницы HTML, использующие gzip, согласно IETF стандартам для браузеров, которые принимают gzip enconding (IE, Netscape, и т.д.). Mod_gzip может ускорять время загрузки страниц в 4-5, раз и я настоятельно предлагаю, чтобы Вы использовали mod_gzip на вашем webserver'е. Однако из-за недостатка механизма фильтрации между модулями в Apache 1.x.x, нет никакого способа сжать PHP вывод, используя mod_gzip. Поэтому, мы должны строить наш собственный движок сжатия в PHP. В этой статье, я объясню, как использовать функции управляющие выводом PHP, чтобы заставить ваши страницы загружаться БЫСТРО!
Введение в функции контроля вывода PHP.
Одна из лучших вещей в PHP4 - то, что Вы можете передать PHP буферу весь контент, сгенерированный в скрипте, так что ничего не будет послано браузеру, пока Вы не решите это сделать. Вы можете применять эту особенность, чтобы использовать заголовок и функции setcookie, везде, где Вы хотите в вашем скрипте. Однако, это - только небольшая часть возможностей мощных функций вывода.
<? void ob_start(void) ?>
Используется, чтобы сообщить PHP процессору о переадресации всего вывода на внутренний буфер. Ничего не будет послано браузеру после запроса к ob_start().
<? string ob_get_contents(void); ?>
Возвращает буфер вывода в строке так, что Вы можете переслать накопленный вывод браузеру (после отключения буферизации!).
<? int ob_get_length(void); ?>
Возвращает длину буфера вывода.
<? void ob_end_clean(void); ?>
Очищает буфер вывода и выключает буферизацию вывода. Вы должны использовать эту функцию до вывода контента браузеру.
void ob_implicit_flush ([int flag])
Используется, чтобы вкл\выкл неявный поток (default=off). Если включено, то "поток" выполняется для каждой print/echo или команды вывода и вывод немедленно посылается в браузер. (Директива неявной отсылки говорит PHP о том, что выводимые данные нужно автоматически передавать браузеру после вывода каждого блока данных. Ее действие эквивалентно вызовам функции flush() после каждого использования print() или echo() и после каждого HTML-блока. Включение этой директивы серьезно замедляет работу, поэтому ее рекомендуется применять лишь в отладочных целях.- из комментариев к php.ini, прим. Alexela)
Использование Контроля вывода, чтобы сжать вывод PHP.
Вы нуждаетесь в Zlib расширении, компилируемом в PHP4, чтобы сжать вывод. Если необходимо, см. Zlib расширение в PHP документации для установки инструкций.
Прежде всего, инициализируйте буферизацию вывода:
<? Ob_start (); Ob_implicit_flush (0); ?>
Затем, сгенерируйте все содержание (контент), используя print, echo, или другое, что Вы хотите. Например:
<? print("Hey this is a compressed output!"); ?>
После того, как страница сгенерирована, мы возвращаемся к выводу, использующему:
<? $contents = ob_get_contents (); Ob_end_clean (); ?>
Затем, мы должны проверить, поддерживает ли браузер сжатие данных. Если да, браузер посылает ACCEPT_ENCODING HTTP заголовок вебсерверу в запросе. Мы можем проверить переменную $HTTP_ACCEPT_ENCODING и проверить " gzip, deflate ":
<? if(ereg('gzip, deflate',$HTTP_ACCEPT_ENCODING)) { // Generation of Gzipped content } else { echo $contents; } ?>
Это просто. Структурировано и достаточно ясно, чтобы использовать. Давайте посмотрим, как мы создадим gzipped вывод:
(Taken from PHP.net) // Tell the browser that they are going to get gzip data // Of course, you already checked if they support gzip or x-gzip // and if they support x-gzip, you'd change the header to say // x-gzip instead, right? header("Content-Encoding: gzip" ); // Display the header of the gzip file // Thanks ck@medienkombinat.de! // Only display this once echo "\x1f\x8b\x08\x00\x00\x00\x00\x00"; // Figure out the size and CRC of the original for later $Size = strlen($contents); $Crc = crc32($contents); // Compress the data $contents = gzcompress( $contents, 9); // We can't just output it here, since the CRC is messed up. // If I try to "echo $contents" at this point, the compressed // data is sent, but not completely. There are four bytes at // the end that are a CRC. Three are sent. The last one is // left in limbo. Also, if we "echo $contents", then the next // byte we echo will not be sent to the client. I am not sure // if this is a bug in 4.0.2 or not, but the best way to avoid // this is to put the correct CRC at the end of the compressed // data. (The one generated by gzcompress looks WAY wrong.) // This will stop Opera from crashing, gunzip will work, and // other browsers won't keep loading indefinately. // // Strip off the old CRC (it's there, but it won't be displayed // all the way -- very odd) $contents = substr($contents, 0, strlen($contents) - 4); // Show only the compressed data echo $contents; // Output the CRC, then the size of the original gzip_PrintFourChars($Crc); gzip_PrintFourChars($Size); // Done. You can append further data by gzcompressing // another string and reworking the CRC and Size stuff for // it too. Repeat until done. function gzip_PrintFourChars($Val) { for ($i = 0; $i < 4; $i ++) { echo chr($Val % 256); $Val = floor($Val / 256); } } ?>
Если вы хотите посмотреть это как рабочий пример, то вот весь скрипт целиком:
<? // Start the output buffer ob_start(); ob_implicit_flush(0); // Output stuff here... print("I'm compressed!\n"); $contents = ob_get_contents(); ob_end_clean(); // Tell the browser that they are going to get gzip data // Of course, you already checked if they support gzip or x-gzip // and if they support x-gzip, you'd change the header to say // x-gzip instead, right? header("Content-Encoding: gzip"); // Display the header of the gzip file // Thanks ck@medienkombinat.de! // Only display this once echo "\x1f\x8b\x08\x00\x00\x00\x00\x00"; // Figure out the size and CRC of the original for later $Size = strlen($contents); $Crc = crc32($contents); // Compress the data $contents = gzcompress($contents, 9); // We can't just output it here, since the CRC is messed up. // If I try to "echo $contents" at this point, the compressed // data is sent, but not completely. There are four bytes at // the end that are a CRC. Three are sent. The last one is // left in limbo. Also, if we "echo $contents", then the next // byte we echo will not be sent to the client. I am not sure // if this is a bug in 4.0.2 or not, but the best way to avoid // this is to put the correct CRC at the end of the compressed // data. (The one generated by gzcompress looks WAY wrong.) // This will stop Opera from crashing, gunzip will work, and // other browsers won't keep loading indefinately. // // Strip off the old CRC (it's there, but it won't be displayed // all the way -- very odd) $contents = substr($contents, 0, strlen($contents) - 4); // Show only the compressed data echo $contents; // Output the CRC, then the size of the original gzip_PrintFourChars($Crc); gzip_PrintFourChars($Size); // Done. You can append further data by gzcompressing // another string and reworking the CRC and Size stuff for // it too. Repeat until done. function gzip_PrintFourChars($Val) { for ($i = 0; $i < 4; $i ++) { echo chr($Val % 256); $Val = floor($Val / 256); } } ?>
Кэширование PHP вывода.
Когда PHP4 не существовал, и я вынужден был использовать PHP3, я был очень заинтересован в появлении некого механизма кэширования для вывода PHP скриптов, чтобы уменьшить нагрузку базы данных, доступ к filesystem, и т.д. Не имело смысла делать это в PHP3, но с буферизацией вывода, это легко в PHP4.
Простой пример:
//Construct a filename for the requested URI $cached_file=md5($REQUEST_URI); if((!file_exists("/cache/$cached_file"))||(!is_valid("/cache/$cached_file"))) //здесь обратите внимание на путь к файлу $cached_file, прим. Alexela { // is_valid validates the cache, you can check for expiration // or particular conditions in that function. // If there's no file or it's invalid we generate the output ob_start(); ob_implicit_flush(0 ); // Output stuff here... $contents = ob_get_contents(); ob_end_clean(); $fil=fopen($cached_file,"w+"); //fwrite($fil,$contents,$strlen($contents));//опечатка в коде, прим. Alexela fwrite($fil,$contents,strlen($contents)); fclose($fil); } //Output the file here we are sure the file exists. readfile($cached_file); ?>
Это простой пример. Используя буферизацию вывода, Вы можете строить очень продвинутую контент генерирующую систему, используя caching механизмы, различные для различных блоков или приложений, и т.д. Это зависит от Вас.
Заключение
Функции, управляющие PHP выводом, очень полезны чтобы переадресовать вывод скрипта на буфер и затем уже манипулировать им. Сжатие буфера для браузеров уменьшает время загрузки в 4-5 раз. Это может также использоваться как caching механизм, чтобы облегчить доступ к источникам данных (базы данных или файлы) и это может иметь значение, если мы используем XML.
Думайте об этом:
Что, если мы строим движок в PHP4, используя caching, который берет данные из источников данных (xml документы и базы данных) и динамически строит содержание в xml . Мы можем тогда брать вывод xml и использовать XSLT, чтобы конвертировать к любому виду представления, которое мы хотим (HTML, wap, palm, pdf, и т.д.). PHP4, с контролем вывода и Sablotron XSLT, является совершенным для этой архитектуры. Я написал " Шикарная architectures "- описание, основанное на xml архитектуре, у которой есть много всего, что делать с функциями, описанными в этой статье. Покупайте, если это издано. Я буду также писать статью о Sablotron XSLT , как только это будет зарегистрировано и полностью годным к употреблению под PHP4. Шлите мне ваши мысли обо всем, что Вы хотите.