You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP自定义Router无法正确匹配路由,请求故障排查

问题排查与解决方案

核心问题分析

你的路由实现存在三个关键问题:

  1. 路由注册逻辑错误:原get方法每次调用都会立即检查请求路径,不匹配就直接触发404并终止脚本,导致后续注册的路由永远无法执行。比如访问/user时,第一个get("/", ...)会因路径不匹配直接抛出404,不会走到/user的路由判断。
  2. 动态查询参数匹配缺失:原代码的getPath方法会剥离URL中的查询字符串,因此/user?id={[0-9]}这种带查询参数的路由永远无法匹配,同时也没有实现查询参数的正则校验逻辑。
  3. 期望输出未触发:原代码仅当callback是字符串时才输出Succesfuly navigated to {$route},但你注册的路由用的都是匿名函数,因此不会触发这段输出。

修改后的Router.php

<?php
class Router {
    private $routes = []; // 存储所有注册的路由规则

    public function get($route, $callback) {
        // 拆分路由的路径与查询参数规则
        $routeParts = parse_url($route);
        $routePath = $routeParts['path'] ?? $route;
        $queryRules = [];

        // 解析路由中的查询参数正则规则
        if (!empty($routeParts['query'])) {
            parse_str($routeParts['query'], $queryParams);
            foreach ($queryParams as $key => $regex) {
                $queryRules[$key] = $regex;
            }
        }

        $this->routes[] = [
            'path' => $routePath,
            'queryRules' => $queryRules,
            'callback' => $callback
        ];
    }

    // 统一处理请求分发
    public function dispatch() {
        $currentPath = $this->getPath();
        $currentQuery = $_GET;

        // 遍历所有路由寻找匹配项
        foreach ($this->routes as $route) {
            // 先匹配路径部分
            if ($currentPath !== $route['path']) {
                continue;
            }

            // 校验查询参数是否符合规则
            $queryMatch = true;
            foreach ($route['queryRules'] as $key => $regex) {
                if (!isset($currentQuery[$key]) || !preg_match("/^{$regex}$/", $currentQuery[$key])) {
                    $queryMatch = false;
                    break;
                }
            }

            if (!$queryMatch) {
                continue;
            }

            // 匹配成功,输出期望内容并执行回调
            echo "Succesfuly navigated to {$route['path']} ";
            echo call_user_func($route['callback']);
            exit();
        }

        // 无匹配路由,返回404
        $this->notFound();
    }

    // 获取去除查询字符串的路径
    private function getPath() {
        $path = $_SERVER['REQUEST_URI'] ?? '/';
        $paramPosition = strpos($path, "?");
        return $paramPosition === false ? $path : substr($path, 0, $paramPosition);
    }

    private function notFound() {
        http_response_code(404);
        echo "Not Found";
        exit();
    }
}
?>

修改后的index.php

<?php
include_once './core/Router.php';

$router = new Router();

$router->get("/", function() {
    return "Path / traversed";
});

$router->get("/user", function() {
    return "Path /user traversed";
});

// 注册带数字id查询参数的路由
$router->get("/user?id=[0-9]+", function(){
    return "Path /user with numeric id traversed";
});

// 触发路由分发
$router->dispatch();
?>

关键修改说明

  1. 路由收集与延迟匹配:新增$routes数组存储所有路由,get方法仅负责添加路由规则,新增dispatch方法统一处理请求,遍历所有路由寻找匹配项,避免了原代码提前终止脚本的问题。
  2. 查询参数校验逻辑:通过parse_url拆分路由的路径和查询参数规则,匹配时先校验路径,再检查查询参数是否符合正则要求,实现了/user?id=数字的匹配逻辑。
  3. 输出逻辑修复:在路由匹配成功后统一输出期望的导航提示,回调函数返回内容而非直接输出,保证输出顺序符合预期。

内容的提问来源于stack exchange,提问作者mitas1c

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 14:55:19