本文目录导读:

在 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();
最佳实践建议
-
HTTP 方法语义化:
- GET:获取资源
- POST:创建资源
- PUT/PATCH:更新资源
- DELETE:删除资源
-
方法覆盖策略:
- 支持
X-HTTP-Method-Override头 - 支持
_method表单参数
- 支持
-
错误处理:
- 404:路由不存在
- 405:方法不允许(可以返回允许的方法列表)
-
性能优化:
- 使用前缀树(Trie)优化
- 缓存已编译的正则表达式
这些实现方式涵盖了从简单到复杂的各种场景,可以根据项目需求选择合适的方案。