Przepisywanie adresów URL w PHP

139

Mam adres URL, który wygląda następująco:

url.com/picture.php?id=51

Jak bym się zajął konwersją tego adresu URL na:

picture.php/Some-text-goes-here/51

Myślę, że WordPress robi to samo.

Jak mam zrobić przyjazne adresy URL w PHP?

Jazerix
źródło
1
Musisz dużo poprawić w konfiguracji plików Apache ... podać przykład tego, czego faktycznie potrzebujesz? Jak wstawiasz ten tekst? czy jest predefiniowany czy tworzony dynamicznie? podaj jak najwięcej informacji, aby uzyskać właściwą odpowiedź ... tak, adres URL może być nieco bardziej opisowy :)
user123_456
1
Dlaczego PHP? Apache mod_rewriteto wszystko, czego potrzebujesz do tego i jest wiele pytań dotyczących tego tutaj, na SO. A jeśli „myślisz”, że wordpress robi to samo, dlaczego po prostu go nie sprawdzisz ? ;)
nietonfir
przeczytaj mod_rewrite
Dlaczego to robisz? Czy celem jest przepisanie adresu URL, jeśli tak, użyj .htaccess lub czegoś podobnego. Jeśli celem jest tylko zmiana ciągu, $ _GET pobiera tylko querystring?
adeneo
Tekst będzie tytułem obrazka, a następnie id po ukośniku :) Muszę sprawdzić, co ma do zaoferowania mod_rewrite Apache. Mam nadzieję, że to nie jest zbyt trudne: D
Jazerix

Odpowiedzi:

194

Zasadniczo możesz to zrobić na dwa sposoby:

Trasa .htaccess z mod_rewrite

Dodaj plik o nazwie .htaccessw folderze głównym i dodaj coś takiego:

RewriteEngine on
RewriteRule ^/?Some-text-goes-here/([0-9]+)$ /picture.php?id=$1

To powie Apache, aby włączył mod_rewrite dla tego folderu, a jeśli zostanie poproszony o adres URL pasujący do wyrażenia regularnego, przepisuje go wewnętrznie do tego, co chcesz, bez oglądania go przez użytkownika końcowego. Łatwe, ale nieelastyczne, więc jeśli potrzebujesz więcej mocy:

Trasa PHP

Zamiast tego umieść w swoim .htaccess: (zwróć uwagę na początkowy ukośnik)

FallbackResource /index.php

Poinstruuje to, aby uruchomić Twoje index.phpdla wszystkich plików, których normalnie nie może znaleźć w Twojej witrynie. W tym miejscu możesz na przykład:

$path = ltrim($_SERVER['REQUEST_URI'], '/');    // Trim leading slash(es)
$elements = explode('/', $path);                // Split path on slashes
if(empty($elements[0])) {                       // No path elements means home
    ShowHomepage();
} else switch(array_shift($elements))             // Pop off first item and switch
{
    case 'Some-text-goes-here':
        ShowPicture($elements); // passes rest of parameters to internal function
        break;
    case 'more':
        ...
    default:
        header('HTTP/1.1 404 Not Found');
        Show404Error();
}

W ten sposób robią to duże witryny i systemy CMS, ponieważ pozwala to na znacznie większą elastyczność w analizowaniu adresów URL, adresów URL zależnych od konfiguracji i bazy danych itp. Jednak w przypadku sporadycznego użycia zakodowane na stałe reguły przepisywania w programie .htaccessbędą wystarczające.

