Ušetřete až 80 % datového přenosu

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
// 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
<?
// 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();
?>

Napsat komentář

Vaše emailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *