【PHP7内核剖析】-1.2 执行流程
1.2 执行流程
PHP的核心设计遵循着清晰的启动-处理-关闭的生命周期。这个生命周期根据PHP的运行模式(如CLI、FPM、Apache模块等)略有不同,但其核心阶段是一致的。理解这些阶段是深入理解PHP内核的基石。
PHP的生命周期:
一个完整的PHP生命周期主要包括以下五个核心阶段,其流程可概括为:
1.2.1 模块初始化阶段 (MINIT)
此阶段在PHP进程启动后仅执行一次,在整个进程生命周期内永久有效。它的主要目的是初始化一些全局的、可在多个请求间共享的设施。
- 触发时机:
- Web模式:当服务器(如Nginx+FPM)启动时,PHP解释器被加载到内存中。
- CLI模式:每次执行一个PHP脚本时(与FPM不同,CLI通常是单请求进程)。
- 主要工作:
- 初始化全局变量:初始化
php.ini
中定义的全局配置设置,将其存入EG(ini_directives)
哈希表。 - 注册常量:注册PHP内置的常量,如
E_ERROR
,PHP_VERSION
,TRUE
,FALSE
等。 - 注册全局函数和类:将内置的函数(如
strlen
,array_map
)和类(如PDO
,Exception
)注册到全局函数表(CG(function_table)
)和类表(CG(class_table)
)中。 - 模块初始化:调用所有已加载扩展(如
json
,pdo_mysql
)的PHP_MINIT_FUNCTION(extension_name)
钩子函数,让每个扩展执行自己的一次性初始化工作(例如,注册自己的函数、类、常量、定义资源类型等)。 - 初始化Zend引擎:初始化编译器、执行器等核心组件。
- 初始化全局变量:初始化
- 特点:在此阶段分配的资源是全局的,不会被请求重置。如果资源在此阶段分配,开发者必须非常小心地管理,避免造成内存泄漏或跨请求的数据污染。
1.2.2 请求初始化阶段 (RINIT)
此阶段在每个请求开始前都会被执行一次。它的目的是为即将到来的单个请求初始化一个干净、崭新的执行环境。
- 触发时机:当一个HTTP请求到达(Web模式)或一个CLI脚本开始执行时。
- 主要工作:
- 初始化执行环境:重置Zend引擎的编译器、执行器状态。
- 初始化符号表:初始化全局变量表(
$GLOBALS
)和超级全局变量($_GET
,$_POST
,$_SERVER
等),这些变量在请求开始时是空的。 - 设置超时时间:根据
php.ini
中的max_execution_time
设置脚本最大执行时间。 - 扩展初始化:调用所有已加载扩展的
PHP_RINIT_FUNCTION(extension_name)
钩子函数。扩展可以在此阶段为当前请求初始化私有存储(例如,$_SESSION
模块可能会在此阶段反序列化session数据)。
- 特点:此阶段初始化的所有内容都是请求级的,会在请求结束后被销毁和回收。
1.2.3 执行PHP脚本阶段
这是PHP核心的“工作”阶段,脚本代码在此阶段被解析、编译和执行。
- 触发时机:在RINIT阶段完成后立即开始。
- 主要工作:
- 词法分析 & 语法分析 (Lexing & Parsing):Zend引擎的编译器(
zend_compile_file
)读取PHP源代码,将其分解为令牌(Tokens),并根据语言规则生成抽象的语法树(Abstract Syntax Tree, AST)。PHP7的重大优化之一就是将之前的zend_language_parser.y
直接生成OPCode改为先生成AST,然后再生成OPCode,允许在中间层进行更多优化。 - 编译 (Compilation):遍历AST并将其转换为Zend虚拟机可执行的OPCode(操作码)。OPCode是PHP自己定义的一套中间指令集,类似于Java的字节码。
- 执行 (Execution):Zend虚拟机(
zend_execute
)逐条执行编译生成的OPArray中的OPCode。这个虚拟机会处理所有的变量分配、函数调用、内存管理等。- 函数调用会进入虚拟机栈。
- OPCode的执行可能会触发更多操作,如数据库查询、文件读写等。
- 词法分析 & 语法分析 (Lexing & Parsing):Zend引擎的编译器(
- 特点:此阶段的性能直接决定了PHP应用的快慢。OPCache等扩展的核心作用就是通过缓存第1、2步的结果(即编译好的OPCode),来避免重复的解析和编译开销。
1.2.4 请求结束阶段 (RSHUTDOWN)
此阶段在每个请求结束后执行,负责清理该请求期间分配的所有资源,为下一个请求准备好一个干净的环境。
- 触发时机:脚本执行完毕、主动调用
exit
/die
或发生致命错误导致脚本终止时。 - 主要工作:
- 执行析构函数:调用所有已创建对象的
__destruct()
方法。 - 刷新输出:调用
flush
函数,将输出缓冲区的内容发送给客户端。 - 清理全局变量:销毁在请求期间创建的所有变量(包括
$_GET
,$_POST
等超全局变量,但它们的内存空间会被保留以供下一个请求RINIT时复用)。 - 关闭扩展:调用所有已加载扩展的
PHP_RSHUTDOWN_FUNCTION(extension_name)
钩子函数。扩展可以在此阶段释放为当前请求分配的所有资源(例如,关闭数据库连接、保存session数据等)。 - 清理执行器:Zend引擎清理本次执行产生的所有OPCode、符号表等。
- 执行析构函数:调用所有已创建对象的
- 特点:这是防止内存泄漏的关键阶段。所有请求级别的资源都应在此阶段被妥善释放。完成后,进程会回到RINIT阶段,等待处理下一个请求。
1.2.5 模块关闭阶段 (MSHUTDOWN)
此阶段在PHP进程终止时执行一次,是模块初始化阶段的逆过程,负责清理所有全局资源。
- 触发时机:
- Web模式:当Web服务器被关闭或重启时(例如,重启PHP-FPM主进程)。
- CLI模式:脚本执行结束后。
- 主要工作:
- 关闭扩展:调用所有已加载扩展的
PHP_MSHUTDOWN_FUNCTION(extension_name)
钩子函数。扩展必须在此阶段注销自己注册的所有函数、类、常量,并释放所有在MINIT中分配的持久资源。 - 清理全局配置:销毁
EG(ini_directives)
哈希表,释放所有配置选项的内存。 - 卸载Zend引擎:清理编译器、执行器等核心组件的全局状态。
- 清理其他全局资源:释放所有其他在MINIT阶段分配的全局内存。
- 关闭扩展:调用所有已加载扩展的
- 特点:此阶段是确保整个PHP进程能够干净退出、无内存泄漏的最后一道关卡。
【参考链接】https://github.com/pangudashu/php7-internal/blob/master/1/base_process.md