您的位置:首页>科学 >在ThinkPHP框架中做一个类似PATHINFO模式的功能

在ThinkPHP框架中做一个类似PATHINFO模式的功能

2023-09-29 14:34
-->

1、PATHINFO功能简介

  所有从事 PHP 工作的人都知道 ThinkPHP 是一个免费开源的轻量级 PHP 框架。虽然它很轻量,但是它的功能却非常强大。

  这也是我接触的第一个框架。 TP框架中默认的URL模式是PathInfo模式。这种模式非常强大。每次访问一个网站,肯定都会有一长串的参数,但太长了,不友好。访问MVC模式构建的网站,必须有三个参数,分别是module、controller、action、M、C、A,这些参数需要用“&”分隔。如果参数很多的话就特别不友好。然而PathInfo模式的功能就是为了缩短和简化这个长长的列表,让这个路径显示起来更加友好。

传统的访问路径是这样的:

  http://www.webguidecorpuschristi.com/index.php?m=module&c=controller&a=action&var1=vaule1&var2=vaule2.....

而 ThinkPHP 在默认 URL 模式下可以实现这样的路径:

  http://www.webguidecorpuschristi.com/index.php?module/controller/action/var1/vaule1/var2/value2.....

两者对比,很容易得出结论:PathInfo模式下的访问路径显示更加友好!

不过,这篇文章我想讲的是如何构建这样一个友好的访问路径。

我的目标路径是这样的(当然TP也可以轻松做到):

  http://www.webguidecorpuschristi.com/module/controller/action/var1/vaule1/var2/value2.....

以上三个路径代表的意思是一样的,即都是访问同一个站点,参数相同

2。写作小背景

  因为最近打算模仿写一个小框架来加强和巩固自己的基础知识。它是用MVC模式构建的,所以与模型、控制器、行为等接触较少。之前一直在使用ThinkPHP,感觉ThinkPHP中URL中的默认模式PATHINFO非常强大。所以我决定创建这样一个函数并在我自己的小框架的 URL 上使用它。一直想研究一下ThinkPHP的原代码,但是由于时间不够,一直没有实现。我计划在这个寒假期间研究一下这个框架的原始代码,这是我大学生涯的最后一个寒假。

---- 想要学习,就必须坚定、坚持。如果不坚定,就会有毅力。不坚定就会有退路。

  鼓励大家,坚持就是成功!好吧,让我们回到正题。我创建的PATHINFO在功能上与ThinkPHP一致。至于底层原理和效率问题,和TP中的PATHINFO是否一样还不清楚。毕竟我还没学过TP。这里原代码是按照我自己的想法写的。

3。涉及核心知识

