В данный момент все ЧПУ на сайте делаются через 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'].
Логика в ядре такая (схематично):
Ниже будет пример, чтобы стало понятнее.
Внутри самих компонентов (в frontend.php) никаких отличий в логике видно не будет. Т.е. как раньше компонент получал свои параметры через $inCore→request(…), так это остается и сейчас.
Мы просто делаем эмуляцию, чтобы сильно не править компоненты. Единственное - нужно будет из всех ссылок внутри всех компонентов убрать menuid, но об этом ниже.
В папке с каждым компонентом, начиная с версии 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 тоже является массивом и может содержать следующие записи:
В .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 => '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 был введен в адреса по образцу item-id в Джумле. У него было два предназначения:
Сейчас логика в ядре изменена и определение активного пункта меню происходит не по id, а по ссылке (поле link в таблице cms_menu).
То есть, если ссылка пункта меню совпадает с адресом страницы (или его началом, если адрес длиннее ссылки), то пункт меню считается активным.
Опять же, для компонентов и модулей никаких отличий нет. Т.е. они так же как и раньше могут запросить menuid через cmsMenuId() или $inCore→menuId().
Отличие лишь в том, что раньше menuid брался из адреса страницы, а теперь определяется ядром по ссылке.
Начиная с версии 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 в том, что последний не будет включен в состав дистрибутива и не будет затираться при обновлениях.