Niels Keurentjes
źródło
Zamiast zakodować go na stałe, możesz po prostu użyć wyrażenia regularnego, aby całkowicie zignorować ciąg. Innymi słowy, liczy się tylko część identyfikacyjna. Przejście do picture.php/invalid-text/51spowoduje również przekierowanie do tej samej lokalizacji. Możesz również dodać sprawdzenie, czy ciąg jest poprawny, a jeśli nie, przekierować ponownie do właściwej lokalizacji. Tak właśnie zrobiłem to na jednej z moich witryn za pomocą htaccess.
Mike
7
Wygodny w przypadku mniejszych witryn, ale niezbyt praktyczny, jeśli musisz analizować /blog/25zarówno pliki /picture/51i /download/684. Ponadto jest to uważane za bardzo złą praktykę (i powoduje karę za PR w Google!), Jeśli nie wszystkie losowo generowane adresy URL poprawnie zwracają 404.
Niels Keurentjes
5
przynajmniej w moim systemie było to FallbackResource /index.php(uwaga na wiodący slash)
Jack James
4
@olli: komentarz dotyczący złej praktyki odnosi się konkretnie do „nie zwracania błędów 404 w przypadku nieistniejących adresów URL”, co jest rozwiązywane przez rozwiązanie w samej odpowiedzi. Jeśli chodzi o pierwsze pytanie - FallbackResourceuruchamia się tylko w przypadku plików, które w rzeczywistości nie istnieją w systemie plików, stąd rozwiązanie awaryjne . Więc jeśli masz plik /static/styles.cssi odnosisz się do niego, ponieważ http://mydomain.tld/static/styles.csskod nigdy nie jest wykonywany, dzięki czemu działa zgodnie z oczekiwaniami i zamierzeniami w sposób przejrzysty.
Niels Keurentjes
powinieneś użyć if($elements[0] === NULL)zamiast tego, ponieważ $elementsnadal zwróci liczbę jeden, nawet jeśli jest pusta.
Fireinspace
57

Jeśli chcesz tylko zmienić trasę, picture.phpto dodanie reguły przepisywania w .htaccesscelu zaspokoi Twoje potrzeby, ale jeśli chcesz przepisać adres URL tak, jak w Wordpress, to PHP jest drogą. Oto prosty przykład na początek.

Struktura folderów

Istnieją dwa pliki, które są potrzebne w folderze głównym, .htaccessa index.php, i dobrze byłoby, aby umieścić resztę .phpplików w osobnym folderze, np inc/.

root/
  inc/
  .htaccess
  index.php

.htaccess

RewriteEngine On
RewriteRule ^inc/.*$ index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]

Ten plik ma cztery dyrektywy:

  1. RewriteEngine - włącz silnik przepisywania
  2. RewriteRule- odmów dostępu do wszystkich plików w inc/folderze, przekieruj każde wywołanie do tego folderu doindex.php
  3. RewriteCond - zezwalaj na bezpośredni dostęp do wszystkich innych plików (takich jak obrazy, css lub skrypty)
  4. RewriteRule - przekierować cokolwiek innego do index.php

index.php

Ponieważ wszystko jest teraz przekierowywane do index.php, zostanie ustalone, czy adres URL jest poprawny, wszystkie parametry są obecne i czy typ parametrów jest poprawny.

Aby przetestować adres URL, musimy mieć zestaw reguł, a najlepszym narzędziem do tego jest wyrażenie regularne. Używając wyrażeń regularnych, zabijemy dwie muchy jednym ciosem. Adres URL, aby przejść ten test, musi mieć wszystkie wymagane parametry, które są testowane na dozwolonych znakach. Oto kilka przykładów reguł.

$rules = array( 
    'picture'   => "/picture/(?'text'[^/]+)/(?'id'\d+)",    // '/picture/some-text/51'
    'album'     => "/album/(?'album'[\w\-]+)",              // '/album/album-slug'
    'category'  => "/category/(?'category'[\w\-]+)",        // '/category/category-slug'
    'page'      => "/page/(?'page'about|contact)",          // '/page/about', '/page/contact'
    'post'      => "/(?'post'[\w\-]+)",                     // '/post-slug'
    'home'      => "/"                                      // '/'
);

Następnie należy przygotować żądanie uri.

$uri = rtrim( dirname($_SERVER["SCRIPT_NAME"]), '/' );
$uri = '/' . trim( str_replace( $uri, '', $_SERVER['REQUEST_URI'] ), '/' );
$uri = urldecode( $uri );

Teraz, gdy mamy żądanie uri, ostatnim krokiem jest przetestowanie uri na regułach wyrażeń regularnych.

foreach ( $rules as $action => $rule ) {
    if ( preg_match( '~^'.$rule.'$~i', $uri, $params ) ) {
        /* now you know the action and parameters so you can 
         * include appropriate template file ( or proceed in some other way )
         */
    }
}

