Thinkphp5框架简单理解

news2024/9/23 15:29:48

说明

该文章来源于同事lu2ker转载至此处,更多文章可参考:https://github.com/lu2ker/

目录

  • 说明
  • TP5框架简单理解
    • 1. 架构总览
      • 1.1 控制器/操作
      • 1.2 MVC模式流程
      • 1.3 类库自动加载
      • 1.4 URL访问检测
      • 1.5 路由模式
        • 1.5.1 普通模式
        • 1.5.2 混合模式
        • 1.5.4 强制路由
      • 1.6 路由定义
        • 1.6.1方式 1:路由到模块/控制器
        • 1.6.2 方式2:路由到重定向地址
        • 1.6.3 方式3:路由到控制器的方法
        • 1.6.4 方式4:路由到类的方法
      • 1.7 路由使用
      • 1.8 路由的其他知识
      • 1.9 其他东西
    • 2.请求相关
      • 2.1 如何获取请求参数的?
      • 2.2 检测变量是否设置
      • 2.3 请求方法
      • 2.4 【▲】请求方法伪装
      • 2.5 伪静态配置效果
      • 2.6 参数绑定
      • 2.7 请求缓存
    • 3. 数据库
    • 4. 模板
      • 4.1 变量输出
      • 4.2 使用函数、默认值
      • 4.3 TP内置标签
    • 5. 日志和错误
    • 6. 杂项
      • 6.1 缓存
      • 6.2 上传规则
    • Controller
    • Request(5.0.18版本)
      • 初始化和构造函数:instance和__construct
      • 方法注入:hook和__call
      • 当前的请求类型:method
      • 获取当前请求的参数:param
      • 获取GET数据:get
      • 获取POST数据:post
      • 获取cookie参数:cookie
      • 获取上传的文件信息:file
      • 被多处调用的方法:input
      • 递归过滤方法:filterValue

TP5框架简单理解

(PS:只做粗略、关键知识的记录,TP程序的开始。详情请阅读官方手册)

1. 架构总览

TP程序的开始

PHP >=5.3.0, PHP7

ThinkPHP5.0应用基于MVC(模型-视图-控制器)的方式来组织。

MVC是一个设计模式,它强制性的使应用程序的输入、处理和输出分开。使用MVC应用程序被分成三个核心部件:模型(M)、视图(V)、控制器(C),它们各自处理自己的任务。

5.0的URL访问受路由决定,如果关闭路由或者没有匹配路由的情况下,则是基于:

http://serverName/index.php/模块/控制器/操作/参数/值…

1.1 控制器/操作

5.0的控制器类比较灵活,可以无需继承任何基础类库。控制器不应该过多的介入业务逻辑处理

一个典型的Index控制器类如下:控制器就是一个类,类里的不同的方法就是不同的操作

namespace app\index\controller;
use think\Request;

class Index 
{
    public function index()
    {
        return 'hello,thinkphp!';
    }
    public function test(){
        $name = Request::instance()->param('name');
        echo "My name is " . $name;
    }
}

操作方法可以不使用任何参数,如果定义了一个非可选参数,则该参数必须通过用户请求传入,如果是URL请求,则通常是$_GET或者$_POST方式传入。

  • 如上代码第一行是确定了命名空间。什么是命名空间?

在PHP中,命名空间用来解决在编写类库或应用程序时创建可重用的代码如类或函数时碰到的两类问题。

  1. 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突
  2. 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。

而TP5的思想是

ThinkPHP5采用命名空间方式定义和自动加载类库文件,有效的解决了多模块和Composer类库之间的命名空间冲突问题,并且实现了更加高效的类库自动加载机制。

  • 如上代码第二行use就是加载类库,调用TP5写好的Request类,其中实现了一些请求方法。

在这里插入图片描述

相对应的就是上面代码11行的Request::instance()->param('name');即获取客户端传入的参数name

1.2 MVC模式流程

通常一个基于TP开发的CMS,可能有很多个模块,而每个模块都是控制器—模型—视图完整的。控制器根据要求选择调用模型,模型进行业务逻辑处理,交给视图渲染。这个流程。

一个模块下面有多个控制器负责响应请求,而每个控制器其实就是一个独立的控制器类控制器主要负责请求的接收,并调用相关的模型处理,并最终通过视图输出。严格来说,控制器不应该过多的介入业务逻辑处理。

模型类通常完成实际的业务逻辑和数据封装,并返回和格式无关的数据

