PHP Router 怎么匹配请求方式

wen PHP项目 1

本文目录导读:

PHP Router 怎么匹配请求方式

  1. 基础方法匹配
  2. 支持多个方法的组合路由
  3. 基于正则表达式的路由匹配
  4. 方法覆盖(处理表单和兼容性)
  5. 完整示例 - 小型 MVC 框架
  6. 最佳实践建议

在 PHP 路由中匹配请求方式(HTTP Method)是 RESTful API 开发的基础,以下是几种常见的实现方式:

基础方法匹配

简单数组映射

class Router {
    private $routes = [
        'GET' => [],
        'POST' => [],
        'PUT' => [],
        'DELETE' => []
    ];
    public function add($method, $path, $handler) {
        $this->routes[$method][$path] = $handler;
    }
    public function dispatch($method, $uri) {
        $uri = parse_url($uri, PHP_URL_PATH);
        if (!isset($this->routes[$method])) {
            return "405 Method Not Allowed";
        }
        if (isset($this->routes[$method][$uri])) {
            return call_user_func($this->routes[$method][$uri]);
        }
        // 尝试匹配动态路由
        foreach ($this->routes[$method] as $route => $handler) {
            $pattern = preg_replace('/\{([^}]+)\}/', '([^/]+)', $route);
            if (preg_match("#^$pattern$#", $uri, $matches)) {
                array_shift($matches);
                return call_user_func_array($handler, $matches);
            }
        }
        return "404 Not Found";
    }
}
// 使用示例
$router = new Router();
$router->add('GET', '/users/{id}', function($id) {
    return "获取用户 {$id}";
});
$router->add('POST', '/users', function() {
    return "创建用户";
});
$method = $_SERVER['REQUEST_METHOD'];
$uri = $_SERVER['REQUEST_URI'];
echo $router->dispatch($method, $uri);

支持多个方法的组合路由

