博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ThinkPHP控制器学习(二)
阅读量:7094 次
发布时间:2019-06-28

本文共 7400 字,大约阅读时间需要 24 分钟。

一、写在前面

这几天准备写控制器中输出模板的方法display,但在阅读源码的时候遇到了一点问题。整个源码我看了一遍,有几部分理解有点问题,如系统钩子类Hook的原理,行为类ParseTemplateBehavior中的checkCache方法,Storageload方法。

这些类与方法的源码阅读都遇到一些问题,并没有能够解决。因此现在就不说display方法了,等以后看懂了再来说说display方法。

那么今天来说说TP的快捷函数I函数,它虽然不是Controller中的方法,但的确在控制器中需要接受数据的时候这个函数应用颇多。

二、I函数介绍

该函数在TP的自定义函数库中算是比较长的,有134行代码。先来简单介绍这个方法。

I($name[,$default=''][,$filter=null][,$datas=null])复制代码

2.1 参数

变量名 变量类型 参数介绍
$name string 变量的名称 支持指定类型
$default mixed 不存在的时候默认值
$filter mixed 参数过滤方法
$datas mixed 要获取的额外数据源

name 变量的名称,支持指定类型,格式为 变量类型.变量名/修饰符post.表示取出POST数据,get.表示GET数据。支持通过点运算符来取出特定数据,如:

post.name   // 取出$_POST['name']get.act     // 取出$_GET['act']  复制代码

同时支持仅传递元素参数,如idnameact,会自动判断是POST,还是GET方式。

支持传递的类型有:

类型 含义
get 获取GET参数
post 获取POST参数
put 获取PUT 参数
param 自动判断请求类型获取GET、 POST或者PUT参数
path 获取 PATHINFO模式的URL参数
data 获取 其他类型的参数, 需要配合额外数据源参数
request 获取REQUEST 参数
session 获取 $_SESSION 参数
cookie 获取 $_COOKIE 参数
server 获取 $_SERVER 参数
globals 获取 $GLOBALS参数

支持以下修饰符的使用:

修饰符 作用
s 强制转换为字符串类型
d 强制转换为整型类型
b 强制转换为布尔类型
a 强制转换为数组类型
f 强制转换为浮点类型

default 要取数据不存在的时候的默认值,可以传递一个报错提示,使网站更具友好性,如

echo I('get.main' , 'hello');复制代码

如果没取到main变量,就会返回hello

filter 可以传递一些参数过滤函数名作为参数,在I函数内就会回调用该函数来过滤取得的数据。这些过滤函数可以是PHP内置函数,也可以是自定义的函数。如:htmlspecialchars, addslashes

如果为空,自动调用系统默认的过滤函数,从DEFAULT_FILTER设置,如果传递了参数,则会忽略DEFAULT_FILTER

甚至支持使用正则表达式进行过滤,如:

print_r(I('post.user_name' , '' , '/^[A-Z][a-z]+$/')); 复制代码

如果匹配成功,则会将该变量返回,否则返回false

datas 要使用该参数,必须使$name的值为data,之后可以调用$datas来获取其他方法的数据,如要获取上传文件的超全局变量$_FILES:

I('datas' , '' , '' , $_FILES);复制代码

三、自己写一个I函数

可见I函数的功能很强大,但在看I函数源码之前,不如先自己模仿着写一个I函数,这样看源码就会有更深的理解:

