GZipování podruhé: skripty a styly
Kdysi v seriálu zapomíná se na… jsem zmínil, že se zapomíná na GZip, tenkrát to bylo myslím čistě ve vztahu k HTML dokumentům. Dnes bych připomenul to ostatní co je možné, vhodné a snadné gzipovat – jsou to skripty a stylopisy.
K čemu je to dobré?
Je to velmi dobré. Je doba JS frameworků, mnohé z nich jsou tak trošku masivní, mnoho-desítek-kilobajtové a zde přichází šance pro kompresi! Místo abyste návštěvníkům cpali například takový prototype s lightboxem za 70 KB, můžete jim to samé nabídnout za skvělých 16 KB. Podobně můžete stlačit cojávím jQuery na pouhých 13 KB a potom už přestává platit rovnice „JS FW = ach jo, tolik KB navíc“. A tak dále.
Kód (verze 2008.1)
<?php
/* Superior GZip compressor
* - version: 2008.1
*
* - script for easy compressing of external java-script files, css files or else.
* - (c) Peter Kahoun aka Kahi
*/
if (!isset($_GET['file']))
die('// ERROR: use $_GET[\'file\'] variable to select file(s). Multiple filenames separate with +.');
$file = $_GET['file'];
//*********************************
// prepare list of files
$files = explode(' ', $file);
$dates = array();
$report = array();
foreach ($files as $key => $file) {
$file = basename($file);
if (empty($file)) {
unset($files[$key]);
}
elseif (!is_file($file)) {
unset($files[$key]);
$report[] = " * file: $file ... not found";
}
else {
$files[$key] = $file;
$dates[] = filemtime($file);
}
}
if (!count($files))
die('// ERROR: no file included');
//*********************************
// headers & caching
// headers #1 -- if-modified-since
$top_date = max($dates);
$top_date_gmt = gmdate('D, d M Y H:i:s ', $top_date) . 'GMT';
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) AND $topdate <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
Header('HTTP/1.1 304 Not Modified');
exit();
}
// headers #2 -- content-type
$content_types = array (
'js' => 'text/javascript',
'css' => 'text/css');
// manual choice
if (isset($_GET['type']) AND array_key_exists($_GET['type'], $content_types)) {
$content_type = $content_types [$_GET['type']];
}
// automatic choice
else {
$extension = $files[0];
// TODO: handle parametrized-scripts (script.js?par=val)
$extension = substr($extension, strrpos($extension, '.') + 1);
if (array_key_exists($extension, $content_types))
$content_type = $content_types [$extension];
else
$content_type = $content_types ['js'];
}
Header('Content-type: ' . $content_type . '; charset: UTF-8');
// headers #3 -- last-modified & expires
Header('Last-Modified: ' . $top_date_gmt);
Header('Expires: ' . gmdate('D, d M Y H:i:s ', time() + 3600*24) . 'GMT'); // maybe you would like to make a change here... default = 1 day
//*********************************
// print it out!
$content = '';
foreach ($files as $file) {
if ($content_type != 'text/css')
$content .= "/// file: $file\n\n";
$content .= file_get_contents($file);
$content .= "\n\n";
}
// start compression
if (function_exists('ob_get_level') && (ob_get_level() > 0))
ob_start("ob_gzhandler");
echo $content;
if (count($report))
echo "/* REPORT!\n" . implode("\n", $report) . "\n */";
?>
…který umí prakticky všechno co po něm chci – komprimovat skripty a ještě něco navíc.
Co umí navíc, Kahi?
Umí spojovat JS soubory do jednoho, takže můžete ušetřit pár dialogů server-klient, a vaše stránka bude zase o něco svižnější. Tento princip jsem už někde viděl, ale abych si nekřivdil, viděl jsem ho v momentě, kdy jsem ho už dávno sám používal, aby bylo jasno. Jo, a byly přidány kešovací hlavičky, poděkujte dgx.
K tomuto slučování JS souborů bych ještě chtěl poznamenat, že nefunguje vždy – některé kombinace zkrátka mohou způsobit nefunkčnost, nevím přesně proč, dokonce občas může být java-skript znefunkčněn pouhou gzip kompresí, opět nevím proč. Berte to tak, že slučování je funkce ve verzi beta a jestli to chcete používat, tak obezřetně, opatrně.
Mimochodem, není možné definovat cestu k .js souboru – skripty musí být ve stejném adresáři jako kompresní PHP skript. Zatím.
Jak se teda Superior GZip compressor používá?
Dříve:
<script src="/scripts/prototype.js" type="text/javascript"></script>
<script src="/scripts/lightbox.js" type="text/javascript"></script>
Dnes:
<script src="/scripts/_zip.php?file=prototype.js+lightbox.js" type="text/javascript"></script>
Háčky, potenciály, vize
- Kromě háčků které jsem zmínil výše (to jest problém se slučováním) je tu ještě dost… ne přímo háčků, ale spíše potenciálu.
- Skript se neumí poprat se situací, kdy chcete zkomprimovat skript
doplněný parametrem (konkrétní případ:
scriptaculous.js?load=effects
)
O verzích
- 2007.2 – bezpečnostní záplata
- 2007.3 – přidány kešovací hlavičky, značná reorganizace kódu
- 2007.4 – automatická volba MIME typu (tzn. pomocí toho samého souboru
můžete komprimovat js i css); přidána hlavička
Expires
pro ještě lepší kešování - 2008.1 – úpravy v gzip nastavení, předchozí řešení za určitých okolnostích způsobovalo chyby
Komentáře (8)
k formuláři
RSS kanál komentářů
18.11. 2007 — 16:44
a co toto href=„http://code.google.com/p/jscsscomp/“ rel=„nofollow“>http://code.google.com/p/jscsscomp/ ?
18.11. 2007 — 16:56
[1] OndroNR: – nojo, pěkný. Ale… nějak to není ono :-)
19.11. 2007 — 3:24
Sleduj, jo?
19.11. 2007 — 5:40
Trochu jsem to přepsal, díra zmíněná by dgx už tam není…
19.11. 2007 — 13:08
Hi, misto Last-Modified hlavicky bych radeji pouzil Expires. Jde o to, ze u Last-Modified se musi udelat pro kazde nacteni stranky dotaz, zda se cilovy soubor nezmenil. Expires tohle proste nedela, pac pres Expires mu reknes, na jak dloho si ma uchovat soubor v cache.
Osobne pouzivam kod:
header_mod.php:
a pak jeste na to aplikuju mod_rewrite:
.htaccess:
Poznamka: Osdobne mam zlib.output_compression vypnuty, ale pro lepsi prenositelnost by bylo lepsi pridat testovani, tak jak ho tam ma Kahi.
19.11. 2007 — 13:26
[3] dgx: tam urco neprotlacis. Minimalne Texy! prevede <a> na entity. Pripadne kazdy poradny RS pro PHP ma osetrene v komentarich, aby jsi nemohl do textu narvat surovy php kod.
21.11. 2007 — 22:13
[5] Arcao: – zajímavé. Osobně bych možná vyřešil kešování obrázků přes mod_expires a kód obou souborů by se pak mohl trochu zjednodušit…
23.11. 2007 — 13:03
[5] Arcao: nejde o to použít Expires místo Last-Modified, použít by se mělo obojí zároveň. Last-Modified aby dělal dotaz a Expires aby ho dělal třeba jen ob den.
[6] Arcao: o to tady nešlo. Původní kód obsahoval nějaké díry, přes které bylo možné spustit kód nebo vypsat konfigurační soubor. Ty už Kahi opravil.
Přidat komentář