1。 Apache 的重写模块。

  使用apache的重写模块,该站点的所有访问路径只能从单个index.php条目输入。 (由于Apache重写规则也是个硬骨头,所以这里就不赘述了,我会另外写一篇文章来总结当时的重写规则,和大家互相学习。作者博客:http:// www.webguidecorpuschristi.com/phpstudy2015-6/)

2。正则表达式

  正则表达式和PHP中的preg_match()函数的基础知识。这个函数是制作这个功能的关键,所以需要仔细理解。

3。类文件的自动加载和路径问题

  在MVC模式中,最基本需要处理的参数是M、C、A这三个参数,这三个参数的思想贯穿了整个模式代码。

  这里我们需要处理的是URL,即我们只需要确定通过路径的模块、控制器、动作来访问哪个类、哪个控制器、行为即可。这些类的对象的生成以及行为方法的调用都是自动的,我们不需要编写额外的代码来一一处理。

  因此,如何准确地加载类文件和调用方法是非常关键的一步。 PHP有一个内置函数,当新Object创建时会自动触发,这就是__autoload()。它扩展了函数 spl_autoload_register() 来注册自动加载函数。

  关于路径问题,由于需要实现自动化,即自动加载class文件等,所以需要一个相对健壮的加载路径代码,使其更加可移植。例如,它可以在Windows和Linux系统下顺利工作,因此需要在PHP中使用一个魔术常量__DIR__来编写路径代码。

4。环境描述

  Linux虚拟机,PHP5.3.6,域名www.webguidecorpuschristi.com

5。代码示例

1。创建相应的文件夹。虽然这个例子很简单,但是我们不能含糊其辞,养成良好的习惯,争取早日成为高手,哈哈

  这个文件夹可以根据个人想法创建。如果是用在框架上,这一步就非常重要了。具体请参考各框架的文件目录结构。我的文件目录如下:

  

  我刚刚开始构建它。该目录非常简单,还不够完善。毕竟我还是个新手。各位大师,请不要批评我。如果可以的话,请给我一些建议。

  这里我主要展示一下我的文件夹,方便后面的理解和分析。这个函数的重点是Url.class.php文件。

2。启用apache的重写模块

  在对应的配置文件中打开即可,这里就不解释了。

  然后在根目录下创建.htaccess文件,这里主要放置重写规则,如下图:


RewriteEngine 开启
RewriteCond %{Request_FILENAME} !-f
RewriteRule !\.(js|ico|gif|jpg|png|css)$ /index.php

  简单分析:

    1。 RewriteEngine on 打开重写 

    2。 RewriteRule重写规则,表示非以上后缀的路径都适合  

    3。 RewriteCond判断是否为文件

  这里的作用是让www.webguidecorpuschristi.com的所有路径只能通过index.php路径访问,这是一个单一的入口。

3。主码

Url.class.php

我把这个文件放到/Framework/Core文件夹下

/*
*@作者:那片叶子随风而去
*@博客:http://www.webguidecorpuschristi.com/phpstudy2015-6/
*@时间:2017.1.3
*/
班级网址
{
//定义正则表达式常量
const REGEX_ANY="([^/]+?)"; #不以/
开头的任意数量的字符 const REGEx_INT="([0-9]+?)"; #编号
const REGEX_ALPHA="([a-zA-Z_-]+?)"; #字母
const REGEX_ALPHANUMERIC="([0-9a-zA-Z_-]+?)"; #任意数量的字母和数字_-
常量 REGEX_STATIC="%s"; #占位符
const ANY="(/[^/]+?)*"; #不以/开头的任意数量的字符 受保护的$routes=array(); #保存路径正则表达式 #添加路由正则表达式
公共函数addRoute($route)
{
$this->routes[]=$this->_ParseRoute($route);
} /*私有
@input :$route 输入路由规则
@output:return 返回路由正则规则
*/
私有函数_ParseRoute($route)
{
$parts=explode("/", $route); #分解路由规则
$正则表达式 =“@^”; #开始拼接常规路由规则
if(!$parts[0])
{
array_shift($parts); #删除第一个空元素
}
foreach($parts 作为 $part)
{
$regex.="/";
$args=爆炸(":",$part);
if(!sizeof($args)==2)
{
继续;
}
$type=array_shift($args);
$key=array_shift($args);
$this->_normalize($key); #规范参数,排除其他非法符号
$regex.='(?P<'.$key.'>'; #为preg_match正则匹配铺平道路
开关 (strtolower($type))
{
case 'int': #纯数字
$regex.=self::REGEX_INT;
打破;
case 'alpha': #纯字母
$regex.=self::REGEX_ALPHA;
打破;
case 'alphanum': #Alphanumeric
$regex.=self::REGEX_ALPHANUMBERIC;
打破;
默认:
$正则表达式。=$类型; #自定义正则表达式
打破;
}
$regex.=")";
}
$regex.=self::ANY; #其他网址参数
$regex.='$@u';
返回 $regex;
} /*public,将输入的URL与定义的正则表达式进行匹配
@input :$request 输入传入的URL
@output :return 如果成功,则输出规则数组数据。如果失败则输出 false
*/
公共函数 getRoute($request)
{
#处理请求并进行参数处理。如果小于M、C、A,会自动填充home、index、index,即构建MVC结构URL
$请求=rtrim($请求,'/'); #去掉右边多余的斜杠/
$arguments=explode('/',$request);
$参数=array_filter($参数); #删除数组中的空元素
$long=sizeof($arguments); #数组中的数字
switch ($long) #判断数量,不足则补
{
案例“0”:
$request='/home/index/index';
打破;
案例“1”:
$request.='/index/index';
打破;
案例“2”:
$request.='/index';
打破;
}
$matches=数组(); #定义匹配后存储的数组
$temp=数组(); #中间缓存数组 foreach ($this->routes as $v) #开始匹配
{
preg_match($v, $request, $temp); #需要重点理解这个数组
$temp?$matches=$temp:'';
}
if($matches) #判断$matches是否有数据,如果没有则返回false
{
foreach ($matches as $key => $value) #Filter
{
if(is_int($key))
{
取消设置($matches[$key]); #移除数字键元素并保留关联元素。和上面的preg_match一起理解
}
}
$结果=$匹配;
if($long > sizeof($result)) #URL参数超过
后的处理 {
$i=1;
foreach($参数为$k => $v)
{
if($k > sizeof($结果))
{
if($i==1)
{
$结果[$v]='';
$temp=$v;
$i=2;
}
否则
{
$结果[$temp]=$v;
$i=1;
}
}
}
}
返回$结果;
}
返回 false;
}
#标准化参数,不能存在任何符号,只能有a-zA-Z0-9组合
私有函数_normalize(&$param)
{
$param=preg_replace("/[^a-zA-Z0-9]/", '', $param);
}
}
/*使用示例:
include './Url.class.php';
$router=new Url();
$router->addRoute("/alpha:module/alpha:controller/[0-9]+?:a1a");
$router->addRoute("/alpha:module/alpha:controller/alpha:action");
$router->addRoute("/alpha:module/alpha:controller/alpha:action/(www[0-9]+?):id");
$url=$_SERVER['REQUEST_URI'];
$urls=$router->getRoute($url);
echo "
";
print_r($urls);
echo "
";
*/
?>

代码功能分析:

  上面的Url.class.php类文件代码大致可以分为两部分。在第75行可以分为上半部和下半部,也就是getRoute方法所在的地方。

  上半部分是方法addRoute,用于添加路径正则表达式,并存储在类属性$routes中。

  下半部分是方法getRoute,用于匹配和处理访问路径。传入要访问的路径,然后与$routes中的正则表达式进行匹配。成功后,将进行进一步处理,并返回处理结果。

  对于上面的Url.class.php类文件,我们可以在根目录下创建一个test.php测试文件或者直接在index.php文件上进行测试(方便快捷),帮助我们进一步理解其原理和该类文件的功能。功能。 (这里我不会创建测试文件,而是直接在index.php文件上测试)

测试 1:

index.php代码如下

包括“./Framework/Core/Url.class.php”; #加载类文件
$router=new Url();
#添加规则
$router->addRoute("/alpha:module/alpha:controller/alpha:action");
?>

在Url.class.php的addRoute方法中添加一个输出进行观察,如下图:

首先访问:http://www.webguidecorpuschristi.com/

结果:

总结:

  1。显然,输出类属性$routes存储的是正则表达式。

  2。在私有方法_ParseRoute中,调用_normalize()方法来处理$key。该方法是过滤掉$key中除a-zA-Z0-9以外的符号。

  3。正则表达式中,[P<'.$key.'>]用于preg_match匹配,后面会解释。

  4。在switch中,正则表达式被匹配并选择。可以自己定义,也可以自己写。例如:

     int 代表 self::REGEX_INT,即正则表达式 =》([0-9]+?)

    Alpha 代表 self::REGEX_ALPHA,即正则表达式 =》([a-zA-Z_-]+?)

    Alphanum 代表自身::REGEX_ALPHANUMBERIC=》([0-9a-zA-Z_-]+?)

    (正则表达式)表示使用自己写的表达式=》例如:(www[0-9]+?)

   因此,添加正则路由addRoute的参数形式:[/int:module/alpha:controller/alphanum:action/(www[0-9]+?):id]任意组合(数量不限),冒号 以下参数与 preg_match 一起使用,稍后解释。

  5。 $regex.=self::ANY;这里的作用是匹配URL路径的其他参数,即 http://www.webguidecorpuschristi.com/module/controller/action/var1/values1 ,action后面的var1、values1等参数。

测试完成后,将Url.class.php类文件恢复到原来的状态!

测试2:

index.php代码修改如下:

header("内容类型:text/html;charset=utf8");
包括“./Framework/Core/Url.class.php”; #加载类文件
$router=new Url();
#添加规则
$router->addRoute("/alpha:module/alpha:controller/[0-9]+?:a1a");
$router->addRoute("/alpha:module/alpha:controller/alpha:action");
$router->addRoute("/alpha:module/alpha:controller/alpha:action/(www[0-9]+?):id");
$url=$_SERVER['REQUEST_URI'];
echo "
";
echo "index.php 第一个输出
";
print_r($url);
echo "
";
$urls=$router->getRoute($url);
echo "
";
echo "index.php getRoute 输出结果第五次输出
";
print_r($urls);
echo "
";死;
?>

在Url.class.php的getRoute方法中添加以下输出:

  第二个输出用于查看使用多个正则表达式时$routes的值,如下图:

  第三个输出,如下图

  第四个输出,如下图

  第三个和第四个输出用于查看和理解preg_match()函数

开始访问:http://www.webguidecorpuschristi.com/m/c/a/var/value

结果与总结:

  1。输出

  这里需要你了解和体验$_SERVER

  2。输出2

  有多条路径时,都会保存在$routes中

  3。输出三、四

  这里我们需要重点关注preg_matches()函数。

注:

  使用 PCRE 函数时,模式需要用分隔符括起来。分隔符可以是任何非字母数字、非反斜杠或非空白字符。如果分隔符在模式中频繁出现,更好的选择是使用其他分隔符来提高可读性。

  由此我们可以知道$routes中的值@的含义,它是分隔符,但是我们经常使用/。

  preg_matches()的第一个参数是正则表达式。这里我们把它放在 $routes 中。

  preg_matches()的第二个参数是需要匹配的数据。这里我们放入传入的URL(这里的URL就是输出一的值)。

  preg_matches()的第二个参数是一个不必要的参数。如果填写该参数,则所有成功匹配的值都会放入该数组中。

  preg_matches() 在 PHP5.2.2 中有一个新的小语法,小语法在这里至关重要。

  如果使用这个小语法(?P),如果这个子组匹配,那么它将与匹配数据和 name 参数形成一对关联元素,这些元素将存储在 preg_matches() 中三参数数组。这就很好的解释了为什么使用上面addRoute()参数冒号后面的值,以及

特别注意:

  匹配foreach时,如果$routes包含多个正则表达式,则会按照顺序一一匹配URL。如果全部匹配成功,则后面的值会覆盖前面的值。

  4。输出五

  这里是getRoute()方法处理URL时返回的结果。

测试完成后,需要将Url.class.php文件恢复到原来的状态

至此,整个Url.class.php类文件已经解释和分析完毕,接下来就是接入MVC了。

下面简单介绍一下自动加载类文件、生成对象、调用方法。

可以查看上面的文件目录来了解下面的各个文件。

index.php文件

包括'./Framework/Core/Core.php'; $router=new Url();
$router->addRoute("/alpha:module/alpha:controller/[0-9]+?:a1a");
$router->addRoute("/alpha:module/alpha:controller/alpha:action");
$router->addRoute("/alpha:module/alpha:controller/alpha:action/(www[0-9]+?):id"); $url=$_SERVER['REQUEST_URI'];
$urls=$router->getRoute($url);
$_GET['urls']=$urls;
$m=$urls['模块'];
$c=$urls['控制器'];
$a=$urls['action'];
如果($m&&$c)
{
$autoload=new 自动加载($m,$c);
$autoload->PutFile();
}
$object=new $c;
$object->$a(); ?>

/Framework/Core/Core.php

#核心文件
# 加载PHP自动加载函数文件
include_once(__DIR__."/../Function/AutoLoad.function.php");
?>

/框架/函数/AutoLoad.function.php

#用于加载核心文件
spl_autoload_register('CoreFunction');
函数 CoreFunction($classname)
{
$file=__DIR__."/../Core/".$classname.".class.php";
if(is_file($file))
{
require_once($file);
}
}
?>

/Framework/Core/Autoload.class.php

#用于加载控制器文件
自动加载类
{
私人$path='';
公共函数 __construct($module,$controller)
{
$this->path=__DIR__."/../../Application/".$module."/Controller/".$controller."Controller.class.php";
}
公共函数 PutFile()
{
if(is_file($this->path)) #判断文件是否存在
{
require_once($this->path);
}
否则
{
返回 false;
}
}
} ?>

模块中还有两个控制器文件

TestController.class.php 在首页

班级测试
{
公共 $aa=2;
公共函数action()
{
echo "home-->test----->action";
echo "
";
print_r($_GET);
echo "
";
}
} 管理中的

TestController.class.php

班级测试
{
公共 $aa=2;
公共函数action()
{
echo "管理---->测试----->操作";
echo "
";
print_r($_GET);
echo "
";
}
}

开始访问:

1、http://www.webguidecorpuschristi.com/Home/Test/action/var1/vaule1/var2/value2

结果是:

2、http://www.webguidecorpuschristi.com/Admin/Test/action/var1/vaule1/var2/value2

结果是:

完成了,我厌倦了每个字的编码。这里的URL路径不处理大小写,因此模块、控制器和操作都是区分大小写的。

多思考,多思考,多编码,争取早日成为大师!

下次打算改成存储方式,将addRoute的常规路径存储在文件中的$routes中,然后在使用getRoute时取出来。然后使用apache的ab进行压力测试。

(以上是我自己的一些看法,如有不足或错误还请指出)

作者:那叶随风

声明:本博文为原创,仅代表本人在一段时间内工作和学习中总结的观点或结论。转载时,请在文章页面明显位置提供原文链接。

-->