在说PHP生命周期之前,我们需要了解一个概念,SAPI (Server Application Programming Interface) 服务器应用程序编程接口。他是PHP和其他应用程序交互的接口。
这里引用篇文章的解释,感觉讲的简洁还明了
PHP的SAPI,CLI SAPI,CGI SAPI
原文链接
叶剑峰(包含链接http://www.cnblogs.com/yjf512/
Q: 首先一个问题:在命令行下执行:php -r 'echo 12;' 控制台会打印出 12;
这个过程不是很奇妙么,我输入的是shell命令,但是执行的却是php脚本。php脚本执行完成之后的输出还能在控制台输出。
那在这个shell命令(控制台命令)和php中间一定有一种接口,能将shell的参数,代码,等转换成php,然后将php的输出转换成shell的输出。这个接口就叫做SAPI(Server Application Programimg Interface)。它就相当于PHP外部环境的代理器。
那么由于PHP可以应用在终端上,也可以应用在Web服务器中,所以呢,应用在终端上的SAPI就叫做CLI SAPI,应用在Web服务器中的就叫做CGI SAPI。在windows下安装php你会看到两个exe:php.exe和php-cgi.exe这个就对应的是这两种SAPI。再比如,在控制台上使用php -v,你就会发现PHP的版本信息中有个(cli)标示,就代表你这里的php应用程序使用的是cli SAPI。
关于CLI SAPI:手册上有很详细的说明:http://php.net/manual/zh/features.commandline.php
PHP的生命周期
原文链接
模块初始化阶段 --- php_module_startup() 启动SAPI
请求初始化阶段 --- php_request_startup()
执行脚本阶段 --- php_execute_script()
请求关闭阶段 --- php_request_shutdown()
模块关闭阶段 --- php_module_shutdown() 关闭SAPI
php变量的生命周期
原文链接
1、PHP使用的回收算法是引用计数法,每个php变量存在一个叫"zval"的变量容器中。
2、一个zval变量容器,除了包含变量的类型和值,还包括两个字节的额外信息。第一个是"is_ref",是个bool值,用来标识这个变量是否是属于引用集合(reference set)。 第二个额外字节是"refcount",用以表示指向这个zval变量容器的变量(也称符号即symbol)个数。
3、把一个变量赋值给另一变量,将增加引用次数(refcount),当任何关联到某个变量容器的变量离开它的作用域(比如:函数执行结束),或者对变量调用了函数unset()时,"refcount"就会减1。
4、变量容器在"refcount"变成0时就被销毁。
一切的开始: SAPI接口
原文链接
通常我们编写php Web程序都是通过Apache或者Nginx这类Web服务器来测试脚本. 或者在命令行下通过php程序来执行PHP脚本. 执行完成脚本后,服务器应答,浏览器显示应答信息,或者在命令结束后在标准输出显示内容. 我们很少关心PHP解释器在哪里. 虽然通过Web服务器和命令行程序执行脚本看起来很不一样. 实际上她们的工作是一样的. 命令行程序和Web程序类似, 命令行参数传递给要执行的脚本,相当于通过url 请求一个PHP页面. 脚本戳里完成后返回响应结果,只不过命令行响应的结果是显示在终端上. 脚本执行的开始都是通过SAPI接口进行的.
简要的过程如下:
1. PHP是随着Apache的启动而运行的;
2. PHP通过mod_php5.so模块和Apache相连(具体说来是SAPI,即服务器应用程序编程接口);
3. PHP总共有三个模块:内核、Zend引擎、以及扩展层;
4. PHP内核用来处理请求、文件流、错误处理等相关操作;
5. Zend引擎(ZE)用以将源文件转换成机器语言,然后在虚拟机上运行它;
6. 扩展层是一组函数、类库和流,PHP使用它们来执行一些特定的操作。比如,我们需要mysql扩展来连接MySQL数据库;
7. 当ZE执行程序时可能会需要连接若干扩展,这时ZE将控制权交给扩展,等处理完特定任务后再返还;
8. 最后,ZE将程序运行结果返回给PHP内核,它再将结果传送给SAPI层,最终输出到浏览器上。
PHP的开始和结束阶段
原文链接
开始阶段有两个过程:
- 第一个过程:apache启动的过程,即在任何请求到达之前就发生。
是在整个SAPI生命周期内(例如Apache启动以后的整个生命周期内或者命令行程序整个执行过程中)的开始阶段(MINIT),该阶段只进行一次.。启动Apache后,PHP解释程序也随之启动; PHP调用各个扩展(模块)的MINIT方法,从而使这些扩展切换到可用状态。看看php.ini文件里打开了哪些扩展吧; MINIT的意思是“模块初始化”。各个模块都定义了一组函数、类库等用以处理其他请求。 模块在这个阶段可以进行一些初始化工作,例如注册常量, 定义模块使用的类等等.典型的的模块回调函数MINIT方法如下:
PHP_MINIT_FUNCTION(myphpextension) { /* Initialize functions, classes etc */ }
{
// 注册常量或者类等初始化操作
return SUCCESS;
}
- 第二个过程发生在请求阶段,当一个页面请求发生时.则在每次请求之前都会进行初始化过程(RINIT请求开始).
请求到达之后,SAPI层将控制权交给PHP层,PHP初始化本次请求执行脚本所需的环境变量,例如创建一个执行环境,包括保存php运行过程中变量名称和变量值内容的符号表. 以及当前所有的函数以及类等信息的符号表.例如是Session模块的RINIT,如果在php.ini中启用了Session 模块,那在调用该模块的RINIT时就会初始化$_SESSION变量,并将相关内容读入; 然后PHP会调用所有模块RINIT函数,即“请求初始化”。 在这个阶段各个模块也可以执行一些相关的操作, 模块的RINIT函数和MINIT函数类似 ,RINIT方法可以看作是一个准备过程,在程序执行之间就会自动启动。
PHP_RINIT_FUNCTION(myphpextension)
{
// 例如记录请求开始时间
// 随后在请求结束的时候记录结束时间.这样我们就能够记录下处理请求所花费的时间了
return SUCCESS;
}
结束阶段分为两个环节:
- 第一个环节:请求处理完后结束阶段:
请求处理完后就进入了结束阶段,PHP就会启动清理程序。它会按顺序调用各个模块的RSHUTDOWN方法。 RSHUTDOWN用以清除程序运行时产生的符号表,也就是对每个变量调用unset函数。典型的RSHUTDOWN方法如下:
PHP_RSHUTDOWN_FUNCTION(myphpextension)
{
// 例如记录请求结束时间, 并把相应的信息写入到日至文件中.
return SUCCESS;
}
- 第二个环节:最后,所有的请求都已处理完毕,SAPI也准备关闭了, PHP调用每个扩展的MSHUTDOWN方法,这是各个模块最后一次释放内存的机会。(这个是对于CGI和CLI等SAPI,没有“下一个请求”,所以SAPI立刻开始关闭。)
典型的RSHUTDOWN方法如下:
PHP_MSHUTDOWN_FUNCTION(extension_name) {
/* Free handlers and persistent memory etc */
return SUCCESS;
}
SAPI运行PHP都经过下面几个阶段:
1. 模块初始化阶段(Module init) :
即调用每个拓展源码中的的PHP_MINIT_FUNCTION中的方法初始化模块,进行一些模块所需变量的申请,内存分配等。
2. 请求初始化阶段(Request init) :
即接受到客户端的请求后调用每个拓展的PHP_RINIT_FUNCTION中的方法,初始化PHP脚本的执行环境。
3. 执行PHP脚本
4. 请求结束(Request Shutdown) :
这时候调用每个拓展的PHP_RSHUTDOWN_FUNCTION方法清理请求现场,并且ZE开始回收变量和内存。
5. 关闭模块(Module shutdown) :
Web服务器退出或者命令行脚本执行完毕退出会调用拓展源码中的PHP_MSHUTDOWN_FUNCTION 方法
参考:
https://www.cnblogs.com/yjf512/p/3482040.html
https://blog.csdn.net/wuhuagu_wuhuaguo/article/details/80637277
https://blog.csdn.net/wuhuagu_wuhuaguo/article/details/80637277