Niewielka sztuczka, którą od zeszłego roku stosuję w PHP, pozwala na łatwe budowanie czytelnych linków. Dzięki temu twój odnośnik zamiast http://example.com/index.php?cat=shop&id=25, może wygląć tak: http://example.com/shop/25, albo nawet tak: http://example.com/shop/25/swieze_jaja_wiejskie.
Oczywiście, to samo można zrobić za pomocą mod_rewrite, ale wada rozwiązania jest taka, że dodanie nowej sekcji do serwisu wymaga ingerencji w pliki serwera WWW.
Mój niewielki hack polega na dodaniu do konfiguracji Apache’a jednej jedynej linijki (dla ułatwienia umieszczamy ją w pliku .htaccess):
ErrorDocument 404 /index.php
Jak można się domyślić, plik index.php jest jedynym plikiem w całym drzewie dokumentów danej domeny i od tego momentu odpowiada on na wszelkie zapytania o nieistniejące dokumenty.
Pozostało jeszcze zatroszczyć się o obsługę tego po stronie kodu:
// sprawdzamy, o jaki dokument jest zapytanie
$request = $_SERVER['REDIRECT_URL'];
// sprawdzamy, czy zapytanie dotyczy nieistniejacego pliku
if (($request) && ($request != '/'))
{
$request = explode('/', $request);
$section = $request[1];
$request = array_splice($request, 2);
$subsection = implode('/', $request);
}
else // jesli nie, to przekierowujemy na index
{
header('Location: /index');
die();
}
// obslugujemy zadanie
switch($section)
{
case 'index':
// [...]
break;
case 'shop':
// [...]
break;
default:
// [...] wyswietl blad 404
break;
}
Zalety - rozwiązanie jest nieinwazyjne względem serwera i pozwala strukturę dokumentów kontrolować na poziomie kodu. Dzięki temu łatwo utrzymywać strukturę, którą ciężko byłoby zarządzać na poziomie mod_rewrite (np. tłumaczone nazwy kategorii).
Wady - nieistniejące strony nie mogą otrzymywać danych przez zlecenia GET i POST, więc wszystkie parametry muszą być częścią URI.
Najciekawsze efekty uzyskuje się przez połączenie tak serwowanych dokumentów ze strukturą statyczną (istniejące pliki nie są przekierowywane). Można zachować wtedy elastyczność rozwiązania z możliwością przekazywania danych z przeglądarki. Dzięki temu możliwa jest obsługa formularzy na stronach.

