最新消息:XAMPP默认安装之后是很不安全的,我们只需要点击左方菜单的 "安全"选项,按照向导操作即可完成安全设置。

PHP开发教程_什么是路由器

XAMPP新闻 admin 80浏览 0评论

1、什么是路由

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

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

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

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

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

  1. 1、原始的路由:?controller=文件夹&action=文件名&其他=参数&...
  2. 2、INFO_PATH美化路由:/文件夹/文件名/其他1/参数1/其他2/参数2/...
  3. 3、SFY_PATH简化路由:自定义路由 == 真实路由
  4. 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地址:

  1. <?php
  2. /*--------------------------------- 此处可做一些预处理操作 ---------------------------------*/
  3. # 设置统一的浏览器编码
  4. header("Content-type: text/html; charset=utf-8");
  5.  
  6. /*----------------------------------- 此处做单一入口加载 -----------------------------------*/
  7. if (empty($_GET['controller'])) { die('请在地址栏中输入:controller节点 - 对应业务文件夹名称'); }
  8. if (empty($_GET['action'])) { die('请在地址栏中输入:action节点 - 对应业务文件名称'); }
  9.  
  10. # 接收单一入口参数
  11. $controller = $_GET['controller']; // 业务文件夹名称
  12. $action = $_GET['action']; // 业务文件名称
  13.  
  14. # 组装文件路径
  15. $file = "$controller/$action.php";
  16.  
  17. # 检测文件是否存在 - 不存在则直接转发到404页面
  18. if (!file_exists($file)) {
  19. require_once 'error/404.php';
  20. } else {
  21. require_once $file;
  22. }
  23.  
  24. /*--------------------------------- 此处可做一些后处理操作 ---------------------------------*/
  25. # 例如关闭数据库链接
  26. # 清空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. 1、我们需要先将开始的/符号去掉,可以使用ltrim()函数,
  2. 2、再将末尾的.html伪后缀名去掉,可以使用rtrim()函数,
  3. 3、最后再按-符号,将字符串分割为数组,
  4. 4、取出第一个值为controller参数,可以使用array_shift()函数,该函数的作用是取出数组中第一个元素,并从该数组中删除,
  5. 5、取出第二个值为action参数
  6. 6、然后将剩下的参数,按1 => 2 ,3 => 4的方式,重新进行$_GET赋值;
  7. 7、最后就是使用controller与action加载对应的业务文件

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

  1. <?php
  2. /*--------------------------------- 此处可做一些预处理操作 ---------------------------------*/
  3. # 自己引入Path_Info()函数
  4. # 设置统一的浏览器编码
  5. header("Content-type: text/html; charset=utf-8");
  6.  
  7. # 设置后缀名
  8. $suffix = '.html';
  9. # 设置路由分隔符
  10. $delimiter = '-';
  11. # 获得路由地址
  12. $path_info = Path_Info();
  13.  
  14. if (!$path_info) { die('路由地址为空!'); }
  15. # 先删除左侧/符号
  16. $path_info = ltrim($path_info, '/');
  17. # 再删除右侧后缀名
  18. $path_info = rtrim($path_info, $suffix);
  19. # 再按分隔符生成数组
  20. $route = explode($delimiter, $path_info);
  21.  
  22. if (empty($route[0])) { die('请在地址栏中输入:controller节点 - 对应业务文件夹名称'); }
  23. if (empty($route[1])) { die('请在地址栏中输入:action节点 - 对应业务文件名称'); }
  24.  
  25. # 取出单一入口参数
  26. $controller = array_shift($route); // 业务文件夹名称
  27. $action = array_shift($route); // 业务文件名称
  28.  
  29. # 组装文件路径
  30. $file = "$controller/$action.php";
  31.  
  32. # 如果URL还有后续参数,则重新赋值到$_GET中
  33. if ( count($route) !=0 ) {
  34. # 根据隔行算法,将参数转化为GET参数
  35. foreach ($route as $key => $value) {
  36. if( $key%2 == 0 ){
  37. $_GET[$value] = '';
  38. }else{
  39. $_GET[$route[$key-1]] = $value;
  40. }
  41. }
  42. # 打印下结果看看有没有赋值成功
  43. var_dump($_GET);
  44. }
  45.  
  46. # 检测文件是否存在 - 不存在则直接转发到404页面
  47. if (!file_exists($file)) {
  48. require_once 'error/404.php';
  49. } else {
  50. require_once $file;
  51. }
  52.  
  53. /*--------------------------------- 此处可做一些后处理操作 ---------------------------------*/
  54. # 例如关闭数据库链接
  55. # 清空session - cookie

5、实现SFY_PATH简化路由模式

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

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

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

/ceshi-1-2.html

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

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

12都是$_GET[]对应的键值

