Relativní odkazy a chybová stránka
Při vytváření interních odkazů používám relativní cesty a ne absolutní. Jednak je s tím méně psaní a jednak mi odkazy správně fungují např. i na adrese http://localhost/projekt/
. Výborně se to používá za předpokladu, že jsou všechny stránky na stejné úrovni.
Tento předpoklad ale není splněn v případě, že pomocí direktivy ErrorDocument definujeme chybovou stránku se stejným vzhledem jako má zbytek webu. Stránka se totiž zobrazí i v podřízených adresářích (např. img/chybi.html
) a relativní odkazy tím pádem povedou do daného adresáře.
Vyřešit se to dá tak, že pro tuto chybovou stránku definujeme ve značce <base href="">
jiný základ pro výpočet relativních odkazů:
<?php if ($_SERVER["REDIRECT_STATUS"] == 404) { echo "<base href='http://$_SERVER[SERVER_NAME]" . preg_replace('~[^/]*$~', '', $_SERVER["SCRIPT_NAME"]) . "' />\n"; } ?>
Pokud má web různé stránky v různých úrovních, je samozřejmě pohodlnější vytvářet absolutní odkazy a pro vývoj používat pojmenovaného virtuálního hosta např. http://projekt-dev
.
Diskuse
Petr Soukup:
Osobně před všemi odkazy uvádím lomítko. Funguje to bezpečně i "ve vnořených složkách" při použití mod_rewrite, přežije to bez problémů přesun na jinou doménu (popř. localhost) a obecně s tím není problém.

Michal Aichinger:
No lomítko je sice pěkné ale na tím pádem nemůžeš vyvíjet na localhostu tak jako Jakub. Tedy projekty umístěné v adresářích (http://localhost/projekt/) jsou pro tebe tabu :-) Radši tedy nemať čtenáře.

Ondřej Brejla:
Snad to chápu správně, ale pokud píšu na localhostu, tak mi přijde pohodlnější nastavit si pro každý projekt virtual host. Mohu pak beztrestně využívat adresy s lomítkem na začátku a přenositelnost není problémem.afu:
nejsou tabu, stačí si cestu z document root do podadresáře s projektem nadefinovat jako konstantu a používat ji coby součást odkazů, např.:define(BASEDIR,'/projekt/');
...
echo BASEDIR.'odkaz.php?id='.$id;
při přesunu do jiného adresáře pak stačí jen změnit hodnotu konstanty.
Jakub Vrána
:
Tomuto přístupu bych se za každou cenu vyhnul a to i ve variantě, kdy by se BASEDIR získával dynamicky. Je to spousta psaní a starostí navíc a nic z toho.


Vojtěch Vysloužil:
Já naopak dynamické získávání BASEDIR vřele doporučuji :-) Toho psaní navíc zase tak moc není, starosti s tím nejsou + získate výhody, které už uvedl Petr Soukup.
Jakub Vrána
:
U každého odkazu psát místo <a href="archiv.php"> ošklivé <a href="<?php echo BASEDIR; ?>archiv.php"> za spoustu psaní považuji. Naopak <a href="/archiv.php"> by bylo z pohledu psaní OK.


vkuzel:
Používám přístup s použitím BASEDIR. Odkazy generuju v šablonovacím systému dynamicky, protože mám několik modulů, snažím se tak předejít tomu aby se dvě stránky se shodnými názvy z různých modulů tloukly, tzn. jedné se automaticky přiřadí nějaký prefix.V šabloně mám tedy záspis <a href="{href page="nazevStranky" module="nazevModulu"}">...
Abych nemusel při každém přesunutí projektu nahánět konfiguraci BASEDIR, případně, abych předešel problémům které popisuje Martin Hassman ve svém příspěvku z 17.4, získávám tuto hodnotu automaticky pomocí nějakého skriptu, který jsem kdesi stáhnul z webu.
<?php
function getURL()
{
$s = empty($_SERVER["HTTPS"]) ? '' : ($_SERVER["HTTPS"] == "on") ? "s" : "";
$protocol = substr(strtolower($_SERVER["SERVER_PROTOCOL"]), 0, strpos(strtolower($_SERVER["SERVER_PROTOCOL"]), "/"));
$port = ($_SERVER["SERVER_PORT"] == "80") ? "" : (":".$_SERVER["SERVER_PORT"]);
return $protocol."://".$_SERVER['SERVER_NAME'].$port.$_SERVER['REQUEST_URI'];
}
?>
Výsledek této funkce, lze potom jednoduše rozložit funkcí parse_url().
Nevýhody:
* Komplikovanější zápis, někdo cizí musí nastudovat filosofii, jakou se odkazy zapisují, než může nějaký vytvořit.
Výhody:
* Funguje to, při přesunu projektu do jakéhokoli adresáře nebo na jakoukoli doménu, bez nutnosti konfigurace (snad).
* Lze snadno zapnout/vypnout/upravit rewrite, protože odkaz je výsledkem funkce. Kvůli tomuto bodu jsem to celé dělal. Předtím jsem též používal relativní cesty, ale začalo mi to dělat brikule se styly externími javascripty a pod., když jsem chtěl použít rewrite ve tvaru /server/stranka/polozka/
afu:
V tom případě chápu, jako uživatel Smarty to vidím pouze jako {$bd}odkaz.php. Každý projekt ale vyžaduje své, proto basedir používám tam, kde nemám zaručenou práci v kořeni webu (na dev i ostrém serveru), u těch ostatních začínám cestu přímo lomítkem. Miniaplikace, které po mě mohou upravovat jiní pak používám relativní odkazy.
Petr Soukup:
Jak už to padlo, tak pohodlné řešení je virtualhost. Ale pokud se nepletu, tak jeden řádek v mod_rewrite by to hravě zvládl taky

