问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

PHP 中的行为 与什么是切面

发布网友 发布时间:2022-04-18 01:03

我来回答

2个回答

懂视网 时间:2022-04-18 05:24

前言

如果不清楚框架是怎么执行的,那么看在多的代码都是只是认识代码而已,阅读源码是为了学习其框架的设计思想和代码模式。

而执行流程则是将我们学习的东西串联在一起,从而更好地理解。咔咔也会给大家把执行流程用思维导图的方式画出来。

只要大家在本文学习到一点点的知识点,咔咔也是心满意足的。

这个流程图只是针对initialize的执行过程,其余的执行过程后期会进行补充,都是以脑图的形式呈现给大家的。

1.png

执行流程图

一、框架执行流程之初始化应用的数据设置

这里的内容跟容器的内容有点重复,因为执行流程是从入口文件开始的,并且最后也是通过容器执行的。

2.png

入口文件

然后就会进入到文件thinkphp/library/think/App.php的run方法,在这个方法中主要就是下图框出来的地方,执行的initialize方法。

3.png

来到initialize这个方法,先看上半部分。

  • microtime(true);返回的是unix的微秒数
  • memory_get_usage返回的是分配给PHP的内存量,单位为字节
  • 在接下来就是对框架的几个路径进行设置
  • static::setInstance($this);这里是将app这个实例设置为容器实例
  • $this->instance('app', $this);这个在之前容器章节就提到了,就是为了把app这个类绑定到容器里边去,也就是注册树模式。

  • 4.png

    这里有一个小的问题点给大家提出来,在初始化应用的这个方法里边存在这样一行代码。

    有没有小伙伴对这个$this->env和下边的$this->config这俩个调用有疑惑。

    如果你有疑惑那就跟着咔咔一起来看,没疑惑的就可以继续往下看了。

    App这个类是继承的容器类,那么这个env和config不论是在app还是container类中都是没有这俩个属性的。

    那么怎么就可以直接调用呢!而且代码追踪都会追踪到env类和container类中。

    需要知道这个源头就需要我们去在大致的看一遍container类的代码。

    5.png

    解决疑惑,为什么可以这样使用

    经过一番苦读之后,可以看到下图的几行代码。这几行代码全部使用的是魔术方法。

    当访问env类不存在的时候就会去执行make方法。

    make这个方法在容器那一章节进行的细的不能再细的解读了。

    这个make方法最终会返回一个类的实例,并且还会存到容器里边。

    6.png

    这里只放一个make方法的代码,如果有不会的可以去看之前的文章。

    7.png

    最后就是加载一系列的数据,加载详情请看前言的思维导图。

    8.png

    执行加载

    二、如何查看一个方法都在哪里执行了

    在阅读源码的过程中,有一个很难把控的问题就是一个方法在不同的地方进行了调用,但是咱们确一时半会根本不知道都在哪里调用了。

    这里用init方法来做一个演示。

    init方法是初始化应用或者模块的一个方法,但是这里的module参数确实一个空值。

    9.png先做一个断点查看一下相关的数据信息。

    打印的结果就是空,这就是一些新学习的伙伴会犯的一个错误,因为这个方法不可能只调用一次的。

    如果初始化模块都是空那么这个方法就没有存在的必要了。

    10.png11.png那么正确的断点方式应该是这个样子的。

    12.png13.png此时就会有一个问题,这个init方法明显是被调用了俩次的,那么另一次调用的地方是在哪里呢!

    如果在不知道新的技巧之前,就会进行一系列的断点打印,看在哪里进行了执行,比如在这个init的上层去打印。

    也就是在initialize那个方法里边去打印做断点,但是这样很是麻烦的,而且很有可能浪费了大量的时间还是找不到正确的地方。

    小技巧之debug_backtrace()

    这个方法会产生一条回溯追踪,会显示出一个方法所有的调用位置。

    使用方式就是如下图,只需要把debug_backtrace这个方法打印出来即可。

    14.png15.png16.png

    根据得到的数据信息,就可以非常快的进行定位。

    第一次就是在app类的215行。

    18.png


    第一次调用init的地方

    第二次是在thinkphp/library/think/route/dispatch/Module.php类的60行

    18.png

    第二次调用地方

    可以在这里做一个打印,看一下这个module是否为index

    19.png20.png所以说有了这个方法就可以非常快速地定位调用位置。

    三、框架执行流程之初始化应用init分析

    上文给大家提供了一个小技巧debug_backtrace实战演示了如何查看一个方法都在哪里执行的。

    并且案例也是使用的init这个方法来演示的,因为接下来就是要对init这个方法进行深入的了解。

    在init方法里边主要做的事情在上边的脑图已经描述的很清楚了。

  • 从一开始就对模块的定位,就是在第二节中的对init方法的调用,会传入对应的模块
  • 加载app目录下的tags文件,在tags文件里边就是对行为扩展定义的文件。在之前门面的文章中定义钩子执行就在这个文件中设置的。
  • 加载common文件,也就是公共文件,所以说公共文件就是在这里进行加载的。
  • 加载助手函数文件helper,在助手函数里边有一个大家特别熟悉的一个方法,那就是dump。这就是为什么在有的地方使用dump会报错的原因。
  • 加载中间件文件,这里的直接给出的是直接加载app目录下的中间件文件,但是在框架中我们需要在定义一个目录为http,在这个目录下定义中间件文件。
  • 注册服务的容器对象实例,这里注册就使用的是容器类中的bindTo方法进行绑定注册的。
  • 读取配置文件,这段在配置文件加载那一节中已经进行深入的说明了, 这里就不提了。配置文件会读取俩个地方一个是第一步模块下的config文件,另一个就是config目录下的配置文件。
  • 设置模块路径,会把第一步获取到的模块进行env环境变量配置里边
  • 最后一步就是对容器中的对象实例进行配置更新,具体更新了什么在后文中给大家详细说来。
  •  /**
    * 初始化应用或模块
    * @access public
    * @param string $module 模块名
    * @return void
    */
    public function init($module = '')
    {
    // 定位模块目录
    $module = $module ? $module . DIRECTORY_SEPARATOR : '';
    /**
    * 第一次:D:phpstudy_proWWWThinkPHPSourceCodeAnalysisapplication
    * 第二次:D:phpstudy_proWWWThinkPHPSourceCodeAnalysisapplicationindex
    */
    $path = $this->appPath . $module;

    // 加载初始化文件
    if (is_file($path . 'init.php')) {
    include $path . 'init.php';
    } elseif (is_file($this->runtimePath . $module . 'init.php')) {
    include $this->runtimePath . $module . 'init.php';
    } else {
    // 加载行为扩展文件
    if (is_file($path . 'tags.php')) {
    $tags = include $path . 'tags.php';
    if (is_array($tags)) {
    $this->hook->import($tags);
    }
    }

    // 加载公共文件
    if (is_file($path . 'common.php')) {
    include_once $path . 'common.php';
    }

    if ('' == $module) {
    // 加载系统助手函数
    include $this->thinkPath . 'helper.php';
    }

    // 加载中间件
    if (is_file($path . 'middleware.php')) {
    $middleware = include $path . 'middleware.php';
    if (is_array($middleware)) {
    $this->middleware->import($middleware);
    }
    }

    // 注册服务的容器对象实例
    if (is_file($path . 'provider.php')) {
    $provider = include $path . 'provider.php';
    if (is_array($provider)) {
    $this->bindTo($provider);
    }
    }

    /**
    * $path : "D:phpstudy_proWWWThinkPHPSourceCodeAnalysisapplication"
    * "D:phpstudy_proWWWThinkPHPSourceCodeAnalysisapplicationindex"
    */
    // 自动读取配置文件
    if (is_dir($path . 'config')) {
    $dir = $path . 'config' . DIRECTORY_SEPARATOR;
    } elseif (is_dir($this->configPath . $module)) {
    // D:phpstudy_proWWWThinkPHPSourceCodeAnalysisconfig
    $dir = $this->configPath . $module;
    }
    // scandir:以升序的方式读取目录中的文件
    // 返回就是config目录中的所有文件
    $files = isset($dir) ? scandir($dir) : [];

    foreach ($files as $file) {
    /**
    * $this->configExt:配置文件的后缀
    * pathinfo返回的是文件后缀,关于pathinfo共有三个可选的参数PATHINFO_DIRNAME、PATHINFO_BASENAME、PATHINFO_EXTENSION,分别为只返回文件名,文件目录名,文件扩展
    */
    if ('.' . pathinfo($file, PATHINFO_EXTENSION) === $this->configExt) {
    /**
    * 俩个参数分别为
    * 1.目录+config目录下的文件
    * 2.config目录下文件名
    */
    $this->config->load($dir . $file, pathinfo($file, PATHINFO_FILENAME));
    }
    }
    }

    $this->setModulePath($path);

    if ($module) {
    // 对容器中的对象实例进行配置更新
    $this->containerConfigUpdate($module);
    }
    }

    这里附带上一份代码,可以对着代码看上边的执行流程,对每一步都做了简单的说明。

    咔咔个人见解对源码进行优化

    在设置模块的这步代码咔咔感觉不是很是严谨,因为init方法会在俩个地方进行执行。

    第一次的模块为空,这块代码执行是没有任何意义的。

    下面在对容器的对象实例进行配置更新时进行了一次判断,判断模块的这个参数是否为空,如果不为空才会执行。

    那么同样的道理,咔咔感觉在设置模块路径这块也应该在这个判断里边。

    虽说第二次执行会把第一次的结果覆盖掉,但是咔咔感觉下图这样使用才会更好。

    21.png


    咔咔对源码修改建议

    四、对容器中的对象实例进行更新配置

    在上一节中这里就是最后的内容,那这个对实例进行更新配置,到底更新了什么,怎么更新没有说明。

    在这一小节中就会做出说明,同样可以配合着前言的思维导图看。

  • 先会把config目录下的所有配置信息全部获取出来
  • 从app配置文件中将注册异常处理类
  • 第三大块是把第一步获取出来的所有配置信息给对应的类进行注册配置。
  • 第四步就是在把模块确定下来之后加载对应的语言包,语言包功能就可以实现多语言功能,之前咔咔写过一篇文章实现多语言功能,如果感兴趣的可以去查看。
  • 最后一步就是根据app配置文件中的三个属性进行缓存的处理
  • 在这一节中咔咔感觉最重要的就是下图的内容了。

    22.png我们可以随意追踪一到俩个方法查看一下那边到底执行了什么方法。

    追踪方法Db::init()

    追踪方法过来后可以看到就是对Db类中的config属性进行赋值,把database中的值赋值给Db类中的config属性。

    23.png追踪方法$this->middleware->setConfig()

    来到中间件这个类里边,可以看到就是把本类的配置和传递过来的参数类进行合并,同样也是进行config属性的赋值。

    跟上边案例的Db类的init方法实现的效果是一致的。

    这里在提一嘴就是在对容器中的对象实例进行更新配置这一幅图中可以看到紫色部分是在本类中没有引用的。

    那么这是怎么可以进行执行的呢!是因为App类继承了容器类,容器类中有四个魔术方法,其中有一个__get方法,就是在获取不存在的属性时会执行那个方法。

    在魔术方法__get方法中执行了一个make方法,这个make方法说了好多次了,这个方法最终会返回一个应用的实例,然后用这个实例调用对应实例类的方法。

    这一块一定要理解好,阅读源码就是这个样子,我们需要对一切未知的进行的解决,只有这样才能提高我们的编程能力和思想。

    24.png


    中间件的设置配置

    五、浅谈调试模式以及代码冗余

    本节会对调试模式做出简单的说明,并且会对框架代码冗余情况进行简单的提出。

    没有人写的代码是没有漏洞的,如果有那就是你还没有达到一定的造诣。

    调试模式

    在第一节中只提到了initialize方法的上半部分,因为在这一节之前聊的都是关于应用初始化init的内容。

    接下来会对这一块的内容进行简单的说明。

  • 从app配置文件中获取到app_debug的配置项
  • 给环境变量设置debug级别
  • 当框架中的debug是关闭状态时会执行ini_set这个方法,这个方法是为一个配置选项进行赋值。
  • 接下来的内容估计不是很好理解,都是平时在工作中根本使用不到的。

  • ob_get_level:返回输出缓冲机制的嵌套级别,那么怎么去理解呢!其实就是当缓存区不起作用时会返回0。
  • ob_get_clean:这个函数将会返回输出缓冲的内容并终止输出缓冲。如果缓冲区没有有效内容则返回false。本质上相当于同时执行了ob_getcontens()和ob_end_clean()。
  • ob_start:打开输出控制缓冲
  • 上边这三个先暂时认识就行,后期如果有机会会专门出一篇文章做解释的。

    25.png关于框架代码冗余

    这里也仅仅代表咔咔个人的观点。

    可以先看看这部分的代码,这俩处代码是不是很是熟悉,没错就是在上文的init方法中容器对象实例配置更新见到过。

    26.png如图

    27.png这块也就是咔咔个人提出的见解,由于咔咔式针对5.1做的源码解读,不太了解新版版是否做出了改动。

    六、总结

    本节主要是针对框架执行流程中的初始化应用做了简单的探讨。

    至于在app类的run方法下面还有很多的执行过程在这一节中没有做过多的解释。

    在阅读源码的过程中给大家提了一个很好得小技巧,那就是如何去查看一个方法都在哪里进行了执行。

    这个方法为debug_backtrace,这个方法需要大家多使用几次就知道怎么使用了,因为在打印出来的结果中也存在很多无用的信息。

    这个方法在调试源码的过程中是非常有效的,一定要好好利用这个方法。

    在就是对初始化应用init方法进行了特别详细的介绍。

    其中咔咔感觉这块设计最好的就是在容器中的对象实例进行更新配置那一块,先读取所有的配置,然后在通过各个类的方法进行配置的设置。

    这种代码规划和设计思路值得我们去学习。

    最后聊到了调试模式和框架的代码冗余问题,关于调试模式这里咔咔给大家提个醒项目在线上的调试模式一定要关闭。

    否则你的项目就类似于裸奔的存在,没有一点点的安全可言。

    这块有点不好理解的就是对于缓冲区,关于这块的内容咔咔认为暂时没有必要去钻牛角尖,先认识认识然后在进行深入的研究。

    缓冲区的这块内容估计工作了三四年的也很少有人使用,所以先认识,知道怎么一回事,咔咔后期学习了之后在给大家进行补充。

    直到这里关于框架的执行流程之初始化应用就结束了,这一节没有过深需要学习的,主要是其中的代码设计模式和实现思路。

    最后这个图大家一定要跟着源码看一看哈!

    1.png

    在这里插入图片描述

    坚持学习、坚持写博、坚持分享是咔咔从业以来一直所秉持的信念。希望在偌大互联网中咔咔的文章能带给你一丝丝帮助。我是咔咔,下期见。

    热心网友 时间:2022-04-18 02:32

    行为(Behavior)是ThinkPHP扩展机制中比较关键的一项扩展,行为即可以独立调用,也可以绑定到某个标签中进行监听,官方提出的CBD模式中行为也占了主要的地位,可见行为在ThinkPHP框架中意义非凡。
    这里指的行为是一个比较抽象的概念,你可以想象成在应用执行过程中的一个动作或者处理。在框架的执行流程中,各个位置都可以有行为,例如路由检测是一个行为,静态缓存是一个行为,用户权限检查也是行为,大到业务逻辑,小到浏览器检测,多语言检测等等都可以当做是一个行为,甚至说你希望你的网站用户的第一次访问弹出Holle ,World!这些都可以当做一个行为,行为的存在让你无需改动框架和应用,而在外围通过扩展或者配置来改变或者增加一些功能。而不同行为之间也具有位置共同性!
    PHP怎么实现面向切面编程

    下面是向切面式组件源码,是根据AOP的思路设计的:<?php if (function_exists('__autoload')) { trigger_error("Extension: It looks like your code is using an __autoload() function. Extension uses spl_autoload_register() which will bypass your __autoload() function and may break aut...

    Django和Thinkphp哪个好?

    后来的很多框架,包括django(python),lavarel(php),cakephp(php)都是模仿rails,但都不如rails,主要原因在于ruby语言本身的简洁与优美,读rails的代码感觉特别舒服,特别精练,一点不绕(很少有php框架中常见的大段检测、对比、if判断代码),而其它语言的框架都达不到这点 目前最活跃的框架是thinkphp、C...

    谁有有关于php编程相关的网站推荐呀?

    ASP.NET;HTML编程基础;比较ASP和ASP.NET;ASP.NET中的事件;比较HTML控件和ASP控件;基本的ASP控件 API;代码隐藏窗体;使用Visual Studio .NET集成开发环境;跟踪,调试和错误处理;ASP.NET中的验证控件;数据绑定技术基础;列表控件和DataGrid控件;ADO.NET指南;调用存储过程;更新数据库记录;Repeater和DataList? ... 要建...

    自学前端,前端开发的学习路线是什么

    面向对象终极篇:从内存角度到理解JS面向对象、基本类型、复杂类型、原型链、ES6中的面向对象、属性读写权限、设置器、访问器。面向对象三大特征:继承性、多态性、封装性、接口。设计模式:面向对象编程思维、单例模式、工厂模式、策略模式、观察者模式、模板方法模式、代理模式、装饰者模式、适配器模式、面向...

    前端开发需要学什么啊?

    1、HTML语言 掌握HTML是网页的核心,是一种制作万维网页面的标准语言,是万维网浏览器使用的一种语言,它消除了不同计算机之间信息交流的障碍。因此,它是网络上应用最为广泛的语言,也是构成网页文档的主要语言,学好HTML是成为Web开发人员的基本条件。HTML是一种标记语言,能够实现Web页面并在浏览器中显示...

    实木板材圈的分类以及优缺点分析

    容易加工,切面欠光滑,干燥时易翘曲。油漆涂装性能好,胶合性强。主要用于板材类贴薄面。 8、杉木 1、它具有天然的原木香味,对人体有多种有益的作用!而且杉木家具中所含的香杉木醇能杀死空气中的细菌、可抑制人体病原菌,对各种皮肤炎症有抑制作用;对人体有消除疲劳,舒解压力的保健作用!比较环保,而且还带有一种...

    如何鉴别金丝檀木家具?金丝檀木的特点是什么呢?

    1、在金丝檀木天然的木色中,金色和黑色曲线跃然眼前,色彩醒目且极具流动感,任何一个切面都可以清晰地表现出木纹的美感。而且金丝檀木木质硬、加工难度大,所以用金丝檀木制作出来的每一件产品都满溢浓烈的自然气息。2、金丝檀木最大可达到3250px,生长环境特殊,生长在平均海拔1600米以上山丘地带.土壤表层...

    圣象强化木地板怎么保养 强化木地板的安装步骤是什么

    安装中如有伸出地板的管道,需在地板上开出孔洞,其直径至少比管径大10cmm,锯切面应与板面成45度角。通过上文的详细介绍,相信大家对于圣象强化木地板都已经比较熟悉了,圣象强化木地板深受广大消费者的信赖,圣象强化木地板不同材质的地板,其保养方法也会有所不同,但只要好好保养圣象强化木地板,圣象...

    非洲酸枝家具是什么木? 非洲酸枝是红木吗?

    射线可见,大、距不等,大者略宽于轮界状薄壁组织;在弦切面呈斑点状。非洲酸枝木心材板面美观,耐腐抗蛀(白蛀也不蛀蚀);虽重硬(气干重800-900kg/m3)而加工不难;干燥缓慢,但干后材性稳定;创光、砂光、油漆和胶粘性能良好。广泛用于高级家具、木架、墙壁、地板、亭柱、乐器、旋切品、刀柄、刷背...

    Web前端开发主要学哪些?

    更加简单,官网有注册优惠别错过~理论知识是基础,对于前端开发者来说,技术应用是进一步的需求,所以thingjs提供免费3D源码和3D模型库,让你在项目开发过程中有东风助力,在实操的过程中你会更好地吸收前端技术知识!前端技术肯定不是最终的目的,做成一个有价值的商业项目,才能让你更有成就感!

    超声中的锥状切面是啥 什么是切面 超声中常用的切面 su中剖切面怎么移动 颜面正中矢状切面 切面灰白质中 数学当中有几种切面 中脑横切面 切面灰白实性质中
    声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
    ...这一夜你还会为他熬夜吗?你有什么最想对他说的? 西南大学党委宣传部主要职责 平衡车进水晒干能用吗 平衡车进水了怎么办?可以晒吗? 客厅装修需要哪些电位 遗产继承如何规定 郴州酒店有哪些 郴州最大的酒店排名,郴州市内景区酒店 郴州酒店排名前十名,郴州旅游住酒店推荐 求生欲是什么意思? 翡翠手镯太小怎么戴 翡翠手镯小了怎么戴进去 车子户主是老婆名字老公使用能申请顺风车吗? 孩子健康问题让人堪忧 高端儿童保险来解愁 有关孩子健康的问题 少年儿童面临哪五大健康问题? 巜憨豆特工》电影主要内容是什么? 电影《憨豆特工3》相比前两部有什么亮点? 跪求2011年由奥利弗·帕克执导,罗温·艾金森主演的喜剧影片《憨豆特工2》高清免费百度云资源。 私刻公章去哪里举报 私刻假章向哪里举报 MPLS 为什么分为二层和三层? 现在的鸟哥Linux私房菜第三版 ,本来还打算买的,看到这个"王世江改编"就不想买了,难道他技术比鸟哥牛? 鸟哥的Linux私房菜:基础学习篇,第二版与第三版有什么区别呢? 求 深入理解LINUX内核第三版pdf的下载地址 和 第二版中文版下载地址 "熊猫烧香"的病毒案子已经破了,但是现在能浏览"熊猫烧香"的图片了吗? 熊猫烧香网络病毒是什么鬼? 孩子刚上幼儿园很开心,过几天开始哭,是在幼儿园被欺负了吗?家长该咋办? 44类商标是否可以作为公司名称使用 44类商标购买技巧 申请中的44类商标,被他人使用是否构成侵权? 2012年农历九月初三十时二十八分生辰八字是什,五行缺什么? 婚礼邀请群发内容 怎样将打火机里的气体放出来呢? 打火机充气口要不要拧开来?还是直接可以充? 如何将一次性打火机里的气体提取出来?并有效保存起来 有没有一款软件是能够详细列出php的执行流程,比如说向哪个页面发送get,post请求了,请求的数据等 手机拍摄的身份证可以拿去贷款吗? 手机拍摄的身份证可以拿去贷款吗 新浪微博求关注语怎么写才能让对方很乐意关注自己?有经验的回答,谢谢 日记(围绕中秋节来写400字) 中秋日记四百字。 过中秋的日记,400字以上 没有银行卡在不绑定亲属卡的情况下怎么样可以用微信支付和收款? 白雪歌送武判官归京的两句诗是什么 《白雪歌送武判官归京》的诗句是什么? 白雪歌送武判官归京的诗句 白雪歌送武判官归京的诗 忽如一夜春风来千树万树梨花开这句诗句出自唐朝边塞诗人谁的白雪歌送武判官归京 白雪歌送武判官归京中最能结实送别已经的两句诗是 白雪歌送武判官归京的哪一句身孤附远影碧空尽,惟见长冮天际流 白雪歌送武判官归京,写诗人对战争对边关将士的忧愁是哪一句?