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

时间:2015-05-08 18:53:22

标签: php regex routing match

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

/hello/:name

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

/hello/{name}    

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

/hello/:name(/)

or

/hello/{name}(/)

这是我用于/hello/:name

的正则表达式
@^/hello/([a-zA-Z0-9\-\_]+)$@D

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

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

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

2 个答案:

答案 0 :(得分:6)

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

例如,/hello/:name(/)的路由模式将获得正则表达式@^/hello/(?<name>[a-zA-Z0-9\_\-]+)/?$@D。与匹配的网址匹配,例如preg_match( <regex above>, '/hello/sarah', $matches),即$matches['name'] == 'sarah'

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

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
    );
}

输出:

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

答案 1 :(得分:1)

只需将此正则表达式替换为可选的(a, Float, Float)

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