Podporuje-li váš webový server skriptování v jazyce PHP, můžete omezit datový tok mezi serverem a klientským počítačem zhruba o 80 procent a tím i případné poplatky za přenášená data. O omezení toku se postará output buffering. Podívejme se na jeho výhody.

Output buffering můžete zapnout kdykoli v průběhu skriptu. Veškerý výstup PHP skriptu (jakýkoli HTML kód nebo jakýkoli jiný výstup pomocí funkcí echo či print) pak nebude okamžitě odesílán klientskému počítači, ale bude uchováván v paměti serveru (bufferu). Pokud tento buffer ve skriptu načteme do proměnné, můžeme s ním nakládat jako s kteroukoli jinou proměnnou, kterou můžeme například uložit na disk. Output buffering je podporován každou verzí PHP4. PHP3 ho bohužel nepodporuje.

Pokud ve skriptu zapnete output buffering, nastává několik vyjímek z pravidel PHP. Konkrétně se jedná o posílání cookies a posílání hlaviček. Ty musí být skriptem za normálních okolností odeslány ještě dříve, než PHP vyprodukuje jakýkoli datový výstup (tento výstup může být i prázdný řádek nebo mezera – zdroj častých chyb), jinak dojde k varování (nelze poslat hlavičku, výstup skriptu byl již zahájen). Při zapnutém bufferingu však toto pravidlo neplatí. Veškerý výstup skriptu totiž končí v bufferu a PHP nepošle klientovi nic, dokud skript neskončí, nebo mu to nepřikážete. Díky tomu lze docela dobře ošetřit například chybu při práci s databází   stávající výstup skriptu se jednoduše zruší a na výstup pošlete celou stránku s upozorněním na chybu při práci s databází.

Jak jsem uvedl již v názvu článku, je možné použitím této metody ušetřit až 80 % datového toku. Jak toho docílit? Nejdříve bude nutné zapnout output buffering. Jsou dvě možnosti. Jedna spočívá v nastavení php.ini. Zapnutí provede direktiva:

output_buffering = On

Pokud chcete velikost výstupního bufferu omezit (omezí se paměťové nároky PHP), můžete v direktivě místo parametru On uvést velikost bufferu v bajtech. Toto použití však příliš nedoporučuji, protože by se mohlo stát, že generovaný výstup skriptu by mohl být větší než maximální velikost bufferu. To by mohlo vést k neočekávanému chování PHP skriptů nebo nežádoucím chybám.

Druhou možností je zapnout buffering přímo ve skriptu. Toho docílíte pomocí funkce:

Ob_start();

Doporučuji použít tuto funkci hned na začátku skriptu (je-li to možné). Skript pak může pokračovat normálním způsobem. Při ukončení PHP skriptu bude buffer odeslán klientovi. Odeslání bufferu klientovi můžete vynutit. K tomu slouží funkce, která zastaví buffering a pošle data klientovi:

Ob_end_flush();

Pokud bude nevhodné posílat bufferovaná data klientovi, můžete bufferování ukončit a stávající buffer vymazat pomocí funkce

Ob_end_clean();

GZIPovaný přenos

GZIPovaný přenos posílá data (HTML kód, JS, CSS, obrázky nebo i binární soubory) prohlížeči gzipovaná. Hlavička na to prohlížeč upozorní a po přijetí celého přenášeného obsahu prohlížeč data rozbalí a zcela normálním způsobem zpracuje. Výhoda takového přenosu je jasná: nekomprimovaná data jsou náročnější na datový přenos. GZIPová metoda dokáže ušetřit až 80 procent tohoto přenosu, zaleží však na obsahu gzipovaných dat. Rozhodně se touto metodou nevyplatí posílat obrázky. Zmíněné komprese lze dosáhnout u textových souborů, což urychlí načítání např. informačních serverů, jejichž stránky jsou datově náročné.

Všechny moderní internetové prohlížeče podporují posílání gzipovaného obsahu. Zda to podporuje i ten váš, zjistíte v proměnné ACCEPT_ENCODING (například pomocí phpinfa). Pokud tam bude mj. uvedeno gzip, tak prohlížeč tuto metodu zasílaných dat podporuje. Pokud tam gzip uvedeno není, nebude prohlížeč umět gzipovaná data přijmout, respektive zpracovat.

Pokud máte PHP ve verzi 4.0.4 a vyšší, a je zkompilováno s podporou knihovny zlib, můžeme bufferovaná data posílat na klienta gzipované. Toho docílíme drobnou změnou funkce ob_start() a to tak, že použijeme nepovinný atribut funkce, kterým definujeme funkci, která se o bufferovaná data postará před odesláním prohlížeči. Zápis funkce bude vypadat následovně:

ob_start(„ob_gzhandler“);

Funkce ob_gzhandler se tedy postará o gzipovaní výstupních dat. Automaticky pozná, zda prohlížeč podporuje zpracování gzipovaných dat, proto se nemusíte zabývat ošetřením skriptu proti posílání gzipovaných dat klientovi, který gzip nepodporuje. Také nemusíte buffer načíst do proměnné a ručně ho gzipovat.

Při použití ob_gzhandler nejste ale schopni ovlivnit úroveň komprese, což je docela škoda. Pak by se tak dalo předejít případnému přetěžování serveru snížením úrovně komprese.

Výsledný skript posílající gzipovaná data bude vypadat následovně:

