您的位置:首页 > 技术中心 > php框架 >

ThinkPHP框架使用的特性fastcgi_finish_request和trait

时间:2022-03-03 11:49

本文会对控制器最后的执行流程和使用的俩个高级属性进行简单的学习,一个是fastcgi_finish_request方法巧用,另一个是trait特性,超类的概念多少都有过了解,接下来一起来解析一下。

一、如何输出数据到终端

当执行完控制器中的方法响应数据给App类的run方法,直到这里就已经执行完了。

是不是有点懵这里的数据最终会返回哪里呢!

image.png

执行应用程序

之前写过的框架执行流程、路由、控制器实例化都是从这里开始进入的。

所以当run方法执行完成之后,就会把对应的结果给返回到这里。

image.png

执行应用并响应

这一部分的代码Container::get('app')应该都知道了是返回一个App类的实例。

然后通过App类去执行run方法,才会有之前讲过的一切。

下图是咔咔从半中腰做的一个思维导图,前面的没有,后边的所有知识点都会写在这个思维导图里。

image.png

执行流程

执行完run方法就会去执行Container::get('app')->run()->send()send这个方法,有多少人会认为在App类里边执行send方法。

其实不是的,回想一下之前执行控制器方法然后返回的响应结果是什么?

如果你不是很粗略的看都会记得是Response的一个对象实例。

所以说send方法会去response类里边去执行。

image.png

发送数据到客户端

先不看其它的,先看这行代码$this->app['hook'],现在知道是执行的那里吗?

这种形式就是通过访问数组形式去访问对象的属性,也就是之前解析的ArrayAccess这个类。当访问的属性不存在时会去执行offsetGet,然后执行魔术方法__get,最终通过make方法返回实例,这一切的操作都是在容器中。

对这行代码具体是监听的什么就不去做解析了。

接着需要看处理输出数据的这行代码$data = $this->getContent();

这个方法做的事情就是将传过来的数据赋值给本类的content属性。

image.png

获取输出数据

其实在获取输出数据这个方法中,请看咔咔圈出来的第一个地方感觉是很没有必要。

可以看到根本对数据就没有任何的处理,只是简单的返回了,所以说框架有好的地方也有不好的地方,只有你去阅读了才会知道,否则你会对你经常使用的工具一无所知。

image.png

处理数据

在接着就是Trace调试注入,就是通过配置文件配置的,通过调用debug类实现的,这里就不详解了。

然后就是缓存判断,缓存会在后文中单独拎出来讲,所以也是过。

在接下来就对响应头的设置了,检测 HTTP 头是否已经发送,这块的东西就很重要了,也是平时接触不多的知识点了。

  • headers_sent() : 检测 HTTP 头是否已经发送
  • http_response_code() :获取/设置响应的 HTTP 状态码
  • header : 函数向客户端发送原始的 HTTP 报头。

image.png

检测 HTTP 头是否已经发送

最后一步,来了来了,它来了,它带着echo来了,执行了一个方法$this->sendData($data);

给人一种媳妇熬成娘的感觉,终于来到的终点站,一个echo输出了咔咔几十天的心酸啊!

为了到达这个echo咔咔是经历九九八十一难啊!战斗还未停止,同志仍需努力啊!

image.png

输出数据

那么到这里关于框架执行然后到应用初始化,在到路由检测、控制器的实例化、然后返回response实例,在通过入口文件执行send方法。

最后将数据输出到终端,也就是一个echo的事情。

虽然这里的战斗结束了,但是在下面还有一个非常重要的知识点,咔咔将重新提一节来进行说明。

二、fastcgi_finish_request方法巧用

在上一节中通过Container::get('app')->run()->send();在response类中执行了send方法,输出了数据。

但是在输出数据之后还执行了一个方法fastcgi_finish_request();,给的注释是提高页面响应,接下来好好来扒一扒其中的奥秘。

在PHP官网中看到这样一段话

The script will still occupy a FPM process after fastcgi_finish_request(). So using it excessively for long running tasks may occupy all your FPM threads up to pm.max_children. This will lead to gateway errors on the webserver.

在fastcgi_finish_request()之后,脚本仍将占用FPM进程。 因此,对于长时间运行的任务过度使用它可能会占用您的所有FPM线程,直到pm.max_children。 这将导致Web服务器上的网关错误。

所以说在没有彻底的了解这个方法之前不要轻易的在自己的项目中使用这个方法。

接下来咔咔将使用一个案例来演示这个方法的使用,仅仅只是演示使用,如果需要使用到项目中请仔细阅读文档应该注意的问题。

案例演示

公司有一个业务需要发送通知给用户,但是由于发送时间太久,非常费时间,有可能需要好几十秒的时间,更严重的会直接导致浏览器连接超时。

在一个问题就是用户体验的问题,用户等待时间过程,体验当然不好。

为了解决以上俩个问题,今天谈论的fastcgi_finish_request就派上了用场。

