PHP 路由器实现

lightCat-Code

一,环境准备

  • PHP: 7.3.3 +
  • severe: Apache

二,路由重写先前条件

我们要实现PHP处理路由请求的功能,就需要Apache打开路由重写,这是因为在默认情况下,Apache不会将所有请求都发送到PHP解释器进行处理,所以就需要使用.htaccess拓展文件来告诉Apache你要进行路由重写!

一般情况下,.htaccess是隐藏的,如果你不做特殊处理,它是无法直接访问到的!

在你开始编写.htaccess之前,你先要在Apache的配置文件中声明:

Apache 复制代码
<Directory "/path/to/存放.htaccess文件的目录">
    AllowOverride All
</Directory>

以此确保覆盖 htaccess 是被允许的,保证你可以正确地重写 URL!

接下来,开始编写.htaccess,让Apache知道要将所有的请求都提交给index.php来处理:

语句 功能
RewriteEngine On 这一行指示 Apache 启用 URL 重写引擎,这样才能开始使用重写规则
RewriteCond %{REQUEST_FILENAME} !-f 这一行表示如果请求的文件名不是一个真实存在的文件,就继续进行重写
RewriteCond %{REQUEST_FILENAME} !-d 这一行表示如果请求的文件名不是一个真实存在的目录,则继续进行重写
RewriteRule ^(.*)% index.php?url=$1 [QSA,L] 这一行指将任意的请求都交付给index.php处理

总结就是:

.htaccess 复制代码
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]

三,具体实现

接下来,我们开始编写路由器代码:

  1. 路由注册:
php 复制代码
$routes = array(
    '/' => array('controller' => 'UserController', 'method' => 'show', 'request_method' => 'GET')
);

我们定义一个路由列表,注册了一个路由/在这个路由注册时,我们在后面定义了一个映射表,这个映射表的作用就是方便之后匹配对应的方法,就像上面这样,我们定义了controller,method,request_method这几个映射,这几个映射的作用就是:当访问/根路径的时候,控制器方法为UserController,然后执行UserController下的show方法,并且请求方式为GET请求。 但是光是这样,并不能让php知道我们到底要做什么,因为我们只是写了一个映射框架,php并不知道到底要怎么映射,所以接下来我们就要来编写这些方法的映射方法:

  1. 映射方法:

在映射开始之前,你需要做的第一件事就是获取请求的路由:

php 复制代码
$request_uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$request_method = $_SERVER['REQUEST_METHOD'];

注意,此处的uri并没有写错,有小伙伴可能会问了,难道不应该是url吗?其实不然,URL(学名:统一资源定位器)它是 URI 的一种具体形式,用于定位和访问互联网上的资源,URL 包含了资源的位置信息,可以指示资源的协议、主机名、路径、查询参数等,例如https://www.example.com/index.html就是一个url,然而URI 包括了完整的 URL 路径和查询字符串,但不包括协议、主机名和端口等信息,举个例子,如果客户端发送了一个 HTTP 请求是 GET /post.html?name=lightCat HTTP/1.1,那么其中的 request_uri 就是 /post.html?name=lightCat在服务器端,也就可以通过读取 request_uri 的值获取到客户端实际请求的资源路径和查询参数,从而进行相应的处理和响应!

拿到路由后,你就可以开始匹配路由了!

php 复制代码
foreach ($routes as $route => $params) {}

首先,就是要对刚刚注册的路由进行拆分,而这一句就是对刚刚的路由进行拆分!刚刚注册的路由经过这一句解析后可以分别得到controller,method,request_method分别对应的值:

  • $route可以得到/
  • $params['controller']可以得到UserController
  • 以此类推就可以得到其他对应的值

接着:

php 复制代码
foreach ($routes as $route => $params) {
if (preg_match('/^' . $regex . '$/', $request_uri, $matches) && $request_method === $params['request_method']) {
    }
}

