php什么是路由,PHP入坑之 什么是路由器

PHP入坑之 什么是路由器

6cd5e4fd49658da7be73f2e8e3760c00.png

11个月前

阅读 1465

评论 0

喜欢 0

###1、什么是路由

在学习路由器之前,我们必须先来了解什么是路由, 在PHP开发中,路由往往是指一段文件路径、一条URL地址,例如:

`http://127.0.0.1/index.php?controller=user&action=login`

实际上这就是一条路由,不过这条路由是最原始、并且没有经过优化的路由地址,所以他看起来非常囊肿。

而路由器的作用,就是要优化这些看起来非常囊肿的路由地址,将其美化、缩短。

###2、理论上存在的三种路由模式

```

1、原始的路由:?controller=文件夹&action=文件名&其他=参数&...

2、INFO_PATH美化路由:/文件夹/文件名/其他1/参数1/其他2/参数2/...

3、SFY_PATH简化路由:自定义路由 == 真实路由

4、CLE_PATH混合路由:INFO_PATH与SFY_PATH两种路由模式混合使用

```

可能同学们还看不懂3、4种路由模式的具体含义是什么,没关系,我们先从第2种路由开始学习,如何实现`INFO_PATH`模式,美化路由。

###3、实现路由器的关键知识点:精确获取到用户请求的URL链接

不管是`INFO_PATH、SFY_PATH`还是`CLE_PATH`路由模式,的实现前提都是需要先获取到用户当前请求的URL链接,然后再进行转发处理。

而一般获取用户的请求信息,都是使用`$_SERVER`超全局变量, 同时,由于并不是所有操作系统中都能通过`$_SERVER['PATH_INFO']`而获取用户当前请求的URL地址,所以我们需要实现三种优先级:

当`$_SERVER['PATH_INFO']`,获取不到URL地址时,尝试使用`$_SERVER['REDIRECT_PATH_INFO']`获取,如果还是获取不到,则最后再尝试使用`$_SERVER['REDIRECT_URL']`

如果三种情况都无法获取到URL地址,则代表当前服务器不支持路由美化功能,需要对服务器进行一些文件修改,不过这些知识点需要对PHP的配置文件修改有所了解才行,而且这种情况的出现几率很低,所以不用担心。

下面我们来编写一个函数,实现上面三种情况下获取URL地址:

```php

/*--------------------------------- 此处可做一些预处理操作 ---------------------------------*/

# 设置统一的浏览器编码

header("Content-type: text/html; charset=utf-8");

/*----------------------------------- 此处做单一入口加载 -----------------------------------*/

if (empty($_GET['controller'])) { die('请在地址栏中输入:controller节点 - 对应业务文件夹名称'); }

if (empty($_GET['action'])) { die('请在地址栏中输入:action节点 - 对应业务文件名称'); }

# 接收单一入口参数

$controller = $_GET['controller']; // 业务文件夹名称

$action = $_GET['action']; // 业务文件名称

# 组装文件路径

$file = "$controller/$action.php";

# 检测文件是否存在 - 不存在则直接转发到404页面

if (!file_exists($file)) {

require_once 'error/404.php';

} else {

require_once $file;

}

/*--------------------------------- 此处可做一些后处理操作 ---------------------------------*/

# 例如关闭数据库链接

# 清空session - cookie

```

这个函数的作用主要是获取url后面的字符串,例如:

`http://127.0.0.1/index.php/user/login就可以获得/user/login这段字符串`

同时这个函数对

`http://127.0.0.1/index.php?controller=user&action=login`这种原始路由无效,会返回`false`。

###4、实现INFO_PATH美化路由模式

首先,我们先从一条路由地址进行分析:

`http://127.0.0.1/index.php/ceshi-showlist-page-1.html`

从上面的路由中,我们可以截取出一段地址

`/ceshi-showlist-page-1.html`

这是我们使用` Path_Info()` 函数后,精确获取到的字符串

而其中`ceshi`是文件夹名称

`showlist`是文件名称

而`page`是`$_GET`键名

`1`是`$_GET['page']`对应的键值

`.html`是伪装的后缀名

而中间的`-`符号的是路由分隔符

下面我们再来思考下`INFO_PATH`美化路由模式的实现步骤:

```

1、我们需要先将开始的/符号去掉,可以使用ltrim()函数,

2、再将末尾的.html伪后缀名去掉,可以使用rtrim()函数,

3、最后再按-符号,将字符串分割为数组,

4、取出第一个值为controller参数,可以使用array_shift()函数,该函数的作用是取出数组中第一个元素,并从该数组中删除,

5、取出第二个值为action参数

6、然后将剩下的参数,按1 => 2 ,3 => 4的方式,重新进行$_GET赋值;

7、最后就是使用controller与action加载对应的业务文件

```

下面我们将单一入口文件中`index.php`,的核心代码,修改为`INFO_PATH`美化路由模式:

