V předchozím článku jsme testovali a doplňovali lomítka tam, kde bylo potřeba, v tomto článku o nekonečných krásách nástroje mod_rewrite se podíváme ještě trochu hlouběji do útrob příkazu RewriteCond a seznámíme se s příkazem RewriteBase.

Zjištění existence souboru

Často se může stát, že si vytvoříme nějaké podobné pravidlo:

RewriteEngine On
RewriteRule ^([^.^/]+)(/)?$ %{DOCUMENT_ROOT}/index.php?action=$1 [L]

Vše funguje spolehlivě, možná až moc – pokud bude například adresa mujserver.cz/nove/ fyzicky existujícím adresářem, stejně se přepíše na index.php?akce=nove. Nemilé, že?

Řešení jsou prakticky dvě. Můžeme tento problém řešit obecně za pomocí detekce skutečných souborů (adresářů), nebo přidat další pravidla s ručně vypsanými názvy adresářů. Nelze jednoznačně říci, které řešení je lepší, protože v případě nemnoha reálných adresářů se jistě vyplatí spíše napsat podobné pravidlo:

RewriteEngine On
RewriteRule ^existujíci-adresar/.*$ – [PT]

Ona pomlčka za regulárním výrazem značí, že se nebude nikam přesměrovávat, vše zůstane tak, jak je. Příznak PT asi vidíte poprvé, jedná se o takzvaný pass through to next handler. Jak název napovídá, používá se hlavně při součinném působení mod_rewrite a dalších nástrojů typu mod_alias či mod_redirect. V tomto případě, pokud nemáme žádná pravidla těchto direktiv nadefinována, nebude prostě mod_rewrite zpracovávat příkazy pro tuto složku.

Druhá možnost, jak už jsem zmínil, je provádět test, zda náhodou to, co uživatel chce, opravdu existuje:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^.^/]+)(/)?$ %{DOCUMENT_ROOT}/index.php?action=$1 [L]

Pomocí rozhodovacího příkazu RewriteCond ověříme, že adresář neexistuje (můžeme zjišťovat i zda se jedná o soubor pomocí -f). Pokud je podmínka splněna, provede se přepis. Pravidlo se tak nebude týkat reálných adresářů.

Podstrkávání MIME-typu

V souvislosti s validitou dokumentů XHTML a možností v mod_rewrite podstrkávat MIME-typy mě napadlo, že bychom klidně mohli nechat správné posílání hlavičky application/xhtml+xml na tomto skvělém nástroji. Jak víme, jeden prohlížeč od Microsoftu bohužel není schopen tento typ akceptovat. Použití mého řešení je ovšem v dnešní dynamické době skriptovacích jazyků hodně omezené, protože nemůžeme prostě jen poslat nějaký PHP soubor s touto hlavičkou, jelikož bychom ho dynamicky vůbec neprovedli a jen vysypali jeho obsah do prohlížeče. Tento příklad, který pošle prohlížečům založeným na jádře Gecko nebo Opera podle hlavičky HTTP_USER_AGENT správný MIME-typ, je tak odsouzen spíše do pozice demonstrace síly mod_rewrite, než ke skutečnému praktickému využití. Toto řešení je použitelné na „hotové“ XHTML soubory s koncovkou .html.

RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} ^.*Gecko.*$ [OR]
RewriteCond %{HTTP_USER_AGENT} ^.*Opera.*$
RewriteRule ^([^/]+).html$ – [T=application/xhtml+xml]

Ve dvou RewriteCond se pomocí jednoduchých regulárních výrazů spojených logickým součtem zjišťuje, zda se jedná o některý z prohlížečů podporujících application/xhtml+xml. Pokud ano, pomocí příznaku TRewriteRule pošleme odpovídající MIME-typ.

Celé řešení je hodně teoretické, jednak takto neodhalíme veškeré browsery podporující application/xhtml+xml (viz XHTML media type test – results), druhak by možná bylo vhodnější celý postup udělat inverzně – tedy detekovat MSIE a pouze jemu posílat vybranou hlavičku, zatímco zasílání hlaviček ostatním prohlížečům ponechat na jiných nástrojích (což ovšem může následně způsobit řadu dalších komplikací, viz též Trable s MIME typem XHTML).

Inteligentní stahovací služba

Nyní dvě nově nabité znalosti spojím do jedné. Vytvoříme si jakousi stahovací službu, která jakýkoli soubor (s výjimkou PHP) nabídne ke stáhnutí. Pokud ovšem soubor nebude existovat, přesměrujeme na jakousi chybovou stránku, která nás informuje o tom, že hodláme stáhnout nepovolený či neexistující soubor.

Celé řešení je založeno na příkladu, kterým sílu mod_rewrite demonstroval Petr Neuman. Vylepšil jsem jen stávající mechanismus o detekci existence daného souboru.

RewriteEngine On
RewriteBase /
RewriteCond %{QUERY_STRING} ^stahuj$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .(jpe?g|gif|png)$ index.php?soubor=neexistuje [L,NC]
RewriteCond %{QUERY_STRING} ^stahuj$
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule .(jpe?g|gif|png)$ – [L,NC,T=application/octet-stream]

Abych se zbytečně neopakoval, ve stručnosti jen dodám, že jsem k původním příkazům dodal ještě jeden RewriteCond pro zjištění existence souboru. V prvním případě, pokud soubor neexistuje, přesměrujeme na chybovou stránku. Poslední trojice příkazů už jen existujícímu souboru podstrčí jiný MIME-typ. Za zmínku stojí i příznak NC (no case), který vypíná citlivost na velká a malá písmena, regulární výraz je tak „case insensitive“.

RewriteBase

Ve výše uvedené ukázce mod_rewrite se vyskytl nový příkaz RewriteBase, se kterým jsem vás ještě neseznámil, a protože bychom měli být slušní, je dobré se o něm alespoň zmínit. A navíc, je hodně užitečný, posuďte na tomto jednoduchém příkladu:

RewriteBase /nova-verze
RewriteRule ^([^/]+).html$ index.php?action=$1 [L]

Schválně, zkuste hádat, který index.php se vykoná? Ano, ten co je v podadresáři nova-verze (vzhledem k umístění .htaccess)!

Příkaz RewriteBase nastaví výchozí adresář pro cíle všech přesměrování. Pokud tedy budete používat relativní adresaci, jako v tomto příkladě, můžete velice snadno manipulovat s adresáři. Může se tedy vyplatit opustit absolutní adresace s nastaveným %{DOCUMENT_ROOT}, protože nám příkaz RewriteBase poskytuje větší modularitu našich přepisovacích pravidel.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

2 Příspěvků v diskuzi

  1. To je skvělé, díky, určitě by se to dalo napsat lépe už :) neskutečné, že tento článek je už skoro 10 let starý

Odpovědět