Пишем роутер uri с помощью регулярных выражений - 1

Регулярные выражения - исключительно мощный инструмент для извлечения данных путем сопоставления с образцом. Применим регулярки (regexps) к задаче разбора адресной строки на параметрыВ простейшем случае нас будет интересовать разделение строки на контроллер и действие. В более сложных случаях хотелось бы получать имя параметра со значением. Например для строк вида index/contact index есть контроллер, contact - действие. Для url с параметром page/view/100 мы хотели бы отождествить номер со значением переменной, которую передадим методу. Вводная теория. Регулярное выражение это образец, с которым сравнивается текст. В регулярном выражении можно обозначить захватывающие (capturing) группы, которые будут определять части выражения, имеющие для нас какой-то самостоятельный смысл. Образец - это шаблон, под который текст либо подходит, либо нет. Пример образца - \w+ означает, что это текст, \d+ означает, что это целое число. Для интересующихся рекомендую ознакомится с книгой Фридла "Регулярные выражения" Группы указываются с помощью круглых скобок. Например, (\w+) - захватывающая группа для текста, (\d+) для целого числа В зависимости от того, в каком стандарте и какой программой мы пользуемся, используется соглашения о том, как обозначать группы, если они были найдены. Например, в редакторе Kate \0 - вся строка, подходящая под образец, \1 - первая группа, \2 вторая и т.п. В PHP используется знак $ - $0, $1, $2 и т.п. Однако нумеровать группы не так наглядно. Гораздо удобнее задавать захватывающим группам имена. Это делается путем указания имени параметра (?<parameter>regexp). Допустим, мы хотим найти телефоны вида 33-44-55, тогда наш образец будет иметь вид (?<phone>(\d+)-(\d+)-(\d+)) Теперь у нас есть все составляющие, чтобы написать роутер (диспетчер) на PHP на регулярках

<?php

 class Linker {
 	public string $controller;
 	public string $router;
    public $matches;
 	public function __construct(string $c, string $r){
 		$this->controller = $c;
 		$this->router = $r;
        $this->matches = [];
 	}



 	public function check($path, $ifNot){
        if (preg_match($this->router, $path, $this->matches)===1){
            return
                ['controller' => $this->controller,
                'matches' => $this->matches ];
        }
            else return
                ['controller' => $ifNot,
                  'matches' => []];
    }




 }

 class RouteDispatcher {
     public array $routes = [];
     public string $defaultController = 'alien';
     public function __construct($def){
          $this->defaultController = $def;
    }
     public function add($c, $r){
         array_push ( $this->routes , new Linker($c, $r));
         return $this;
    }
    public function check($path){

        for ($i=0; $i<count($this->routes); $i++){
            $r = $this->routes[$i];
            $next_check = $r->check($path, $this->defaultController);
            if ($next_check['controller'] !== $this->defaultController) {
                return $next_check;
            }
        };
        return ['controller' => $this->defaultController, 'matches' => [] ];
    }



}

 $router = new RouteDispatcher("nowhere");

 $router->add('test', "#^(?<controller>test)/(?<action>\w+)$#i");
 $router->add("site", "#^(?<controller>site)/(?<action>\w+)$#i");
 $router->add("super", "#^(?<controller>super)/(?<action>\w+)$#i");
 $router->add("view", "#^(?<controller>page)/(?<action>\w+)/(?<page>\d+)$#i");


 $requests_uris = [
      'page/view/12',
      'page/delete/100',
      'page/view/xxx',
      'test/Index',
      'test/About',
      'site/About',
      'site/Index',
      'super/shop',
      'super/buy',
      'no/where'
  ];


      foreach( $requests_uris as $path ){

 echo PHP_EOL . "Get $path" . PHP_EOL;
 $w = $router->check($path);
 echo "Контроллер ". $w['controller'] . PHP_EOL;
 if ($w['matches']){
     $action = $w['matches']['action'];
     echo " --> действие " . $action . PHP_EOL;

}

if ($w['controller']=='view'){
    $page = $w['matches']['page'];
    echo " параметр page = $page" . PHP_EOL;
}

    }

Запустим данную программу Результат вывода показывает, что маршруты корректно разбираются. Обратите внимание, что имя контроллера явно задается при назначении маршрута (это нужно чтобы переадресовывать запросы на другой контроллер, например, при тестировании)
┬─[artem@laptop:~]─[11:15:06]
╰─>$ php test-router.php

Get page/view/12
Контроллер view
 --> действие view
 параметр page = 12

Get page/delete/100
Контроллер view
 --> действие delete
 параметр page = 100

Get page/view/xxx
Контроллер nowhere

Get test/Index
Контроллер test
 --> действие Index

Get test/About
Контроллер test
 --> действие About

Get site/About
Контроллер site
 --> действие About

Get site/Index
Контроллер site
 --> действие Index

Get super/shop
Контроллер super
 --> действие shop

Get super/buy
Контроллер super
 --> действие buy

Get no/where
Контроллер nowhere
 

Теги документа