Перезапись URL с помощью PHP

139

У меня есть URL-адрес, который выглядит так:

url.com/picture.php?id=51

Как мне преобразовать этот URL в:

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

Я думаю, что WordPress делает то же самое.

Как мне сделать удобные URL-адреса в PHP?

Джазерикс
источник
1
Вам нужно сильно настроить конфигурацию файлов Apache ... приведите пример того, что вам действительно нужно? Как вставить этот текст? он предопределен или создается динамически? дайте как можно больше информации, чтобы получить правильный ответ ... и да, URL-адрес мог бы быть немного более
информативным
1
Почему именно PHP? Apache - mod_rewriteэто все, что вам нужно для этого, и здесь, на SO, есть много вопросов по этому поводу. И если вы «думаете», что wordpress делает то же самое, почему бы вам просто не поискать его ? ;)
ниетонфир 05
прочитал mod_rewrite
Зачем ты это делаешь? Является ли цель переписать URL-адреса, если это так, используйте .htaccess или что-то подобное. Если цель - просто изменить строку, $ _GET получает только строку запроса?
adeneo
Текст будет заголовком изображения, а затем идентификатором после косой черты :) Мне нужно будет посмотреть, что может предложить apache mod_rewrite. Надеюсь, это не так уж и сложно: D
Джазерикс

Ответы:

194

Вы можете сделать это двумя способами:

Маршрут .htaccess с mod_rewrite

Добавьте файл с именем .htaccessв вашу корневую папку и добавьте что-то вроде этого:

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

Это скажет Apache включить mod_rewrite для этой папки, и если он получит запрос на URL-адрес, соответствующий регулярному выражению, он внутренне переписывает его на то, что вы хотите, чтобы конечный пользователь этого не видел. Легко, но негибко, поэтому, если вам нужно больше мощности:

Маршрут PHP

Вместо этого поместите в свой .htaccess следующее: (обратите внимание на ведущую косую черту)

FallbackResource /index.php

Это скажет ему запустить ваш index.phpдля всех файлов, которые он обычно не может найти на вашем сайте. Там вы можете, например,:

$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();
}

Именно так это делают большие сайты и CMS-системы, потому что это дает гораздо большую гибкость в синтаксическом анализе URL-адресов, URL-адресов, зависящих от конфигурации и базы данных, и т. Д. Для спорадического использования жестко запрограммированные правила перезаписи подойдут .htaccess.

Нильс Керентьес
источник
Вместо того, чтобы жестко его кодировать, вы можете просто использовать регулярное выражение, чтобы полностью игнорировать строку. Другими словами, единственное, что имеет значение, - это идентификационная часть. Переход к picture.php/invalid-text/51также приведет к перенаправлению в то же место. Вы также можете добавить проверку, чтобы убедиться, что строка верна, а если нет, перенаправить снова в правильное место. Вот как я сделал это на одном из своих сайтов с помощью htaccess.
Майк
7
Удобно для небольших сайтов, но не очень практично, если вам нужно разбирать /blog/25так же, как /picture/51и /download/684. Кроме того, это считается очень плохой практикой (и вы будете наказаны за PR в Google!), Если не все случайно сгенерированные URL-адреса будут правильно возвращать 404.
Нильс Керентьес
5
по крайней мере, в моей системе это было FallbackResource /index.php(обратите внимание на косую черту)
Джек Джеймс
4
@olli: комментарий о плохой практике конкретно относится к "не возвращать 404 для несуществующих URL", что решается решением в самом ответе. Что касается первого вопроса - FallbackResourceсрабатывает только для файлов, которых на самом деле не существует в файловой системе, отсюда и откат . Итак, если у вас есть файл /static/styles.cssи вы ссылаетесь на него, так как http://mydomain.tld/static/styles.cssкод никогда не выполняется, он работает так, как ожидалось, и прозрачно.
Niels Keurentjes
вы должны использовать if($elements[0] === NULL)вместо этого, поскольку $elementsвсе равно будет возвращать счетчик, равный единице, даже если он пуст.
fireinspace
57