```

/*--------------------------------- 此处可做一些预处理操作 ---------------------------------*/

# 自己引入Path_Info()函数

# 设置统一的浏览器编码

header("Content-type: text/html; charset=utf-8");

# 设置后缀名

$suffix = '.html';

# 设置路由分隔符

$delimiter = '-';

# 获得路由地址

$path_info = Path_Info();

if (!$path_info) { die('路由地址为空!'); }

# 先删除左侧/符号

$path_info = ltrim($path_info, '/');

# 再删除右侧后缀名

$path_info = rtrim($path_info, $suffix);

# 再按分隔符生成数组

$route = explode($delimiter, $path_info);

if (empty($route[0])) { die('请在地址栏中输入:controller节点 - 对应业务文件夹名称'); }

if (empty($route[1])) { die('请在地址栏中输入:action节点 - 对应业务文件名称'); }

# 取出单一入口参数

$controller = array_shift($route); // 业务文件夹名称

$action = array_shift($route); // 业务文件名称

# 组装文件路径

$file = "$controller/$action.php";

# 如果URL还有后续参数,则重新赋值到$_GET中

if ( count($route) !=0 ) {

# 根据隔行算法,将参数转化为GET参数

foreach ($route as $key => $value) {

if( $key%2 == 0 ){

$_GET[$value] = '';

}else{

$_GET[$route[$key-1]] = $value;

}

}

# 打印下结果看看有没有赋值成功

var_dump($_GET);

}

# 检测文件是否存在 - 不存在则直接转发到404页面

if (!file_exists($file)) {

require_once 'error/404.php';

} else {

require_once $file;

}

/*--------------------------------- 此处可做一些后处理操作 ---------------------------------*/

# 例如关闭数据库链接

# 清空session - cookie

```

###5、实现SFY_PATH简化路由模式

首先,我们先从一条路由地址进行分析:

`http://127.0.0.1/index.php/ceshi-1-2.html`

从上面的路由中,我们可以截取出一段地址

`/ceshi-1-2.html`

这是我们使用 `Path_Info()` 函数后,精确获取到的字符串

而其中`/ceshi`是路由匹配关键词

`1`、`2`都是`$_GET[]`对应的键值

`.html`是伪装的后缀名

而中间的`-`符号的是路由分隔符

而这时候同学们可能会有以下几个疑问:

```

1、如何用/ceshi关键字来解析出对应的controller参数,和action参数?

2、1、2参数都应该赋值给哪些$_GET[]键?

```

以上的2点问题,实际上也是`SFY_PATH`简化路由模式的精髓所在,我们通过创建一个路由表文件,用于存储不同的路由,其格式如下:

```php

[

'/ceshi', // 路由匹配关键字

'ceshi/showlist', // 真实的controller和action地址

'id-page', // GET对应的参数名,使用 - 符号作为分隔符

'get|post|ajax' // 允许访问的请求类型,为空则不过滤,允许多个访问类型过滤,使用|符号分隔

],

```

通过上面的路由表,我们再来思考下`SFY_PATH`简化路由模式的实现步骤:

```

1、我们需要先将末尾的.html伪后缀名去掉,可以使用rtrim()函数,

2、再按-符号,将字符串分割为数组,

3、取出第一个值为路由匹配关键词,可以使用array_shift()函数

4、创建一个名为route.php的文件,用于存储路由表,存储格式为二维数组,其中一条数组代表一条路由地址

5、根据路由匹配关键词在路由表中匹配出对应的路由地址

6、然后将剩下的路由参数,根据提取出来的路地址中的$_GET键名,按1 => 1 ,2 => 2的方式,重新进行$_GET赋值;

7、然后再判断路由地址中对应的请求类型

8、最后就是使用controller与action加载对应的业务文件

```

下面我们先在单一入口文件`index.php`的同级目录下,创建一个`route.php`文件,并写入以下代码:

```php

return [

[ // 每个二维数组对应一条记录

'/ceshi', // 路由匹配关键字

'ceshi/showlist', // 真实的controller和action地址

'id-page', // GET对应的参数名,使用 - 符号作为分隔符

'get|post|ajax' // 允许访问的请求类型,为空则不过滤,允许多个访问类型过滤,使用|符号分隔

],

[

'/order',

'order/index',

'',

'ajax'

],

[

'/login',

'user/login',

'',

'post'

],

];

```

最后再将单一入口文件中`index.php`,的核心代码,修改为`SFY_PATH`简化路由模式:

