Как устроен новый роутинг

В данный момент все ЧПУ на сайте делаются через htaccess. Наша цель - убрать все правила из htaccess и перенести их обработку на уровень php. Параллельно избавиться от menuid, об этом ниже будет написано отдельно. Все адреса в примерах будут уже без menuid.

Общий принцип нового роутинга:

Из .htaccess убираются все правила, остается только одно, которое говорит, что каждый введенный ЧПУ нужно передать в виде параметра в index.php.

Простой пример для наглядности: допустим, юзер запрашивает адрес www.site.ru/content/novosti, в реале этот запрос будет преобразован апачем в www.site.ru/index.php?uri=content/novosti

(исключение составляют адреса, заканчивающиеся на .jpg, .gif, .png, .css, .js и .php. например адрес site.ru/core/ajax/karma.php не будет преобразован)

Получается, что теперь ядро определяет какой компонент вызвать не по параметру $_GET['view'], а на основе анализа $_GET['uri'].

Логика в ядре такая (схематично):

  1. Сохраняем параметр $_GET['uri'] в переменную $uri ядра
  2. Определяем какой компонент нужно будет подключить:
    1. Если $uri пустой, то значит у нас главная страница, берем компонент главной страницы и сохраняем в $component;
    2. Если $uri не пустой, то берем его первый сегмент (кусок до первого слэша) - это и будет название компонента. Сохраняем в $component;
  3. Теперь нам известны переменные $uri и $component, осталось определить какие параметры передать компоненту:
    1. Подключаем файл router.php лежащий в папке с нужным компонентом
    2. Получаем из роутера список правил (грубо говоря - строки, что были раньше в htaccess, только переделанные в массивы) для этого компонента
    3. Проходим по всем правилам, ищем совпадение правила и $uri
    4. Если совпадение найдено, заполняем массив $_REQUEST параметрами согласно правилу

Ниже будет пример, чтобы стало понятнее.

Внутри самих компонентов (в frontend.php) никаких отличий в логике видно не будет. Т.е. как раньше компонент получал свои параметры через $inCore→request(…), так это остается и сейчас.

Мы просто делаем эмуляцию, чтобы сильно не править компоненты. Единственное - нужно будет из всех ссылок внутри всех компонентов убрать menuid, но об этом ниже.

Формат файла router.php

В папке с каждым компонентом, начиная с версии 1.5.4, будет лежать файл router.php

Абстрактный пример такого файла (для компонента registration):

<?php
 
    function routes_registration(){
 
        $routes[] = array(
                            '_uri'  => '/^registration\/login$/i',
                            'do'    => 'auth'
                         );
 
        $routes[] = array(
                            '_uri'  => '/^registration\/logout$/i',
                            'do'    => 'auth',
                            'logout' => 1
                         );
 
        return $routes;
 
    }
 
?>

Внутри файла должна быть определена функция routes_XXX() { }, где XXX - это название компонента (совпадает с именем папки).

Внутри функции заполняется массив $routes который потом передается ядру. Каждый элемент массива $routes тоже является массивом и может содержать следующие записи:

  1. _uri ⇒ регулярное выражение, с которым сравнивается адрес страницы (аналог первой части RewriteRule из .htaccess) - обязательный элемент
  2. параметр ⇒ значение (сколько угодно пар) - опционально
  3. номер сегмента ⇒ параметр (столько же пар, сколько пар круглых скобок в рег.выражении, аналог $1, $2 из .htaccess ) - опционально

Пример:

В .htaccess было правило для просмотра фото одного автора:

RewriteRule ^photos/([0-9]*)/byuser([0-9]*).html$ /index.php?view=photos&do=by_user&id=$1&userid=$2

Это правило говорит, что адрес вида /photos/111/byuser222.html нужно преобразовать в /index.php?view=photos&do=by_user&id=111&userid=222

Для файла router.php это правило мы должны переписать так:

        $routes[] = array(
                            '_uri'  => '/^photos\/([0-9]+)\/byuser([0-9]+).html$/i',
                            'do'    => 'by_user',
                            1       => 'id',
                            2       => 'userid'
                         );

