absolutní adresy a mod rewrite

Při vývoji aplikace, která počítá s přepisováním adres pomocí mod_rewrite (Cool URL, přátelské adresy), je většinou nutné řešit problém s relativními a absolutními cestami. Projekt obvykle běží na vlastní doméně, např.
www.exaple.com. URL stránky potom může vypadat třeba takto:
www.example.com/clanek/adresa-clanku/. Všechny adresy ve stránce (cesty k obrázkům, CSS stylům, ...) potom musí být zapsané tak, aby ukazovaly do kořenového adresáře webu, jinak nebudou fungovat. To lze vyřešit více způsoby, nejoblíbenější je použití lomítka k určení absolutní cesty
(<img src="/obrazky/obrazek.jpg" alt="" />).

Problém nastává v případě, kdy se projekt vyvíjí na lokálním počítači. Na něm existuje více různých aplikací vedle sebe. Běžně jsou potom jednotlivé weby v oddělených adresářích, takže adresa potom vypadá nějak takto:
http://localhost/nazev-projektu/. Výše zmíněné lomítko potom ale bude ukazovat na adresu http://localhost/, což způsobí nefunkčnost aplikace.

Existuje více řešení popsané situace. Mě se osvědčilo využít spojení se šablonami SMARTY.

V každém projektu mám soubor settings.php, ve kterém definuji jeho základní nastavení. Mimo jiné je v něm i tento kód:
if($_SERVER["HTTP_HOST"] == "127.0.0.1" || $_SERVER["HTTP_HOST"]== "localhost") {
  //localhost - predpoklada cestu http://localhost/adresar/
  $url = explode("/", $_SERVER["REQUEST_URI"]);
  $rewrite = "/" . $url[1];
  $smarty->register_outputfilter('f_rewrite');
}
else
  $rewrite = false;

Pokud je podmínka platná, uloží se do proměnné $rewrite adresář projektu (včetně počátečního lomítka), v opačném případě se proměnná nastaví na false.

Nejdůležitější řádek je umístěn uvnitř podmínky -
$smarty->register_outputfilter('f_rewrite');. Tento kód nastaví tzv. výstupní filtr. Znamená to, že před samotným zobrazením šablony (= HTML výstup stránky) se zavolá PHP funkce f_rewrite.

Ptáte se, k čemu je to dobré? V momentě, kdy se zavolá tato funkce, je již šablona kompletně zpracovaná, a funkce tak může modifikovat HTML kód. Toho lze s výhodou využít k převedení adres na požadovaný tvar:

function f_rewrite($tpl_source, &$smarty) {
  $nahrada = $GLOBALS["rewrite"] . "/";

  $vyrazy = array (
    '~<(img)( [^>]*)? (src)="/([^"]+)"([^>]*)>~i',
    '~<(a)( [^>]*)? (href)="/([^"]+)?"([^>]*)>~i',
    '~<(link)( [^>]*)? (href)="/([^"]+)"([^>]*)>~i',
    '~<(script)( [^>]*)? (src)="/([^"]+)"([^>]*)>~i',
    '~<(form)( [^>]*)? (action)="/([^"]+)"([^>]*)>~i',
    '~<(input)( [^>]*)? (src)="/([^"]+)"([^>]*)>~i'
  );
  $replace = "<\\1\\2 \\3=\"$nahrada\\4\"\\5>";
  foreach ($vyrazy as $key=>$vyraz) {
    $tpl_source = preg_replace($vyraz, $replace, $tpl_source);
  }
  return $tpl_source;
}

Funkce bere 2 parametry - $tpl_source je HTML kód šablony, $smarty potom objekt SMARTY. Samotné tělo se skládá z definice regulárních výrazů a jejich následným nahrazením v požadovaném formátu.