Если вы хотите изменить только маршрут, picture.phpтогда добавление правила перезаписи .htaccessбудет соответствовать вашим потребностям, но если вы хотите, чтобы URL-адрес переписывался, как в Wordpress, то подходящим вариантом является PHP. Вот простой пример для начала.

Структура папки

В корневой папке нужны два файла, .htaccessи index.php, а остальные .phpфайлы было бы хорошо поместить в отдельную папку, например inc/.

root/
  inc/
  .htaccess
  index.php

.htaccess

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

В этом файле четыре директивы:

  1. RewriteEngine - включить движок перезаписи
  2. RewriteRule- запретить доступ ко всем файлам в inc/папке, перенаправить любой вызов в эту папку наindex.php
  3. RewriteCond - разрешить прямой доступ ко всем другим файлам (например, изображениям, CSS или скриптам)
  4. RewriteRule - перенаправить все остальное на index.php

index.php

Поскольку теперь все перенаправлено на index.php, будет определено, правильный ли URL-адрес, все параметры присутствуют и правильный ли тип параметров.

Чтобы проверить URL-адрес, нам нужен набор правил, и лучший инструмент для этого - регулярное выражение. Используя регулярные выражения, мы убьем двух мух одним ударом. URL-адрес для прохождения этого теста должен иметь все необходимые параметры, которые проверяются на разрешенные символы. Вот несколько примеров правил.

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

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

Теперь, когда у нас есть URI запроса, последний шаг - проверить 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 )
         */
    }
}

Успешное совпадение, поскольку мы используем именованные подшаблоны в регулярном выражении, заполнит $paramsмассив почти так же, как PHP заполняет $_GETмассив. Однако при использовании динамического URL-адреса $_GETмассив заполняется без каких-либо проверок параметров.

    / picture / some + text / 51

    Массив
    (
        [0] => / picture / some text / 51
        [text] => какой-то текст
        [1] => какой-то текст
        [id] => 51
        [2] => 51
    )

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

    Массив
    (
        [text] => какой-то текст
        [id] => 51
    )

Этих нескольких строк кода и базовых знаний о регулярных выражениях достаточно, чтобы начать построение надежной системы маршрутизации.

Полный источник

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' );
Даниэль
источник
2
Как читать параметры? Он не работает с $ post_id = htmlentities ($ _ GET ['post']);
andrebruton
@Danijel Могу я получить полный исходный код? Я пробовал приведенный выше код, но выводился только текст, CSS - никакого эффекта. Спасибо.
4 Leave Cover
6

это файл .htaccess, который пересылает почти все на 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

тогда вы должны проанализировать $ _SERVER ["REQUEST_URI"] и перенаправить на picture.php или что-то еще

Лука Рокки
источник
8
Apache представил эту FallbackResourceдирективу несколько основных версий назад, которая сейчас является предпочтительным способом реализации этого поведения с меньшими затратами производительности, поскольку не требуется запускать весь механизм перезаписи. Документация здесь . Ваши правила также ошибочны, потому что вы не ссылаетесь на каталоги ( !-d), а все фильтры расширений устарели - они -fуже должны их отловить.
Niels Keurentjes
5

PHP - это не то, что вы ищете, проверьте mod_rewrite

Rauchmelder
источник
повлияет ли это на все URL-адреса?
Джазерикс
@Jazerix Это зависит от определенных вами правил.
ниетонфир 05
2

Хотя уже ответил, и намерение автора состоит в том, чтобы создать приложение типа фронт-контроллера, но я публикую буквальное правило для заданной проблемы. если у кого-то возникла проблема.

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

Выше должно работать для URL-адреса picture.php/Some-text-goes-here/51. без использования index.php в качестве приложения для перенаправления.

Абхишек Гурджар
источник
Мне нужно знать, какой URL мы должны написать в href? потому что я получаю 404
questionbank