理解

对这个函数的理解其实就是发送响应给浏览器,用户等待时间大大缩短,但是PHP进程还是在运行的。

这样就达到了来个目的,就类似于我们经常说的异步执行。

直观的来说就是发送邮件有可能需要10秒,但是用户是没有感知的,用户点击发送邮件之后直接就返回发送成功,浏览器响应结束,用户做其它事情,后台进程继续执行发送邮件的任务。

案例

image.png

演示案例

具体代码

<?php
/**
* 设置超时时间,变成不限制
*
*/

set_time_limit(0);

/**
* 本函数模拟非常耗时的任务,执行完毕需要5秒的时间
*/

function writeFile()
{
   $path = 'D:/phpstudy_pro/WWW/kaka.txt';
   file_put_contents($path,'程序运行开始' . PHP_EOL,FILE_APPEND);
   for($i =0;$i < 5;$i++) {
       file_put_contents($path,time() . PHP_EOL,FILE_APPEND);
       sleep(1);
   }

   file_put_contents($path,'程序运行结束' . PHP_EOL,FILE_APPEND);

}

/**
* 输出文字标记,任务开始
*/

echo('任务开始');

/**
*  后台执行非常耗时的任务
*/

register_shutdown_function(writeFile);

/**
* 立即发送请求
*/

fastcgi_finish_request();



image.png

演示结果

以上测试全部使用linux系统进行测试哈,否则你看不到直观的效果。

经过上面的演示,响应非常快,浏览器响应结束后,后台程序依然进行执行每秒执行一个时间戳。

以上就是对fastcgi_finish_request方法的简单介绍,如果你也感兴趣可以进行简单的尝试一下,有助于更好的去理解其中的小秘密。

三、trait特性讲解

应该在俩年前咔咔就对这个特性进行过一次解析,trait就是常说的超类。

这个特性是在PHP5.4才加入的,这个特性不是经常使用的接口更不是类。

这个特性是为了解决PHP的一大弱点只能单继承的缺点,但是也不能叫多继承,严谨一点的就是类似多继承的功能而已。

接下来给大家演示一个案例。

创建test文件一,并且返回对应类名。

image.png

超类的第一个文件

创建test1文件,并且返回对应类名

image.png

超类的第二个文件

创建控制器文件用来输出信息。

image.png

创建控制器

然后在控制器中引入对应的超类文件,这里需要注意的是圈住的第一个框,这个框就是直接引入超类test文件。

image.png

在控制器中引入对应的超类文件

然后可以直接进行访问,看会返回什么。

image.png

返回结果

通过上图访问结果结果可以看得到返回的是Test超类文件的方法,但是此控制器同样也基础了Controller控制器,这也就是在文章一开头就说的超类就是实现了一种多继承的功能而已。

但是这里会存在一个问题,请看下图报错信息。

image.png

报错信息

上图的报错信息是因为在控制器中使用了俩个超类导致的,也就是下图的使用方式。

image.png

控制器使用俩个超类

那么如何解决这种报错信息呢!接下来跟这咔咔的节奏一起来。

解决报错信息

在解决之前问题之前得先清楚这个问题是由于什么引起的。

出现这个错误的原因是引用的两个trait里面有同名的hello函数,出现了冲突。

但是在日常开发中这种情况都是可以避免的,因为手动改方法名还是很方便的,但是这里咔咔教大家如何解决这种问题。

一是用其中一个trait里的hello方法覆盖另外一个trait的同名方法,因为两个方法内容是一致的,所以我这里直接选择insteadof覆盖;

二是给他们用as起别名,这样就不会有冲突了。as关键词还有另外一个用途,那就是修改方法的访问控制。

image.png

解决后的方法实现

经过上图的改动之后,再一次的进行访问,看一下返回结果。

image.png

返回结果

那么这个时候就会有伙伴有疑问了,就是案例打印结果一直是Test类的方法,Test1类的方法一直没有进行打印。

那是如何进行访问的呢!来接着看一下。

image.png

访问超类2的代码

从上图可以看到将访问方法改为了别名控制访问,接着来看一下访问结果。

image.png

超类二返回结果

从上图中可以可以看到返回结果就是超类Test1类的返回结果。

那么关于as这个的使用就需要大家在去搜索一下使用方式,有时候注意一下细节就可以学到很多知识点。

总结

直到这里关于控制器的源码解析就到这了,咔咔通过源码给大家分析控制器的如如何进行实例化的。

也再一次的进行了对ArrayAccess和魔术方法的调用关系,一定要有自己的思考去想问题。

<p data-tool="mdnice编辑器" style="margin-top: 0.8em; margin-bottom: 0.8em; color: rgb(53, 53, 53); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif; font-size: 16px; letter-spacing: 0.8px; white-space: normal; word-spacing: 0.8px; padding-to

本类排行

今日推荐

热门手游