如何为路由框架进行URL匹配正则表达式?

时间:2022-12-17 16:50:28

I already have a routing method that matches this pattern:

我已经有一个匹配此模式的路由方法:

/hello/:name

that set name to be a dynamic path, I want to know how to make it:

将名称设置为动态路径,我想知道如何制作它:

/hello/{name}    

with the same regex. How to add optional trailing slash to it, like this?

与正则表达式相同。如何添加可选的尾部斜杠,像这样?

/hello/:name(/)

or

/hello/{name}(/)

This is the regex I use for /hello/:name

这是我用于/ hello /:name的正则表达式

@^/hello/([a-zA-Z0-9\-\_]+)$@D

The regex is auto generated from PHP class

正则表达式是从PHP类自动生成的

private function getRegex($pattern){
        $patternAsRegex = "@^" . preg_replace('/\\\:[a-zA-Z0-9\_\-]+/', '([a-zA-Z0-9\-\_]+)', preg_quote($pattern)) . "$@D";
        return $patternAsRegex;
    }

If the route is /hello/:name(/) I want it to make the match with optional thing else continue normal

如果路由是/ hello /:name(/)我希望它与可选的东西匹配,则继续正常

2 个解决方案

#1


This will create a regular expression for the $pattern route with both :name and {name} parameters, as well as the optional slash. As a bonus, it will also add a ?<name> to make the parameter easier to handle down the line.

这将为$ pattern路由创建一个正则表达式,其中包含:name和{name}参数,以及可选的斜杠。作为奖励,它还会添加一个? ,以使参数更容易处理。

For example, a route pattern of /hello/:name(/) will get the regular expression @^/hello/(?<name>[a-zA-Z0-9\_\-]+)/?$@D. When matched with a URL, like preg_match( <regex above>, '/hello/sarah', $matches) that would give you $matches['name'] == 'sarah'.

例如,/ hello /:name(/)的路由模式将获得正则表达式@ ^ / hello /(? [a-zA-Z0-9 \ _ \ _] +)/?$ @ D 。当与URL匹配时,比如preg_match( ,'/ hello / sarah',$ matches),它会给你$ matches ['name'] =='sarah'。

There are some tests to be found below the actual function.

在实际功能下面有一些测试。

function getRegex($pattern){
    if (preg_match('/[^-:\/_{}()a-zA-Z\d]/', $pattern))
        return false; // Invalid pattern

    // Turn "(/)" into "/?"
    $pattern = preg_replace('#\(/\)#', '/?', $pattern);

    // Create capture group for ":parameter"
    $allowedParamChars = '[a-zA-Z0-9\_\-]+';
    $pattern = preg_replace(
        '/:(' . $allowedParamChars . ')/',   # Replace ":parameter"
        '(?<$1>' . $allowedParamChars . ')', # with "(?<parameter>[a-zA-Z0-9\_\-]+)"
        $pattern
    );

    // Create capture group for '{parameter}'
    $pattern = preg_replace(
        '/{('. $allowedParamChars .')}/',    # Replace "{parameter}"
        '(?<$1>' . $allowedParamChars . ')', # with "(?<parameter>[a-zA-Z0-9\_\-]+)"
        $pattern
    );

    // Add start and end matching
    $patternAsRegex = "@^" . $pattern . "$@D";

    return $patternAsRegex;
}

// Test it
$testCases = [
    [
        'route'           => '/hello/:name',
        'url'             => '/hello/sarah',
        'expectedParam'   => ['name' => 'sarah'],
    ],
    [
        'route'           => '/bye/:name(/)',
        'url'             => '/bye/stella/',
        'expectedParam'   => ['name' => 'stella'],
    ],
    [
        'route'           => '/find/{what}(/)',
        'url'             => '/find/cat',
        'expectedParam'   => ['what' => 'cat'],
    ],
    [
        'route'           => '/pay/:when',
        'url'             => '/pay/later',
        'expectedParam'   => ['when' => 'later'],
    ],
];

printf('%-5s %-16s %-39s %-14s %s' . PHP_EOL, 'RES', 'ROUTE', 'PATTERN', 'URL', 'PARAMS');
echo str_repeat('-', 91), PHP_EOL;

foreach ($testCases as $test) {
    // Make regexp from route
    $patternAsRegex = getRegex($test['route']);

    if ($ok = !!$patternAsRegex) {
        // We've got a regex, let's parse a URL
        if ($ok = preg_match($patternAsRegex, $test['url'], $matches)) {
            // Get elements with string keys from matches
            $params = array_intersect_key(
                $matches,
                array_flip(array_filter(array_keys($matches), 'is_string'))
            );

            // Did we get the expected parameter?
            $ok = $params == $test['expectedParam'];

            // Turn parameter array into string
            list ($key, $value) = each($params);
            $params = "$key = $value";
        }
    }

    // Show result of regex generation
    printf('%-5s %-16s %-39s %-14s %s' . PHP_EOL,
        $ok ? 'PASS' : 'FAIL',
        $test['route'], $patternAsRegex,
        $test['url'],   $params
    );
}

Output:

