
PHP从5.4版本开始引入了内置的轻量级Web服务器,极大地简化了本地开发环境的搭建。开发者无需配置Nginx或Apache,只需一个简单的命令即可运行PHP应用。然而,现代PHP框架普遍采用单一入口机制,依赖URL重写将所有请求路由到入口文件。与Nginx的rewrite或Apache的mod_rewrite不同,PHP内置服务器并没有原生的重写规则配置文件。本文将详细讲解如何利用PHP内置服务器的Router Script机制,优雅地实现URL重写。
一、内置服务器URL重写原理
PHP内置服务器处理请求的逻辑非常直接:当接收到一个HTTP请求时,它会首先检查请求的URI是否对应一个存在的静态文件。如果文件存在,则直接返回该文件内容;如果文件不存在,则会将请求交给一个指定的PHP脚本(即Router Script)进行处理。
利用这个特性,我们可以在Router Script中拦截请求,判断当前请求的文件是否存在。如果不存在,我们就将请求转发给应用的单入口文件(通常是index.php),从而实现URL重写。
二、基础重写实现
假设我们的项目根目录下有一个index.php作为单一入口,我们需要创建一个路由脚本,例如命名为router.php。以下是router.php的基础实现代码:
<?php
// 解析请求的URI,去除查询参数
$uri = urldecode(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
// 如果请求的URI不是根目录,且对应的文件或目录真实存在于磁盘上
// 则返回false,交由内置服务器直接返回该静态文件
if ($uri !== '/' && file_exists(__DIR__ . $uri)) {
return false;
}
// 对于不存在的文件路径,加载单一入口文件
require __DIR__ . '/index.php';代码解析:首先使用 parse_url 函数提取URI中的路径部分,忽略类似 ?id=1 的查询字符串,避免干扰文件存在性判断。file_exists 是关键,它确保了CSS、JS、图片等静态资源能够被正常加载,而不会错误地全部路由到index.php。当脚本返回 false 时,PHP内置服务器会中断当前脚本,直接输出对应的静态文件。
启动服务器时,只需将router.php作为参数传递即可:
php -S www.ipipp.com:8000 router.php
三、安全增强:过滤敏感文件
在实际开发中,项目根目录下可能包含 .env 配置文件、.git 目录等敏感信息。默认情况下,如果访问者知道这些文件的路径,内置服务器会直接将其作为静态文件返回,造成安全隐患。我们可以在路由脚本中增加黑名单机制来拦截这些请求。优化后的router.php代码如下:
<?php
$uri = urldecode(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
// 拦截敏感文件和隐藏文件的访问
if (preg_match('/^/(.(?!well-known).*|env|git|svn)/i', $uri)) {
header('HTTP/1.1 403 Forbidden');
echo 'Access Denied!';
exit;
}
// 静态文件放行
if ($uri !== '/' && file_exists(__DIR__ . $uri)) {
return false;
}
// 路由到入口文件
require __DIR__ . '/index.php';正则表达式 /^/(.(?!well-known).*|env|git|svn)/i 会匹配以斜杠开头,紧跟着点(排除标准目录 .well-known)、env、git 或 svn 的路径。一旦匹配成功,直接返回403状态码并终止脚本,有效防止敏感信息泄露。
四、PATH_INFO的兼容处理
部分框架依赖于 PATH_INFO 环境变量来解析路由,而PHP内置服务器在某些情况下可能不会正确设置该变量。为了确保框架路由组件正常工作,我们需要在加载入口文件前,手动对 $_SERVER 进行补全。完善后的最终路由脚本如下:
<?php
$uri = urldecode(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
// 拦截敏感文件和隐藏文件的访问
if (preg_match('/^/(.(?!well-known).*|env|git|svn)/i', $uri)) {
header('HTTP/1.1 403 Forbidden');
echo 'Access Denied!';
exit;
}
// 静态文件放行
if ($uri !== '/' && file_exists(__DIR__ . $uri)) {
return false;
}
// 兼容PATH_INFO与SCRIPT_NAME
$_SERVER['PATH_INFO'] = $uri;
$_SERVER['SCRIPT_NAME'] = '/index.php';
// 路由到入口文件
require __DIR__ . '/index.php';五、总结
通过PHP内置服务器的Router Script机制,我们可以轻松实现类似Nginx和Apache的URL重写功能,满足现代PHP框架的单一入口需求。同时,结合正则拦截和超全局变量的修改,还能提升本地开发的安全性和框架兼容性。需要特别注意的是,PHP内置服务器是单线程阻塞的,仅适用于开发和测试环境,其性能和并发处理能力无法满足生产环境的要求,线上部署请务必使用Nginx或Apache搭配PHP-FPM。