Маршрутизация (роутинг) в PHP

Управление адресами страниц в приложении

Давным давно, в стране первых веб-программистов PHP файлы произвели революцию в разработке сайтов. Ведь тогда каждый ремесленник мог построить мега-портал с использованием GET параметров, которые он передавал из форм и ссылок на свой сервер

Он мог динамически выводить страницы:

/page.php?method=view&alias=about

товары в своём магазине:

/shop.php?method=category&category=printers&brand=canon

редкие записи в своём блоге:

/blog.php?method=view&id=15

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

/?module=page&method=view&alias=about

/?module=shop&method=index
/?module=shop&method=category&category=printers&page=2
/?module=shop&method=view&id=printers&page=2

/?module=blog&method=index
/?module=blog&method=category&category=design
/?module=blog&method=tag&tag=отпуск
/?module=blog&method=view&id=15
/?module=blog&method=rss

а в нём уже через операцию include подключать нужный PHP файл из нужного модуля module и запускать в нём процедуру method.
Include уязвимости

Но вдруг король Гуглиан провозгласил декрет о ненамерении терпеть такие нечеловеческие адреса. Тут-то со всех сторон слетелись SEO-мастера и стали советовать внедрить человекопонятную переадресацию в .htaccess:

RewriteEngine on
RewriteBase /

RewriteRule ^/page/([A-Za-z0-9]+)\.html$ index.php?module=page&method=view&alias=$1 [L,QSA]

RewriteRule ^/shop(/page-([0-9]+))?$ index.php?module=shop&method=index&page=$3 [L,QSA]
RewriteRule ^/shop/category/([A-Za-z0-9]+)(/page-([0-9]+))?$ index.php?module=shop&method=view&category=$1&page=$3 [L,QSA]
RewriteRule ^/shop/item/([0-9]*)\.html$ index.php?module=shop&method=view&id=$1 [L,QSA]

RewriteRule ^/blog/category/([A-Za-z0-9]+)(/page-([0-9]+))?$ index.php?module=blog&method=category&category=$1&page=$3 [L,QSA]
RewriteRule ^/blog(/page-([0-9]+))?$ index.php?module=blog&method=index&page=$2 [L,QSA]
RewriteRule ^/blog/tag/([A-Za-zА-Яа-яЁё0-9]+)(/page-([0-9]+))?$ index.php?module=blog&method=tag&tag=$1&page=$3 [L,QSA]
RewriteRule ^/blog/post/([0-9]*)\.html$ index.php?module=blog&method=view&id=$1 [L,QSA]
RewriteRule ^/feed\.xml$ index.php?module=blog&method=rss&id=$1 [L,QSA]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php

чтобы вернуться к имитации старых добрых .html страниц и реализации приятных для глаза адресов:

/page/about.html

/shop
/shop/category/printers/page-2
/shop/item/341.html

/blog
/blog/category/design
/blog/tag/отпуск
/blog/post/15.html
/feed.xml

Всё стало хорошо, но смущали размеры файла .htaccess и невозможность подключать новые модули динамически, без ручной вставки правил в этот файл. К тому же, кое-кто использовал Nginx с php-fpm вместо Apache, что требовало умения переписывать правила в специфический формат конфигурационного файла Nginx и, возможно, других серверов.

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

RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php

и в конфигурации Nginx:

location / {
try_files $uri $uri/ /index.php?$args;
}

а регулярные выражения переместить в конфигурационные файлы приложения и ту же работу производить вручную в PHP скрипте. Кроме того, удобно разместить правила преобразования адресов в модулях, например правила для блога поместить в соответствующий файл modules/blog/routes.php:

# modules/blog/routes.php
return array(
'#^/blog$#' => 'blog/list/index',
'#^/blog/category/(?P[\w-]+)$#' => 'blog/list/category',
'#^/blog/tag/(?P[\w-]+)$#' => 'blog/list/tag',
'#^/blog/post/(?P\d+)\.html$#' => 'blog/post/view',
'#^feed\.xml$#' => 'blog/list/rss',
);

Здесь мы представили правила в виде массива. Слева у нас для удобства размещён шаблон адреса, а справа – маршрут в виде строки модуль/файл-контроллер/функция-действие. Также мы воспользовались возможностью регулярных выражений в PHP указывать именованные параметры. Удобнее использовать символьные имена $matches['category'] и $matches['tag'], чем путаться c номерами вроде $maches[1] или $maches[3].

Категория: 
The code has been tested and works
Мультитег: 

Add new comment

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.