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.

Jakub Vrána, Dobře míněné rady, 16.4.2008, diskuse: 27 (nové: 0)

Diskuse

ikona 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.

ikona 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.

ikona Jakub Vrána OpenID:

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.

ikona Jakub Vrána OpenID:

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.

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL: Reakce na: afu

ikona 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

ikona 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

ikona Jakub Vrána OpenID:

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

ikona 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/

ikona Jakub Vrána OpenID:

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.

ikona 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.

ikona 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?

ikona Jakub Vrána OpenID:

Nejlepší je, když web běží jen na jedné adrese a z druhé je na ni přesměrování.

ikona Jakub Vrána OpenID:

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

ikona Jakub Vrána OpenID:

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.

ikona Jakub Vrána OpenID:

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čů.

ikona 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 /.

ikona 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..
avatar © 2005-2019 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.