Techi:
Jsem rád, že tohle už s MVC přístupem v Zend Frameworku řešit nemusím :)
Všechny odkazy jsou relativní a začínají s lomítkem.
Pokud chci web někam přemístit, stačí jenom v konfiguračním souboru změnit baseUrl

Jakub Vrána
:
Odkazy začínající na lomítko označuji v článku (ne zcela správně) jako absolutní.


David Grudl:
Je to sice *relativní* odkaz, ale tvořený *absolutní* cestou. Relativní odkaz je vlastně i //example.com/produkt/123. Takže je možná lepší v článku diskutovat relativní a absolutní cesty, namísto odkazů.
Každopádně s relativními odkazy je docela kříž, viz třeba http://php.vrana.cz/index.php/

Jakub Vrána
:
Toto rozlišení mi chybělo, napadal mě pouze relativně-absolutní odkaz :-).
Za upozornění na slabé místo blogu děkuji, chybovou stránku tam vkládám programově, takže se automaticky nenastavil REDIRECT_STATUS.


David Grudl:
Tak tady máš další slabé místo http://php.vrana.cz/index.php/test/ ;)
Snažil jsem se tím naznačit, že než vynakládat obrovské úsilí na vychytání problémů s relativními cestami, je lepší používat cesty absolutní. Myslím že to je i snadnější, zmíněné echo BASEDIR, 'odkaz.php' zase tolik nebolí.
Já používám trik, kdy v šabloně píšu absolutní cesty a pak je jedním regulárním výrazem nahradím za BASEDIR. Cca takto:
$s = preg_replace('#(src|href|action)\s*=\s*"/#', '$1="' . $basePath, $s);
Rychlé, kešovatelné, přenositelné, a hlavně: bez relativních cest.

tiso:
Šikovné... Ja som prešiel od relatívnych v HTML cez relatívne v PHP a odkazy s absolútnou cestou až k BASEDIR.

Martin Hassman:
Pár let nazpátek bych s tebou bez výjimky souhlasil, než jsem jednou narazil. Problém může nastat, pokud je web přístupný přes reverzní proxy (třeba na základě nějakých politik organizace).
Kdysi jsem dělal http://15esf.vscht.cz/ (dostupné jen z intranetu), které bylo z internetu ve výsledku vidět jako http://www.vscht.cz/15esfc/ - na veškerých absolutních dotazech jsem samozřejmě pohořel (směřovaly na úplně odlišný webserver než ten můj, proxy předávala pouze dotazy z adresáře 15esfc). Od té doby vím, že absolutní URL jen v 80% případů.
ajs:
Jaka je z toho plyne ponauceni :) ? Jak elegantne zaridit aby fungoval web zaroven na adrese xyz.domena.cz a www.domena.cz/xyz pokud ma stranky v ruznych urovnich. jak na odkazy?
Jakub Vrána
:
Nejlepší je, když web běží jen na jedné adrese a z druhé je na ni přesměrování.


Jakub Vrána
:
Tak napotřetí snad už :-).
Trik je prima do té doby, než začneš řešit problémy s tím, že se ti nepřekládá <a href='/'> nebo <param name="movie" value="/movie.swf">…


JsemNikdo :):
Mohu se zeptat, jak jsi opravil web.cz/index.php/ ?Na svých webech mám web.cz/index.php?page=... , ale na tuhle bezpečnostní chybu nevím jak vyzrát. Děkuji
Jakub Vrána
:
Na chybovou stránku se vloží <base href> z cestou zjištěnou z $_SERVER["SCRIPT_NAME"].


Martin Hassman:
Base a funguje ti to dobře v prohlížečích? Já si pamatuji, že svého času s tím byl docela problém, a proto jsem se base vyhýbal, ale možná že se mezitím situace zlepšila.
Jakub Vrána
:
Já si naopak nepamatuji, že by s tím kdykoliv jakýkoliv problém byl (tedy pokud se uvede absolutní URL). Tuto značku s oblibou používají např. cache vyhledávačů.


v6ak:
Já osobně mám něco jako webroot.cfg.php, kde napíšu <?php return 'cestaDoKorenu'; ?>. Místo cestaDoKorenu píšu doma /projekt/ a na hostingu /.

Maxell:
Já k překladu využívám výhod šablon, kdy všechny adresy píšu s / a na localhostu se automaticky nahradí za /projekt/. Viz http://blog.maxell-cz.cz/item/absolutni-adresy-a-mod-rewrite

fos4:
Ja pouzivam virtual host a host. Pokud mam web example.com tak pouzivam example.my..Diskuse je zrušena z důvodu spamu.