Pojďme si rozebrat třeba řádek
'~<(img)( [^>]*)? (src)="/([^"]+)"([^>]*)>~i':

  • Samotný výraz začíná hranatou závorkou, která značí začátek HTML tagu,
  • (img) je název HTML tagu. Závorky uloží obsah do proměnné k pozdějšímu použití (viz. dále),
  • ( [^>]*)? je trochu složitější výraz, ale ne moc :-) Rozebereme ho zevnitř:
    • [^>] znamená jakýkoli znak kromě >,
    • * určuje možný vícenásobný výskyt předchozího, [^>]* tedy znamená žádný, nebo více znaků, neobsahující >,
    • závorky ukládají svůj obsah do proměnné,
    • ? značí, že celá sekvence je nepovinná (žádný nebo jeden výskyt předchozí sekvence),
    • celý výraz slouží pro zachycení atributů obrázku, jako například <img class="x"...>
  • mezera odděluje předchozí část od atributu src, který se opět uloží do proměnné,
  • následuje trojice znaků (="/), která nemá speciální význam, hledají se znaky rovnítka, uvozovek a lomítka,
  • ([^"]+) je podobné předchozímu výrazu:
    • [^"] znamená jakýkoli znak kromě ",
    • přepínač + určuje minimálně jeden výskyt, [^"]+ dohromady znamená alespoň jeden znak jiný, než uvozovky,
    • tento výraz určuje samotnou cestu, např.
      <img src="/img/obrazek.jpg" /> (všimněte si, že nezachycuje první lomítko - o to se postará znak před touto sekvencí -, ani poslední uvozovku),
  • znak " uzavírá atribut,
  • poslední sekvence ([^>]*) je stejná jako ta výše, ukládá další atributy - například <img src="/img/obrazek.jpg" alt="" /> (včetně mezery a koncového lomítka),
  • celý výraz uzavírá značka > pro konec tagu.

Pole $vyrazy obsahuje definice pro nejdůležitější HTML značky. Pokud by nějaká chyběla, není problém jí doplnit. Regulární výraz by se dal napsat i jednodušeji (jeden pro všechny značky), tento kód ale dává větší flexibilitu při určování, jaké všechny tagy se budou nahrazovat.

Za definicí pole následuje cyklus, který projde všechny jeho položky a použije je do následující konstrukce:
$tpl_source = preg_replace($vyraz, $replace, $tpl_source);
Funkce preg_replace nahradí všechny výskyty vzoru (proměnná $vyraz) hodnotou (proměnná $replace) a vrátí takto upravený řetězec.

Hodnota je textový řetězec "<\\1\\2 \\3=\"$nahrada\\4\"\\5>". Nejzajmavější v něm jsou proměnné, např. \\1. V regulárním výrazu se určité sekvence uzavřely do závorek. Tyto části jsou přístupné právě přes proměnné - \\1 se tak nahradí obsahem první závorky, \\2 obsahem druhé závorky atd. Pokud tedy srovnáme regulární výraz
'~<(img)( [^>]*)? (src)="/([^"]+)"([^>]*)>~i' a
"<\\1\\2 \\3=\"$nahrada\\4\"\\5>" s konkrétním HTML tagem
<img class="xx" src="/img/obrazek.jpg" alt="" />, dostaneme toto:

  • Prvním znakem je v obou případech závorka <, ta tedy v novém řetězci zůstane - <,
  • v reguláru následuje (img), což uloží img do proměnné \\1. V řetězci tak vznikne <img,
  • ( [^>]*)? zachytí atribut class, \\2 ho vloží do řetězce -
    <img class="xx",
  • (src) a \\3 vloží do řetězce mezeru a atribur src -
    <img class="xx" src,
  • v regulárním výrazu i v hodnotě následují znaky =" (ve výrazu je znak uvozovky escapován zpětným lomítkem - to ruší jinak speciální význam uvozovky), ty se tedy přidají do řetězce - <img class="xx" src=",
  • v nahrazované hodnotě se objevuje PHP proměnná $nahrada. Ta obsahuje název adresáře včetně počátečního a koncového lomítka. Řetězec se tedy modifikuje takto:
    <img class="xx" src="/nazev-projektu/,
  • \\4 přidá původní adresu:
    <img class="xx" src="/nazev-projektu/img/obrazek.jpg,
  • vloží se obsah proměnné \\5, ve které jsou další atributy -
    <img class="xx" src="/nazev-projektu/img/obrazek.jpg" alt="" /,
  • řetězec se uzavře > -
    <img class="xx" src="/nazev-projektu/img/obrazek.jpg" alt="" />.

Jak je vidět, změnila se pouze cesta obrázku tak, že nyní již ukazuje do adresáře projektu a bude tedy fungovat i na lokálním počítači. Obdobně se převedou všechny cesty tagů, které jsou uvedené v poli $vyrazy.

V HTML šabloně se všechny adresy zapisují absolutní cestou -
<img class="xx" src="/img/obrazek.jpg" /> a o nic víc se není třeba starat. Nahrazují se pouze absolutní cesty začínající /, adresy s http:// zůstanou v původním stavu.

Největší výhodou tohoto postupu je, že nahrazení se provádí pouze na lokálním počítači, při ostrém provozu na serveru tedy nedochází k žádné zátěži navíc. Kodér se také nemusí zdržovat psaním navíc, jednoduše vše píše s lomítky.

 

vydáno 19.08.08 - Programování - 27760x - trvalý odkaz trvalý odkaz
Karma: 17. Líbil se vám článek? [ano/ne]
RSS komentářů článku - vytisknout - Odeslat emailem

Komentáře:

  • komentováno 20.08.08, 20:14:59
    Sevirjo

    Díky za postup, něco takového jsem docela dlouho hledal. Já jsem tenhle problém řešil pomocí "<?php echo $dir ?>/odkaz". To je ale poměrně dost práce navíc a kód není přehlednej. Tenhle článek mě přesvědčil k používání šablon, který jsem už sice vyzkoušel, ale při práci nepoužil.

    Odpověz na komentář
  • komentováno 30.06.09, 11:53:12
    gmo

    Cau, ja tenhle problem resil pomoci direktivy VirtualHost v nastaveni Apache, viz google

    Odpověz na komentář
  • komentováno 29.11.09, 15:57:04
    I.C.

    Není mnohem jednodušší využívat standartního
    <base href="http://domena.cz/ " />
    a pak všude používat jen relativní cesty vzhledem k výše uvedenému? ;-)

    Odpověz na komentář
    1. Na komentář reagoval Maxell — #4
  • komentováno 29.11.09, 20:41:33
    Maxell

    Odpověď na I.C. — #3 Značku base určitě není dobré používat, například některé vyhledávače s tím mohou mít problém. Pokud vím, důležité vyhledávače to podporují, ale rozhodně bych se na ni nespoléhal.

    Odpověz na komentář

Nový komentář:

Komentář čeká na schválení.

Tento článek byl uzavřen. Už není možné k němu přidávat komentáře ani hlasovat


Nejoblíbenější články

Poslední komentáře

  • Maxell: [3] Značku base určitě není dobré používat, například některé vyhledávače s tím mohou mít problém. Pokud vím, důležité vyhledávače to podporují, ale rozhodně bych se na ni nespoléhal....
  • I.C.: Není mnohem jednodušší využívat standartního <base href="http: domena.cz " > a pak všude používat jen relativní cesty vzhledem k výše uvedenému? ;-)...
  • gmo: Cau, ja tenhle problem resil pomoci direktivy VirtualHost v nastaveni Apache, viz google...
  • Maxell: [1] Ano, zkoušel jsem to na více místech a chová se to všude stejně. Vyřešil jsem to tak, že jsem zobrazil pouze Nejbližší shodu, která by měla fungovat....
  • Maxell: [3] České vyhledávače description opravdu zatím nevyužívají. U Seznamu o tom již delší dobu uvažují, nicméně k realizaci je stále ještě daleko. Jak je psáno v článku, description by vždy mělo shrnovat obsah dané stránky. Každá kategorie a podkategorie by tak ideálně měla mít vlastní popisek, stejně tak např. každý výrobek. Vámi navrhované texty jsou také poměrně krátké, rozhodně bych je prodloužil na cca 100 - 150 znaků....

Poslední články

Nejlépe hodnocené

Nejčtenější

Copyright © Maxell | Maxell-cz | Design
Web-Design Blog - Blog nejen o webdesignu