RES   ROUTE            PATTERN                                 URL            PARAMS
-------------------------------------------------------------------------------------------
PASS  /hello/:name     @^/hello/(?<name>[a-zA-Z0-9\_\-]+)$@D   /hello/sarah   name = sarah
PASS  /bye/:name(/)    @^/bye/(?<name>[a-zA-Z0-9\_\-]+)/?$@D   /bye/stella/   name = stella
PASS  /find/{what}(/)  @^/find/(?<what>[a-zA-Z0-9\_\-]+)/?$@D  /find/cat      what = cat
PASS  /pay/:when       @^/pay/(?<when>[a-zA-Z0-9\_\-]+)$@D     /pay/later     when = later

#2


Simply replace your regex with this for optional / :

只需将此正则表达式替换为可选/:

@^/hello/([a-zA-Z0-9-_]+)/?$@

#1


This will create a regular expression for the $pattern route with both :name and {name} parameters, as well as the optional slash. As a bonus, it will also add a ?<name> to make the parameter easier to handle down the line.

这将为$ pattern路由创建一个正则表达式,其中包含:name和{name}参数,以及可选的斜杠。作为奖励,它还会添加一个? ,以使参数更容易处理。

For example, a route pattern of /hello/:name(/) will get the regular expression @^/hello/(?<name>[a-zA-Z0-9\_\-]+)/?$@D. When matched with a URL, like preg_match( <regex above>, '/hello/sarah', $matches) that would give you $matches['name'] == 'sarah'.

例如,/ hello /:name(/)的路由模式将获得正则表达式@ ^ / hello /(? [a-zA-Z0-9 \ _ \ _] +)/?$ @ D 。当与URL匹配时,比如preg_match( ,'/ hello / sarah',$ matches),它会给你$ matches ['name'] =='sarah'。

There are some tests to be found below the actual function.

在实际功能下面有一些测试。

function getRegex($pattern){
    if (preg_match('/[^-:\/_{}()a-zA-Z\d]/', $pattern))
        return false; // Invalid pattern

    // Turn "(/)" into "/?"
    $pattern = preg_replace('#\(/\)#', '/?', $pattern);

    // Create capture group for ":parameter"
    $allowedParamChars = '[a-zA-Z0-9\_\-]+';
    $pattern = preg_replace(
        '/:(' . $allowedParamChars . ')/',   # Replace ":parameter"
        '(?<$1>' . $allowedParamChars . ')', # with "(?<parameter>[a-zA-Z0-9\_\-]+)"
        $pattern
    );

    // Create capture group for '{parameter}'
    $pattern = preg_replace(
        '/{('. $allowedParamChars .')}/',    # Replace "{parameter}"
        '(?<$1>' . $allowedParamChars . ')', # with "(?<parameter>[a-zA-Z0-9\_\-]+)"
        $pattern
    );

    // Add start and end matching
    $patternAsRegex = "@^" . $pattern . "$@D";

    return $patternAsRegex;
}

// Test it
$testCases = [
    [
        'route'           => '/hello/:name',
        'url'             => '/hello/sarah',
        'expectedParam'   => ['name' => 'sarah'],
    ],
    [
        'route'           => '/bye/:name(/)',
        'url'             => '/bye/stella/',
        'expectedParam'   => ['name' => 'stella'],
    ],
    [
        'route'           => '/find/{what}(/)',
        'url'             => '/find/cat',
        'expectedParam'   => ['what' => 'cat'],
    ],
    [
        'route'           => '/pay/:when',
        'url'             => '/pay/later',
        'expectedParam'   => ['when' => 'later'],
    ],
];

printf('%-5s %-16s %-39s %-14s %s' . PHP_EOL, 'RES', 'ROUTE', 'PATTERN', 'URL', 'PARAMS');
echo str_repeat('-', 91), PHP_EOL;

foreach ($testCases as $test) {
    // Make regexp from route
    $patternAsRegex = getRegex($test['route']);

    if ($ok = !!$patternAsRegex) {
        // We've got a regex, let's parse a URL
        if ($ok = preg_match($patternAsRegex, $test['url'], $matches)) {
            // Get elements with string keys from matches
            $params = array_intersect_key(
                $matches,
                array_flip(array_filter(array_keys($matches), 'is_string'))
            );

            // Did we get the expected parameter?
            $ok = $params == $test['expectedParam'];

            // Turn parameter array into string
            list ($key, $value) = each($params);
            $params = "$key = $value";
        }
    }

    // Show result of regex generation
    printf('%-5s %-16s %-39s %-14s %s' . PHP_EOL,
        $ok ? 'PASS' : 'FAIL',
        $test['route'], $patternAsRegex,
        $test['url'],   $params
    );
}

Output:

RES   ROUTE            PATTERN                                 URL            PARAMS
-------------------------------------------------------------------------------------------
PASS  /hello/:name     @^/hello/(?<name>[a-zA-Z0-9\_\-]+)$@D   /hello/sarah   name = sarah
PASS  /bye/:name(/)    @^/bye/(?<name>[a-zA-Z0-9\_\-]+)/?$@D   /bye/stella/   name = stella
PASS  /find/{what}(/)  @^/find/(?<what>[a-zA-Z0-9\_\-]+)/?$@D  /find/cat      what = cat
PASS  /pay/:when       @^/pay/(?<when>[a-zA-Z0-9\_\-]+)$@D     /pay/later     when = later

#2


Simply replace your regex with this for optional / :

只需将此正则表达式替换为可选/:

@^/hello/([a-zA-Z0-9-_]+)/?$@