How to do URL matching regex for routing framework

2019-06-13 03:49发布

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/([a-zA-Z0-9\-\_]+)$@D

The regex is auto generated from PHP class

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

2条回答
相关推荐>>
2楼-- · 2019-06-13 03:53

Simply replace your regex with this for optional / :

@^/hello/([a-zA-Z0-9-_]+)/?$@
查看更多
贼婆χ
3楼-- · 2019-06-13 04:16

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.

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'.

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
查看更多
登录 后发表回答