Здрасте!

Продолжаем лупить статьи на тему «Битрикс не так уж и плох, если его доработать».
На этот раз разговор пойдет на тему «url_rewrite», потому как я считаю, что текущий вариант вообще не идеален.
А идеальным я считаю вариант маршрутизации в микрофреймворках, например Slim (или тот же Lumen), вообщем тех, которые дружат с PSR-7.
Кому интересно, го под кат.
Кому не интересно, ну тут уж сами решайте ;-)

INTRO


На самом деле мои предыдущие статьи носили более менее абстрактный характер (ну кроме статьи про Juggernaut пожалуй), поэтому в данном посте постараюсь меньше писать теории и побольше кода.

Кстати про Juggernaut


Документация будет в скором времени, к сожалению есть некоторые преграды:
  1. время
  2. рефакторинг
  3. мне полюбился TDD, так что рефакторинг остановился до тех пор пока не напишу тесты
  4. направление развитие библиотеки как оказалось я не совсем еще до конца определил


Но это как говорить «совсем другая история», поэтому вернемся к тому, о чем собственно данная статья: роутинг.

UrlRewrite by Bitrix


Порядок маршрутизации я думаю лучше изобразить схемкой (и понятно, и наглядно):



Что это все значит:

include bitrix/urlRewrite.php
Подключаем файл который занимается маршрутизацией (ну это я думаю и так все поняли).
Вообще данный пункт (и все что выше на блок схеме) — это заслуги .htaccess:

RewriteCond %{REQUEST_FILENAME} !-f # не файл 
RewriteCond %{REQUEST_FILENAME} !-l # не символьная ссылка 
RewriteCond %{REQUEST_FILENAME} !-d # не директория 
RewriteCond %{REQUEST_FILENAME} !/bitrix/urlrewrite.php$ # не файл маршрутизации 
RewriteRule ^(.*)$ /bitrix/urlrewrite.php [L] 

fix request_uri for IIS
Данный пункт, судя по комменту в коде, ответственен за какие то косяки IIS (бедняга MS), за какие я не в курсе, но логика следующая:
если QUERY_STRING имеет вид «wtf=404;http(s)://wtf.ru», то все GET параметры запроса чистятся и данная конструкция убирается из адреса.
На вопрос «что проиходит?» я не могу дать ответа, так что едем дальше.

