使用可选参数实现 PHP 路由器

问题描述 投票:0回答:1

我一直致力于实现一个简单的 PHP 路由器,并设法使其能够使用所需的参数。但是,我正在努力实现可选参数,但失败了。为了清楚起见,我将提供相关代码。

首先,这是我的index.php:

<?php
$app->route->get("/user/:id/?post_id", [SiteController::class, "contact"]);
?>

在上面的代码中,我使用冒号 (:id) 表示必需参数,使用问号 (?post_id) 表示可选参数。

其次,这是我的 Router 类:

class Router {
    public function resolve() {
        $method = $this->request->getMethod();
        $url = $this->request->getUrl();
        foreach ($this->router[$method] as $routeUrl => $target) {
            $pattern = preg_replace('/\/:([^\/]+)/', '/(?P<$1>[^/]+)', $routeUrl);
            if (preg_match('#^' . $pattern . '$#', $url, $matches)) {
                $params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
                call_user_func([new $target[0], $target[1]], ...array_values($params));
                exit();
            }
        }
        throw new \Exception();
    }
}

我需要帮助来解决实现可选参数的谜团。任何帮助将不胜感激。谢谢!

php regex preg-match
1个回答
0
投票

这是一个将路由路径模式编译为具有命名捕获组的正则表达式的函数。它使用回调函数根据指示符字符返回不同的模式。

function compileRoutePattern(string $route): string {
    // replace parameter syntax with named capture groups
    return '(^'.preg_replace_callback(
      '((?<prefix>^|/)(?:(?<indicator>[:?])(?<name>[\w\d]+)|[^/]+))',
      function($match) {
          return match ($match['indicator'] ?? '') {
               // mandatory parameter 
              ':' => sprintf('(?:%s(?<%s>[^/]+))', $match['prefix'], $match['name']),
               // optional parameter 
              '?' => sprintf('(?:%s(?<%s>[^/]+))?', $match['prefix'], $match['name']),
              // escape anything else
              default => preg_quote($match[0], '(')
          };
      },
      $route
    ).')i';
}

下一步是匹配路径并仅返回命名的捕获组(而不是数字键)。

function matchRoute(string $path, string $route): ?array {
    // compile route into regex with named capture groups
    $pattern = compileRoutePattern($route);
    // match
    if (preg_match($pattern, $path, $match)) {
        // filter numeric keys from match
        return array_filter(
            $match, 
            fn($key) => is_string($key),
            ARRAY_FILTER_USE_KEY
        );   
    }
    return null;
}
© www.soinside.com 2019 - 2024. All rights reserved.