里面多了一个if判断语句,作用是判断请求 URI 与路由规则是否匹配,并且请求方法是否符合要求,如果符合就执行里面的函数:

为了满足对于动态参数的获取,我们要预留正则表达位置:

php 复制代码
$regex = str_replace(':id', '([a-z]+)', $route);
$regex = str_replace('/', '\/', $regex);

以此允许它匹配字母,便于之后使用,你也可以在此替换此正则表达式,来匹配不同类型的参数!

获取控制器和方法名:

php 复制代码
$controller_name = $params['controller'];
$method_name = $params['method'];

实例化控制器对象:

php 复制代码
$controller = new $controller_name();

提取动态参数:

php 复制代码
$params = array_slice($matches, 1);

调用对应的方法,并传递参数:

php 复制代码
call_user_func_array(array($controller, $method_name), $params);

最后,如果匹配完了,就要结束此函数

php 复制代码
break

总结一下,全部PHP代码就是:

php 复制代码
<?php
$routes = array(
    '/' => array('controller' => 'UserController', 'method' => 'show', 'request_method' => 'GET')
);
$request_uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$request_method = $_SERVER['REQUEST_METHOD'];
foreach ($routes as $route => $params) {
    $regex = str_replace(':id', '([a-z]+)', $route);
    $regex = str_replace('/', '\/', $regex);
    if (preg_match('/^' . $regex . '$/', $request_uri, $matches) && $request_method === $params['request_method']) {
        $controller_name = $params['controller'];
        $method_name = $params['method'];
        $controller = new $controller_name();
        $params = array_slice($matches, 1);
        call_user_func_array(array($controller, $method_name), $params);
        break;
    }
}
  1. 控制器:

针对/路由的控制器我们只需要:

php 复制代码
class UserController {
    public function show() {
        echo 'hello world';
    }
}

就可以实现访问/路径时执行对应的函数了!如果你需要获取路由动态数据,只需要根据你刚刚书写的正则表达式规则,修改路由和控制器就可以了,因为刚刚我写的路由正则表达式是:

php 复制代码
$regex = str_replace(':id', '([a-z]+)', $route);
$regex = str_replace('/', '\/', $regex);

所以我只需要把注册的路径修改为:

php 复制代码
'/:id' => array('controller' => 'UserController', 'method' => 'show', 'request_method' => 'GET')

再把控制器修改为:

php 复制代码
class UserController {
    public function show($id) {
        echo $id;
    }
}

就可以实现在访问:/a时页面输出a了。

如果你需要处理post请求也是可以的,只需要将注册的路由最后面的GET改为POST就可以了!

四,写在最后

如果你也喜欢这篇文章的话不要忘记点赞哦!

感谢您的阅读!

相关推荐
程序猿麦小七25 分钟前
基于springboot的景区网页设计与实现
java·spring boot·后端·旅游·景区
蓝田~33 分钟前
SpringBoot-自定义注解,拦截器
java·spring boot·后端
theLuckyLong34 分钟前
SpringBoot后端解决跨域问题
spring boot·后端·python
.生产的驴36 分钟前
SpringCloud Gateway网关路由配置 接口统一 登录验证 权限校验 路由属性
java·spring boot·后端·spring·spring cloud·gateway·rabbitmq
小扳39 分钟前
Docker 篇-Docker 详细安装、了解和使用 Docker 核心功能(数据卷、自定义镜像 Dockerfile、网络)
运维·spring boot·后端·mysql·spring cloud·docker·容器
v'sir1 小时前
POI word转pdf乱码问题处理
java·spring boot·后端·pdf·word
李少兄1 小时前
解决Spring Boot整合Redis时的连接问题
spring boot·redis·后端
码上一元6 小时前
SpringBoot自动装配原理解析
java·spring boot·后端
枫叶_v8 小时前
【SpringBoot】22 Txt、Csv文件的读取和写入
java·spring boot·后端
杜杜的man8 小时前
【go从零单排】Closing Channels通道关闭、Range over Channels
开发语言·后端·golang