Pomyślne dopasowanie spowoduje, że ponieważ używamy nazwanych wzorców podrzędnych w wyrażeniu regularnym, wypełni $paramstablicę prawie tak samo, jak PHP wypełnia $_GETtablicę. Jednak w przypadku korzystania z dynamicznego adresu URL $_GETtablica jest wypełniana bez sprawdzania parametrów.

    / obrazek / jakiś + tekst / 51

    Szyk
    (
        [0] => / obrazek / jakiś tekst / 51
        [tekst] => jakiś tekst
        [1] => jakiś tekst
        [id] => 51
        [2] => 51
    )

    picture.php? text = some + text & id = 51

    Szyk
    (
        [tekst] => jakiś tekst
        [id] => 51
    )

Tych kilka wierszy kodu i podstawowa znajomość wyrażeń regularnych wystarczy, aby zacząć budować solidny system routingu.

Kompletne źródło

define( 'INCLUDE_DIR', dirname( __FILE__ ) . '/inc/' );

$rules = array( 
    'picture'   => "/picture/(?'text'[^/]+)/(?'id'\d+)",    // '/picture/some-text/51'
    'album'     => "/album/(?'album'[\w\-]+)",              // '/album/album-slug'
    'category'  => "/category/(?'category'[\w\-]+)",        // '/category/category-slug'
    'page'      => "/page/(?'page'about|contact)",          // '/page/about', '/page/contact'
    'post'      => "/(?'post'[\w\-]+)",                     // '/post-slug'
    'home'      => "/"                                      // '/'
);

$uri = rtrim( dirname($_SERVER["SCRIPT_NAME"]), '/' );
$uri = '/' . trim( str_replace( $uri, '', $_SERVER['REQUEST_URI'] ), '/' );
$uri = urldecode( $uri );

foreach ( $rules as $action => $rule ) {
    if ( preg_match( '~^'.$rule.'$~i', $uri, $params ) ) {
        /* now you know the action and parameters so you can 
         * include appropriate template file ( or proceed in some other way )
         */
        include( INCLUDE_DIR . $action . '.php' );

        // exit to avoid the 404 message 
        exit();
    }
}

// nothing is found so handle the 404 error
include( INCLUDE_DIR . '404.php' );
Danijel
źródło
2
Jak odczytujesz parametry? Nie działa z $ post_id = htmlentities ($ _ GET ['post']);
andrebruton
@Danijel Czy mogę otrzymać pełny kod źródłowy? Wypróbowałem powyższy kod, ale wyprowadzany był tylko tekst, CSS bez efektu. Dziękuję Ci.
4 Zostaw okładkę
6

jest to plik .htaccess, który przekazuje prawie wszystko do index.php

# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l
RewriteCond %{REQUEST_URI} !-l
RewriteCond %{REQUEST_FILENAME} !\.(ico|css|png|jpg|gif|js)$ [NC]
# otherwise forward it to index.php
RewriteRule . index.php

to do Ciebie należy przeanalizowanie $ _SERVER ["REQUEST_URI"] i skierowanie do picture.php lub cokolwiek

Luca Rocchi
źródło
8
Apache wprowadził FallbackResourcedyrektywę kilka głównych wersji temu, co jest teraz preferowanym sposobem implementacji tego zachowania przy niższych kosztach wydajności, ponieważ nie wymaga uruchamiania całego silnika przepisywania. Dokumentacja tutaj . Twoje reguły są również wadliwe, ponieważ nie odwołujesz się do katalogów ( !-d), a wszystkie filtry rozszerzeń są przestarzałe - -fpowinny już je wychwycić.
Niels Keurentjes
5

PHP nie jest tym, czego szukasz, sprawdź mod_rewrite

rauchmelder
źródło
czy wpłynie to na wszystkie adresy URL?
Jazerix
@Jazerix To zależy od zdefiniowanych przez Ciebie reguł.
nietonfir
2

Chociaż już udzielono odpowiedzi, a intencją autora jest stworzenie aplikacji typu kontrolera frontowego, ale publikuję dosłowną regułę dla zadanego problemu. jeśli ktoś ma problem za to samo.

RewriteEngine On
RewriteRule ^([^/]+)/([^/]+)/([\d]+)$ $1?id=$3 [L]

Powyższe powinno działać dla url picture.php/Some-text-goes-here/51. bez używania pliku index.php jako aplikacji przekierowującej.

Abhishek Gurjar
źródło
Muszę wiedzieć, jaki adres URL musimy wpisać w href? ponieważ otrzymuję 404
questionbank