首页投稿(暂停使用,暂停投稿)程序员

ThinkPHP控制器学习(二)

2016-06-04  本文已影响162人  阿V薄荷加可乐
小样儿

一、写在前面

这几天准备写控制器中输出模板的方法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_p
Array
(
    [user_name] => wangba
    [password] => ssfwjona       
)
// $data_g
Array ( [act] => publish ) 

5.2 参数过滤

传递系统过滤函数

$data_p = I('post.' , '' , 'htmlspecialchars');

结果:

Array
(
    [user_name] => <p>wangba</p>
    [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]+$/'));                

结果:

结果1
Array
(
    [user_name] => 
    [password] => 
)
// 结果2
wangba

filter_var的使用

// 传递参数 email=wangwu
print_r(I('post.email' , 'error' , 'FILTER_VALIDATE_EMAIL')); 
// 传递参数 email=wangba@qq.com 
print_r(I('post.email' , 'error' , 'FILTER_VALIDATE_EMAIL'));        

结果:

error
wangba@qq.com

5.3 修饰符的使用

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

结果

Array ( [0] => hello world ) 

5.4 额外数据请求类型

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

六、总结

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

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

上一篇下一篇

猜你喜欢

热点阅读