include dbconn.php
Подключаем базу.
Зачем? Непонятно, т.?к. запросов к базе дальше нет и работа идет только с файловой системой.
Я конечно не опускался в реализацию классов для работы с файлами, но если им нужно что-либо от базы, то это не иначе как печально :-(

decode request page (for UTF-8)
Все понятно из названия, кодирование REQUEST_URI.
Зачем? Зачем Битрикс любит Windows-1251? Без понятия. Но это будет продолжаться вечно (и это инсайдерская информация).

include /urlRewrite.php
Собственно подключаем сами правила маршрутизации.

process Url
Немного странные действия, но все же происходит следующее:
Если есть GET параметр SEF_APPLICATION_CUR_PAGE_URL, то приравниваем REQUEST_URI к его значению, а затем переписываем все зависимые переменные и глобальные массивы ($_GET, $_SERVER, …)

process urlRewrite
О, да!
Мы до него добрались.
Собственно что происходит:
  • Парсим параметр CONDITION.
  • Заменяем параметр CONDITION на RULE в REQUEST_URI
  • Добавляет в $_GET и $_REQUEST переменные из правила маршрутизации.
  • Проверяем существует ли указанный файл, валидный ли у него путь и не является ли он административным (upload, bitrix, bitrix/services, bitrix/groupdavphp).
  • Если все ок, то подключаем.

Меня одного смущает что проверка идет после того как мы уже сунули все параметры в глобальные переменные?

Много неясностей, зачем сделано так, а не иначе?
Ну и много неясностей, зачем вообще это сделано?
Так что теперь перейдем к идеальному варианту Slim’a.

UrlRewrite by Slim


Как делает этот замечательный фреймворк:

$app = new \Slim\App(); 
$app->get('catalog/{sectionCode}/{elementCode}/', function(Request $request, Response $response) { 
    // code 
}); 
$app->post('catalog/{sectionCode}/{elementCode}/', function(Request $request, Response $response) { 
    // code 
}); 
$app->put('catalog/{sectionCode}/{elementCode}/', function(Request $request, Response $response) { 
    // code 
}); 
$app->delete('catalog/{sectionCode}/{elementCode}/', function(Request $request, Response $response) { 
    // code 
}); 
$app->run(); 

Легко и непринужденно цепляемся к нужному действию, с нужным маршрутом, параметрами и реализацией.

UrlRewrite by Juhhernaut


Ну, а теперь пробуем все это миксануть.
Выкидываем из Slim'a указание метода и собственно реализацию действия, вместо нее будет путь до файла.
Для начала обозначим синтаксис привязки маршрутов к реальным физически файлам (по факту это является мануалом использования):

/* 
 * подключаем файлы для роутрера 
 */ 
include_once __DIR__.'/modules/olof.juggernaut/includeRewrite.php'; 
 
use Jugger\Context\Router; 

/** 
 * создаем роутер 
 * в отличии от Slim'a маршруты не добавляются, а выполняются 
 * таким образом как только маршрут найден, 
 * остальной код не будет выполняться 
 */ 
$r = new Router(); 
/* 
 * поиск файла с комнца маршрута 
 * например, URL запроса выглядит так: "/catalog/section1/section2/element1/", 
 * То поиск поочередно будет перебирать директории в поисках файла 'index.php': 
 * - /catalog/section1/section2/element1/index.php 
 * - /catalog/section1/section2/index.php 
 * - /catalog/section1/index.php 
 * - /catalog/index.php 
 *  
 * в корень сайта опускаться поиск не будет 
 * также никакие параметры не будут добавлены в переменные GET и REQUEST, 
 * т.к. нет шаблона маршрута 
 * данный способ хорошо подходит для стандартной ситуации Битрикс 
 * когда маршрутизация ложится на плечи компонентов 
 */ 
$r->runRecursive(); 
/* 
 * добавляем маршрут 
 * формат записи: 
 *  {nameParam}, 
 *  {nameParam:regExp} 
 * где 'regExp' - регулярное выражение. Например, '\d+' или '[0-9]+' 
 */ 
$r->run( 
    "/page/", 
    "/page/index1.php" 
); 
$r->run( 
    "/page/{p1:[0-9]+}/{p2}", 
    "/page/index2.php" 
); 
$r->run( 
    "/catalog/", 
    "/catalog/index1.php" 
); 
/* 
 * добавляем сразу несколько маршрутов 
 */ 
$r->run( 
    [ 
        "/catalog/{sectionCode}/", 
        "/catalog/{sectionCode}/{elementId:\d+}/", 
    ], 
    "/catalog/index2.php" 
); 
/* 
 * маршрутизация в один файл с параметром ?r=path/to/file 
 */ 
$r->run("{r:.+}", "index.php"); 
/* 
 * окончание роутига 
 * если никакой маршрут не подошел 
 * то подключается /bitrix/urlrewrite.php 
 */ 
$r->end(); 

По факту, если реализацию маршрутов оставить на совести компонентов, то достаточно будет прописать следующую конструкцию (да, так тоже можно ;-) ):

(new Router()) 
    ->runRecursive() 
    ->end(); 

Данный файл нужно (можно) назвать urlrewrite.php, кинуть его в папку /local/ и внести правки в .htaccess файл, который лежит в корне.

Вместо:
RewriteCond %{REQUEST_FILENAME} !/bitrix/urlrewrite.php$ 
RewriteRule ^(.*)$ /bitrix/urlrewrite.php [L] 

Нужно прописать:
RewriteCond %{REQUEST_FILENAME} !/local/urlrewrite.php$ 
RewriteRule ^(.*)$ /local/urlrewrite.php [L] 

И собственно все. Простой и понятный роутинг у вас в кармане.

Ссылки:


Juggernaut: github.com/irpsv/juggernaut.bitrix
Реализация роутера: github.com/irpsv/juggernaut.bitrix/blob/master/olof.juggernaut/lib/Context/Router.php
Поделиться с друзьями
-->

