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就可以了!

四,写在最后

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

感谢您的阅读!

相关推荐
柏油5 小时前
MySQL InnoDB 行锁
数据库·后端·mysql
咖啡调调。5 小时前
使用Django框架表单
后端·python·django
白泽talk5 小时前
2个小时1w字| React & Golang 全栈微服务实战
前端·后端·微服务
摆烂工程师5 小时前
全网最详细的5分钟快速申请一个国际 “edu教育邮箱” 的保姆级教程!
前端·后端·程序员
一只叫煤球的猫6 小时前
你真的会用 return 吗?—— 11个值得借鉴的 return 写法
java·后端·代码规范
Asthenia04126 小时前
HTTP调用超时与重试问题分析
后端
颇有几分姿色6 小时前
Spring Boot 读取配置文件的几种方式
java·spring boot·后端
AntBlack6 小时前
别说了别说了 ,Trae 已经在不停优化迭代了
前端·人工智能·后端
@淡 定7 小时前
Spring Boot 的配置加载顺序
java·spring boot·后端