class Router {
    private $routes = [];
    public function add($methods, $path, $handler) {
        // 支持单个或多个方法
        $methods = is_array($methods) ? $methods : [$methods];
        foreach ($methods as $method) {
            $this->routes[$method][] = [
                'path' => $path,
                'handler' => $handler
            ];
        }
    }
    public function get($path, $handler) {
        return $this->add('GET', $path, $handler);
    }
    public function post($path, $handler) {
        return $this->add('POST', $path, $handler);
    }
    public function put($path, $handler) {
        return $this->add('PUT', $path, $handler);
    }
    public function delete($path, $handler) {
        return $this->add('DELETE', $path, $handler);
    }
    public function any($path, $handler) {
        return $this->add(['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], $path, $handler);
    }
    public function dispatch($method, $uri) {
        $uri = parse_url($uri, PHP_URL_PATH);
        $uri = rtrim($uri, '/');
        if (!isset($this->routes[$method])) {
            return $this->methodNotAllowed();
        }
        foreach ($this->routes[$method] as $route) {
            $pattern = $this->convertToPattern($route['path']);
            if (preg_match($pattern, $uri, $matches)) {
                array_shift($matches);
                return call_user_func_array($route['handler'], $matches);
            }
        }
        return $this->notFound();
    }
    private function convertToPattern($path) {
        // 支持 {param} 和 {param:regex}
        return '#^' . preg_replace(
            '/\{(\w+)(?::([^}]+))?\}/',
            '(?P<$1>$2)',
            $path
        ) . '$#';
    }
    private function methodNotAllowed() {
        http_response_code(405);
        header('Allow: ' . implode(', ', array_keys($this->routes)));
        return "405 Method Not Allowed";
    }
    private function notFound() {
        http_response_code(404);
        return "404 Not Found";
    }
}
// 使用示例
$router = new Router();
$router->get('/users', function() {
    return "获取用户列表";
});
$router->get('/users/{id}', function($id) {
    return "获取用户 #{$id}";
});
$router->post('/users', function() {
    $data = json_decode(file_get_contents('php://input'), true);
    return "创建用户: " . json_encode($data);
});
$router->put('/users/{id}', function($id) {
    return "更新用户 #{$id}";
});
$router->delete('/users/{id}', function($id) {
    return "删除用户 #{$id}";
});
// 支持多个方法的组合
$router->any('/ping', function() {
    return "Pong";
});

基于正则表达式的路由匹配

class AdvancedRouter {
    private $routes = [];
    public function route($method, $pattern, $handler) {
        $this->routes[] = [
            'method' => $method,
            'pattern' => $pattern,
            'handler' => $handler
        ];
    }
    public function dispatch($method, $uri) {
        foreach ($this->routes as $route) {
            // 检查方法是否匹配(支持通配符 *)
            if ($route['method'] !== '*' && $route['method'] !== $method) {
                continue;
            }
            if (preg_match($route['pattern'], $uri, $matches)) {
                // 提取命名参数
                $params = array_filter(
                    $matches, 
                    'is_string', 
                    ARRAY_FILTER_USE_KEY
                );
                return call_user_func_array(
                    $route['handler'],
                    array_merge([$method], $params)
                );
            }
        }
        return null;
    }
}
// 使用示例
$router = new AdvancedRouter();
$router->route('GET', '#^/api/articles/(\d+)$#', function($method, $id) {
    return "文章 #{$id}";
});
$router->route('*', '#^/api/status$#', function($method) {
    return "方法: {$method}, 状态: OK";
});

方法覆盖(处理表单和兼容性)

class MethodOverrideRouter {
    public function getActualMethod() {
        $method = $_SERVER['REQUEST_METHOD'];
        // 支持通过 _method 参数覆盖
        if ($method === 'POST') {
            if (isset($_POST['_method'])) {
                $method = strtoupper($_POST['_method']);
            } elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
                // 支持通过自定义头覆盖
                $method = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']);
            }
        }
        return $method;
    }
}
// 使用示例
$method = (new MethodOverrideRouter())->getActualMethod();

完整示例 - 小型 MVC 框架

class Router {
    private $routes = [];
    private $basePath = '';
    public function __construct($basePath = '') {
        $this->basePath = rtrim($basePath, '/');
    }
    public function add($method, $path, $handler, $name = null) {
        $route = [
            'method' => strtoupper($method),
            'path' => $this->basePath . '/' . trim($path, '/'),
            'handler' => $handler
        ];
        if ($name) {
            $route['name'] = $name;
        }
        $this->routes[] = $route;
        return $this;
    }
    public function dispatch() {
        $method = $_SERVER['REQUEST_METHOD'];
        $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
        // 移除基础路径
        if ($this->basePath && strpos($uri, $this->basePath) === 0) {
            $uri = substr($uri, strlen($this->basePath));
        }
        $uri = '/' . trim($uri, '/');
        foreach ($this->routes as $route) {
            // 方法检查
            if ($route['method'] !== $method) {
                continue;
            }
            // 转换路由模式
            $pattern = '#^' . preg_replace(
                '/\{([^}]+)\}/',
                '(?P<$1>[^/]+)',
                $route['path']
            ) . '$#';
            if (preg_match($pattern, $uri, $matches)) {
                // 提取命名参数
                $params = array_filter(
                    $matches,
                    'is_string',
                    ARRAY_FILTER_USE_KEY
                );
                if (is_callable($route['handler'])) {
                    return call_user_func_array($route['handler'], $params);
                } else {
                    // 支持 "Controller@method" 格式
                    list($controller, $method) = explode('@', $route['handler']);
                    $controllerInstance = new $controller();
                    return call_user_func_array([$controllerInstance, $method], $params);
                }
            }
        }
        // 未找到路由
        http_response_code(404);
        return "404 Not Found";
    }
}
// 控制器示例
class UserController {
    public function show($id) {
        return "显示用户 #{$id}";
    }
    public function store() {
        return "创建用户";
    }
}
// 使用示例
$router = new Router('/api');
$router->add('GET', '/users/{id}', function($id) {
    return "获取用户 #{$id}";
});
$router->add('POST', '/users', function() {
    $data = json_decode(file_get_contents('php://input'), true);
    return "创建用户: " . json_encode($data);
});
$router->add('PUT', '/users/{id}', [new UserController(), 'show']);
echo $router->dispatch();

最佳实践建议

  1. HTTP 方法语义化

    • GET:获取资源
    • POST:创建资源
    • PUT/PATCH:更新资源
    • DELETE:删除资源
  2. 方法覆盖策略

    • 支持 X-HTTP-Method-Override
    • 支持 _method 表单参数
  3. 错误处理

    • 404:路由不存在
    • 405:方法不允许(可以返回允许的方法列表)
  4. 性能优化

    • 使用前缀树(Trie)优化
    • 缓存已编译的正则表达式

这些实现方式涵盖了从简单到复杂的各种场景,可以根据项目需求选择合适的方案。

抱歉,评论功能暂时关闭!