function I($name , $default = '' , $filter = null , $datas = null) {    if(!isset($name)) {        return false;    }    // 判断是否有修饰符存在    if(strpos($name, '/')) {        list($name , $type) = explode('/', $name);    }    // 如果有点号运算符 将取数据方式 与 要取得变量分开    if(strpos($name, '.')) {        list($method , $var) = explode('.', $name);    } else {        $method = $name;    }    // 判断取参方式    switch($method) {        case 'post':            $content = $_POST;            break;        case 'get':            $content = $_GET;            break;        case 'session':            $content = $_SESSION;            break;        case 'cookie' :            $content = $_COOKIE;            break;        case 'request' :            $content = $_REQUEST;            break;        case 'server' :            $content = $_SERVER;            break;        case 'globals' :            $content = $GLOBALS ;            break;        case 'data':            $content = $datas;            break;        default:            $content = false;    }        // 如果content不存在 且$default存在 直接返回default存在    if(empty($content) && !empty($default)) {        return $default;    }     // 取出特定参数    if(!empty($var)) {        $content = $content[$var];    }    // 参数过滤    if(!empty($filter)) {        // 支持正则匹配 正则由'/'开头        if(strpos('/', $filter) == 0) {            $flag = true;            if(is_array($content)) {                                foreach ($content as $key => $value) {                    // 匹配失败 直接跳出                    if(1 !== preg_match($filter,$value)) {                        $flag = false;                        break;                    }                 }            } else {                // 匹配成功 直接跳出                if(1 !== preg_match($filter,$content)) {                    $flag = false;                }             }            if(!$flag) {                return empty($default)?'匹配失败':$default;            }        } else {            if(is_array($content)) {                            foreach ($content as $key => $value) {                    $tmp[$key] = call_user_func($filter , $value);                }                $content = $tmp;            } else {                $content = call_user_func($filter , $content);            }        }            }    if(!empty($type)) {        switch ($type) {            case 's':                $content = (String)$content;                break;            case 'd':                $content = (Integer)$content;                break;            case 'b':                               $content = (Boolean)$content;                break;            case 'a':                $content = (Array)$content;                break;            case 'f':                $content = (Float)$content;                break;              }    }        return $content;}复制代码

以上是自己模仿I函数写的具有相似功能的函数:

支持 参数一 '类型.变量/修饰符' 不支持 path | param(自动判断使用环境) | put 支持其他方式 不支持 使用filter_var 进行过滤 支持 $default 返回默认值 支持 传递过滤函数 正则匹配

这个函数还是有一些bug的,不能和TP里的函数比较,但作为学习其源码的一个比较,还是足够的。接下来,就来看看TP的I函数的源码,观察它的实现与自己写的有什么不同之处。

四、两个I函数的比较

写完自己的I函数,现在就来看一看TP的I函数。这个函数有点长,为不浪费篇幅,就不放出来了。 观其源码,实现的步骤和自己写的有点类似,但实现的细节有很多不同。

  • 修饰符 与 变量 与 类型分离
  • 判断具体是那种传递数据的方式
  • 对数据进行处理:如过滤函数过滤变量 正则匹配变量 修饰符等

TP的I函数的变量类型比我自己写的多了三个,path | param(自动判断使用环境) | put,path是在TP的pathinfo模式下才起作用。我对与PUT方式不是很了解。所有这两个都没有加上。

4.1 自动判断当前请求类型的实现

自动判断使用环境,TP利用$_SERVERREQUEST_METHOD元素来完成的,我并没有想到这个属性,因此在自己的I函数中并没有该功能。这个元素记录的是访问页面使用的方法,有PUT , GET , POST , HEAD。源代码如下:

switch($_SERVER['REQUEST_METHOD']) {    case 'POST':        $input  =  $_POST;        break;    case 'PUT':        if(is_null($_PUT)){            parse_str(file_get_contents('php://input'), $_PUT);        }        $input  =   $_PUT;        break;    default:        $input  =  $_GET;}复制代码

引用传递的使用 TP在获取变量请求类型时并不是直接赋值,而是使用了引用传递,而我个人对引用传递理解不深,就不在这里穿凿附会了。

4.2 正则过滤的实现

TP的I函数的正则过滤仅在传递了具体变量时起作用,如下:

if(0 === strpos($filters,'/')){    if(1 !== preg_match($filters,(string)$data)){        // 支持正则验证        return   isset($default) ? $default : null;    }}else{    $filters    =   explode(',',$filters);                    }复制代码

可以看出TP在判断是不是正则的方法与我一致,但它的正则过滤仅仅在传递了具体的变量名才能使用,如果是一个数组,就会返回空,如下:

print_r(I('post.' , '' , '/^[A-Za-z]+$/'));// 打印Array(    [user_name] =>     [password] => )复制代码

4.3 filter_var 的调用

Filter函数是PHP内置的函数库,用于特定的过滤,如email,callback等,要使用filter_var,传递的变量,就必须是filter_list中的内容。

$data   =   filter_var($data,is_int($filter) ? $filter : filter_id($filter));复制代码

还有很多细节的实现有不同之处,可以看出我自己写的函数在细节上还有不少问题,如对变量的处理并没有将其转同一的大小写等等。

五、应用

看完了源码,甚至模拟了一个I函数,现在就来说说I的具体应用。

5.1 普通应用

// 获取post 数据$data_p = I('post.');// 获取get数据$data_g = I('get.');复制代码

结果

// $data_pArray(    [user_name] => wangba    [password] => ssfwjona       )// $data_gArray ( [act] => publish ) 复制代码

5.2 参数过滤

传递系统过滤函数

$data_p = I('post.' , '' , 'htmlspecialchars');复制代码

结果:

Array(    [user_name] => 

wangba

[password] => sfkhanasf)复制代码

传递自定义过滤参数

print_r(I('post.' , '' , '_addslashes'));复制代码

结果:

Array(    [user_name] => wang\"haf    [password] => safegawh)复制代码

正则表达式的使用

// 如果传递数组print_r(I('post.' , '' , '/^[A-Za-z]+$/'));            // 如果传递特定值print_r(I('post.user_name' , '' , '/^[A-Z][a-z]+$/'));                复制代码

结果:

结果1Array(    [user_name] =>     [password] => )// 结果2wangba复制代码

filter_var的使用

// 传递参数 email=wangwuprint_r(I('post.email' , 'error' , 'FILTER_VALIDATE_EMAIL')); // 传递参数 email=wangba@qq.com print_r(I('post.email' , 'error' , 'FILTER_VALIDATE_EMAIL'));        复制代码

结果:

errorwangba@qq.com复制代码

5.3 修饰符的使用

// 转换为数组print_r(I('post.user_name/a' , 'error' ));   复制代码

结果

Array ( [0] => hello world ) 复制代码

5.4 额外数据请求类型

// 获取上传文件内容I('data.' , '' , '' , $_FILES);复制代码

六、总结

从自己写的I函数可以看出,虽然功能可以实现,但在细节上欠缺很多,如对请求类型的格式化,代码也有一定的冗余。看完了源码后,我也对其做了一定的修改,因为看着别人的源码写,总是会模仿它,所有就不再发出来了。但是,还是有很多收获的,下次再写的时候,自然会注意这些问题。

好了,啰里啰唆一大堆,今天就写到这里了。因为我是类似于日记一样写的东西,可能,逻辑有点混乱。看本文的小伙伴请见谅,以后有时间,我再修改修改 :)

转载于:https://juejin.im/post/5a30f2d36fb9a0450167f3d4

你可能感兴趣的文章
Geek爱旅行 - 龙猫
查看>>
《NLP汉语自然语言处理原理与实践》结构图
查看>>
cache和buffer之区别
查看>>
路由协议
查看>>
实用土方之妇科男性科
查看>>
初始MyBatis
查看>>
debian下使用dig/nslookup
查看>>
135.003 智能合约后端优化和产品化
查看>>
解释器模式 Interpreter 行为型 设计模式(十九)
查看>>
K-Modes算法[聚类算法]
查看>>
Const #define
查看>>
ThinkPHP下S()函数的使用
查看>>
java实现佛洛依德(Floyd)算法关于求有向图每对顶点间的最短路径问题
查看>>
shell检测网站状态码和访问时间
查看>>
天。鬼。法
查看>>
MongoDB启用身份验证
查看>>
我的友情链接
查看>>
我的友情链接
查看>>
5.SpringMVC
查看>>
4.Java JSON使用
查看>>