目录¶
理解路由参数¶
路由参数允许在 URL 中捕获动态值,并将这些值传递给控制器的方法。例如:
/user/123
:捕获用户 ID123
。/post/45/comment/67
:捕获帖子 ID45
和评论 ID67
。
实现路由参数的关键在于:
定义带参数的路由模式:使用特定的语法(如
{id}
)来标识动态部分。解析请求 URI:匹配请求 URI 与路由模式,并提取参数值。
传递参数给控制器:将提取的参数值作为方法参数传递给控制器。
更新目录结构(可选)¶
为了更好地组织代码,将控制器类放在 src/Controllers/
目录下,并使用命名空间。以下是目录结构:
my_framework/
├── composer.json
├── composer.lock
├── vendor/
├── index.php
├── src/
│ ├── Router.php
│ └── Controller.php
├── src/Controllers/
│ ├── HomeController.php
│ └── UsersController.php
└── logs/
└── app.log
修改路由器类以支持路由参数¶
为了支持路由参数,我们需要增强现有的 Router
类,使其能够:
识别路由中的动态参数。
匹配请求 URI 并提取参数值。
将参数值传递给控制器方法。
Router.php¶
更新 Router.php
文件如下:
<?php
// src/Router.php
namespace MyFramework;
class Router
{
private $routes = [];
/**
* 添加路由规则
*
* @param string $method HTTP 方法(GET, POST, etc.)
* @param string $uri 请求的 URI,支持参数,例如 '/user/{id}'
* @param string $action 控制器和方法,例如 'UsersController@show'
*/
public function add($method, $uri, $action)
{
// 转换 URI 模式为正则表达式,并提取参数名称
$pattern = preg_replace('/{([a-zA-Z0-9_]+)}/', '(?P<1>[a-zA-Z0-9_]+)', $uri);
$pattern = '#^' . $pattern . '$#';
$this->routes[] = [
'method' => strtoupper($method),
'pattern' => $pattern,
'action' => $action,
'params' => $this->extractParams($uri)
];
}
/**
* 分发请求到相应的控制器方法
*
* @param string $requestMethod HTTP 方法
* @param string $requestUri 请求的 URI
*/
public function dispatch($requestMethod, $requestUri)
{
foreach ($this->routes as $route) {
if ($route['method'] === strtoupper($requestMethod)) {
if (preg_match($route['pattern'], $requestUri, $matches)) {
// 提取命名参数
$params = [];
foreach ($route['params'] as $param) {
if (isset($matches[$param])) {
$params[$param] = $matches[$param];
}
}
$this->executeAction($route['action'], $params);
return;
}
}
}
// 如果没有匹配的路由,返回 404
$this->sendNotFound();
}
/**
* 执行控制器的方法并传递参数
*
* @param string $action 控制器和方法,例如 'UsersController@show'
* @param array $params 路由参数
*/
private function executeAction($action, $params = [])
{
list($controllerName, $method) = explode('@', $action);
$fullControllerName = "MyFramework\Controllers\$controllerName";
if (class_exists($fullControllerName)) {
$controller = new $fullControllerName();
if (method_exists($controller, $method)) {
// 调用方法并传递参数
call_user_func_array([$controller, $method], $params);
return;
}
}
// 如果控制器或方法不存在,返回 404
$this->sendNotFound();
}
/**
* 发送 404 响应
*/
private function sendNotFound()
{
header("HTTP/1.0 404 Not Found");
echo "404 Not Found";
}
/**
* 提取路由中的参数名称
*
* @param string $uri 路由 URI
* @return array 参数名称列表
*/
private function extractParams($uri)
{
preg_match_all('/{([a-zA-Z0-9_]+)}/', $uri, $matches);
return $matches[1];
}
}
?>
代码解释:
路由模式转换:
使用正则表达式将 URI 中的
{param}
转换为命名捕获组(?P<param>[a-zA-Z0-9_]+)
。例如,
/user/{id}
会转换为#^/user/(?P<id>[a-zA-Z0-9_]+)$#
。
参数提取:
extractParams
方法提取路由中的参数名称(如id
)。在
dispatch
方法中,使用preg_match
匹配请求 URI,并提取参数值。
执行控制器方法:
使用
call_user_func_array
调用控制器方法,并将参数值作为参数传递。
定义带参数的路由¶
在 index.php
中,定义带参数的路由。例如,添加一个路由 /user/{id}
,映射到 UsersController@show
方法:
<?php
// index.php
// ... 之前的代码 ...
// 定义带参数的路由
$router->add('GET', '/user/{id}', 'UsersController@show');
// 其他路由
$router->add('GET', '/', 'HomeController@index');
$router->add('GET', '/about', 'HomeController@about');
$router->add('GET', '/contact', 'HomeController@contact');
$router->add('POST', '/submit', 'HomeController@submit');
$router->add('GET', '/users', 'UsersController@list'); // 用户列表路由
// 处理请求
$router->dispatch($requestMethod, $requestUri);
?>
说明:
/user/{id}
表示/user/
后面跟着一个动态参数id
。这个参数会被提取并传递给
UsersController
的show
方法。
在控制器中处理路由参数¶
现在,我们需要在控制器中创建 UsersController
,并定义 show
方法来处理传递的参数。
UsersController.php¶
创建 src/Controllers/UsersController.php
,并添加 show
方法:
<?php
// src/Controllers/UsersController.php
namespace MyFrameworkControllers;
use MyFrameworkController;
use MonologLogger;
use MonologHandlerStreamHandler;
class UsersController extends Controller
{
private $logger;
public function __construct()
{
// 初始化日志
$this->logger = new Logger('users');
$this->logger->pushHandler(new StreamHandler(__DIR__ . '/../../logs/app.log', Logger::DEBUG));
}
/**
* 用户列表方法
*/
public function list()
{
$this->logger->info("请求用户列表");
try {
// 示例:从外部 API 获取用户数据
// 这里为了简单使用静态数据
$users = [
['id' => 1, 'name' => '张三', 'email' => 'zhangsan@example.com'],
['id' => 2, 'name' => '李四', 'email' => 'lisi@example.com'],
['id' => 3, 'name' => '王五', 'email' => 'wangwu@example.com'],
];
echo "<h1>用户列表</h1>";
echo "<ul>";
foreach ($users as $user) {
echo "<li>" . htmlspecialchars($user['name']) . " (" . htmlspecialchars($user['email']) . ")</li>";
}
echo "</ul>";
} catch (Exception $e) {
$this->logger->error("获取用户列表失败", ['error' => $e->getMessage()]);
echo "<h1>无法获取用户列表</h1>";
}
}
/**
* 显示单个用户的方法
*
* @param string $id 用户ID
*/
public function show($id)
{
$this->logger->info("请求用户详情", ['id' => $id]);
try {
// 示例:根据ID获取用户数据
// 这里为了简单使用静态数据
$users = [
1 => ['id' => 1, 'name' => '张三', 'email' => 'zhangsan@example.com'],
2 => ['id' => 2, 'name' => '李四', 'email' => 'lisi@example.com'],
3 => ['id' => 3, 'name' => '王五', 'email' => 'wangwu@example.com'],
];
if (isset($users[$id])) {
$user = $users[$id];
echo "<h1>用户详情</h1>";
echo "<p>ID: " . htmlspecialchars($user['id']) . "</p>";
echo "<p>姓名: " . htmlspecialchars($user['name']) . "</p>";
echo "<p>邮箱: " . htmlspecialchars($user['email']) . "</p>";
} else {
echo "<h1>用户未找到</h1>";
}
} catch (Exception $e) {
$this->logger->error("获取用户详情失败", ['error' => $e->getMessage()]);
echo "<h1>无法获取用户详情</h1>";
}
}
}
?>
代码解释:
构造函数:
初始化 Monolog 日志记录器,将日志写入
logs/app.log
文件。
list
方法:显示用户列表。
show
方法:接受一个参数
$id
,表示用户 ID。根据 ID 显示相应的用户详情。
如果用户不存在,显示“用户未找到”。
示例:添加动态用户路由¶
现在,已经有了支持路由参数的路由系统和控制器方法。以下是如何添加一个动态用户路由的具体步骤:
定义路由:
在
index.php
中添加一个新的路由/user/{id}
,映射到UsersController@show
方法。<?php // index.php // ... 之前的代码 ... // 定义带参数的路由 $router->add('GET', '/user/{id}', 'UsersController@show'); // 其他路由 $router->add('GET', '/', 'HomeController@index'); $router->add('GET', '/about', 'HomeController@about'); $router->add('GET', '/contact', 'HomeController@contact'); $router->add('POST', '/submit', 'HomeController@submit'); $router->add('GET', '/users', 'UsersController@list'); // 用户列表路由 // 处理请求 $router->dispatch($requestMethod, $requestUri); ?>
访问路由:
访问
http://localhost:8000/users
显示用户列表。访问
http://localhost:8000/user/1
显示用户 ID 为 1 的详情。访问
http://localhost:8000/user/999
显示“用户未找到”。
完整代码示例¶
以下是更新后的完整框架代码,包括路由参数支持和控制器方法的实现。
目录结构¶
my_framework/
├── composer.json
├── composer.lock
├── vendor/
├── index.php
├── src/
│ ├── Router.php
│ └── Controller.php
├── src/Controllers/
│ ├── HomeController.php
│ └── UsersController.php
└── logs/
└── app.log
1. composer.json
¶
确保 composer.json
包含必要的依赖和自动加载配置:
{
"name": "yourname/my_framework",
"description": "A simple PHP framework example with Composer integration and route parameters",
"type": "project",
"require": {
"monolog/monolog": "^2.0",
"guzzlehttp/guzzle": "^7.0"
},
"autoload": {
"psr-4": {
"MyFramework\": "src/"
}
}
}
说明:
monolog/monolog
:用于日志记录。guzzlehttp/guzzle
:用于发送 HTTP 请求。autoload
:配置 PSR-4 自动加载,MyFramework
命名空间对应src/
目录。
2. index.php
¶
更新后的 index.php
:
<?php
// index.php
// 启用错误报告(开发阶段使用,生产环境请关闭)
ini_set('display_errors', 1);
error_reporting(E_ALL);
// 自动加载 Composer 及框架类
require __DIR__ . '/vendor/autoload.php';
// 使用命名空间
use MyFrameworkRouter;
// 获取请求的 URI 和方法
$requestUri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$requestMethod = $_SERVER['REQUEST_METHOD'];
// 实例化路由器并添加路由
$router = new Router();
// 定义路由规则
$router->add('GET', '/', 'HomeController@index');
$router->add('GET', '/about', 'HomeController@about');
$router->add('GET', '/contact', 'HomeController@contact');
$router->add('POST', '/submit', 'HomeController@submit');
$router->add('GET', '/users', 'UsersController@list'); // 用户列表路由
$router->add('GET', '/user/{id}', 'UsersController@show'); // 动态用户路由
// 处理请求
$router->dispatch($requestMethod, $requestUri);
?>
3. src/Router.php
¶
更新后的 Router.php
支持路由参数:
<?php
// src/Router.php
namespace MyFramework;
class Router
{
private $routes = [];
/**
* 添加路由规则
*
* @param string $method HTTP 方法(GET, POST, etc.)
* @param string $uri 请求的 URI,支持参数,例如 '/user/{id}'
* @param string $action 控制器和方法,例如 'UsersController@show'
*/
public function add($method, $uri, $action)
{
// 转换 URI 模式为正则表达式,并提取参数名称
$pattern = preg_replace('/{([a-zA-Z0-9_]+)}/', '(?P<1>[a-zA-Z0-9_]+)', $uri);
$pattern = '#^' . $pattern . '$#';
$this->routes[] = [
'method' => strtoupper($method),
'pattern' => $pattern,
'action' => $action,
'params' => $this->extractParams($uri)
];
}
/**
* 分发请求到相应的控制器方法
*
* @param string $requestMethod HTTP 方法
* @param string $requestUri 请求的 URI
*/
public function dispatch($requestMethod, $requestUri)
{
foreach ($this->routes as $route) {
if ($route['method'] === strtoupper($requestMethod)) {
if (preg_match($route['pattern'], $requestUri, $matches)) {
// 提取命名参数
$params = [];
foreach ($route['params'] as $param) {
if (isset($matches[$param])) {
$params[$param] = $matches[$param];
}
}
$this->executeAction($route['action'], $params);
return;
}
}
}
// 如果没有匹配的路由,返回 404
$this->sendNotFound();
}
/**
* 执行控制器的方法并传递参数
*
* @param string $action 控制器和方法,例如 'UsersController@show'
* @param array $params 路由参数
*/
private function executeAction($action, $params = [])
{
list($controllerName, $method) = explode('@', $action);
$fullControllerName = "MyFramework\Controllers\$controllerName";
if (class_exists($fullControllerName)) {
$controller = new $fullControllerName();
if (method_exists($controller, $method)) {
// 调用方法并传递参数
call_user_func_array([$controller, $method], $params);
return;
}
}
// 如果控制器或方法不存在,返回 404
$this->sendNotFound();
}
/**
* 发送 404 响应
*/
private function sendNotFound()
{
header("HTTP/1.0 404 Not Found");
echo "404 Not Found";
}
/**
* 提取路由中的参数名称
*
* @param string $uri 路由 URI
* @return array 参数名称列表
*/
private function extractParams($uri)
{
preg_match_all('/{([a-zA-Z0-9_]+)}/', $uri, $matches);
return $matches[1];
}
}
?>
4. src/Controller.php
¶
保持不变:
<?php
// src/Controller.php
namespace MyFramework;
class Controller
{
// 在这里可以添加公共的方法或属性
}
?>
5. src/Controllers/HomeController.php
¶
保持不变:
<?php
// src/Controllers/HomeController.php
namespace MyFrameworkControllers;
use MyFrameworkController;
use MonologLogger;
use MonologHandlerStreamHandler;
class HomeController extends Controller
{
private $logger;
public function __construct()
{
// 创建一个日志通道
$this->logger = new Logger('home');
$this->logger->pushHandler(new StreamHandler(__DIR__ . '/../../logs/app.log', Logger::DEBUG));
}
/**
* 主页方法
*/
public function index()
{
$this->logger->info("访问主页");
echo "<h1>欢迎来到主页!</h1>";
}
/**
* 关于页面方法
*/
public function about()
{
$this->logger->info("访问关于页面");
echo "<h1>关于我们</h1><p>这是关于页面。</p>";
}
/**
* 联系我们页面方法
*/
public function contact()
{
$this->logger->info("访问联系我们页面");
echo "<h1>联系我们</h1><p>这是联系我们页面。</p>";
}
/**
* 处理表单提交的方法
*/
public function submit()
{
// 处理 POST 数据
$data = $_POST;
$this->logger->info("表单提交", $data);
echo "<h1>表单已提交</h1>";
echo "<pre>";
print_r($data);
echo "</pre>";
}
}
?>
6. src/Controllers/UsersController.php
¶
新增的 UsersController
支持显示用户详情:
<?php
// src/Controllers/UsersController.php
namespace MyFrameworkControllers;
use MyFrameworkController;
use GuzzleHttpClient;
use MonologLogger;
use MonologHandlerStreamHandler;
class UsersController extends Controller
{
private $logger;
private $client;
public function __construct()
{
// 初始化日志
$this->logger = new Logger('users');
$this->logger->pushHandler(new StreamHandler(__DIR__ . '/../../logs/app.log', Logger::DEBUG));
// 初始化 Guzzle 客户端
$this->client = new Client();
}
/**
* 用户列表方法
*/
public function list()
{
$this->logger->info("请求用户列表");
try {
// 示例:从外部 API 获取用户数据
// 这里为了简单使用静态数据
$users = [
['id' => 1, 'name' => '张三', 'email' => 'zhangsan@example.com'],
['id' => 2, 'name' => '李四', 'email' => 'lisi@example.com'],
['id' => 3, 'name' => '王五', 'email' => 'wangwu@example.com'],
];
echo "<h1>用户列表</h1>";
echo "<ul>";
foreach ($users as $user) {
echo "<li>" . htmlspecialchars($user['name']) . " (" . htmlspecialchars($user['email']) . ")</li>";
}
echo "</ul>";
} catch (Exception $e) {
$this->logger->error("获取用户列表失败", ['error' => $e->getMessage()]);
echo "<h1>无法获取用户列表</h1>";
}
}
/**
* 显示单个用户的方法
*
* @param string $id 用户ID
*/
public function show($id)
{
$this->logger->info("请求用户详情", ['id' => $id]);
try {
// 示例:根据ID获取用户数据
// 这里为了简单使用静态数据
$users = [
1 => ['id' => 1, 'name' => '张三', 'email' => 'zhangsan@example.com'],
2 => ['id' => 2, 'name' => '李四', 'email' => 'lisi@example.com'],
3 => ['id' => 3, 'name' => '王五', 'email' => 'wangwu@example.com'],
];
if (isset($users[$id])) {
$user = $users[$id];
echo "<h1>用户详情</h1>";
echo "<p>ID: " . htmlspecialchars($user['id']) . "</p>";
echo "<p>姓名: " . htmlspecialchars($user['name']) . "</p>";
echo "<p>邮箱: " . htmlspecialchars($user['email']) . "</p>";
} else {
echo "<h1>用户未找到</h1>";
}
} catch (Exception $e) {
$this->logger->error("获取用户详情失败", ['error' => $e->getMessage()]);
echo "<h1>无法获取用户详情</h1>";
}
}
}
?>
说明:
show
方法:接受一个参数$id
,用于显示指定用户的详情。安全处理:使用
htmlspecialchars
防止 XSS 攻击。
示例:添加动态用户路由¶
现在,可以通过以下步骤测试带参数的路由:
启动本地服务器:
在项目根目录下运行:
php -S localhost:8000
访问用户列表:
打开浏览器,访问 http://localhost:8000/users,将看到用户列表。
访问单个用户详情:
访问 http://localhost:8000/user/1:
输出用户 ID 1 的详情。
访问 http://localhost:8000/user/2:
输出用户 ID 2 的详情。
访问 http://localhost:8000/user/999:
输出“用户未找到”。
完整代码示例¶
以下是所有相关文件的完整代码。
1. composer.json
¶
{
"name": "yourname/my_framework",
"description": "A simple PHP framework example with Composer integration and route parameters",
"type": "project",
"require": {
"monolog/monolog": "^2.0",
"guzzlehttp/guzzle": "^7.0"
},
"autoload": {
"psr-4": {
"MyFramework\": "src/"
}
}
}
2. index.php
¶
<?php
// index.php
// 启用错误报告(开发阶段使用,生产环境请关闭)
ini_set('display_errors', 1);
error_reporting(E_ALL);
// 自动加载 Composer 及框架类
require __DIR__ . '/vendor/autoload.php';
// 使用命名空间
use MyFrameworkRouter;
// 获取请求的 URI 和方法
$requestUri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$requestMethod = $_SERVER['REQUEST_METHOD'];
// 实例化路由器并添加路由
$router = new Router();
// 定义路由规则
$router->add('GET', '/', 'HomeController@index');
$router->add('GET', '/about', 'HomeController@about');
$router->add('GET', '/contact', 'HomeController@contact');
$router->add('POST', '/submit', 'HomeController@submit');
$router->add('GET', '/users', 'UsersController@list'); // 用户列表路由
$router->add('GET', '/user/{id}', 'UsersController@show'); // 动态用户路由
// 处理请求
$router->dispatch($requestMethod, $requestUri);
?>
3. src/Router.php
¶
<?php
// src/Router.php
namespace MyFramework;
class Router
{
private $routes = [];
/**
* 添加路由规则
*
* @param string $method HTTP 方法(GET, POST, etc.)
* @param string $uri 请求的 URI,支持参数,例如 '/user/{id}'
* @param string $action 控制器和方法,例如 'UsersController@show'
*/
public function add($method, $uri, $action)
{
// 转换 URI 模式为正则表达式,并提取参数名称
$pattern = preg_replace('/{([a-zA-Z0-9_]+)}/', '(?P<1>[a-zA-Z0-9_]+)', $uri);
$pattern = '#^' . $pattern . '$#';
$this->routes[] = [
'method' => strtoupper($method),
'pattern' => $pattern,
'action' => $action,
'params' => $this->extractParams($uri)
];
}
/**
* 分发请求到相应的控制器方法
*
* @param string $requestMethod HTTP 方法
* @param string $requestUri 请求的 URI
*/
public function dispatch($requestMethod, $requestUri)
{
foreach ($this->routes as $route) {
if ($route['method'] === strtoupper($requestMethod)) {
if (preg_match($route['pattern'], $requestUri, $matches)) {
// 提取命名参数
$params = [];
foreach ($route['params'] as $param) {
if (isset($matches[$param])) {
$params[$param] = $matches[$param];
}
}
$this->executeAction($route['action'], $params);
return;
}
}
}
// 如果没有匹配的路由,返回 404
$this->sendNotFound();
}
/**
* 执行控制器的方法并传递参数
*
* @param string $action 控制器和方法,例如 'UsersController@show'
* @param array $params 路由参数
*/
private function executeAction($action, $params = [])
{
list($controllerName, $method) = explode('@', $action);
$fullControllerName = "MyFramework\Controllers\$controllerName";
if (class_exists($fullControllerName)) {
$controller = new $fullControllerName();
if (method_exists($controller, $method)) {
// 调用方法并传递参数
call_user_func_array([$controller, $method], $params);
return;
}
}
// 如果控制器或方法不存在,返回 404
$this->sendNotFound();
}
/**
* 发送 404 响应
*/
private function sendNotFound()
{
header("HTTP/1.0 404 Not Found");
echo "404 Not Found";
}
/**
* 提取路由中的参数名称
*
* @param string $uri 路由 URI
* @return array 参数名称列表
*/
private function extractParams($uri)
{
preg_match_all('/{([a-zA-Z0-9_]+)}/', $uri, $matches);
return $matches[1];
}
}
?>
4. src/Controller.php
¶
<?php
// src/Controller.php
namespace MyFramework;
class Controller
{
// 在这里可以添加公共的方法或属性
}
?>
5. src/Controllers/HomeController.php
¶
<?php
// src/Controllers/HomeController.php
namespace MyFrameworkControllers;
use MyFrameworkController;
use MonologLogger;
use MonologHandlerStreamHandler;
class HomeController extends Controller
{
private $logger;
public function __construct()
{
// 创建一个日志通道
$this->logger = new Logger('home');
$this->logger->pushHandler(new StreamHandler(__DIR__ . '/../../logs/app.log', Logger::DEBUG));
}
/**
* 主页方法
*/
public function index()
{
$this->logger->info("访问主页");
echo "<h1>欢迎来到主页!</h1>";
}
/**
* 关于页面方法
*/
public function about()
{
$this->logger->info("访问关于页面");
echo "<h1>关于我们</h1><p>这是关于页面。</p>";
}
/**
* 联系我们页面方法
*/
public function contact()
{
$this->logger->info("访问联系我们页面");
echo "<h1>联系我们</h1><p>这是联系我们页面。</p>";
}
/**
* 处理表单提交的方法
*/
public function submit()
{
// 处理 POST 数据
$data = $_POST;
$this->logger->info("表单提交", $data);
echo "<h1>表单已提交</h1>";
echo "<pre>";
print_r($data);
echo "</pre>";
}
}
?>
6. src/Controllers/UsersController.php
¶
<?php
// src/Controllers/UsersController.php
namespace MyFrameworkControllers;
use MyFrameworkController;
use GuzzleHttpClient;
use MonologLogger;
use MonologHandlerStreamHandler;
class UsersController extends Controller
{
private $logger;
private $client;
public function __construct()
{
// 初始化日志
$this->logger = new Logger('users');
$this->logger->pushHandler(new StreamHandler(__DIR__ . '/../../logs/app.log', Logger::DEBUG));
// 初始化 Guzzle 客户端
$this->client = new Client();
}
/**
* 用户列表方法
*/
public function list()
{
$this->logger->info("请求用户列表");
try {
// 示例:从外部 API 获取用户数据
// 这里为了简单使用静态数据
$users = [
['id' => 1, 'name' => '张三', 'email' => 'zhangsan@example.com'],
['id' => 2, 'name' => '李四', 'email' => 'lisi@example.com'],
['id' => 3, 'name' => '王五', 'email' => 'wangwu@example.com'],
];
echo "<h1>用户列表</h1>";
echo "<ul>";
foreach ($users as $user) {
echo "<li>" . htmlspecialchars($user['name']) . " (" . htmlspecialchars($user['email']) . ")</li>";
}
echo "</ul>";
} catch (Exception $e) {
$this->logger->error("获取用户列表失败", ['error' => $e->getMessage()]);
echo "<h1>无法获取用户列表</h1>";
}
}
/**
* 显示单个用户的方法
*
* @param string $id 用户ID
*/
public function show($id)
{
$this->logger->info("请求用户详情", ['id' => $id]);
try {
// 示例:根据ID获取用户数据
// 这里为了简单使用静态数据
$users = [
1 => ['id' => 1, 'name' => '张三', 'email' => 'zhangsan@example.com'],
2 => ['id' => 2, 'name' => '李四', 'email' => 'lisi@example.com'],
3 => ['id' => 3, 'name' => '王五', 'email' => 'wangwu@example.com'],
];
if (isset($users[$id])) {
$user = $users[$id];
echo "<h1>用户详情</h1>";
echo "<p>ID: " . htmlspecialchars($user['id']) . "</p>";
echo "<p>姓名: " . htmlspecialchars($user['name']) . "</p>";
echo "<p>邮箱: " . htmlspecialchars($user['email']) . "</p>";
} else {
echo "<h1>用户未找到</h1>";
}
} catch (Exception $e) {
$this->logger->error("获取用户详情失败", ['error' => $e->getMessage()]);
echo "<h1>无法获取用户详情</h1>";
}
}
}
?>
7. 创建日志目录¶
确保有一个 logs/
目录,并且 PHP 有权限写入日志文件:
mkdir logs
chmod 755 logs
测试新功能¶
启动本地服务器
在项目根目录下运行:
php -S localhost:8000
访问带参数的路由
访问用户列表:
输出:
<h1>用户列表</h1> <ul> <li>张三 (zhangsan@example.com)</li> <li>李四 (lisi@example.com)</li> <li>王五 (wangwu@example.com)</li> </ul>
访问单个用户详情:
-
输出:
<h1>用户详情</h1> <p>ID: 1</p> <p>姓名: 张三</p> <p>邮箱: zhangsan@example.com</p>
-
输出:
<h1>用户详情</h1> <p>ID: 2</p> <p>姓名: 李四</p> <p>邮箱: lisi@example.com</p>
http://localhost:8000/user/999
输出:
<h1>用户未找到</h1>
-
检查日志
查看
logs/app.log
文件,确认日志记录:[2024-04-27 14:23:01] users.INFO: 请求用户详情 {"id":"1"} [] [2024-04-27 14:23:05] users.INFO: 请求用户详情 {"id":"2"} [] [2024-04-27 14:23:09] users.INFO: 请求用户详情 {"id":"999"} []
总结¶
通过以上步骤,我们已经成功地在自定义的 PHP 框架中增加了路由参数支持。具体实现包括:
路由器类的增强:通过正则表达式匹配路由模式,提取参数值,并将其传递给控制器方法。
定义带参数的路由:在
index.php
中使用{param}
语法定义动态路由。控制器方法的调整:在控制器中定义接受参数的方法,并在方法中使用这些参数。
日志记录:使用 Monolog 记录请求和参数,便于调试和监控。
进一步的扩展建议:
支持可选参数:
修改路由匹配逻辑,允许某些参数可选。例如,
/user/{id}?
,以支持/user
和/user/123
。高级路由匹配:
支持正则表达式中的更复杂的匹配,如限制参数类型(数字、字母等)。
中间件机制:
实现中间件,处理认证、授权、日志记录等功能。
命名路由和反向路由:
支持为路由命名,并根据名称生成 URL。
错误处理和异常管理:
增强错误处理机制,提供更友好的错误页面和日志记录。
支持多种 HTTP 方法:
除了 GET 和 POST,支持 PUT、DELETE 等其他 HTTP 方法。
集成模板引擎:
使用 Twig 或 Blade 等模板引擎,将视图与控制器逻辑分离。