Je tomu právě tři a půl roku, kdy jsem distribuci Texy doplnil o tzv. kompaktní verzi. Zjednodušeně se dá říci, že jde o skript vzniklý spojením všech souborů tvořících knihovnu do jednoho jediného a vynecháním komentářů a nadbytečných mezer. Výsledkem je funkčně identický kód, jen s krapet hustší konzistencí. (Prostě je hustéééj. Jak Salko!)

Záměrem bylo zjednodušit nahrávání knihovny na server – uploadovat desítku malých souborů přes FTP bývá značně pomalejší, než nahrát soubor jeden. Jako vedlejší efekt se ukázalo, že jednosouborová knihovna znatelně zvyšuje výkon aplikace – klidně o 30 %. Používání kompaktních verzí se ujalo a od té doby většina bugreportů hlásila chybu na řádce 1 🙂

S příchodem jmenných prostoru v PHP 5.3 se generování kompaktních verzí poněkud zkomplikovalo – zatím totiž platí, že jeden soubor může obsahovat jen jeden jmenný prostor už to neplatí. To znamená, že „jednosouborová“ verze bude mít přinejmenším tolik souborů, kolik je v knihovně jmenných prostorů, plus případné další soubory řešící problémy se vzájemnou závislostí mezi prostory.

Zároveň však PHP 5.3 přichází s novinkou, která si klade za cíl vnést do kompaktních verzí nový svěží vítr: PHAR – PHP archive. Ostatně myslím, že promluvím za nás všechny, když řeknu: nemusí pršet, hlavně když kape!

Jak takový PHAR balíček vytvořit? Nejprve je nutné v PHP.INI zapnout extension=php_phar.dll a nastavit direktivu phar.readonly = Off. Následující kód zkonvertuje celý Nette Framework do souboru nette.phar

$phar = new Phar('nette.phar');

$phar->buildFromDirectory('libs/Nette'); // enter here path to Nette

$phar->setStub("<?php
Phar::mapPhar('nette.phar');
require 'phar://nette.phar/loader.php';
__HALT_COMPILER();");

$phar->compressFiles(Phar::GZ); // or Phar::BZ2

Nejprve se vytvoří nekomprimovaný archiv ze všech souborů v určeném adresáři a jeho podadresářích – jde o obdobu TAR archivu. Následně se definuje tzv. stub neboli zaváděcí program. Jeho úkolem bude načíst soubor loader.php, který je součástí Nette Frameworku a nalézá se vlastně uvnitř PHAR archivu. Stub musí být ukončen zvoláním __HALT_COMPILER(). Poté je možno archiv zkomprimovat.

Příklad použití:

require 'nette.phar';

echo Html::el('strong')->setText('It works!');

Vypadá to báječně, že? Ovšem podívejme se na věc podrobněji. PHP skripty nejsou v archivu nijak minimalizované, nejsou z nich odstraněny zbytečné komentáře ani mezery. To je potřeba udělat ručně. Takže zatímco komprimovaný nette.phar má nějakých 180 kB, stejný archiv vygenerovaný z kompaktní verze Nette (tj. místo adresáře libs/Nette použijeme libs/Nette.compact) váží pouhých 50 kB. Nicméně pokud si uvědomíme, že

  • minimalizace kódu vede k rychlejšímu parsování (závislost bude dost možná lineární)
  • dekomprimace naopak nutně zpomaluje

dojdeme k závěru, že ideální bude generovat PHAR rovnou z kompaktní verze a ukládat jej nekomprimovaný. Nojo – ale proč teda rovnou nezůstat u kompaktní verze? Bez PHAR režie navíc?

Důvod existuje. Kompaktní verze se vždy načte celá, zatímco PHAR lze parsovat a kompilovat po částech. Takže Zend Framework bude vhodnější zabalit do PHARu, u dibi nebo Texy si můžeme dovolit plný výkon kompaktní verze.