ThinkPHP5.0完整请求简述

目录

ThinkPHP5.0完整请求简述

本文会对ThinkPHP5.0的一次完整请求(其实也不完整)进行记录,主要是在访问/index/index进行记录。

载入文件及配置

base.php

index.php 定义应用目录,然后包含/../thinkphp/start.php文件

start.php会载入base.php文件,在base.php中会定义很多的环境变量:

Loader::register()

创建完环境变量后,就会载入\think\Loader::register()

之后加载根目录下的环境变量(如果有的话)

然后注册自动加载

register()会自动调用spl_autoload_register()

并注册最基本的三个命名空间

注册命名空间的是使用PSR-4,它用来自动加载命名空间和文件系统

PSR-4的详情可以看文末的文文章链接

之后是加载类库映射文件,通常没有,但是下面的Composer自动加载支持才是重要的

registerComposerLoader()先是载入三个常规的文件:

这三个文件也是文件包文件,每个文件内的文件都会被循环注册命名空间

载入第四个文件autoload_files的时候会有一点不同

上面的__require_file()就是require,只是被封装了一下

autoload_files()里面的文件有:

其中第二个是验证码模块,这个模块在载入的时候会自动注册一个路由和一个验证模块:

在调用Route和Validate的时候就会触发autoload函数从而自动寻找并载入

这是因为之前被spl_autoload_register()注册了

spl_autoload_register()注册的命名空间的类被调用的时候,会自动调用Loader中的autoload()函数,从而实现自动include操作,非常高级。

当然之后也有很多地方触发这个autoload()函数

到此Loader::register()函数就执行完毕了

错误异常处理及惯例配置

回到上面的base.php文件之后就开始是注册一个错误的对象:\think\Error::register();

当然,在注册之前需要走一遍autoload流程从而载入Error对象

然后register()对象注册:

注意,注释中1开头表示笔者添加的注释,非官方的

注册之后就是base.php的最后 一行指令,载入惯例配置convention.php

然后调用set函数将所有配置都载入到Config中的$config数组中

到此第一部分—文件注册和配置载入就完成了

进行App::run()操作

首先就是调用\think\App类进行例行的autoload操作:

最终定位到文件\thinkphp\library\think\App.php文件下,然后include这个文件

接着就是进入run函数,也就是最主要的操作

构造Request

进入函数第一步是初始化一个request函数,如果传入了ruquest函数就不用初始化了

传入之前还是一段autoload()函数操作

最后定位到\thinkphp\library\think\Request.php文件中,然后include

紧接着就是直接调用instance函数

如果当前的$instance不为空,就静态绑定一个$instance然后返回这个$instance

这个地方意思就是说,第一次需要创建request的时候就会很正常的创建,但是之后如果还需要Request的时候就不需要创建新的,直接返回当前Request类型,该对象被存储在$instance

**__construct(): **​

这里面显示对对象属性进行判断,确保传入的属性可以被赋值,然后循环赋值参数,接着判断$filter参数是否有值,若无则调用配置文件中的默认设置

调试的时候发现,默认设置里啥也没有(无效配置)

initCommon()初始化

之后是一段try代码

首先是对自身进行初始化设置

initCommon()

显示判断是否需要初始化,不需要就直接return,这次调试中,是需要进行配置的

之后是申请app命名空间到application目录下

接着$init()初始化,并把配置文件保存到$config

init()中首先定位模块目录,默认没有,就是空

然后到APP目录下即application目录下寻找并包含进来(当然,这个文件是没有的,所以就直接跳过)

接着是加载配置文件,也是区aplication目录下看有没有config.php文件,然后load()

load()的时候先判断是否是文件,是文件的时候,就把config.php 传入到Config::set()函数中

set()里一判断是是数组,拼接在一起,结束。

书会init()函数中

之后是就是两个配置文件载入,一个是数据库载入,一个是额外拓展载入

额外载入时application\extra文件夹下的所有文件

之后载入引用状态配置,默认没有,就直接跳过