控制器调用模型类后返回的数据通过视图组装成不同格式的输出。视图根据不同的需求,来决定调用模板引擎进行内容解析后输出还是直接输出。

1.3 类库自动加载

命名空间的路径与类库文件的目录一致,那么就可以实现类的自动加载

如果想调用不同命名空间下的类库的方法,就要先用use给加载上。

1.4 URL访问检测

应用初始化完成后,就会进行URL的访问检测,包括PATH_INFO检测和URL后缀检测。

5.0的URL访问必须是PATH_INFO方式(包括兼容方式)的URL地址,例如:

http://serverName/index.php/index/index/hello/val/value

所以,如果你的环境只能支持普通方式的URL参数访问,那么必须使用

http://serverName/index.php?s=/index/index/hello&val=value

但是,这样也是行的:

http://serverName/index.php/index/index/hello/?val=value

我的环境可以用上面三种方式,都是默认配置。

1.5 路由模式

1.5.1 普通模式

‘url_route_on’ => false,

关闭路由,使用默认的PATH_INFO模式访问URL:如上面 1.4URL访问检测 的第一个
http://serverName/index.php/module/controller/action/param/value/…

1.5.2 混合模式

‘url_route_on’ => true,

‘url_route_must’=> false,

开启路由,也就是允许了自定义路由,同时对没有定义的路由使用默认的普通模式

1.5.4 强制路由

‘url_route_on’ => true,

‘url_route_must’=> true,

这种方式下面必须严格给每一个访问地址定义路由规则(包括首页),否则将抛出异常。

首页的路由规则采用/定义即可,例如下面把网站首页路由输出Hello,world!

Route::get('/',function(){
    return 'Hello,world!';
});

1.6 路由定义

ThinkPHP5 中支持 5种 路由地址方式定义:

定义方式定义格式
方式1:路由到模块/控制器‘[模块/控制器/操作]?额外参数1=值1&额外参数2=值2…’
方式2:路由到重定向地址‘外部地址’(默认301重定向) 或者 [‘外部地址’,‘重定向代码’]
方式3:路由到控制器的方法‘@[模块/控制器/]操作’
方式4:路由到类的方法‘\完整的命名空间类::静态方法’ 或者 ‘\完整的命名空间类@动态方法’
方式5:路由到闭包函数闭包函数定义(支持参数传入)

1.6.1方式 1:路由到模块/控制器

// 路由到默认或者绑定模块
'blog/:id'=>'blog/read', 				# 意思是如果访问blog/5,就会调用blog/read并传入参数id=5,下面同理
// 路由到index模块
'blog/:id'=>'index/blog/read',
----------------------------------------------
namespace app\index\controller;
class Blog {
    public function read($id){
        return 'read:'.$id;
    }
}

还支持多级控制器:

//多级控制器方式
'blog/:id'=>'index/group.blog/read'
----------------------------------------------
namespace app\index\controller\group;
class Blog {
    public function read($id){
        return 'read:'.$id;
    }
}

1.6.2 方式2:路由到重定向地址

举个例子,如果我们希望avatar/123重定向到/member/avatar/id/123_small的话,只能使用:

'avatar/:id'=>'/member/avatar/id/:id_small'

# 区别于 'avatar/:id'=>'avatar/read' ,这个不是采用301重定向的。重定向的外部地址必须以“/”或者http开头的地址。

路由地址采用重定向地址的话,如果要引用动态变量,直接使用动态变量即可。

采用重定向到外部地址通常对网站改版后的URL迁移过程非常有用,例如:

'blog/:id'=>'http://blog.thinkphp.cn/read/:id'

