За последние 24 часа нас посетили 21110 программистов и 1022 робота. Сейчас ищут 857 программистов ...

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

Тема в разделе "Прочие вопросы по PHP", создана пользователем Вероломство, 25 мар 2021.

  1. Вероломство

    Вероломство Активный пользователь

    С нами с:
    19 июн 2017
    Сообщения:
    615
    Симпатии:
    24
    PHP:
    1. <?php
    2.  
    3.  
    4. namespace core;
    5.  
    6.  
    7. use Error;
    8.  
    9. abstract class Router
    10. {
    11.     private static $routes = [];
    12.     private static $route = [];
    13.     private static $params = [];
    14.     private static $namespace = 'app\controllers\\';
    15.  
    16.     public static function addRule($route, $params = [])
    17.     {
    18.         $route = preg_replace('~{([a-z]+)}~', '(?<$1>[a-z]++(?:-[a-z]++)?)', $route);
    19.         $route = preg_replace('~{([a-z_]+):([^}]+)}~', '(?<$1>$2)', $route);
    20.  
    21.         self::$routes["~^{$route}$~"] = $params;
    22.     }
    23.  
    24.     public static function dispatch($url)
    25.     {
    26.         if (!self::matchRoute($url)) {
    27.             throw new Error('Страница не найдена');
    28.         }
    29.  
    30.         $controller = self::$namespace . self::$route['controller'] . 'Controller';
    31.  
    32.         if (!class_exists($controller)) {
    33.             throw new Error("Контроллер {$controller} не найден", 404);
    34.         }
    35.  
    36.         $cObj = new $controller(self::$route);
    37.  
    38.         $action = self::$route['action'] . 'Action';
    39.  
    40.         if (!method_exists($cObj, $action)) {
    41.             throw new Error("Метод {$controller}::{$action} не найден", 404);
    42.         }
    43.  
    44.         $cObj->$action(...self::$params);
    45.  
    46.         $cObj->getView();
    47.     }
    48.  
    49.     private static function matchRoute($url)
    50.     {
    51.         foreach (self::$routes as $route => $params) {
    52.  
    53.             if (preg_match($route, $url, $matches)) {
    54.  
    55.                 foreach ($matches as $match => $param) {
    56.  
    57.                     if (is_string($match)) {
    58.                         $params[$match] = $param;
    59.                     }
    60.                 }
    61.  
    62.                 $params = array_filter($params);
    63.  
    64.                 if (isset($params['module'])) {
    65.                     self::$namespace .= $params['module'] . '\\';
    66.                     self::$route['module'] = $params['module'] . '/';
    67.                 }
    68.  
    69.                 self::$route['controller'] = self::upperCamelCase($params['controller'] ?? 'home');
    70.                 self::$route['action'] = lcfirst(self::upperCamelCase($params['action'] ?? 'index'));
    71.  
    72.                 unset($params['module'], $params['controller'], $params['action']);
    73.  
    74.                 self::$params = $params;
    75.  
    76.                 return true;
    77.             }
    78.         }
    79.  
    80.         return false;
    81.     }
    82.  
    83.     private static function upperCamelCase($name)
    84.     {
    85.         return str_replace(' ', '', ucwords(str_replace('-', ' ', $name)));
    86.     }
    87. }

    Теперь я задаю правила:
    PHP:
    1. // Правило рефссылки
    2. // Router::addRule('start/{referer_id:[1-9]\d*}', ['controller' => 'start', 'action' => 'referer']);
    3. Router::addRule('{controller:start}/{referer_id:[1-9]\d*}', ['action' => 'referer']);
    4.  
    5. // Дефолт
    6. Router::addRule('{controller}?/?{action}?');
    7.  
    8. Router::dispatch(ltrim($_SERVER['REQUEST_URI'], '/'));
    Что происходит?

    Я подменил экшен, и у меня вместо StartController::indexAction отрабатывает StartController::refererAction

    Метод выглядит так:

    PHP:
    1. public function refererAction($referer_id)
    2. {
    3.     debug($referer_id); // дебажим
    4.     if ($referer_id and !isset($_SESSION['referer_id']) and User::findOne($referer_id)) {
    5.         $_SESSION['referer_id'] = $referer_id;
    6.     }
    7.  
    8.     $this->redirectToPage('/');
    9. }
    Теперь если я набираю адрес /start/123, то всё отрабатывает как надо: в дебаге - string '123' (length=3)

    Но если я наберу /start/referer, то естественно отработает дефолт, потому что цикл матчера продолжает искать совпадения в массиве роутов и мы получаем - Too few arguments to function app\controllers\StartController::refererAction()

    Эту ошибку у меня отловит обработчик ошибок, покажет её красиво разрабу, а пользуну 404, но я не хочу ИМЕННО таким образом ловить её.

    Есть ли возможность сделать правило рефссылки ОБЯЗАТЕЛЬНЫМ и делать выброс в роутере при адресе /start/referer - это вообще можно как-то реализовать или дефолт не позволит этого сделать?

    P.S. я знаю что можно сделать public function refererAction($referer_id = 0), но всё же :)
     
    #1 Вероломство, 25 мар 2021
    Последнее редактирование: 25 мар 2021
  2. miketomlin

    miketomlin Старожил

    С нами с:
    9 авг 2016
    Сообщения:
    3.792
    Симпатии:
    650
    Ну, сделай более общую маску идентификатора, чтобы /start/referer попал в экшин.
    --- Добавлено ---
    А внутри можешь делать проверку с более жесткой маской или надеяться на БД. Или, может, findOne сам выплюнет нечисловой id без обращения к БД.
     
    Вероломство нравится это.
  3. Вероломство

    Вероломство Активный пользователь

    С нами с:
    19 июн 2017
    Сообщения:
    615
    Симпатии:
    24
    @miketomlin вроде понял, нужно просто тупо написать правило под /start/referer, и там указать в массиве параметров referer_id => 0, что-то вроде такого

    то есть держать в массиве роутов дефолтное значение передаваемого параметра
    --- Добавлено ---
    @miketomlin либо избавиться от подмены и обращаться /start/referer/123, что наверное и сделаю...
     
  4. miketomlin

    miketomlin Старожил

    С нами с:
    9 авг 2016
    Сообщения:
    3.792
    Симпатии:
    650
    Не. Говорю, сделай, чтобы и с буквами в id запрос попадал на обработку в экшин.
    --- Добавлено ---
    А внутри уже переводи в 404-ую запросы с левыми id.