@tester_toster

Как правильно написать маршрутизация на php с регулярными выражениями?

Добрый день.
Пишу простую маршрутизацию на регулярных выражениях, прошу помощи.
Про велосипеды говорить не нужно - пишу в целях обучения и , возможно, для своих проектов.

Имеются наработки:
<?php
    class Route
    {
        private $map = null;
        private $patterns = [
            '{all}',
            '{number}',
            '{char}'
        ];
        private $replacements = [
            '(.+)',
            '([0-9]+)',
            '([a-zA-Z]+)'
        ];

        public function add($method, $string, $action, $vars = null){

            $method = strtoupper($method);
            $string = strtolower($string);

            if(!strstr($string,'{')){
               $this->map[$method]['static'][$string] =[
                'action' => $action
                ];
                return;
            }

            $replaced = str_replace($this->patterns, $this->replacements, $string);
            $replaced = '#^'.$replaced.'$#';
            $this->map[$method]['regexp'][$replaced] = [
                    'action' => $action
            ];

            if($vars){
                $vars = (array)$vars;
                $this->map[$method]['regexp'][$replaced]['vars'] = $vars;
            }
        }

        public function start(){
            $url = explode('?',urldecode($_SERVER['REQUEST_URI']))[0];
            $method = $_SERVER['REQUEST_METHOD'];
            // in static
            if(isset($this->map[$method]['static'][$url])){
                return $this->load($this->map[$method]['static'][$url]);
            }
            // in regex
            foreach ($this->map[$method]['regexp'] as $pattern => $data) {
                if(!preg_match($pattern, $url,$match)){
                    continue;
                }
                return $this->load($data,$match);
            }
                // 404 - controller/action
                die('not founded');
        }

        private function load($data, $match = null){
           /*
                $data - массив с controler/actions + возможно имена переменных
           */
        }
    }
    
    $route = new Route();
    $route->add('GET', '/news/{number}', 'controller/action', 'id');
    $route->add('POST', '/news/{number}_{all}_{sdcsdc}', 'controller/action', '[id,name]');
    $route->add('GET', '/news', 'News');
    $route->add('GET', '/', 'home');
    $route->start();

Массив адресов имеет структуру [Метод(POST|GET)][ТИП(статичный|REGEXP)].
При добавлении правила проверяю наличие в строке символа "}", если символа нет, то добавляю статичный маршрут, если есть, то подставляю регулярные выражения через str_replace() (число|строка|все), подразумевается, что неправильный маршрут не буду добавлять.
При добавлении правила с регулярным выражением можно дать имена переменным - '1|2 добавление'.
При старте, ищу сначала через isset() в статике, потом, если не нашел - через preg_match().
Вопросы:
На сколько рабочим является текущий вариант?
Планирую добавить кешрование карты маршрутов ($map) и готовых адресов.
При инициализации добавить загрузку из файла, проверенные regex маршруты дописывать в еще один файл и проверять вначале в нем, либо в тот-же $map, в static, какой из вариантов предпочтительней?
Есть ли разница в чем хранить - json или serialize?
Насколько будет производительный isset()?
Что еще можно добавить, в какую сторону смотреть?
Буду благодарен за любую полезную информацию.
  • Вопрос задан
  • 1533 просмотра
Пригласить эксперта
Ответы на вопрос 1
index0h
@index0h
PHP, Golang. https://github.com/index0h
1. Что такое роут?
Это некая сущность, умеющая проверять, подходит ли она для запроса, или нет.
2. Что такое роутер?
Это объект, умеющий выбирать подходящий роут по запросу и возвращать его.

Рекомендую создать:
1. RouteInterface с методами: supports(Request $request): bool
2. Создайте интерфейс RequestRunnerInterface: run(Request $request): Response
3. Создайте RegExpRoute, который будет уметь по регулярке проверять запрос. Там же имплементируйте оба интерфейса.
4. Создайте Router, в который можно будет по интерфейсу насетапать роуты
5. Процесс выполнения будет примерно такой:
- создать объект Request
- насетапать роуты
- занести роуты в роутер
- получить роут по запросу
- для роута с имплементацией 2-го интерфейса выполнить run
- отрендерить ответ
- завершить выполнение

НЕ используйте глобальные и суперглобальные переменные, начале выполнения создайте HttpFoundation Request и работайте с ним.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы