Cool URI pomocou mod_rewrite

07.11.2007 22:55 | blackhole

Našiel som článok o tom, ako docieliť cool URI pomocou apache-ovej funkcie lookback. Na komplexnejšie stránky je ale IMHO vhodnejšie použiť apache-ovský mod_rewrite. V tomto článku ukážem, ako riešim cool uri ja a poskytnem aj jednu moju utilitku na spracúvanie adries, ktorá sa šikne aj v riešení pomocou lookback (na ktorú som ju pôvodne robil).

mod_rewrite slúži na "prepísanie" URL-ky dotazu pred tým, ako ju ide spracovať napríklad PHP. Dokážete ním presmerovať prišlý request na iný súbor, prípadne PHP skript alebo CGI program, ktorý request spracuje. Pekné na tom je to, že sa to celé deje v rámci servra a pre klienta to stále vyzerá tak, ako by prechádzal úhľadnú adresárovú štruktúru.

Na používanie mod_rewrite potrebujete:
apache s enablovaným modulom mod_rewrite (no, vcelku samozrejmé, že)
možnosť zasahovania do konfigurácie adresára so stránkou (buď v konfiguračnom súbore virtual hosta alebo súbore .htaccess - čo je väčšina prípadov)

Tieto požiadavky spĺňajú všetky webhostingy, s ktorými som sa stretol. (Ak používate môj obľúbený hosting WebSupport, tak si ale dajte pozor, pretože do súboru .htaccess sa tam ukladajú nastavenia z adminpanelu Vašej domény, potom sa môžete čudovať (ako ja :-), prečo sa nastavenia stratia pri nasadení stránky. V adminpaneli síce je varovanie, ale ja som si ho tam vtedy teda nevšimol.)

Všetky nastavenia a možnosti mod_rewrite sem nebudem písať hneď z dvoch dôvodov - sú na webe apache-u (mod_rewrite) a nevyznám sa v nich až tak dobre, aby som si bol istý, že neuvediem zlú informáciu.

Preto si ukážeme použitie na jednoduchom prípade:
Stránka bude umiestnená priamo v HTTP roote virtual hosta (tj http://www.example.org/ ) a všetky súbory, ktoré majú byť dostupné na stiahnutie (obrázky, CSS, favicon...) budú v podadresári files ( http://www.example.org/files/ ).

Potom by .htaccess súbor obsahoval niečo podobné tomuto:

RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_URI} !^\/files\/.*$
RewriteRule ^(.*)$ main.php [QSA]

V tomto súbore sme zapli mod_rewrite (nie enablovali, to treba spraviť inde) a nastavili koreňový adresár (v tomto prípade tam myslím tento riadok ani nemusí byť, ale až taký istý si tým niesom).

Ďalej nasleduje už samotná práca s mod_rewrite. Ako vidíte, mod_rewrite používa regulárne výrazy.
Najdôležitejším "príkazom" mod_rewrite je RewriteRule, ktorý má dva povinné parametre. Prvým je regulárny výraz, druhým je adresa, na ktorú bude presmerovaný request (ktorého pôvodná adresa spĺňa regulárny výraz). Za týmito dvoma parametrami sú flagy, konkrétne [QSA] znamená Query String Append - tj ak príde request na /abc/def.html?a=b&c=d, tak sa prepíše na main.php?a=b&c=d
Pred RewriteRule môže byť niekoľko "príkazov" RewriteCond, ktoré fungujú ako podmienka na vykonanie RewriteRule. Tj request bude prepísaný iba ak sa všetky jeho podmienky vyhodnotia pravdivo. Ako vidíte, RewriteCond má dva parametre, prvým je výraz, ktorý sa bude vyhodnocovať a druhým je regulárny výraz, ktorým sa bude vyhodnocovať.

V našom prípade teda budú všetky requesty, ktoré nezačínajú na /files/ presmerované na PHP skript main.php.

Toto riešenie predpokladá, že skript main.php sa nejakým spôsobom dozvie, aká bola pôvodná adresa requestu, konkrétne sa to dá zistiť pomocou $_SERVER['REQUEST_URI'], v prípade, že súbor nieje umiestnený v http roote, je možné túto hodnotu porovnať s $_SERVER['SCRIPT_NAME'], v ktorej je už cesta k vykonávanému skriptu.
Možno krajšie riešenie by bolo predať cestu skriptu priamo, aj to sa pomocou mod_rewrite dá:

RewriteCond %{REQUEST_URI} !^\/files\/.*$
RewriteRule ^(.*)$ main.php?curipath=$1 [L,QSA]

Skript main.php si teraz bude myslieť, že ako GET parameter dostal adresu pôvodného requestu.

RewriteCond %{REQUEST_URI} !^\/files\/.*$
RewriteRule ^(.*)/(.*)/(.*)$ main.php?page=$1&subpage=$2&subpage2=$3 [L,QSA]

No a teraz už má skript cestu aj rozparsovanú, ale celkom obmedzujúcim spôsobom (niekedy však postačujúcim)

No a ako spracúvať tieto dáta v PHP?
Na prácu s celou adresou pôvodného requestu som spravil triedu Navigation. (Túto som spravil keď som sa s cool URI ešte iba hral, odvtedy som ju iba pár krát modifikoval, preto tam môžu byť čudesné veci, ale - never touch a running system :-)
Myslím, že všetky metódy sú dostatočne sebapopisné na to, aby stačil príklad na ich používanie:

// $_GET['curipath'] = "/procesory/x86/AMD"
$nav = new Navigation($_GET['curipath']); // v prípade, že ste použili verziu .htacces súboru,
                                          // kde skriptu nepredávate celú cestu pôvodného requestu,
                                          // môžete triedu vyrobiť bez parametra a ona sa pokúsi ju
                                // uhádnuť pomocou $_SERVER['REQUEST_URI'] a $_SERVER['SCRIPT_NAME']
echo $nav->getNext(); // procesory
echo $nav->getNext(); // x86
echo $nav->getNext(); // AMD
echo $nav->getNext(); // vráti NULL - nevypíše nič

Pôvodne som chcel ešte pridať môj engine na webové stránky, ale článok je už aj tak dosť dlhý, preto rozhodnutie o tom, či ho napíšem alebo nie, nechám na Vás.