```php

/*--------------------------------- 此处可做一些预处理操作 ---------------------------------*/

# 自己引入Path_Info()函数

# 设置统一的浏览器编码

header("Content-type: text/html; charset=utf-8");

# 设置后缀名

$suffix = '.html';

# 设置路由分隔符

$delimiter = '-';

# 获得路由地址

$path_info = Path_Info();

if (!$path_info) { die('路由地址为空!'); }

# 引入路由表

$table_route = require_once 'route.php';

# 删除右侧后缀名

$path_info = rtrim($path_info, $suffix);

# 再按分隔符生成数组

$route = explode($delimiter, $path_info);

# 取出路由关键字

$keyword = array_shift($route);

# 开始遍历路由表

$Current_Rou = '';

foreach ($table_route as $key=>$value ) {

# 找到路由

if ($value[0] == $keyword) {

$Current_Rou = $value;

break;

}

}

# 匹配不到路由直接404

if (empty($Current_Rou)) {

require_once 'error/404.php';

exit;

}

# 取出单一入口参数

# 组装文件路径

$file = $Current_Rou[1] . ".php";

# 不为空设置参数

if (!empty($Current_Rou[2]) && count($route) > 0) {

# 获得$_GET键名

$LEFT = explode('-', $Current_Rou[2]);

# 如果URL还有后续参数,则重新赋值到$_GET中

foreach ($LEFT as $key => $value) {

$_GET[$value] = $route[$key];

}

# 打印下结果看看有没有赋值成功

var_dump($_GET);

}

# 判断并过滤请求类型

if (!empty($Current_Rou[3])) {

$Http_Type = explode('|', $Current_Rou[3]);

$status = false;

foreach ( $Http_Type as $value ) {

# 类型小写化

$str = strtolower($value);

# 循环判断三种请求

if (strtolower($_SERVER['REQUEST_METHOD']) == $str) { $status = true;break; }

if (strtolower($_SERVER['REQUEST_METHOD']) == $str) { $status = true;break; }

if ($str == 'ajax' && isset($_SERVER['HTTP_X_REQUESTED_WITH']) == true && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') { $status = true;break; }

}

if ($status == false) { die('请求类型错误'); }

}

# 检测文件是否存在 - 不存在则直接转发到404页面

if (!file_exists($file)) {

require_once 'error/404.php';

} else {

require_once $file;

}

/*--------------------------------- 此处可做一些后处理操作 ---------------------------------*/

# 例如关闭数据库链接

# 清空session - cookie

```

下面我们可以通过访问以下路由地址,查看对应的解析结果:

```

1、http://127.0.0.1/index.php/ceshi-1-2.html

2、http://127.0.0.1/index.php/order.html

3、http://127.0.0.1/index.php/login.html

```

###6、CLE_PATH混合路由模式

实际上`CLE_PATH`混合路由模式,是由`INFO_PATH`与`SFY_PATH`两种路由模式,混合而成,也称之为兼容模式。

当`SFY_PATH`模式在路由表中搜索不到对应的路由规则,则使用`CLE_PATH`模式尝试解析路由。

有兴趣研究的同学可以结合以上的思路,自己尝试实现该路由模式。

###7、路由地址中如何隐藏index.php单一入口文件名

在一条像这样的路由地址中:

`http://127.0.0.1/index.php/ceshi/showlist.html`

我们不难发现存在2个文件名`index.php`与`showlist.html`

一般时候,这种情况是不正常的,很容易误导用户,而且也不美观,所以这时候我们就要现实,如果将`index.php`这个单一入口的文件名,在路由中隐藏掉同时也不会影响程序的正常运行。

这时候,这个功能我们需要Apache的支持,我们在`index.php`文件同级下,新建一个`.htaccess`文件,并下入以下代码:

```php

Options +FollowSymlinks -Multiviews

RewriteEngine on

RewriteCond %{REQUEST_FILENAME} !-d

RewriteCond %{REQUEST_FILENAME} !-f

RewriteRule ^(.*)$ index.php?/$1 [QSA,PT,L]

```

该文件的作用是告诉Apache,将路由中,所有错误的URL请求尝试转发到`index.php`中进行二次处理,

这样的处理机制,间接的实现了我们隐藏`index.php`单一入口文件的功能。

现在我们再来访问下面的路由,就能正确运行了:

`http://127.0.0.1/ceshi/showlist.html`

###8、隐藏index.php文件名后,Path_Info()函数的注意事项

当我们将`index.php`单一入口文件名称隐藏后,

实际上我们访问:

`http://127.0.0.1/demo/ceshi/showlist.html`

这种,带`demo`的真实目录路由时,

`Path_Info()`函数是没办法获取到`/ceshi/showlist.html`的

只能获取到`/demo/ceshi/showlist.html`,因为他没办法识别出哪一段是真实的目录名称,哪一段才是路由参数,

所以这时候同学们要自己做一下字符串处理,将其中对应的真实目录名称删除掉即可。

###9、获取当前所在目录的完整路径

使用系统自带的`__FILE__`常量,可以获取当前php文件所在的绝对路径,

如果想获取当前PHP文件所对应的上级目录,可以使用`dirname(__FILE__)`

如果是上上级,可以使用`dirname(dirname(__FILE__))`,以此类推。

###10、获取当前系统的目录分隔符

在windows系统中书写PHP代码,我们一般习惯性使用`\`符号作为目录分隔符,

但是在linux中目录的分隔符是`/`,于是PHP提供了一个内置常量`DIRECTORY_SEPARATOR`

它可以自动识别,并返回当前操作系统的所使用的目录分隔符。

为了方便朋友们学习,最后献上一个用路由模式封装成的留言板系统供大家阅读学习:[下载](https://blog.junphp.com/cangjingge/php/10.zip "下载")

© 著作权归作者所有

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值