.html是伪装的后缀名

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

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

  1. 1、如何用/ceshi关键字来解析出对应的controller参数,和action参数?
  2. 2、1、2参数都应该赋值给哪些$_GET[]键?

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

  1. [
  2. '/ceshi', // 路由匹配关键字
  3. 'ceshi/showlist', // 真实的controller和action地址
  4. 'id-page', // GET对应的参数名,使用 - 符号作为分隔符
  5. 'get|post|ajax' // 允许访问的请求类型,为空则不过滤,允许多个访问类型过滤,使用|符号分隔
  6. ],

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

  1. 1、我们需要先将末尾的.html伪后缀名去掉,可以使用rtrim()函数,
  2. 2、再按-符号,将字符串分割为数组,
  3. 3、取出第一个值为路由匹配关键词,可以使用array_shift()函数
  4. 4、创建一个名为route.php的文件,用于存储路由表,存储格式为二维数组,其中一条数组代表一条路由地址
  5. 5、根据路由匹配关键词在路由表中匹配出对应的路由地址
  6. 6、然后将剩下的路由参数,根据提取出来的路地址中的$_GET键名,按1 => 1 ,2 => 2的方式,重新进行$_GET赋值;
  7. 7、然后再判断路由地址中对应的请求类型
  8. 8、最后就是使用controller与action加载对应的业务文件

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

  1. <?php
  2. return [
  3. [ // 每个二维数组对应一条记录
  4. '/ceshi', // 路由匹配关键字
  5. 'ceshi/showlist', // 真实的controller和action地址
  6. 'id-page', // GET对应的参数名,使用 - 符号作为分隔符
  7. 'get|post|ajax' // 允许访问的请求类型,为空则不过滤,允许多个访问类型过滤,使用|符号分隔
  8. ],
  9. [
  10. '/order',
  11. 'order/index',
  12. '',
  13. 'ajax'
  14. ],
  15. [
  16. '/login',
  17. 'user/login',
  18. '',
  19. 'post'
  20. ],
  21. ];

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

  1. <?php
  2. /*--------------------------------- 此处可做一些预处理操作 ---------------------------------*/
  3. # 自己引入Path_Info()函数
  4. # 设置统一的浏览器编码
  5. header("Content-type: text/html; charset=utf-8");
  6.  
  7. # 设置后缀名
  8. $suffix = '.html';
  9. # 设置路由分隔符
  10. $delimiter = '-';
  11. # 获得路由地址
  12. $path_info = Path_Info();
  13.  
  14. if (!$path_info) { die('路由地址为空!'); }
  15.  
  16. # 引入路由表
  17. $table_route = require_once 'route.php';
  18. # 删除右侧后缀名
  19. $path_info = rtrim($path_info, $suffix);
  20. # 再按分隔符生成数组
  21. $route = explode($delimiter, $path_info);
  22. # 取出路由关键字
  23. $keyword = array_shift($route);
  24.  
  25. # 开始遍历路由表
  26. $Current_Rou = '';
  27. foreach ($table_route as $key=>$value ) {
  28. # 找到路由
  29. if ($value[0] == $keyword) {
  30. $Current_Rou = $value;
  31. break;
  32. }
  33. }
  34.  
  35. # 匹配不到路由直接404
  36. if (empty($Current_Rou)) {
  37. require_once 'error/404.php';
  38. exit;
  39. }
  40.  
  41. # 取出单一入口参数
  42. # 组装文件路径
  43. $file = $Current_Rou[1] . ".php";
  44.  
  45. # 不为空设置参数
  46. if (!empty($Current_Rou[2]) && count($route) > 0) {
  47. # 获得$_GET键名
  48. $LEFT = explode('-', $Current_Rou[2]);
  49. # 如果URL还有后续参数,则重新赋值到$_GET中
  50. foreach ($LEFT as $key => $value) {
  51. $_GET[$value] = $route[$key];
  52. }
  53. # 打印下结果看看有没有赋值成功
  54. var_dump($_GET);
  55. }
  56.  
  57. # 判断并过滤请求类型
  58. if (!empty($Current_Rou[3])) {
  59. $Http_Type = explode('|', $Current_Rou[3]);
  60. $status = false;
  61. foreach ( $Http_Type as $value ) {
  62. # 类型小写化
  63. $str = strtolower($value);
  64. # 循环判断三种请求
  65. if (strtolower($_SERVER['REQUEST_METHOD']) == $str) { $status = true;break; }
  66. if (strtolower($_SERVER['REQUEST_METHOD']) == $str) { $status = true;break; }
  67. if ($str == 'ajax' && isset($_SERVER['HTTP_X_REQUESTED_WITH']) == true && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') { $status = true;break; }
  68. }
  69. if ($status == false) { die('请求类型错误'); }
  70. }
  71.  
  72. # 检测文件是否存在 - 不存在则直接转发到404页面
  73. if (!file_exists($file)) {
  74. require_once 'error/404.php';
  75. } else {
  76. require_once $file;
  77. }
  78.  
  79. /*--------------------------------- 此处可做一些后处理操作 ---------------------------------*/
  80. # 例如关闭数据库链接
  81. # 清空session - cookie

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

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

6、CLE_PATH混合路由模式

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

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

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

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

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

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

我们不难发现存在2个文件名index.phpshowlist.html

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

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

  1. <IfModule mod_rewrite.c>
  2. Options +FollowSymlinks -Multiviews
  3. RewriteEngine on
  4.  
  5. RewriteCond %{REQUEST_FILENAME} !-d
  6. RewriteCond %{REQUEST_FILENAME} !-f
  7. RewriteRule ^(.*)$ index.php?/$1 [QSA,PT,L]
  8. </IfModule>

该文件的作用是告诉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

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

转载请注明:XAMPP中文组官网 » PHP开发教程_什么是路由器