PHP-роутинг (Routing) для новичков

Дневник / PHPПросмотров: 5772 (383)

Роутинг — это маршрутизация: входящий URL разбирается специальным образом и по его результату выполняется определенный код. С роутингом напрямую связано понятие ЧПУ (человекопонятные урлы), которое позволяет исключить в адресах сложные параметры. Например вместо http://сайт/admin/new-pageпришлось бы использовать http://сайт/admin.php?action=new-page

Любой входящий URL на сервере разбирается по единому стандарту. Полностью приводить документацию не буду (см. как пример функцию parse_url), важно лишь понять, что в адресе передается параметр path (путь на сервере), которого на сервере реально может не быть. Например в адресе http://сайт/adminкаталога adminреально может не существовать.

То есть сервер, получив такой адрес, попытается найти каталог admin, но не найдя его, выдаст 404-страницу (not found).

Чтобы исключить такой вариант, серверу указывается, что для всех несуществующих каталогов и файлов, подключать php-файл (обычно index.php).

Делается это в файле .htaccessс помощью Apache-модуля mod_rewrite. Вот довольно типовой вариант (MaxSite CMS):

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

Тут главная строчка с RewriteRule — именно она определяет шаблон входящего адреса (в примере это регулярное выражение) и что с ним делать. В данном примере будет подключен index.php с параметрами после слэша.

Строчка RewriteCond %{REQUEST_FILENAME} !-f / -dуказывает исключить из обработки реально существующие на сервере файлы и каталоги.

Похожий вариант, только чуть короче, от WordPress:

RewriteRule . /index.php [L]

Вариант LPF:

RewriteRule ^(.*)$ /index.php?page=$1 [QSA]

Здесь принудительно добавляется query-параметр page.

Еще один распространенный вариант (пожалуй самый «типовой»):

RewriteRule (.*) index.php?$1 [QSA,L]

Все эти RewriteRule-правила делают простую вещь: как бы «преобразуют» входящий адрес в набор query-параметров. Например адрес http://сайт/adminпревратится в http://сайт/index.php?admin

Посмотрите на полный код .htaccess:

<IfModule mod_rewrite.c>
RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) /index.php?$1 [QSA,L]
</IfModule>

Если это какой-то подкаталог, то он указываетс в RewriteBase и как путь к php-файлу. Например каталог на сервере route:

<IfModule mod_rewrite.c>
RewriteEngine on
RewriteBase /route/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) /route/index.php?$1 [QSA,L]
</IfModule>

Теперь, все адреса на сервере будут передаваться в файл index.php, а исходный адрес сохранится в виде query-параметра.

Если в index.php разместить

<?php
print_r($_GET);

То мы можем увидеть query-параметры. В PHP за это отвечается суперглобальная переменная $_GET. Например для http://сайт/adminэто будет admin, для http://сайт/admin/new-pageadmin/new-page.

Таким образом, с помощью .htaccessпроисходит первая часть роутинга, где мы получаем готовый $_GET.

Кстати, насчет .htaccessWordPress. Он не создает $_GET, поэтому придется использовать $_SERVER['REQUEST_URI']в который включается подкаталог. Работать с таким адресом уже будет сложней.

Второй этап роутинга выполняется полностью на PHP. Получив $_GETнужно решить что с ним делать. Например если адрес admin, подключить файл admin.php.

Существуют несколько принципиально разных подходов в организации роутинга. Наиболее популярный подход — это когда в адресе передаётся «действие», которое описывается через php-класс. Такой подход хорошо описан в CodeIgniter:

example.com/class/function/id/

Например пусть будет класс admin в нём метод edit, принимающий параметр $id.

example.com/admin/edit/23
 
class admin {
    function edit($id)
    {
        ...
    }
}

Это сильно утрированный пример, но он хорошо показывает соответствие адреса и php-класса.

Другой вариант похожий, но используется не классы, а функции.

example.com/admin/edit/23
 
function admin($params)
{
    ...
}