表示当前网站(可能是http://thinkphp.cn )的 blog/123地址会直接重定向到 http://blog.thinkphp.cn/read/123。

1.6.3 方式3:路由到控制器的方法

'blog/:id'=>'@index/blog/read',
-------------------------------------
会执行 Loader::action('index/blog/read');
相当于直接调用 \app\index\controller\blog类的read方法。
namespace app\index\controller;
class Blog {
    public function read($id){
        return 'read:'.$id;
    }
}

1.6.4 方式4:路由到类的方法

'blog/:id'=>'\app\index\service\Blog@read'(动态方法)
或
'blog/:id'=>'\app\index\service\Blog::read',(静态方法)

执行的是 \app\index\service\Blog类的read方法。

1.7 路由使用

application目录是这个应用的目录,index目录是这个应用的一个模块,其内的controller目录存放的是这个模块的控制器,同理一般modle目录就存放的模型,view目录就放的视图。(这里是单纯的框架所以没有)

在这里插入图片描述

#index.php
<?php
namespace app\index\controller;
use think\Request;
class Index
{
    public function index()
    {
        echo "hahaha";
    }
    public function test(){
        $name = Request::instance()->param('name');
        echo "My name is " . $name;
    }
}

1.8 路由的其他知识

// 使用注解路由
'route_annotation'       => true,

class Index
{
    /**
     * @param  string  $name 数据名称
     * @return mixed
     * @route('hello/:name','get')
     */
	public function hello($name)
    {
    	return 'hello,'.$name;
    }
}

其余的直接看这里(TP5.1的)就好

1.9 其他东西

tarit关键字关于这个东西,看PHP手册就行,我的目录下有CHM格式的手册;

'default_return_type'=>'json'修改config.php下的这个配置,可以使输出自动进行数据转换处理。Response类会统一处理。

2.请求相关

2.1 如何获取请求参数的?

首先要use一下TP写好的Request类,然后调用的话可以用很多种写法,下面是一种

<?php
namespace app\index\controller;
use think\Request;

class Index 		// class Index extends Controller 解释如下:
//如果你继承了系统的控制器基类think\Controller的话,系统已经自动完成了请求对象的构造方法注入了,你可以直接使用$this->request属性调用当前的请求对象。
{
    /**
     * @var \think\Request Request实例
     */
    protected $request;
    /**
     * 构造方法
     * @param Request $request Request对象
     * @access public
     */
    public function __construct(Request $request)
    {
		$this->request = $request;
    }
    public function index()
    {
		return $this->request->param('name');
    }    
}

还可以这些样子:

Request::instance=>param();//获取所有参数[ 结果类型数组],不分请求类型;
Request::instance=>param('name');//获取单个参数[即:直接填写变量名即可];
Request::instance=>get();//获取?后面的参数;
Request::instance=>route();//获取路由里面的参数; 
Request::instance=>post();//获取post请求参数
eg:
public function hello()
{
   $res=Request::instance()->param();
   var_dump($res);
}
//这是依赖注入方式,无论是否继承系统的控制器基类,都可以使用操作方法注入。
public function hello(Request $request)
{
   $res=$request->param();
   var_dump($res);
}
//也可以使用助手函数
$request = request();

2.2 检测变量是否设置

可以使用has方法来检测一个变量参数是否设置,如下:

Request::instance()->has('id','get');
Request::instance()->has('name','post');

或者使用助手函数(助手函数很棒很简单)

input('?get.id');
input('?post.name');

2.3 请求方法

可以通过Request对象完成全局输入变量的检测、获取和安全过滤,支持包括$_GET$_POST$_REQUEST$_SERVER$_SESSION$_COOKIE$_ENV等系统变量,以及文件上传信息。

方法描述
param获取当前请求的变量
get获取 $_GET 变量
post获取 $_POST 变量
put获取 PUT 变量
delete获取 DELETE 变量
session获取 $_SESSION 变量
cookie获取 $_COOKIE 变量
request获取 $_REQUEST 变量
server获取 $_SERVER 变量
env获取 $_ENV 变量
route获取 路由(包括PATHINFO) 变量
file获取 $_FILES 变量

也可以在获取变量的时候添加过滤方法,例如:

Request::instance()->get('name','','htmlspecialchars'); // 获取get变量 并用htmlspecialchars函数过滤
Request::instance()->param('username','','strip_tags'); // 获取param变量 并用strip_tags函数过滤
Request::instance()->post('name','','org\Filter::safeHtml'); // 获取post变量 并用org\Filter类的safeHtml方法过滤
//可以支持传入多个过滤规则,例如:
Request::instance()->param('username','','strip_tags,strtolower');// 获取param变量 并依次调用strip_tags、strtolower函数过滤

2.4 【▲】请求方法伪装

支持请求类型伪装,可以在POST表单里面提交_method变量,传入需要伪装的请求类型,例如:

<form method="post" action="">
    <input type="text" name="name" value="Hello">
    <input type="hidden" name="_method" value="PUT" >
    <input type="submit" value="提交">
</form>

提交后的请求类型会被系统识别为PUT请求。你可以设置为任何合法的请求类型,包括GETPOSTPUTDELETE等。如果你需要改变伪装请求的变量名,可以修改应用配置文件:

// 表单请求类型伪装变量
'var_method'             => '_m',

2.5 伪静态配置效果

'url_html_suffix' => 'shtml'

http://serverName/Home/Blog/read/id/1
等价于
http://serverName/Home/Blog/read/id/1.shtml
如果'url_html_suffix' => ''
则
任何后缀都能正常访问

// 关闭伪静态后缀访问
'url_html_suffix' => false,
http://serverName/index/blog/read/id/3.html
id参数的值会被解析为3.html

2.6 参数绑定

就是给控制器下的方法(函数),给上参数,这样在url访问的时候就直接加上这个参数再给个值就能自动获取,不需要写Request。

在这里插入图片描述

2.7 请求缓存

例子:如果设置了这样一条路由,其中指定了cache字段。

Route::get('bind/:id','Index/bind',['cache'=>3600]);

那么只有第一次访问时会走正常的C-M-V流程,也就是会真正去调用控制器下的操作方法。

之后再访问同样路由的话,检测到同样的路由就不会再去调用控制器下的操作方法了,而是直接从缓存中获取响应。很给力。

3. 数据库

相信跟完那几个SQLi漏洞的代码分析,就已经对TP种数据库操作有些了解了,故略。

4. 模板

4.1 变量输出

// index.php 控制器
use think\Controller;
use think\View;
class Index extends Controller
{
    public function index()
    {
        $view = new View();
        $view->name = 'thinkphp';
        return $view->fetch();
    }
}

# index.html
<html>
    Hello,{$name}</html>

可以大致理解为:访问index方法,fetch渲染时会把name变量给传到index.html,而模板引擎解析的{$name}实际上就是<?php echo($name);?>

注意模板标签的{$之间不能有任何的空格,否则标签无效。

输出数组变量的value用{$array.key}

输出对象属性value用{$obj:property}或者${obj->property}

输出其他变量(比如系统变量)以KaTeX parse error: Expected '}', got 'EOF' at end of input: Think开头,比如`{Think.cookie.name}`

4.2 使用函数、默认值

{$name|md5|strtoupper|substr=0,3}
# 解释:多个函数之间用“|”分割

编译后即为:
<?php echo (substr(strtoupper(md5($name)),0,3)); ?>
    
# 还可以简单的写法
{:substr(strtoupper(md5($name)),0,3)}

# 加个默认值
{$user.nickname|default="这家伙很懒,什么也没留下"}

4.3 TP内置标签

内置标签还是全一点把,遇到忘了的直接去开发手册查

PS:这儿有一个简单的MVC代码案例

5. 日志和错误

6. 杂项

6.1 缓存

支持的缓存类型包括file、memcache、wincache、sqlite、redis和xcache。

驱动方式就是指什么形式存 缓存的数据

如果定义了多个缓存驱动:

// 切换到file操作
Cache::store('file')->set('name','value');
Cache::get('name');
// 切换到redis操作
Cache::store('redis')->set('name','value');
Cache::get('name');

在TP类库里面thinkphp\library\think\db\Query.php这个数据库查询类调用Cache比较多。

Session 、Cookie 和 缓存差不多。

6.2 上传规则

默认情况下,会在上传目录下面生成以当前日期为子目录,以微秒时间的md5编码为文件名的文件。

/upload/20160510/42a79759f284b767dfcb2a0197904287.jpg

而且一般都在public/upload/目录下。

还有可能以32位哈希的前两位当子目录。

上传时可以用 f i l e − > r u l e ( ) 自定义文件名生成的规则,如 ‘ file->rule()自定义文件名生成的规则,如` file>rule()自定义文件名生成的规则,如file->rule(‘md5’)->move(‘/home/www/upload/’);`

File类的成员函数move()执行成功后文件成功上传,返回一个File类的对象,失败返回false。可以看看File类都有哪些成员,毕竟是文件相关的类,很有用。

File类继承了PHP的SplFileObject类,因此可以调用SplFileObject类所有的属性和方法。SplFileObject手册解释

Controller

大多是一些模板渲染的方法。从构造函数中就可以看出来,

public function __construct(Request $request = null)
    {
        $this->view    = View::instance(Config::get('template'), Config::get('view_replace_str'));
        $this->request = is_null($request) ? Request::instance() : $request;

        // 控制器初始化
        $this->_initialize();

        // 前置操作方法
        if ($this->beforeActionList) {
            foreach ($this->beforeActionList as $method => $options) {
                is_numeric($method) ?
                $this->beforeAction($options) :
                $this->beforeAction($method, $options);
            }
        }
    }

刚开始就实例化了View类。想到之前复现的ThinkPHP的SQL注入漏洞,有些测试代码就会调用fetch、assign、display这些方法。而模块开发的开始就是从 application\模块\controller 下面开始的。应用模块的控制器如果继承了Controller类,就能直接调用这些方法进行视图渲染。

这个类还有两个方法:beforeAction和validate

beforeAction是前置操作:设置 beforeActionList属性可以指定某个方法为其他方法的前置操作,数组键名为需要调用的前置方法名,无值的话为当前控制器下所有方法的前置方法。

['except' => '方法名,方法名']
#表示这些方法不使用前置方法,
['only' => '方法名,方法名']
#表示只有这些方法使用前置方法。

# demo
protected $beforeActionList = [
        'first',
        'second' =>  ['except'=>'hello'],
        'three'  =>  ['only'=>'hello,data'],
    ];

# 类内所有方法调用前都会调用first方法
# 类内hello方法不会调用second这个前置方法
# 只有hello,data方法会前置调用three方法。

如果你在自己继承了Controller的控制器代码中定义了protected $beforeActionList,那么它就会在构造函数中foreach处理后,调用beforeAction进行only还是except的判断,最后用call_user_func调用。

call_user_func([$this, $method])

这种写法是对类内方法的调用,这里的$this指向的是继承Controller类的Index类。也就是说回调的函数必须在Index类中定义好了。

validate验证器方法主要还是调用的Validate类,应该是为了方便控制器对数据的验证。

具体流程就是用 加载验证器 的 加载器,来加载验证器。。。。。

如果传入的KaTeX parse error: Undefined control sequence: \library at position 25: …个数组,就先用thinkphp\̲l̲i̲b̲r̲a̲r̲y̲\think\Validate…this里面。不是数组的话就直接调用。

Request(5.0.18版本)

前置PHP知识:PHP中self :: 和 this-> 的用法

前置PHP知识:new static 和 new self的区别 -主要就是有继承的时候的区别

下面只列举部分重要方法

初始化和构造函数:instance和__construct

public static function instance($options = [])
    {
        if (is_null(self::$instance)) {
            self::$instance = new static($options);
        }
        return self::$instance;
    }

protected function __construct($options = [])
    {
        foreach ($options as $name => $item) {
            if (property_exists($this, $name)) {		//类属性覆盖
                $this->$name = $item;
            }
        }
        if (is_null($this->filter)) {
            $this->filter = Config::get('default_filter');		//在应用目录下的config.php
        }
        // 保存 php://input
        $this->input = file_get_contents('php://input');
    }

instance就是Request的初始化方法,编程中这种是叫“单例模式”,目的是为了防止实例化的时候构造方法被多次调用,就先new了一下自己然后返回。

property_exists判断 t h i s ,也就是本类对象是否存在传入的 this,也就是本类对象是否存在传入的 this,也就是本类对象是否存在传入的name属性,存在即赋值。接着还定义了过滤器,并获取了输入流(请求body)

这样初始化调用:$req = Request::instance()->get('a');

方法注入:hook和__call

public static function hook($method, $callback = null)
    {
        if (is_array($method)) {
            self::$hook = array_merge(self::$hook, $method);
        } else {
            self::$hook[$method] = $callback;
        }
    }

public function __call($method, $args)
    {
        if (array_key_exists($method, self::$hook)) {
            array_unshift($args, $this);
            return call_user_func_array(self::$hook[$method], $args);
        } else {
            throw new Exception('method not exists:' . __CLASS__ . '->' . $method);
        }
    }

hook方法和__call魔术方法配合,使得可以在类外面自定义Request的方法。算是扩展性的实现。hook主要是来获取自定义函数的,然后因为这个函数本身不在Request中,所以会调用__call方法,检查这个函数是不是在hook数组里面,在的话就说明这是一个要注入的方法,就将Request类本身的$this对象作为自定义函数的参数传入,通过call_user_func_array进行调用。自定义函数使用闭包的写法:

# application\index\controller\Index.php
public function index()
    {
        $req = Request::instance();
        $callback = function($RR){
            echo "匿名函数";
            $RR->test111();
        };
        Request::hook('invoke',$callback);
        $req->invoke();
    }
# thinkphp\library\think\Request.php添加一个如下方法:
public function test111()
    {
        echo "可以调用Request本身的方法";
    }

# 访问index.php/index/输出:匿名函数可以调用Request本身的方法

当前的请求类型:method

public function method($method = false)
    {
        if (true === $method) {
            // 获取原始请求类型
            return IS_CLI ? 'GET' : (isset($this->server['REQUEST_METHOD']) ? $this->server['REQUEST_METHOD'] : $_SERVER['REQUEST_METHOD']);
        } elseif (!$this->method) {
            if (isset($_POST[Config::get('var_method')])) {
                $this->method = strtoupper($_POST[Config::get('var_method')]);
                $this->{$this->method}($_POST);
            } elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
                $this->method = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']);
            } else {
                $this->method = IS_CLI ? 'GET' : (isset($this->server['REQUEST_METHOD']) ? $this->server['REQUEST_METHOD'] : $_SERVER['REQUEST_METHOD']);
            }
        }
        return $this->method;
    }

IS_CLI是用来判断是使用命令行还是浏览器执行的,cli代表命令行。

t h i s − > s e r v e r 和 this->server和 this>server_SERVER的区别应该是:前者提供了程序可定义的请求方法(在create方法中),后者是获取客户端请求的 请求方法。

如果调用method时没传入true,分三种情况:

① 如果配置了var_method,从$_POST[Config::get('var_method')],即$_POST['_method']获取请求方法。

在这里插入图片描述

有意思的是这条语句$this->{$this->method}($_POST);,用花括号包裹 先执行取值在以 t h i s 调用传入 this调用传入 this调用传入_POST。

在这里插入图片描述

② 通过请求包的HTTP头字段X-HTTP-METHOD-OVERRIDE来设置method

在这里插入图片描述

③ 和true === $method时一样

获取当前请求的参数:param

该方法主体中是对请求参数的处理:包括针对不同的请求方法,选择调用Request类里不同的处理方法;还有获取文件上传信息的处理。获取post、get、put、file等请求参数的合并数据。但这些处理都不是方法内实现的,而是调用的Request类的其他成员方法。

获取GET数据:get

支持传入数组:

t h i s − > g e t = a r r a y m e r g e ( this->get = array_merge( this>get=arraymerge(this->get, $name);

获取POST数据:post

public function post($name = '', $default = null, $filter = '')
    {
        if (empty($this->post)) {
            $content = $this->input;
            if (empty($_POST) && false !== strpos($this->contentType(), 'application/json')) {
                $this->post = (array) json_decode($content, true);
            } else {
                $this->post = $_POST;
            }
        }
        if (is_array($name)) {
            $this->param       = [];
            return $this->post = array_merge($this->post, $name);
        }
        return $this->input($this->post, $name, $default, $filter);
    }

比get特别的地方在于,会从php://input输入流中获取数据,这里的$content = $this->input;是在构造函数中初始化的。然后还能用json_decode处理json数据并返回为数组格式的。

获取cookie参数:cookie

会调用filterValue方法过滤数据。而且如果cookie是个数组的话还会使用array_walk_recursive递归调用filterValue

获取上传的文件信息:file

会实例化File类,@return null|array|\think\File 返回这三种类型的数据。具体获取文件的什么信息再看。

被多处调用的方法:input

该方法有一个参数是$filter默认为空,说明支持可设置的过滤器。

处理流程在ThinkPHP5之SQLI审计分析(一)里面看过了。

递归过滤方法:filterValue

private function filterValue(&$value, $key, $filters)
    {
        $default = array_pop($filters);
        foreach ($filters as $filter) {
            if (is_callable($filter)) {
                // 调用函数或者方法过滤
                $value = call_user_func($filter, $value);
            } elseif (is_scalar($value)) {
                if (false !== strpos($filter, '/')) {
                    // 正则过滤
                    if (!preg_match($filter, $value)) {
                        // 匹配不成功返回默认值
                        $value = $default;
                        break;
                    }
                } elseif (!empty($filter)) {
                    // filter函数不存在时, 则使用filter_var进行过滤
                    // filter为非整形值时, 调用filter_id取得过滤id
                    $value = filter_var($value, is_int($filter) ? $filter : filter_id($filter));
                    if (false === $value) {
                        $value = $default;
                        break;
                    }
                }
            }
        }
        return $this->filterExp($value);
    }

$filters是数组类型的,说明可以进行多个过滤器的处理。在判断了过滤器可以被调用后,就马上用call_user_func来调用过滤器处理数据了。

方法开头会用array_pop弹出默认过滤器,是在getFilter方法将所有接收到的过滤器放到数组里后,又在数组末尾加的那个$default


  1. 利用method的任意方法调用,调用构造函数__construct,且调用时会传入$_POST数据,那么组合起来就是执行method方法可以控制类的成员变量的值。

  2. filterValue中存在 call_user_func函数可以恶意利用,其参数$filters

  3. getFilter方法存在这样$filter = $filter ?: $this->filter;一条语句获取类成员变量filter(由1知可控)。

  4. input方法中存在:先调用getFilter获取到filter,再使用array_walk_recursive递归调用filterValue并传入filter的情况。

综上,如果找到一处先调用method再调用input且的调用点,就能够利用call_user_func造成RCE。

param方法就完美符合条件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/137925.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构与算法学习——栈结构

在程序设计中&#xff0c;一定接触过“堆栈”的概念。其实&#xff0c;“栈 ” 和 “堆 ” 是两个不同的概念。这里&#xff0c;栈是一种特殊的数据结构&#xff0c;在中断处理特别是重要数据的现场保护有着重要意义。 什么是栈结构 从数据的逻辑结构来看&#xff0c;栈结构其…

59. 微调(fine-tuning)代码实现

1. 热狗识别 让我们通过具体案例演示微调&#xff1a;热狗识别。 我们将在一个小型数据集上微调ResNet模型。该模型已在ImageNet数据集上进行了预训练。 这个小型数据集包含数千张包含热狗和不包含热狗的图像&#xff0c;我们将使用微调模型来识别图像中是否包含热狗。 %matp…

专访中银金科:数字驱动成为新的增长引擎,未来业务转化是关键

大数据和信息科技正在逐步颠覆银行业过往的业务模式。建立以数据驱动为核心&#xff0c;以优化客户体验为目标的可持续营销理念&#xff0c;逐渐成为行业的共识。但是&#xff0c;伴随着银行业数字化转型进程加速发展&#xff0c;海量客户数据和低效营销之间的矛盾日益凸显。在…

Linux apt 命令

apt&#xff08;Advanced Packaging Tool&#xff09;是一个在 Debian 和 Ubuntu 中的 Shell 前端软件包管理器。 apt 命令提供了查找、安装、升级、删除某一个、一组甚至全部软件包的命令&#xff0c;而且命令简洁而又好记。 apt 命令执行需要超级管理员权限(root)。 apt 语…

23.2、Junit单元测试反射注解

Java代码执行的三个阶段 Junit单元测试&#xff1a; * 测试分类&#xff1a; 1. 黑盒测试&#xff1a;不需要写代码&#xff0c;给输入值&#xff0c;看程序是否能够输出期望的值。 2. 白盒测试&#xff1a;需要写代码的。关注程序具体的执行流程。 * Junit使用&#…

洛谷千题详解 | P1030 [NOIP2001 普及组] 求先序排列【C/C++、pascal语言】

博主主页&#xff1a;Yu仙笙 专栏地址&#xff1a;洛谷千题详解 目录 题目描述 输入格式 输出格式 输入输出样例 解析&#xff1a; C源码&#xff1a; C源码2&#xff1a; pascal源码&#xff1a; C源码&#xff1a; --------------------------------------------------------…

P4Pi AP转wifi模式

调试时间&#xff1a;2022.11.07 树莓派在安装P4Pi后&#xff0c;会自动设置为AP热点模式。本文档通过配置将树莓派系统从ap模式转变为wifi模式。 1 调试环境 Raspberry 4B 4GB-SDcard 32GB Raspberry Pi Imager v1.7.3 Raspberry Pi OS – Raspberry PiFrom industries lar…

值得信赖的数据同步备份软件 -Allway Sync 安全又可靠,简单又易用!

Allway Sync 是一款可靠的数据同步备份工具&#xff0c;最初的版本发布于 2004 年 4 月 19 号&#xff0c;距离今日大约有 19 年的更新历史了&#xff0c;足以说明软件绝对稳定&#xff0c;时间验证了软件的可靠性&#xff01;而对于我们用户来说&#xff0c;数据同步备份最重要…

基于线性表的查找

目录 一、查找的基本概念 二、顺序查找 关键代码 完整代码 运行结果 增加哨兵 三、二分查找&#xff08;折半查找&#xff09; 关键代码 完整代码 运行结果 四、分块查找 图示 关键代码 完整代码 一、查找的基本概念 对查找表进行的操作 1.查找某个特定的数据元素是否存在 …

攻防世界-fakebook

题目 访问题目场景 我自己尝试了很久&#xff0c;发现怎么都找不到这道题的入手点&#xff0c;然后就去看了大佬们的文章&#xff0c;然后我发现这道题更趋近于真实的场景 解题过程 先使用目录扫描器扫一下发现存在robots.txt访问一下 这里发现存在一个备份文件 <?php…

html、css、js的小米商城

首页的展示 首页的功能 1、搜索栏模糊查询 在我在输入框输入关键字的时候&#xff0c;会匹配关键字&#xff0c;如果我的存放的数据里面包含这些关机键字就会显示出来。做到模糊查询的效果。 2、实现搜索功能 在首页的搜索框点击搜索的时候&#xff0c;就会对你输入的关键字进…

Redis 未授权访问的原理、危害及复现

原理介绍 Redis 未授权访问 准确的来说&#xff0c;其实并不是一个漏洞。而是由于开发人员配置不当&#xff0c;而产生的预料之外的危害。 具体原理&#xff1a; 可能由于部分业务要求&#xff0c;或者开发人员的配置不当&#xff0c;将 redis 服务器的 ip 和 port 暴露在公网…

基础数学(7)——常微分方程数值解法

文章目录期末考核方式基础知识解析解&#xff08;公式法&#xff09;解析解例题&#xff08;使用公式法&#xff0c;必考&#xff09;解析解的局限性数值解数值解的基本流程显示Euler法显示欧拉&#xff08;差值理解&#xff09;显示欧拉&#xff08;Taylor展开理解&#xff09…

ClickHouse表引擎详解看这篇就够了-基本讲解、处理逻辑、测试实例

表引擎是ClickHouse设计实现中的一大特色。表引擎在 ClickHouse 中的作用十分关键&#xff0c;直接决定了数据如何存储和读取、是否支持并发读写、是否支持 index、支持的 query 种类、是否支持主备复制等。1、表引擎概述1.1 介绍ClickHouse 提供了大约 28 种表引擎&#xff0c…

ArcGIS基础实验操作100例--实验43填充面要素空洞

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验43 填充面要素空洞 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;1&a…

JavaScript 条件语句

文章目录JavaScript If...Else 语句条件语句If 语句If...else 语句If...else if...else 语句JavaScript If…Else 语句 条件语句用于基于不同的条件来执行不同的动作。 条件语句 通常在写代码时&#xff0c;您总是需要为不同的决定来执行不同的动作。您可以在代码中使用条件语…

【学习笔记】Shell入门

Shell入门 https://www.bilibili.com/video/BV1WY4y1H7d3 资料&#xff1a;评论区取的 公众号的资料链接 https://pan.baidu.com/s/1_nBKUjE57MB2c96wmfSD5A 提取码&#xff1a;yyds 文章目录一、**Shell** 概述二、**Shell** 脚本入门三、变量1.系统预定义变量2.自定义变量**3…

自学软件测试该如何入门?

互联网行业发展很快技术更新也很快&#xff0c;软件测试技能要求在逐渐提高&#xff0c;自学软件测试要尽快而且入行后需要持续学习。保持好心态&#xff0c;找准教程&#xff0c;按照学习路线和自己的规划一步步学习下去~ 软件测试对代码的要求不像其他编程学科那么高&#x…

30个精品Python练手项目

随着 Python 语言的流行&#xff0c;越来越多的人加入到了 Python 的大家庭中。到底为什么这么多人学 Python &#xff1f;我要喊出那句话了&#xff1a;“人生苦短&#xff0c;我用 Python&#xff01;”&#xff0c;正是因为语法简单、容易学习&#xff0c;所以 Python 深受大…

Java微服务连接云服务器上的ZooKeeper

前言 这次要讲的连接ZooKeeper是在外网的云服务器上&#xff0c;不同于以往的本机上的虚拟机上的ZooKeeper&#xff0c;将会有一些不同于本机的连接方式。连接外网服务器进行操作可以更好的适应企业化的开发&#xff0c;脱离了本机的限制&#xff0c;具有很强的实战意义。 前…