То есть мы говорим, что если адрес совпадает с рег.выражением, то:

  1. нужно создать переменную $_REQUEST['do'] = 'by_user'
  2. нужно создать переменную $_REQUEST['id'] и положить в нее значение из первых круглых скобок внутри регулярки
  3. нужно создать переменную $_REQUEST['userid'] и положить в нее значение из вторых круглых скобок внутри регулярки

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

1       => 'id'

а если указать наоборот:

'id'       => 1

то мы создадим переменную $_REQUEST['id'] = '1', никак не связанную с адресом.

Синтаксис регулярных выражений

Синтаксис регекспов в .htaccess немного отличается, как вы уже могли заметить. Рассмотрим алгоритм преобразования синтаксиса на примере.

Исходное правило .htaccess: RewriteRule ^board/([0-9]*)/view-type/(.*)$ /index.php?view=board&id=$1&obtype=$2

1. Берем часть правила между ^ и $, вместе(!) с этими символами:

^board/([0-9]*)/view-type/(.*)$

2. Перед каждым слэшем(/) и минусом(-) добавляем «\» (экранируем):

^board\/([0-9]*)\/view\-type\/(.*)$

важно: минусы внутри квадратных скобок экранировать не нужно!

3. Все звездочки(*) заменяем на плюсы(+):

^board\/([0-9]+)\/view\-type\/(.+)$

это требование не обязательно, но желательно, т.к. повышает безопасность

4. Добавляем »/» в начало и »/i» в конец выражения:

/^board\/([0-9]+)\/view\-type\/(.+)$/i

Мы получили готовое правило для массива $routes:

        $routes[] = array(
                            '_uri'  => '/^board\/([0-9]+)\/view\-type\/(.+)$/i',
                            1       => 'id',
                            2       => 'obtype'
                         );

Что случилось с menuid

Изначально, menuid был введен в адреса по образцу item-id в Джумле. У него было два предназначения:

  1. определять, какой пункт меню сделать активным
  2. определять, какие модули показать на текущей странице (т.к. модули привязаны к пунктам меню)

Сейчас логика в ядре изменена и определение активного пункта меню происходит не по id, а по ссылке (поле link в таблице cms_menu).

То есть, если ссылка пункта меню совпадает с адресом страницы (или его началом, если адрес длиннее ссылки), то пункт меню считается активным.

Опять же, для компонентов и модулей никаких отличий нет. Т.е. они так же как и раньше могут запросить menuid через cmsMenuId() или $inCore→menuId().

Отличие лишь в том, что раньше menuid брался из адреса страницы, а теперь определяется ядром по ссылке.

Замена адресов через url_rewrite.php

Начиная с версии 1.5.4 в корне сайта будет лежать файл url_rewrite.php

В нем можно определить правила для замены адреса ($uri) по правилам, перед тем, как будет определен текущий компонент и запущен его router.php

Зачем это нужно? Например - авторизация на сайте происходит путем отправки формы на адрес www.site.ru/login

Учитывая алгоритм роутинга, описанный в начале, ядро будет искать компонент login. Но такого компонента нет, т.к. за авторизацию отвечает компонент registration.

Получается, что при обращении по адресу /login, нам нужно заставить ядро думать, что на самом деле адрес - /registration/login.

Прописываем в файле url_rewrite.php :

<?php
 
    function rewrite_rules(){
 
        $rules[] = array(
                            'source'  => '/^login$/i',
                            'target'  => 'registration/login'
                         );
 
        $rules[] = array(
                            'source'  => '/^logout$/i',
                            'target'  => 'registration/logout'
                         );
 
        return $rules;
 
    }
 
?>

Этим мы говорим ядру, что перед тем как запускать роутинг, нужно пройтись по массиву $rules и сравнить наш текущий адрес ($uri) со всеми выражениями source.

Если будет найдено совпадение, то переменная $uri в ядре будет перезаписана на значение target. И только после этого ядро начнет определять компонент и передавать ему управление.

Пользовательская замена адресов

Пользователи смогу создать собственный вариант файла с правилами замены адресов и назвать его custom_rewrite.php

Внутри такого файла должна быть определена функция custom_rewrite_rules() { }

Правила для замены адресов внутри функции задаются в том же формате, что и в url_rewrite.php

Главное отличие url_rewrite.php и custom_rewrite.php в том, что последний не будет включен в состав дистрибутива и не будет затираться при обновлениях.

 
новый_роутер.txt · Последние изменения: 2010/04/22 18:58 От admin