细读PHP的生命周期
在《Extending and Embedding PHP》中,有一张经典的描述PHP单进程生命周期的图,一直也是按这个图理解其生命周期的,可是当准备一次内核分享时,却表现自己没有什么可以说的,于是就有了今天的这篇文章:细读PHP的生命周期。这里,我们会详细说明在CLI模式下PHP一个生命周期中做了哪些事情。
启动
在调用每个模块的模块初始化前,会有一个初始化的过程,它包括:
- 初始化若干全局变量
- 初始化若干常量
- 初始化ZEND引擎和核心组件
- 解析php.ini
INI_DEFAULT("report_zend_debug", "0"); INI_DEFAULT("display_errors", "1");不过在其它模式下却没有这样的初始化操作。接下来会的各种操作都是查找ini文件:
- 判断是否有php_ini_path_override,在CLI模式下可以通过-c参数指定此路径(在php的命令参数中-c表示在指定的路径中查找ini文件)。
- 如果没有php_ini_path_override,判断php_ini_ignore是否为非空(忽略php.ini配置,这里也就CLI模式下有用,使用-n参数)。
- 如果不忽略ini配置,则开始处理php_ini_search_path(查找ini文件的路径),这些路径包括CWD(当前路径,不过这种不适用CLI模式)、执行脚本所在目录、环境变量PATH和PHPRC和配置文件中的PHP_CONFIG_FILE_PATH的值。
- 在准备完查找路径后,PHP会判断现在的ini路径(php_ini_file_name)是否为文件和是否可打开。如果这里ini路径是文件并且可打开,则会使用此文件, 也就是CLI模式下通过-c参数指定的ini文件的优先级是最高的,其次是PHPRC指定的文件,第三是在搜索路径中查找php-%sapi-module-name%.ini文件(如CLI模式下应该是查找php-cli.ini文件),最后才是搜索路径中查找php.ini文件。
- 全局操作函数的初始化
- 初始化静态构建的模块和共享模块(MINIT)
- 禁用函数和类
- 激活ZEND引擎
- 激活SAPI
- 环境初始化
- 模块请求初始化
- 调用所有通过register_shutdown_function()注册的函数。这些在关闭时调用的函数是在用户空间添加进来的。一个简单的例子,我们可以在脚本出错时调用一个统一的函数,给用户一个友好一些的页面,这个有点类似于网页中的404页面。
- 执行所有可用的__destruct函数。这里的析构函数包括在对象池(EG(objects_store)中的所有对象的析构函数以及EG(symbol_table)中各个元素的析构方法。
- 将所有的输出刷出去。
- 发送HTTP应答头。这也是一个输出字符串的过程,只是这个字符串可能符合某些规范。
- 遍历每个模块的关闭请求方法,执行模块的请求关闭操作,这就是我们在图中看到的Call each extension's RSHUTDOWN。
- 销毁全局变量表(PG(http_globals))的变量。
- 通过zend_deactivate函数,关闭词法分析器、语法分析器和中间代码执行器。
- 调用每个扩展的post-RSHUTDOWN函数。只是基本每个扩展的post_deactivate_func函数指针都是NULL。
- 关闭SAPI,通过sapi_deactivate销毁SG(sapi_headers)、SG(request_info)等的内容。
- 关闭流的包装器、关闭流的过滤器。
- 关闭内存管理。
- 重新设置最大执行时间
- flush
- 关闭ZEND引擎