Регулярные выражения - исключительно мощный инструмент для извлечения данных путем сопоставления с образцом. Применим регулярки (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Теги документа