Czym są przyjazne urle? Są to adresy w formacie http://strona.pl/nowosci/php/nowa-wersja-php-wydana, a nie tak standardowe i niewygodne adresy ze zmienny GET: http://strona.pl/index.php?module=news&category=php&title=nowa-wersja-php-wydana. Pierwszą rzeczą która skłania programistów do korzystania z przyjaznych urli jest to że są one rzeczywiście przyjazne. Można bardzo prosto zapamiętać taki link, znacznie łatwiej niż standardowy link ze zmiennymi GET. Wygodnie możemy na przykład umieszczać reklamy w gazetach - http://sklep.pl/konkurs. Drugą sprawą jest to iż przyjazne urle są łatwe do indeksowania dla wyszukiwarek. Roboty indeksujące strony wyżej zindeksują nasz pierwszy przykładowy link niż ten drugi, przestarzały rupieć ;)
Ok, po wstępnie zapewne czytelniku jesteś żądny wiedzy. Jak uzyskać takie urle? Bardzo prosto. Cała magia zawiera się w PATH INFO.
2 krótkie linijki:
Prosty skrypt który po odpaleniu, pokaże nam dump’a tablicy, którą utworzyliśmy rozbijając dane z patch info ;) Wiedząc skąd brać nasze wejście trzeba by to teraz jakoś obsłużyć.
Router – wygodny interfejs
Skoro wszystko trzymane jest w PATH INFO, wiemy również jak to przetworzyć i użyć. Jednak takie rozwiązanie nas w ogóle nie zadowala. Strukturalny kod, który nie wygodnie wdrożyć w systemie. Przydało by się napisać ładną klasę odpowiedzialną za wszystko związane z adresem. I tutaj pojawia się nazwa Router. Cóż to za cudo? Router w aplikacjach internetowych służy do analizowania URLi, oraz ich generowania.
Przedstawię teraz przykładową implementację Routera:
Zadeklarowaliśmy 4 właściwości przechowywujące odpowiednio, wejście, kontroler, akcję oraz tablicę z parametrami. Na końcu jedna stała która przechowuje separator. Dalej piszemy niezbędne metody:
Nie mam tutaj nic skomplikowanego. Krótki opis najważnieszych metod:
Wszystko gotowe, przykładowe użycie świeżo napisanej klasy jest bardzo proste.
Jeszcze tylko regułki mod_rewrite, odpowiedzialne za “usówanie” index.php z adresu:
Po wejściu na http://localhost/news/show-one/65234 powinniśmy zobaczyć wylistowane wartości które zostały dostarczone przez router. Wszystko działa jak powinno.
Jeszcze jedną rzeczą o której trzeba pamiętć jest umieszczenie w head tagu base, podając adres strony. Dzięki temu będziemy mogli użwać ścieżek względnych w adresach naszych obrazków, plików css etc.
Z czasem może się okazać że taka prosta funkcjonalność nam nie wystarczy i będziemy chcieli posiadać różne rodzaje URL. Przykładowo:
Zaczynają się schody, ponieważ w takim zapisie nie ma jakiejś ogólnej zasady. Każdy zbudowany jest inaczej i nie wiemy czy pierwsza pozycja to akacja, czy może parametr. A może być tak że w ogóle nie znajdziemy informacji w URL o kontrolerze bądź akcji, a są to przecież niezbędne informacje by nasz front kontroler mógł uruchamiać system. Dodatkowo na końcu dodaliśmy “.html” symulujące rozszerzenie, statycznej strony html, co ułatwia indeksowanie, ale całkowicie komplikuje naszą sytuację. Można to rozwiązać udoskonalając nasz router o wcześniej deklarowane drogi z wyrażeniami regularnymi, oraz drogami którymi mają zostać zastąpione. Zrobimy sobie dokładnie to samo co robi mod_rewrite w Apache :)
Można sobie pomyśleć że to nie jest łatwa rzecz - nic bardziej mylnego! Prostota implementacji zaskoczyła nawet mnie w momencie gdy programowałem i okazało się że rozwiązanie jest aż tak łatwe.
Zasada działania jest bardzo prosta. Otrzymujemy na wejściu /news/534. A chcemy uruchomić moduł News i akcję ShowOne czyli url powinien wyglądać następująco: /news/showone/534. Potrzebujemy więc przepisać pierwszy URL na drugi i po problemie.
RewriteRouter jest klasą dziedziczącą po NiceUrlsRouter który napisaliśmy przed chwilą. Dodajemy właściwość przetrzymującą reguły, metody do zarządzania regułami, oraz nadpisujemy metodę parse która porównuje URL wejściowy i jeżeli zgadza się on to podmienia go. Oto cała klasa:
Używanie tego routera różni się jedynie tym że przed parsowaniem powinniśmy zadeklarować regułki:
Nasz router przy parsowaniu wykonuje teraz jedną czynność więcej. Sprawdza czy input zgadza się z wyrażeniem regularnym zawartym w regule. Jeżeli tak, to podmienia do z url zadeklarowanym do podmiany i ten url daje do parsowania metodzie rodzica - NiceUrlsRouter::parse().
Stworzone w artykule routery są bardzo proste. Służą one raczej jako przykładowa implementacja. Można do nich dodać bardzo dużo innych funkcjonalności. Jako przykład może posłużyć świetny router frameworka Agavi. W porównaniu do naszych 2 routerów routing w Agavi jest o wiele bardziej złożony i posiada większe możliwości. Całość składa się z kilkunastu plików, a drogi definiuje się w oddzielnym pliku xml. Można tam zagnieżdżać drogi, ustawić callback, opcje odpowiedzialne za i18n ;)
Komentarze
ot, ciekawostka:
zamiast
if(substr($input, 0, 1) == ‘/’){
$input = substr($input, 1);
}
if(substr($input, -1) == ‘/’){
$input = substr($input, 0, -1);
}
proponuję używać po prostu trim($input, NiceUrlsRouter::URL_DELIMITER);
wszystko fajnie - ale jedna rzecz chyba nie tak - ? w mod rewrite - uniemożliwia on wykorzystanie metody get w php np w formularzach. co prawda w wypadku użycia maskowania prawdziwych adresów get przyda się raaczej rzadko - ale czasem sie jednak przyda (wyszukiwarka?)
Ja_Szczur: trim usuwa spacje, a w prepare usuwam z końca slashe. Po co? Ano po to żeby robiąc później explode nie miał pustych wpisów w tablicy.
greensky: nie pamiętam już kiedy ostanio używałem GET’a. A wyszukiwarka? Żaden problem - index.php/szukaj/jak+napisac+router+w+php/.
Pozdrawiam.
Strzałek
1. Proponuje zacząć od nauki języka polskiego:
Po wejściu na http://localhost/news/show-one/65234 i powinniśmy wylistowane wartości które zostały dostarczone przez router
2. nauki gramatyki - usówa
3. Zaglądania do manuala - trim usówa spacje, a w prepare usuwam z końca slashe -> trim — Strip whitespace (or other characters) from the beginning and end of a string
a przykład użycia podał Ci Ja_Szczur
a tu masz link jak nie umiesz znaleźć - http://pl.php.net/manual/pl/function.trim.php
Ano przyznaję Ci rację. Zawsze żyłem w świadomości że trim usuwa tylko spacje ;)
do “miś uszatek”: od kiedy błąd typu “usówa” jest błędem gramatycznym? Odsyłam Cię do podstawówki… Po wiedzę.
a to moje zakręcone rozwiązanie :)
function parsePath ( $url,$pat,$def )
{
$arr = array_fill ( 0, ( ( sizeof ( $url ) >sizeof ( $pat ) ) ?sizeof ( $url ) :sizeof ( $pat ) ) ,” ) ;
$result3 = array_combine ( ( ( array ) $pat + array_keys ( $arr ) ) , ( array ) $url + $arr ) ;
foreach ( $result3 as $key = > $value ) if ( $value = = NULL ) {
unset ( $result3[$key] ) ;
}
$size = sizeof ( $result3 ) -1;
foreach ( $result3 as $k = >$v ) {
if ( is_int ( key ( $result3 ) ) ) {
if ( key ( $result3 ) ‘pl’ ) ;
print_r(parsePath($url,$pat,$def));
przepraszam nie skopiowalo się jak należy
function parsePath ( $url,$pat,$def )
{
$arr = array_fill ( 0, ( ( sizeof ( $url ) >sizeof ( $pat ) ) ?sizeof ( $url ) :sizeof ( $pat ) ) ,” ) ;
$result3 = array_combine ( ( ( array ) $pat + array_keys ( $arr ) ) , ( array ) $url + $arr ) ;
foreach ( $result3 as $key = > $value ) if ( $value = = NULL ) {
unset ( $result3[$key] ) ;
}
$size = sizeof ( $result3 ) -1;
foreach ( $result3 as $k = >$v ) {
if ( is_int ( key ( $result3 ) ) ) {
if ( key ( $result3 ) ‘pl’ ) ;
parsePath($url,$pat,$def);
Polecam zabezpieczyć komentarze. Dużo elementów poprostu wcięło. Tu jest mój zakręcony parser:
http://baael.freeweb7.com/
przepraszam za zamieszanie.
Warto dodac dodatkowe regulki mod_rewrite w przypadku, gdy nasza strona www korzystala z adresow typu example.com/blah=12&costam=15&gdziestam=17, a teraz korzystamy z “ladnych” example.com/costam/gdzestam - w ten sposob uzytkownik zostanie automatycznie przekierowany na “nowa” wersje strony (gdy trafi do nas np. z wyszukiwarki lub z linku na innej stronie).