Relativní odkazy a chybová stránka
Školení, která pořádám
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
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.
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.
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.
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.
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
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
Odkazy začínající na lomítko označuji v článku (ne zcela správně) jako absolutní.
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/
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.
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.
Šikovné... Ja som prešiel od relatívnych v HTML cez relatívne v PHP a odkazy s absolútnou cestou až k BASEDIR.
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ů.
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?
Nejlepší je, když web běží jen na jedné adrese a z druhé je na ni přesměrování.
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
Na chybovou stránku se vloží <base href> z cestou zjištěnou z $_SERVER["SCRIPT_NAME"].
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.
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čů.
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 /.
fos4:
Ja pouzivam virtual host a host. Pokud mam web example.com tak pouzivam example.my..
Diskuse je zrušena z důvodu spamu.