<? ob_start(„ob_gzhandler“); // další funkce, práce s databází, odesílání cookies či hlaviček apod. ?> <HTML> jakýkoli HTML výstup </HTML> <? // odeslání bufferu prohlížeči Ob_end_flush(); ?>

S PHP nižší než 4.0.4

Pokud máte na serveru PHP 4, ale nižší verzi než 4.0.4, můžete posílat gzipovaná data pomocí output bufferungu. Bohužel bude nutné použít pracnějšího postupu. Nejdříve bude potřeba zjistit, zda prohlížeč podporuje zpracování gzipovaných dat. Pokud ano, ahájít buffering a definujet funkci, která zpracuje výstupní data. Pokud ne, nic se nestane a skript bude pokračovat zcela normálně dále. Možností, jak provést gzipování dat v proměnné je několik (uvádím alternativu použitou i v oficiálním manuálu):

<? // funkce gzipuje obsah proměnné function gzip_vystupu($vystup) { // vrátíme gzipovaný obsah return gzencode($vystup); } // pokud prohlížeč zpracovává gzipovaná data, začneme s bufferingem if (strstr($HTTP_SERVER_VARS[‚HTTP_ACCEPT_ENCODING‘], ‚gzip‘)) { // začíná bufferovat výstup a ke zpracování výstupu použije funkci gzip_vystupu() ob_start(„gzip_vystupu“); // hlavička prohlížeči řekne, že server posílá gzipovaná data header(„Content-Encoding: gzip“); } // pokračování skriptu // pokud prohlížeč nepodporuje zpracování gzipovaných dat,
obdrží standardní výstup skriptu nekomprimovaně
?>

Vedle PHP lze nechat gzipovat i samotný apache. Výhodou nastavení apache je možnost gzipovat i jiná data než ta, která vyprodukuje PHP – můžete gzipovat statické html stránky, nebo i perlovské a CGI skripty. Pro příklad uvádím jednoduchou konfiguraci apache:

AddModule mod_gzip.c <IfModule mod_gzip.c> mod_gzip_on Yes mod_gzip_dechunk Yes # minimální délka souboru pro použití GZIPu mod_gzip_minimum_file_size 300 # maximální délka souboru pro použití GZIPu # nula znamená neomezenou délku mod_gzip_maximum_file_size 0 # maximální paměť využitá apachem mod_gzip_maximum_inmem_size 100000 # zachovávání pracovních souborů mod_gzip_keep_workfiles No # adresář pro pracovní soubory mod_gzip_temp_dir /tmp # GZIPovat cokoli s příponou .html mod_gzip_item_include file \.html$ # GZIPovat cokoli s příponou .jsp mod_gzip_item_include file \.jsp$ # GZIPovat cokoli s příponou .php mod_gzip_item_include file \.php$ # GZIPovat cokoli s příponou .cgi mod_gzip_item_include file \.cgi$ # GZIPovat jakýkoli text mod_gzip_item_include mime ^text/.* # GZIPovat jakýkoli výstup PHP skriptu mod_gzip_item_include mime ^application/x-httpd-php mod_gzip_item_include handler ^perl-script$ mod_gzip_item_include handler ^server-status$ mod_gzip_item_include handler ^server-info$ # nekomprimovat obrázky mod_gzip_item_exclude mime ^image/.* </IfModule>

Ukládání na disk

Další praktické využití output bufferingu je v podobě ukládání bufferu na disk. Jelikož buffer v podstatě obsahuje celou HTML stránku, můžete ho uložit na disk jako html soubor a následně jej volat místo PHP skriptu, případně jej například místo složitého a náročného generování z databáze vložit do skriptu. V krátkosti uvedu jednoduchý příklad:

<? // pokud neexistuje HTML soubor se stránkou, vygeneruje se if (!file_exists(„/adresar/nejaky.html“)) { ob_start(); // generování obsahu echo „bláboly“; // funkce vrací obsah bufferu $buffer = ob_get_contents(); // uložení obsahu bufferu na disk @$file = fopen(„/adresar/nejaky.html“, „w“); @fwrite($f, $buffer); @fclose($f); // ukončí buffering a odešle data klientovi ob_end_flush(); } // jinak se soubor načte z disku else { include „/adresar/nejaky.html“; } ?>

Účel tohoto postupu je jasný. Opakované generování stránky z databáze zatíží server daleko více, než volání statické HTML stránky, případně její začlenění do PHP skriptu. V případě aktualizace databáze je však nutné tyto soubory z disku smazat, jinak by se změna samozřejmě neprojevila.

Závěrem předvedu spojení obou možností – vygenerování souboru na disk a následné poslání gzipovaných dat. Vycházet budu z mírně upraveného předchozího příkladu

&lt? // pokud neexistuje HTML soubor se stránkou, vygeneruje se if (!file_exists(„/adresar/nejaky.html“)) { ob_start(); // generování obsahu // spousty selectů do databázace, extrémně dlouhé smyčky apod. echo „bláboly“; // funkce vrací obsah bufferu $buffer = ob_get_contents(); // uložení obsahu bufferu na disk @$file = fopen(„/adresar/nejaky.html“, „w“); @fwrite($f, $buffer); @fclose($f); // ukončí buffering a smaže stávající buffer ob_end_clean(); } // ať už se HTML soubor generuje nebo ne, bude se vkládat // data se budou posílat gzipovaně ob_start(„ob_gzhandler“); // vložení HTML souboru include „/adresar/nejaky.html“; // odeslání výstupu bufferu prohlížeči ob_end_flush(); ?>

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Žádný příspěvek v diskuzi

Odpovědět