Проблемы с классом Router / URL Matcher - PullRequest
0 голосов
/ 05 февраля 2019

, поскольку регулярные выражения на самом деле не моя специальность, мне нужна помощь с этой небольшой проблемой (в PHP).Я хочу сопоставить данный URL с массивом определенных маршрутов, например:

$definedRoute = '/admin/user/[:id]/edit';
$url = '/admin/user/37/edit';

В моем классе это было бы так, я полагаю (getRoutes () возвращает массив определенных маршрутов):

foreach ($this->getRoutes() as $route) {
        $pattern = '~' . preg_replace('~\[\:[a-z]+\]~', '[a-z0-9]+', 
str_replace('/', '\/', $route['definition'])) . '~';
        if (preg_match($pattern, $url)) {
            $parameters = $this->getRouteParameters($route['definition']);
            (new $route['class']())->{$route['method']}($parameters);
            // die? break?
        }
    }

Я сделал это следующим образом: заменяйте каждый вхождения именованного параметра, такого как [: id], регулярным выражением для строчных букв и цифр, например, [a-z0-9]+.Это на самом деле будет работать, но в некоторых случаях будет соответствовать несколько и, следовательно, неправильные маршруты.Кроме того, он всегда будет соответствовать ~\/~ в большинстве случаев.Но каждый URL должен совпадать только один раз.

Правка №1: проблема в том, что маршруты совпадают несколько раз.Как я могу предотвратить это?

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

1 Ответ

0 голосов
/ 05 февраля 2019

Я не знаю, охватит ли это все мыслимые случаи, но вы можете использовать preg_match_all или preg_match в отличие от итерации по шаблонам.Это также должно улучшить производительность.

Важно то, что важен порядок сопоставления (слева направо), с массивом и циклом вы не можете этого сделать (вы можете на самом деле, но это уродливее).Затем мы можем отсортировать маршруты по сложности, например:

//this is intentionally in the opposite order of what I want it.
$routes = ['definition' => ['\/', '\/admin\/user\/[a-z0-9]+\/']];

//the more / separators the closer to the beginning we want it. or the more complex regexs go first.
uasort($routes['definition'], function($a,$b){
    //count the number of / in the route
    //note the <=> spaceship (as it's called) is only available in PHP7+
    return substr_count($b, '/') <=> substr_count($a, '/');
});

$url = '/admin/user/37/edit';

//in regex the pipe | is OR
preg_match('~^('.implode('|', $routes['definition']).')~i', $url, $matches);

print_r($matches);

Песочница

Вывод:

Array
(
    [0] => Array
        (
            [0] => /admin/user/37/edit
        )

    [1] => Array
        (
            [0] => /admin/user/37/edit
        )

)

Что правильно в этомдело.Даже если вы получите несколько совпадений, вы можете посчитать их длину strlen и затем взять самое длинное или «лучшее» совпадение из них.Это довольно просто, используя strlen и, возможно, сортировку по длине, поэтому я оставлю это на ваше усмотрение.

Однако я бы не назвал это гарантией того, что это будет работать 100% времени, это простоПервое, что пришло ко мне.

Другая идея

Другая идея - вы не привязываете совпадение к началу и концу строки.Теоретически, маршрут может / должен совпадать со всей строкой, как в моем примере выше, если вы добавите ^ и $ здесь:

   preg_match('~^('.implode('|', $routes['definition']).')$~i', $url, $matches);

Это обеспечит полное совпадение и в этом случае ~\/~не будет совпадать, даже если массив не отсортирован, как вы можете видеть в песочнице ниже.

Песочница

При этом не исключено, что вы будете иметь только / нужночастичное совпадение.Это зависит от вас и от того, как вы строите свои маршруты и URL-адреса.Конечно, вы можете просто использовать ^ start, как в начале, с типом совпадения, но вам нужно будет отсортировать их в этом случае.

Preg Match против Preg Match All

Предварительный матч также будет работать, но он вернет только первый матч.Так что, если он совпадает несколько раз, вы не можете сравнить их, чтобы найти лучший.Это может быть хорошо, если вы используете ^ и $.

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...