То есть функция — это первый сегмент, а остальные выступают уже как парметры. Встречается более «продвинутый» вариант.

example.com/admin
 
function admin($params)
{
    ...
}
 
example.com/admin/edit/23
 
function admin_edit($params)
{
    ...
}

То есть имя функции строится по сегментам URL.

Третий, тоже распространенный вариант — адрес указывает на подключаемый файл.

example.com/admin
 
$fn = 'pages/admin.php';
 
if (file_exists($fn)) 
    reqiure($fn);
else
    reqiure('pages/404.php');

Здесь все файлы хранятся в каталоге pages и подключаются только если реально существуют. Если файла нет, то подключается предопределенный 404-файл.

Если сайт представляет собой сложный проект, то как правило используют роутинг на основе php-классов. Так его проще поддерживать и развивать. Если же сайт состоит из небольшого количества страниц, то как правило используют подключаемые файлы, где один файл — это одна страница.

Строго говоря, роутинг «на классах» тоже использует «файловое» подключение. Вначале подключается файл с кодом класса, а уже после этого выполняется сам класс.

В задачу роутинга входит не только необходимое «действие», но и валидация входящего адреса и его лексический разбор.

Адреса могут строиться по шаблону. Например какой-то адрес должен содержать только номер, а не текст (например example.com/admin/edit/23, но не example.com/admin/edit/hello). Или адрес может быть неизменным, но обработчик будет меняться от вида запроса GET или POST. В одном случае нужно подключить одну функцию/файл, в другом — другой. Бывают и более сложные задачи, все их перечислять нет смысла, поэтому появились разного рода php-библиотеки для организации роутинга.

Свой «велосипед» не изобретал только ленивый, но я отмечу довольно известный FastRoute, который вобрал в себя наиболее типичные решения.

В первую очередь это использование регулярных выражений при задании правил, например:

$r->addRoute('GET', '/user/{id:[0-9]+}', 'handler1');

Примерно такой же подход используется и в роутинге CodeIgniter.

products/shirts/123
$route['products/([a-z]+)/(\d+)'] = "$1/id_$2";

То есть входящий адрес должен соответствовать шаблону и только в этом случае он «сработает».

В FastRoute реализована поддержка POST и GET-запросов. Такая возможность интересна, хотя на больших проектах такие вещи лучше делать на уровне самого «действия». Но это уже тонкости. Про эту библиотеку я упоминаю в первую очередь из-за того, что она достаточно популярна и уже используется в нескольких интересных проектах: Slim и Lumen.

Для небольших проектов, конечно же, FastRoute будет избыточна, поэтому можно ограничиться вариантом попроще, да и мозги потренировать. grin

ТЗ. Пусть роутинг будет обрабатывать адреса вида http://сайт/blog/contact/map, по которому будет подключен файл content/blog/contact/map/index.php, где каталог content — это общее хранилище всех страниц сайта, а index.php — обязательный файл с кодом страницы. Роутинг должен проверять реальный файл и если его нет, то подключать 404-страницу. Если никаких параметров нет, то это home-страница. Пусть они будут предопределены.

Решение. По сути задача сводится к преобразованию входящего адреса в путь на сервере. У нас есть массив $_GET, где первый ключ и есть входящий URL. Дальше формируем путь к файлу и если он есть, то подключаем. Если нет, то подключаем 404-страницу.

Весь код в 2 строчки:

<?php
 
$fn = ($p = key($_GET)) ? 'content/' . $p . '/index.php' : 'content/home/index.php';
 
(file_exists($fn)) ? require $fn : require 'content/404/index.php'; 
 
# end of file

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

Оставьте комментарий!

Комментарий будет опубликован после проверки. Вы соглашаетесь с правилами сайта.

(обязательно)

РЕКЛАМА
Аналоги запчастей geely ck еще на сайте. | Трансфер в неаполе смотрите здесь. |

О сайте

Здесь вы получите самую полную информацию о создании сайтов на MaxSite CMS.

Рейтинг@Mail.ru