IMHO paskudne rozwiązanie. To samo można zrobić w inny sposób (nawet przy użyciu mod_rewrite nie musisz URLa przemieniać na "skrypt.php?param1=xxx¶m2=yyy"). Jesteś miłośnikiem semantyki, więc dziwię ci się, że ta sztuczka ci się podoba… W końcu używasz dyrektywy ErrorDocument wbrew jej przeznaczeniu.
Phi, servlety takie coś dają za darmo :> Rok temu napisałem, że tak powiem, silnik, który to stosuje. Serwis jest duży, prawie cały znajduje się w bazie danych, ale z zewnątrz wygląda jak stos plików .html ;)
Brakowało mi kiedyś GET i POST dlatego wole mod_rewrite i jedna linijka:
RewriteRule ^(.*)\.html /index.php?id=$1
Jajcuś:
Nie zgodzę się z faktem, że stosuję ją wbrew przeznaczeniu. Wysyłam prawidłowy nagłówek HTTP 200 OK.
Dyrektywa konfiguracji Apache służy właśnie do przejęcia informacji o nieistniejącym pliku.
ErrorDocument nie jest najszczęśliwszą nazwą dla zmiennej, ale żadnego RFC nie złamałem.
I ja przytaknę Patrysiowi.
Ale dodam, że można to osiągnąć inaczej, chyba wygodniej. Tworzymy skrypt shop.php i w nim analizujemy zawartość $_SERVER['PATH_INFO']. W tym momencie adresy mają postać http://…/shop.php/xxx/yyy. Żeby pozbyć się rozszerzenia włączamy MulstiViews w Apache`u.
"Patrysowi" - prooooszęęęęę…
Fajne, i praktyczne. Przyda się na serwerach z wyłączoną obsługą mod_rewrite (np. http://boo.pl). Dzięki.
Zapomniałem dopisać, że zanim zaserwujemy dokument, należy ustawić stosowny nagłówek odpowiedzi:
header('HTTP/1.1 200 OK', true);
"Oczywiście, to samo można zrobić za pomocą mod_rewrite, ale wada rozwiązania jest taka, że dodanie nowej sekcji do serwisu wymaga ingerencji w pliki serwera http://WWW."
Bzdura. Właśnie sprawdziłem.
RewriteEngine On
RewriteRule ".*" "/test.php"
RewriteRule może być też użyte w <Directory> i w .htaccess, więc jest naprawdę bardzo elastyczne.
I wszystkie URLe lądują do test.php, gdzie oryginalna ścieżka jest dostępna w $_SERVER['REQUEST_URI']. IMHO jest to dużo bardziej eleganckie rozwiązanie niż użycie ErrorDocument. A nawet bezpieczniejsze — na tak skonfigurowanym serwerze nie ma szans, żeby user dobrał się do jakiegoś pliku w DocumentRoot, do którego nie powinien zaglądać. Używając ErrorDocument, to do końca nie wiadomo do czeog user się odwołuje, bo to zależy od tego, czy jakiś plik istnieje, czy nie. A jak w jakimś serwisie część URLi jest obsługiwana tak, a reszta siak… to chyba nie jest najlepiej ten serwis zaprojektowany.
Jajcuś:
Część URI zależy od tego, co user wpisze w swoim CMS, reszta jest sztywna (obsługa głosowania w ankiecie czy formularz kontaktowy).
Dlatego nie mogę przekierować gwiazdki.
Dokumenty, do których ktoś nie ma mieć dostępu trzyma się poza DocumentRoot.
"Dokumenty, do których ktoś nie ma mieć dostępu trzyma się poza DocumentRoot."
To ja wiem… ale widziałem już takie rzeczy, że wolę założyć że ktoś tam może coś głupiego trzymać.
No w każdym razie nie zmienia to faktu, że nie mogę przekierować gwiazdki na żaden plik, bo część ma być fizycznymi plikami na serwerze :]
A mnie się to podoba - nie raz miałem taki problem, że mod_rewrite był niedostępny. A tak wystarczy tylko obsługa PHP :)
Brzydkie. U Jarka Zgody jakiś czas temu było podane rozwiązanie poprawne, nie nadużywające 404. http://zgoda.jogger.pl/comment.php?eid=99976
zdzichuBG:
To nie nadużywa 404, bo do klienta leci 200, nadużywa tylko wewnętrznej struktury Apache, tak samo zresztą jak rozwiązanie Zgody.
W przeciwieństwie do tamtego, moje rozwiązanie pozwala na przepisywanie dowolnych URL, nie wymusza wspólnego prefiksu, a poza tym pozwala na serwowanie /lorem/ipsum/dolor z przepisywaniem, a /lorem/ipsum/sit jako fizyczny plik, co mi jest potrzebne.
Do klienta leci, i co z tego? W logach serwera co masz? Burdel.
W logach serwera mam 200 OK, pokazać statystyki? To zostało sprawdzone i działa, dlatego w ogóle o tym pisałem.
Yep, moja strona domowa jest tak zbudowana i logi wyglądają normalnie. Jedyny problem to brak np. $_POST.
No właśnie, a zgodowe rozwiązanie nie eliminuje POSTów. Przykładowo -> http://mamma.eti.pg.gda.pl/~zdzichu/DNS/domena/edytuj/mci.com.pl (login/pass rwuser/rwuser) - wszystko na POSTach.
Patrys, w Twoim rozwiązaniu Apache serwuje 404 i nie odnotowuje tego w error_log? Dziwne.
Trudno mi powiedziec, co sadze o tym rozwiazaniu. Rozumiem, ze pewne czynniki zewnetrzne moga uniemozliwiac wykorzystanie "Bozych" sposobow, jak czysty mod_rewrite… moze tak: mam nadzieje, ze nigdy nie bede zmuszony korzystac z Twojego hacka, Patrys ;)
A skryptu PHP nie mozna dac jako DirectoryHandler? To tez definiowalne w .htaccess, a duzo zgrabniej…