Комментарии (37)


  1. oxidmod
    11.07.2016 10:49
    +3

    >> Зачем? Зачем Битрикс любит Windows-1251? Без понятия. Но это будет продолжаться вечно (и это инсайдерская информация).
    шел 2016 год… просто слов нет.
    почему люди этим пользуются? неужели у них не бывает клиентов скажем из Германии? или из того же Китая?


    1. rpsv
      11.07.2016 10:56
      +1

      Есть возможность ставить Битрикс под UTF-8, но это не основная кодировка, по-умолчанию 1251.
      А по поводу клиентов: вряд ли)))
      Скорее всего из СНГ только, да и то все равно 1251 не спасет.


  1. AlexLeonov
    11.07.2016 11:19
    +1

    Что только не делают битриксоиды, лишь бы не писать нормальный код.

    Роутер — одна из самых простых частей приложения, особенно в парадигме MVC: подай на вход Request, на выходе получи Route, как некий внутренний путь по приложению, и решай, что с этим путем делать. Ну ОК, еще обработай исключение «Соответствие не найдено», превратив его в ответ 404.

    Чтобы не заморачиваться, создай конфиг роутинга в виде массива, где ключами будут шаблоны, на соответствие которым проверяется Request, а значениями — функции, которые получат этот Request, группы совпавшие в шаблоне, замкнут на себя контекст и вернут Route.

    Всё!

    Что все эти люди делают с этой простейшей концепцией? Зачем? Ради чего вы вообще пишете вот такой код?


    1. maxpsyhos
      11.07.2016 11:40
      +1

      В Битриксе UrlRewrite — это статичный конфиг, в котором кроме копания ручками ещё автоматически вносятся роуты от виджетов, поэтому никаких функций там нет, при его архитектуре они там не нужны.


      1. AlexLeonov
        11.07.2016 11:54

        Вы сейчас прослушали миниатюру «Битрикс: нам не нужно нормально программировать, потому что у нас такая вот архитектура»


        1. maxpsyhos
          11.07.2016 12:16
          -2

          Вы сейчас прослушали миниатюру «Есть только MVC и PSR пророк его, всё остальное от лукавого».


          1. AlexLeonov
            11.07.2016 12:25
            -2

            MVC — нет, конечно. Архитектур много, все они разные. Нет ничего плохого в чем-то, что не вписывается в прокрустово ложе MVC.

            PSR — да. Если вы не уважаете стандарты, принятые сообществом — вон из профессии.


            1. Delphinum
              11.07.2016 12:33
              -2

              А при чем тут уважение стандарта и его использование? И что еще за «принятие сообществом» такое? Где то было совещание PHP разработчиков, на котором подписали договор о безоговорочном следовании PSR всеми участниками сообщества, иначи им запрещается использовать в своей работе данный ЯП?


              1. AlexLeonov
                11.07.2016 12:38

                Где то было совещание PHP разработчиков


                Да, конечно.
                Вот например: http://www.php-fig.org/psr/psr-4/meta/#6-votes
                Если вы не считаете этих людей авторитетами для себя, у меня для вас плохие новости — вы за бортом современного PHP.


                1. Delphinum
                  11.07.2016 12:42

                  А где там речь об исключение из профессии тех, кто не придерживается PSR?

                  Если вы не считаете этих людей авторитетами для себя

                  При чем здесь то, кем я для себя считаю этих людей? Не надо подменять понятия.
                  вы за бортом современного PHP

                  Какой ужас, модное, современное, молодежное проходит мимо меня!


                  1. AlexLeonov
                    11.07.2016 12:44

                    Зря иронизируете. Отстать очень легко.


                    1. Delphinum
                      11.07.2016 12:47
                      -2

                      Я не боюсь отстать, я боюсь убить свое время на нечто переходное. PSR это хорошо, но не нужно быть таким категоричным, это же всего лишь стандарт.


    1. rpsv
      11.07.2016 12:36

      Потому что в Битриксе нет единой точки входа, и поэтому роутер не всегда подключается, а только когда обращение идет не к существующему файлу.

      А в чем сложность собственно? Алгоритм:
      1. Получили URI.
      2. Привели соответствие с шаблонами.
      3. Подключили нужный файл, либо 404 выплюнули.

      По поводу конфига: да, вероятно вы правы, стоит добавить возможность добавить массово конфиг.

      А теперь по поводу кода: чем он вам не по нраву?


      1. AlexLeonov
        11.07.2016 12:41

        Он не семантичен. Что такое run()? Зачем нужен end()?

        Не проще ли:

        try {
          $request = ...;
          $route = (new Router(
            new Config(ROUTE_CONFIG_PATH)
          ))->processRequest($request);
        }
        


        1. rpsv
          11.07.2016 12:46

          run — проверка шаблона на соответствие (если приятней глазу читайте как process).

          По сути весь этот велосипед строился ради runRecursive.

          end — кидает на Битриксовый обработчик, если в текущем нет нужных правил, т.к. данный роутер вспомогательный, стандартные компоненты вряд ли будут ему следовать.

          Request в вашем примере, в данному случае это URI, по нему идет маршрутизация, другие данные не нужны.


          1. Delphinum
            11.07.2016 12:48

            А битрикс не различает GET, POST, PUT, DELETE?


            1. rpsv
              11.07.2016 12:58

              Маршрутизация основана только на URI, методы в счет не идут. Остальной обработкой занимаются либо сами страницы, либо компоненты в них подключенные.
              В Битрикс нет «стандартного» MVC (контроллер, модель, представление). После роутинга, подключается страница, а не действие контроллера.


              1. Delphinum
                11.07.2016 13:00

                Тобишь внутри страницы еще один роутер? А почему тогда это не реализовать на уровне веб-сервера?


                1. rpsv
                  11.07.2016 13:09

                  Чтобы правила были доступны из PHP, для той же генерации ссылок по шаблону.
                  Битрикс также позволяет генерировать шаблоны маршрутизации из админки, сохраняются они соответственно в PHP.
                  Как по мне, писать напрямую правила маршрутизации в htaccess — не круто.


                  1. Delphinum
                    11.07.2016 13:31
                    +1

                    Ну так роутить запрос вне роутера тоже не круто.


                    1. rpsv
                      11.07.2016 13:36

                      Согласен.
                      Это вопросы к архитектуре.
                      Скорее всего (и это мое личное мнение), роутинг в компонентах сделан для удобной настройки компонентов из административного интерфейса, и только.


  1. Delphinum
    11.07.2016 11:53

    А что мешало взять любой готовый пакет для роутинга и подключить его?

    мне полюбился TDD, так что рефакторинг остановился до тех пор пока не напишу тесты

    Тесты это круто, конечно, но чуствую я, «эра» Juggernaut начинает подходить к концу.


    1. rpsv
      11.07.2016 12:41

      А что мешало взять любой готовый пакет для роутинга и подключить его?

      Прокачка скилла. (А вообще FastRoute хорош).

      Тесты это круто, конечно, но чуствую я, «эра» Juggernaut начинает подходить к концу.

      Спасибо за веру)))


      1. Delphinum
        11.07.2016 12:45

        Вы решили писать «замену» битрикса с целью прокачки скилла?


        1. rpsv
          11.07.2016 12:55

          Атата! Не меняйте смысл слов, Вы спросили про роутинг, а не Juggernaut)))

          На самом деле роутер писался глядя в код FastRoute, а не использовал я его из-за неполного понимая open-source лицензий.

          А по поводу прокачки скилла: juggernaut в некоторых местах рефакторит битрикс, но в некоторых что-то привносит свое. Он создан для удобства, если только для моего, то так тому и быть, меньше париться с документацией)


          1. Delphinum
            11.07.2016 13:01

            Ну вы же предлагаете использовать данный роутер в Juggernaut?


            1. rpsv
              11.07.2016 13:07

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


  1. mnepohyi
    11.07.2016 12:13

    Логотип как-то уж слишком похож на лого Beats Electronics.


    1. rpsv
      11.07.2016 12:28

      к Битриксу вопросы))


  1. M-A-XG
    11.07.2016 12:28

    >include /urlRewrite.php

    Там же вроде не camelCase…

    >include dbconn.php

    Там вроде не только база, а и другие настройки…

    Ваш роутер подтягивает битриксовские правила роутинга?

    >Что все эти люди делают с этой простейшей концепцией? Зачем? Ради чего вы вообще пишете вот такой код?

    А я вообще не пишу код роутинга, он в нгинксе. :)


    1. rpsv
      11.07.2016 13:02

      Там же вроде не camelCase…

      В статье: что-то вроде псевдокода

      Там вроде не только база, а и другие настройки…

      И опять вроде))) В основном там база, да и из названия это следует. Помимо базы также присутствуют константы кеша и еще парочку непонятных/неизвестных мне. Но в любом случае это не должно подключаться до роутинга, с учетом того, что данные константы вряд ли там используются (не факт)

      Ваш роутер подтягивает битриксовские правила роутинга?

      Если в роутере не сработало ни одно из правил, то далее уже подключается стандартный роутинг


      1. M-A-XG
        12.07.2016 10:19

        >И опять вроде)))

        Я с Битриксом 2 года не работаю уже, поэтому могу ошибаться, поэтому вроде. :)

        >В основном там база.

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

        >да и из названия это следует

        Название неочевидное… :)

        >это не должно подключаться до роутинга

        http://dev.1c-bitrix.ru/api_help/main/general/pageplan.php

        >Если в роутере не сработало ни одно из правил, то далее уже подключается стандартный роутинг

        А в конфиг вашего роутинга пишутся параметры с вызуального редактирования параметров комплексного компонента?


  1. rpsv
    12.07.2016 12:33

    http://dev.1c-bitrix.ru/api_help/main/general/pageplan.php


    Знаю такую страницу, только не понимаю к чему это?

    А в конфиг вашего роутинга пишутся параметры с вызуального редактирования параметров комплексного компонента?


    Нет они пишутся в файл '/urlrewrite.php' (не путать с 'bitrix/urlrewrite.php' который отвечает за маршрутизацию), не вижу смысла подключать его, если в дальнейшем (если нет подходящих правил) все равно маршрутизацией будет заниматься битирксовый роутер


    1. M-A-XG
      12.07.2016 13:51

      >Знаю такую страницу, только не понимаю к чему это?

      Почему это все подключается…
      В том файле не только БД ну и остальное.

      >Нет они пишутся в файл '/urlrewrite.php' (не путать с 'bitrix/urlrewrite.php' который отвечает за маршрутизацию), не вижу смысла подключать его, если в дальнейшем (если нет подходящих правил) все равно маршрутизацией будет заниматься битирксовый роутер

      Боже, роуты пишутся вручную или с редактора параметров компонента?


      1. rpsv
        13.07.2016 08:58

        Почему это все подключается… В том файле не только БД ну и остальное.

        Вообще нет. В том файле определяются только константы, а страница грузиться начинает после подключения /bitrix/header.php или файла пролога (собственно после маршрутизации).

        Боже, роуты пишутся вручную или с редактора параметров компонента?

        Вы сначала мозги включите, а потом божечку вспоминайте. У Битрикса свой формат роутинга (маршрутов, подстановки параметров, ...), и опять напишу, что не вижу смысла писать, потому как затем подключается битриксовый роутер, а в данный маршрутизации писать нужно ручками


        1. M-A-XG
          13.07.2016 11:04

          Так /bitrix/header.php и подключает их все… :)

          Я знаю, что у битрикса свой формат.
          Я спрашиваю, пишете ли вы в свой файл. :)


  1. kazmenko
    12.07.2016 23:40
    +1

    Битрикс ужасен… И ужасен во всем за исключением того что делает его битриксом (сертификация ФСТЭК и прочие необходимые пряники). Чем меньше в сайте битрикса тем лучше для сайта… Тем лучше для его овнера… Тем лучше для сапорта и криэйтеров проекта… А это «чем меньше» достигается связкой Silex/Slim/Lumen плюс API Bitrix плюс любимый JS+CSS фреймворк. Да это похоже на «мерседес» замаскированный под костыли… Но многолетний опыт разработки на этой «платформе» порой приводит к таким вот странным решениям…