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

Russian

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

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

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

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

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

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

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

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

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

  1. /?module=page&method=view&alias=about
  2.  
  3. /?module=shop&method=index
  4. /?module=shop&method=category&category=printers&page=2
  5. /?module=shop&method=view&id=printers&page=2
  6.  
  7. /?module=blog&method=index
  8. /?module=blog&method=category&category=design
  9. /?module=blog&method=tag&tag=отпуск
  10. /?module=blog&method=view&id=15
  11. /?module=blog&method=rss

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

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

  1. RewriteEngine on
  2. RewriteBase /
  3.  
  4. RewriteRule ^/page/([A-Za-z0-9]+)\.html$ index.php?module=page&method=view&alias=$1 [L,QSA]
  5.  
  6. RewriteRule ^/shop(/page-([0-9]+))?$ index.php?module=shop&method=index&page=$3 [L,QSA]
  7. RewriteRule ^/shop/category/([A-Za-z0-9]+)(/page-([0-9]+))?$ index.php?module=shop&method=view&category=$1&page=$3 [L,QSA]
  8. RewriteRule ^/shop/item/([0-9]*)\.html$ index.php?module=shop&method=view&id=$1 [L,QSA]
  9.  
  10. RewriteRule ^/blog/category/([A-Za-z0-9]+)(/page-([0-9]+))?$ index.php?module=blog&method=category&category=$1&page=$3 [L,QSA]
  11. RewriteRule ^/blog(/page-([0-9]+))?$ index.php?module=blog&method=index&page=$2 [L,QSA]
  12. RewriteRule ^/blog/tag/([A-Za--Яа-яЁё0-9]+)(/page-([0-9]+))?$ index.php?module=blog&method=tag&tag=$1&page=$3 [L,QSA]
  13. RewriteRule ^/blog/post/([0-9]*)\.html$ index.php?module=blog&method=view&id=$1 [L,QSA]
  14. RewriteRule ^/feed\.xml$ index.php?module=blog&method=rss&id=$1 [L,QSA]
  15.  
  16. RewriteCond %{REQUEST_FILENAME} !-f
  17. RewriteCond %{REQUEST_FILENAME} !-d
  18. RewriteRule . index.php

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

  1. /page/about.html
  2.  
  3. /shop
  4. /shop/category/printers/page-2
  5. /shop/item/341.html
  6.  
  7. /blog
  8. /blog/category/design
  9. /blog/tag/отпуск
  10. /blog/post/15.html
  11. /feed.xml

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

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

  1. RewriteEngine on
  2. RewriteBase /
  3. RewriteCond %{REQUEST_FILENAME} !-f
  4. RewriteCond %{REQUEST_FILENAME} !-d
  5. RewriteRule . index.php

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

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

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

  1. # modules/blog/routes.php
  2. return array(
  3. '#^/blog$#' => 'blog/list/index',
  4. '#^/blog/category/(?P<category>[\w-]+)$#' => 'blog/list/category',
  5. '#^/blog/tag/(?P<tag>[\w-]+)$#' => 'blog/list/tag',
  6. '#^/blog/post/(?P<id>\d+)\.html$#' => 'blog/post/view',
  7. '#^feed\.xml$#' => 'blog/list/rss',
  8. );

Здесь мы представили правила в виде массива. Слева у нас для удобства размещён шаблон адреса, а справа – маршрут в виде строки модуль/файл-контроллер/функция-действие. Также мы воспользовались возможностью регулярных выражений в 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.
CAPTCHA
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.
Target Image