Pojďme se podívat podrobněji na adresářovou strukturu a jednotlivé soubory aplikace Automat na kávu (ke stažení na konci článku). Celý projekt je rozdělen do tří adresářů:
app
– obsahuje všechny soubory serverové části aplikacelibs
– obsahuje knihovny třetích stran, mohou být společné pro více aplikacídocument_root
– kořenový adresář dostupný přes prohlížeč
Jakýkoliv požadavek na HTTP server je směrován dovnitř adresáře document_root
. Zde se nacházejí všechny obrázky, kaskádové styly, javascriptové soubory. A také soubor index.php
, přes který vedou všechny požadavky na HTML stránky. Pokud bychom používali tzv. Cool URL, vytvořili bychom pravidlo mod_rewrite
, které by opět všechny požadavky na stránku nasměrovalo skrz index.php
.
Přičemž index.php
nedelá nic jiného, než že definuje cesty k ostatním dvou adresářům app
a libs
(jako konstanty) a předá řízení zaváděcímu souboru aplikace.
define('WWW_DIR', dirname(__FILE__)); // path to the web root
define('APP_DIR', WWW_DIR . '/../app'); // path to the application root
define('LIBS_DIR', WWW_DIR . '/../libs'); // path to the libraries
require APP_DIR . '/bootstrap.php'; // load bootstrap file
Pojmenování a umístění jednotlivých adresářů je zcela ve vaší režii. Pokud preferujete jiné konvence, račte si je dopřát. Stačí jen změnit cesty v index.php
a aplikace dál poběží, jako by se nechumelilo. Nezapomeňte dohlédnout na to, aby adresáře aplikace a knihoven byly nepřístupné z webového prohlížeče. Například pomocí pravidel v souboru .htaccess
:
Order Allow,Deny
Deny from all
Zaváděcí soubor
Zaváděcí soubor app/bootstrap.php
nejprve vykoná nám již známé operace. Načte framework, aktivuje Laděnku a pro jistotu zkontroluje, zda lze zapisovat do adresáře pro dočasné soubory.
// Krok 1: Načtení Nette Framework
require LIBS_DIR . '/Nette/loader.php';
// Krok 2: Konfigurace prostředí
// 2a) zapne NetteDebug pro lepší vizualizaci a zpracování chyb
Debug::enable();
// 2b) kontrola, zda je složka /app/temp zapisovatelná
if (@file_put_contents(Environment::expand('%tempDir%/_check'), '') === FALSE) {
throw new Exception("Make directory '" . Environment::getVariable('tempDir') . "' writable!");
}
Poté následuje nastavení routeru a samotné spuštění aplikace. Zatímco routování je kapitolou samo o sobě (tedy alespoň z pohledu tohoto seriálu), o třídě NetteEnvironment
, jejíž jméno v ukázce zdrojového kódu padlo, si něco povíme hned.
Třída Environment
Kladnozelený vztah Nette Frameworku k životnímu prostředí webové aplikace signalizuje skutečnost, že si nevytváří žádné globální proměnné nebo konstanty. Celé prostředí má pod palcem statická třída NetteEnvironment
. Ta slouží jako globální úložiště pro:
- proměnné prostředí (obvykle cesty, třeba k dočasnému adresáři)
- název a režimy prostředí
- konfiguraci aplikace
- singletony
Proměnné prostředí lze nastavit a získat pomocí těchto dvou metod:
// pokud používáte verzi pro PHP 5.3, odkomentujte následující řádek:
// use NetteEnvironment;
Environment::setVariable('foo', 'Hello World');
echo Environment::getVariable('foo'); // -> Hello World
Framework zjednoduší provázání i na konstanty definované v souboru index.php
. Pokud například požádáte o nedefinovanou proměnnou appDir
, zkusí se podívat, jestli neexistuje konstanta APP_DIR
a vrátit její hodnotu. Hlavní výhoda tohoto řešení se projeví při využití další vlastnosti konstant, a tou je rozvíjení.
// mezi znaky % je název konstanty
Environment::setVariable('foo', 'Rozvíjej se %poupatko%');
Environment::setVariable('poupatko', 'programátore');
echo Environment::getVariable('foo'); // -> Rozvíjej se programátore
Díky tomu je možné definovat jako proměnné absolutní cesty, ačkoliv známe jen cestu relativní. Například jako dočasný adresář chceme nastavit app/temp
, ale absolutní cestu k app
neznáme (tu definuje index.php
konstatnou APP_DIR
). Lze však nastavit:
Environment::setVariable('tempDir', '%appDir%/temp');
echo Environment::getVariable('tempDir');
// -> vypíše absolutní cestu, neboť nahradí %appDir% za konstantu APP_DIR
Pro rozvinutí proměnné bez její definice lze použít metodu expand
, jak toho využívá kód v bootstrap.php
ověřující, zda lze do dočasného adresáře zapisovat.
Sbohem všem require dej
PHP disponuje čtyřmi příkazy pro načtení skriptu z jiného souboru. Tedy require
, include
a jejich sourozenci s koncovkou _once
. Pokud je cesta k souboru relativní, dohledává se skript mně naprosto nepochopitelným způsobem, proto také poučení programátoři používají cesty absolutní. A jelikož magická konstanta __DIR__
přijde až s PHP 5.3, hromadí se na začátcích souborů haldy něčeho takového:
require_once dirname(__FILE__) . '/libs/ClassA.php';
require_once dirname(__FILE__) . '/libs/ClassB.php';
require_once dirname(__FILE__) . '/libs/ClassC.php';
...
Z estetického hlediska jde o zástupce ASCII art deco, povšimněte si zvláště půvabného spojení závorek s podtržítky. V odborných kruzích však podobné obrazy nesou název „Serverova smrt“ a společně s výjevem Hieronyma Bosche „.htaccess obsahující 486 pravidel RewriteRule“ se jimi straší malé, čerstvě vylíhlé, revize Apache.
Skutečně se ukazuje, že jedním z největších zabijáků výkonu PHP je načítání velkého množství souborů (navíc nejsou-li absolutně adresováné). Co se s tím dá dělat? Účinnou zbraní je kompaktní minimalizovaná verze frameworku, viz první díl seriálu. Díky ní celý Nette Framework načtete jedním jediným příkazem:
require 'Nette/loader.php';
Pokud ale minimalizované verze nejsou k dispozici nebo by jejich použití nebylo vhodné, dá se dosáhnout velké úspory výkonu tím, že budeme načítat pouze soubory, které skutečně potřebujeme. A tomu slouží autoloading.
Nutno říci, že zvýšení výkonu aplikací je pouze druhotným efektem autoloadingu. Prim drží pohodlí, které s ním programátor získá. Bude moci přestat používat příkaz require
a velmi rychle zjístí, jak je to návykové.
Autoloading zajišťuje obslužná funkce, která jako parametr dostane jméno třídy/interface a jejím úkolem je načíst skript s definicí. Otázkou zůstává, jak obecně odvodit z názvu třídy jméno souboru?
(Poznámka: špatně napsaný autoloadovací handler může vést k citelnému snížení výkonu aplikace.)
(Poznámka č.2: vyšší zátěž serverů vede ke globálnímu oteplování serverovny.)
RobotLoader
Třída NetteLoadersRobotLoader
na to jde způsobem, který znáte od vyhledávačů. Podobně jako roboti vyhledávačů procházejí a indexují všechny stránky, tak i RobotLoader prochází všechny PHP skripty a zaznamenává si, které třídy a rozhraní v nich našel. Výsledky bádání si poté uloží do cache a použije při dalším HTTP požadavku. Stačí tedy určit, které adresáře má procházet (předpokládejme, že cesty se nacházejí v konstantách APP_DIR
a LIBS_DIR
):
Následující řádky je možné přidat do zaváděcího souboru bootstrap.php
a poté lze odstranit všechny require_once
použité v aplikaci.
// pokud používáte verzi pro PHP 5.3, odkomentujte následující řádek:
// use NetteLoadersRobotLoader;
$loader = new RobotLoader();
$loader->addDirectory(APP_DIR);
$loader->addDirectory(LIBS_DIR);
$loader->register();
Pokud byste chtěli využít služeb RobotLoaderu mimo MVP aplikace Nette, je potřeba nastavit cestu k dočasnému adresáři do proměnné prostředí tempDir
:
Environment::setVariable('tempDir', '/absolutní/cesta/k/temp');
RobotLoader pracuje s cache inteligentně. Při vytvoření nové třídy ji invaliduje a zároveň si pamatuje, které třídy v adresářové struktuře nenašel. Můžete tedy pohodlně rozvíjet svou aplikaci a RobotLoader vám v tom bude sekundovat.
Ještě perlička na závěr: pokud byste chtěli určitý podadresář z indexování vynechat, vytvořte soubor netterobots.txt
se známou syntaxí:
Disallow: Zend
Pokračování příště
Příště nás čeká atraktivní téma: JavaScript, AJAX a bohaté aplikace.
Autor článku je vývojář na volné noze, specializuje se na návrh a programování moderních webových aplikací. Pravidelně pořádá školení pro tvůrce webových aplikací, vyvíjí open-source knihovny Texy, dibi a Nette Framework.
Přehled komentářů