后面载入行为拓展,会把application目录下的tags.php文件传给Hook对象的import()方法(当然,首次使用Hook是需要autoload()

Hook::import()

默认循环add每个参数

Hook::add()

add函数也很简单,判断了下$behavier然后unset一下接着就是拼接了

循环完之后import()也就完成了

之后就直接include common.php文件,由于$module是空的,所以语言载入也就跳过了

最后返回配置文件

Config::get()没有参数默认返回所有配置

书会initCommon()函数,进过init()函数的一顿载入后,现在我们几乎把所有的配置文件都保存起来了

之后开始配置debug模式

当然配置前,又是一顿autoload()载入Env类

这个get可以看出来,是通过传入的app初始配置和环境中的debug来判断是否需要开启debug

之后就是就是设置php.ini文件,然后开启debug模式

然后判断是否需要大一点的buffer

并创建一个输出缓冲区

接着判断是否有root_namespace命名空间,没有就创建一个命名空间

下面是载入额外文件,默认是thinkphp\helper.php文件

这个helper.php是一堆判断,之前就执行过一遍:

后面是设置时区,很简单,重要的是这个Hook::listen()函数

Hook已经导入了,然后就直接传入参数开始监听,下面看看是如何监听的:

首先是创建一个results的空列表

接着是调用本地静态函数get()并传入tag,也就是之前传入的app_init,将得到的结果传入exec方法,并判断是否需要中断行为。

先看下这个get方法

很明显,get返回空,然后就导致后面直接触发break接着就完成listen

最后把$init设置为真,然后返回所有配置

到此initCommen()就结束了

语言及调度信息

现在我们继续回到App::run()函数中

请求从此开始逐渐进入高潮:模块/控制器处理阶段,

首先是一段绑定操作,原生thinkphp是没有这个绑定任何模块的,所以直接运行的时候就跳过了

详情可以看thinkphp模块设计

之后就是过滤函数filter()函数的调用 ,传入缺省过滤配置(也就是空)

Request::filter()函数也是非常简单,如果过滤为空就是获取默认过滤的配置(也是空)不然就修改本地的filter属性

简简单单处理完之后也是些常规操作—载入语言,不过这次Lang类是第一次调用的,所以会触发autoload函数,然后重载一下,最终定位到: \thinkphp\library\think\Lang.php位置

Lang::rang()也是非常简单,存在传参就设置到$range然后返回,不然就直接返回$range

默认语言就是中文

之后配置多语言,然后给Request设置语言

设置也是相当简单,直接就设置了,注意,人家返回的是Request对象

但是如果直接调用就返回当前语言集合,这个地方两个名字重名了,很坑。

接着加载语言包:

调用Lang::load()函数,返回的是当前语言集合(上一步刚设置完)

载入也是非常简单,没有太多设置的话,就是一个include的过程

看下接下来的三个操作:

先是监听app_dispath:但是这个监听也是同上面的监听,并没有实质的监听操作产生,具体情况可以查看关于钩子函数,回调函数的概念。

然后设置回调参数(null)

接着对未设置调度信息则进行 URL 路由检测

routeCheck()

进入后首先是调用传入的request请求的path()方法或者$path

由于Reequest的path是空的,于是进行配置,

首先是取出配置中的url_html_suffix, 默认就是html

之后是调用Request的pathinfo()方法,这个方法是用来处理传入的参数的,让我们看看他是如何工作的

第一步是取出pathinfo信息从配置文件中,并将该值赋值给$_SERVER['PATH_INFO'],然后从配置信息中删除该值,在is_clil模式下直接获得地址

接下来是分析path信息,但是我们的PATH_INFO是空的,于是就跳过了

直接返回就返回了/

后面我们传入参数了再来看这一步的详细信息。

书回path()方法

前面我们获取了path下详细信息,接下来就是进行详细处理

主要是对url后缀处理,不过我们没有参数,就不受影响,直接返回

书回路由检测功能中:

前面获取了路由的信息,接下来先判断是否需要进行路由,如果本地$routeCheck开启的,就检测,不然就去配置文件里查看是否需要路由检测,默认是开启的

首先是读取缓存runtime中的路由表文件:,如果不存在就载入配置文件中的路由信息

载入则是先读取路由文件名字,然后对每个文件依次读取

具体依次的载入时通过Route::import()实现的

首先打开后是一堆检查和设置,这些设置都是写在applocation下的route.php下,然后一下就载入完了。

接着继续routeCheck()方法

前面完成路由检测第一步,接下来则是通过路由信息寻找url调度

进入Route::check()方法前,我们看看此次传入的参数:新生成的request对象,路由地址(\),分界线(\),domain配置(false)

括号里是此次传入的参数

打开后是分隔符替换,把/换成了|

先是路由别名检测,这里没有,就直接跳过了

然后获得请求方法,获取路由规则,这里直接调用了method,所以直接返回GET方法

回到routeCheck()

url绑定么有,所以直接俄就返回false

默认配置中又没有静态路由,所以就直接跳过了

最后就是路由规则检测checkRoute()方法

对规则进行遍历,遍历时,先对规则进行赋值

后面是一个参数有效性检测的函数checkRoute()

这么多检测,但是直接就返回ture了

回到routeCheck()

后面一堆判断,都没通过,最近能用的是

这个CheckRule()方法

虽然代码多,但是大多数都直接跳过,最近能用的函数是

match函数用来匹配规则:

首先是把路由和请求分成两个部分

然后遍历规则,寻找匹配到的规则,然后返回规则字符,这里我之后helper注册了路由,所以匹配不到,就直接返回了

出了match后路由匹配就结束了,剩下的就直接返回了

之后checkRule()结束后,然后回到checkRoute()

之后路由检测也结束,直接返回,回到routeCheck()

进入最后一步,控制器匹配

进入Route::parsuUrl()方法:

进入parsuUrl()中,首先是判断是否有绑定,此次没有绑定,就直接跳过第一步

下一步是把url分隔符又换成了/

接着是对url进行修改为了更好的了解这个过程,我们修改请求为/index.php/Index/index ,修改后:

然后继续观察,由于没有绑定控制器,所以第一步跳过,后面是对信息的赋值,其中path的赋值调用了函数parseUrlPath(),进入查看:

这个函数是对url进行处理的,第一步是对参数处理,可以看到匹配?了匹配到后发送到$var

如果没有? 就对/进行匹配

这个地方的?不是传入的参数

然后匹配/,最后将控制器和操作分开然后返回

返回后直接进入匹配

后面这个\$autoSearch 默认就传入的false,所以直接跳过

接下来就进入到这个解析额外参数的操作中了

parseUrlParams()

这个函数进来后先是对config的参数进行获取

这个参数默认是false

但是url是空的,所以直接跳过,设置当前的请求参数

Request::instance()->route($var)

这个instance()函数之前分析过request的,然后就是route函数

最后返回input的返回结果

之后就是配置路由

最后返回$route结果

最后返回到routeCheck()

最后就是返回模式和模式内容

exec()

然后就回到了久违的App::run()中了

然后就是一些调度信息的记录

然后缓存检查也没有其他东西,就是直接返回

后面就是调用分发,这个exec就是执行操作,$data就是最后需要进过处理后需要发送给用户的东西,也就是说这就是控制器执行部分了。

exec():

protected static function exec($dispatch, $config)
    {
        switch ($dispatch['type']) {
            case 'redirect': // 重定向跳转
                $data = Response::create($dispatch['url'], 'redirect')
                    ->code($dispatch['status']);
                break;
            case 'module': // 模块/控制器/操作
                $data = self::module(
                    $dispatch['module'],
                    $config,
                    isset($dispatch['convert']) ? $dispatch['convert'] : null
                );
                break;
            case 'controller': // 执行控制器操作
                $vars = array_merge(Request::instance()->param(), $dispatch['var']);
                $data = Loader::action(
                    $dispatch['controller'],
                    $vars,
                    $config['url_controller_layer'],
                    $config['controller_suffix']
                );
                break;
            case 'method': // 回调方法
                $vars = array_merge(Request::instance()->param(), $dispatch['var']);
                $data = self::invokeMethod($dispatch['method'], $vars);
                break;
            case 'function': // 闭包
                $data = self::invokeFunction($dispatch['function']);
                break;
            case 'response': // Response 实例
                $data = $dispatch['response'];
                break;
            default:
                throw new \InvalidArgumentException('dispatch type not support');
        }

        return $data;
    }

进入后是一个switch的一个操作,进行选择,我们进入是进入模块/控制器部分的,

然后直接调用module模块:

进入后先获取request的实例

然后进行多模块部署,首先进行模块部分参数的初始化

初始绑定模块无,就直接进行模块初始化

但是这里有个需要注意的,就是这个elseif

当我们传入的控制器不存在这个文件夹的时候,$available就不更改,也就是默认是false,后面回传出错误

先通过module()方法设置当前模块名:index

如果前面的$available不可以,此处就直接返回404然后退出程序,我们此处index存在,所以不用管

然后用当前模块初始化配置,下面看一下具体配置流程:

首先就是设置模块文件位置(其实就是给index加了个\

后面就是加载初始化文件init.php, 模块文件config.php数据库配置文件database.php,以及扩展文件extar.php

这些文件的位置都在application/index下(是index模块),当然,这些文件都不存在,所以要么跳过,要么默认配置

包括后面这些都没有(语言包被定向到默认语言中文了)

最后就是返回所有配置文件,初始化结束,回到module()方法下

接着module()获取参数,然后是通过$controller方法将获取的控制器和操作赋值给request对象

然后启动module_init的监听事件

接着尝试执行控制器,对传入的参数进行分析:

    // 默认的访问控制器层
    'url_controller_layer'   => 'controller',
    // 控制器类后缀
    'controller_suffix'      => false,
    // 默认的空控制器名
    'empty_controller'       => 'Error',

知道参数后我们进行载入操作:

controller()方法十分简短,先是执行getModuleAndClass()来解析模块和类名

里面有一步

会注册这个命名空间,然后返回出来,

最后得到的就是app\index\controllor\Indexindex

后面进入class_exists()会唤起autoload()来载入这个类,也就是控制器类

然后通过invokeClass() 反射来实例化一个类,然后依赖注入

之后控制器就设置完成了,也成功实例化了,然后也重新返回到module()里面

后面则是对方法的执行

$instance就是实例化的控制器对象

如果能存在这个方法,就设置$call变量,不然就直接返回错误

然后还是监听操作的开始

最后通过invokeMethod()来执行操作

先是确定反射对象,然后是通过bindParams()方法绑定对象,

由于我们的方法是空的,所以自动获取请求变量

param()方法如下:

先是获取请求类型,然后将路由参数和请求参数合并

之后会返回内容,返回使用的是input()函数

这个函数也很有意思,已经碰到过多次了

他会将输入的字符内容取出(此处是进行过滤操作),然后进行过滤,其中这个过滤是支持过滤器的

这里有个过滤器很有意思,就是说将传入的两个参数用 call_user_func($filter, $value);是一个经典的代码执行点,当用户输入控制此处时,即可rec

然后再回到invokeMethod()位置,

debug模式,然后反射执行方法即Index/index操作

成功到达指定位置

接着直接返回到exec()位置

然后break;

然后后exec()结束,返回请求内容

然后回到run()方法中

接下来清空实例化类,它的任务完成了

清空操作也是十分的简单粗暴,直接交给内存回收器了

接下来看如何输出到客户端,这时候就需要Response类登场了

首先判断输出的东西是不是一个Response类,如果是的话就直接返回,进行下一步的链式操作;不空的话就调用isAjax()函数,并使用其结果和之前的返回创建返回类型,其他则创建一个空的回复。

这个参数会对传入的内容进行分类,就本次回复,人家的分类就是html

然后就是Response::create()

首先还是常规的autoload()函数来载入文件

载入后进入create()函数

上来就是对$type的一次初始化,由于直接存在内容,所以就们没有变化

后面是对$type的值进行变化,如果有只,就把他变成一个地址,不然就是空

$type被这么一处理就变成了一个路径:think\response\Html

然后后面class_exists直接触发autoload()函数,然后载入这个文件

载入后检查这个类,发现这个类不存在,然后创建它,执行else语句

接着就是创建一个新的对象

将传入的$data赋值给respond对象

然后将把其他只也一起赋值给相应的位置

最后返回这个Respond对象

然后在run里监听app_end

最后返回Respond对象

App::run()结束

进行respond→send()操作

首先进入send操作页面:

首先还是一个监听,暂时就直接跳过

之后是一个getContent()用来处理输出的数据

此方法很简单,content默认是null,最后会给content赋值,也就说明这是初始化操作

output默认就是直接输出

protected function output($data)
    {
        return $data;
    }

后面进行判断是否合法,接着就是赋值

后面的Trace调试注入默认是关的,也就直接跳过

后面获得缓存也直接无,就直接跳过

后面检测头部信息和状态码并发送

最后echo $data发送数据

然后就是监听发送结束

清空当此请求

不出意外,还是直接进行了一次autoload()

也是直接就结束了

因本人能力有限,若文章出现了错误,还请斧正,不吝赐教。

PSR-4 自动加载规范 - 说明文档 | 全部规范 |《PHP PSR 标准规范》| PHP 技术论坛 (learnku.com)

PHP: 后期静态绑定 - Manual

模块设计 · ThinkPHP5.1完全开发手册 · 看云 (kancloud.cn)

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,本站及文章作者不为此承担任何责任。

本站拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经本站允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