【PHP7内核剖析】-1.2 执行流程

发布于:2025-09-14 ⋅ 阅读:(20) ⋅ 点赞:(0)

1.2 执行流程

PHP的核心设计遵循着清晰的启动-处理-关闭的生命周期。这个生命周期根据PHP的运行模式(如CLI、FPM、Apache模块等)略有不同,但其核心阶段是一致的。理解这些阶段是深入理解PHP内核的基石。

PHP的生命周期:

一个完整的PHP生命周期主要包括以下五个核心阶段,其流程可概括为:

模块初始化阶段
MINIT
请求初始化阶段
RINIT
执行PHP脚本阶段
请求结束阶段
RSHUTDOWN
模块关闭阶段
MSHUTDOWN

在这里插入图片描述


1.2.1 模块初始化阶段 (MINIT)

此阶段在PHP进程启动后仅执行一次,在整个进程生命周期内永久有效。它的主要目的是初始化一些全局的、可在多个请求间共享的设施。

  • 触发时机
    • Web模式:当服务器(如Nginx+FPM)启动时,PHP解释器被加载到内存中。
    • CLI模式:每次执行一个PHP脚本时(与FPM不同,CLI通常是单请求进程)。
  • 主要工作
    1. 初始化全局变量:初始化php.ini中定义的全局配置设置,将其存入EG(ini_directives)哈希表。
    2. 注册常量:注册PHP内置的常量,如E_ERROR, PHP_VERSION, TRUE, FALSE等。
    3. 注册全局函数和类:将内置的函数(如strlen, array_map)和类(如PDO, Exception)注册到全局函数表(CG(function_table))和类表(CG(class_table))中。
    4. 模块初始化:调用所有已加载扩展(如json, pdo_mysql)的 PHP_MINIT_FUNCTION(extension_name) 钩子函数,让每个扩展执行自己的一次性初始化工作(例如,注册自己的函数、类、常量、定义资源类型等)。
    5. 初始化Zend引擎:初始化编译器、执行器等核心组件。
  • 特点:在此阶段分配的资源是全局的,不会被请求重置。如果资源在此阶段分配,开发者必须非常小心地管理,避免造成内存泄漏或跨请求的数据污染。

1.2.2 请求初始化阶段 (RINIT)

此阶段在每个请求开始前都会被执行一次。它的目的是为即将到来的单个请求初始化一个干净、崭新的执行环境。

  • 触发时机:当一个HTTP请求到达(Web模式)或一个CLI脚本开始执行时。
  • 主要工作
    1. 初始化执行环境:重置Zend引擎的编译器、执行器状态。
    2. 初始化符号表:初始化全局变量表($GLOBALS)和超级全局变量($_GET, $_POST, $_SERVER等),这些变量在请求开始时是空的。
    3. 设置超时时间:根据php.ini中的max_execution_time设置脚本最大执行时间。
    4. 扩展初始化:调用所有已加载扩展的 PHP_RINIT_FUNCTION(extension_name) 钩子函数。扩展可以在此阶段为当前请求初始化私有存储(例如,$_SESSION模块可能会在此阶段反序列化session数据)。
  • 特点:此阶段初始化的所有内容都是请求级的,会在请求结束后被销毁和回收。

1.2.3 执行PHP脚本阶段

这是PHP核心的“工作”阶段,脚本代码在此阶段被解析、编译和执行。

  • 触发时机:在RINIT阶段完成后立即开始。
  • 主要工作
    1. 词法分析 & 语法分析 (Lexing & Parsing):Zend引擎的编译器(zend_compile_file)读取PHP源代码,将其分解为令牌(Tokens),并根据语言规则生成抽象的语法树(Abstract Syntax Tree, AST)。PHP7的重大优化之一就是将之前的zend_language_parser.y直接生成OPCode改为先生成AST,然后再生成OPCode,允许在中间层进行更多优化。
    2. 编译 (Compilation):遍历AST并将其转换为Zend虚拟机可执行的OPCode(操作码)。OPCode是PHP自己定义的一套中间指令集,类似于Java的字节码。
    3. 执行 (Execution):Zend虚拟机(zend_execute)逐条执行编译生成的OPArray中的OPCode。这个虚拟机会处理所有的变量分配、函数调用、内存管理等。
      • 函数调用会进入虚拟机栈。
      • OPCode的执行可能会触发更多操作,如数据库查询、文件读写等。
  • 特点:此阶段的性能直接决定了PHP应用的快慢。OPCache等扩展的核心作用就是通过缓存第1、2步的结果(即编译好的OPCode),来避免重复的解析和编译开销。

1.2.4 请求结束阶段 (RSHUTDOWN)

此阶段在每个请求结束后执行,负责清理该请求期间分配的所有资源,为下一个请求准备好一个干净的环境。

  • 触发时机:脚本执行完毕、主动调用exit/die或发生致命错误导致脚本终止时。
  • 主要工作
    1. 执行析构函数:调用所有已创建对象的__destruct()方法。
    2. 刷新输出:调用flush函数,将输出缓冲区的内容发送给客户端。
    3. 清理全局变量:销毁在请求期间创建的所有变量(包括$_GET, $_POST等超全局变量,但它们的内存空间会被保留以供下一个请求RINIT时复用)。
    4. 关闭扩展:调用所有已加载扩展的 PHP_RSHUTDOWN_FUNCTION(extension_name) 钩子函数。扩展可以在此阶段释放为当前请求分配的所有资源(例如,关闭数据库连接、保存session数据等)。
    5. 清理执行器:Zend引擎清理本次执行产生的所有OPCode、符号表等。
  • 特点:这是防止内存泄漏的关键阶段。所有请求级别的资源都应在此阶段被妥善释放。完成后,进程会回到RINIT阶段,等待处理下一个请求。

1.2.5 模块关闭阶段 (MSHUTDOWN)

此阶段在PHP进程终止时执行一次,是模块初始化阶段的逆过程,负责清理所有全局资源。

  • 触发时机
    • Web模式:当Web服务器被关闭或重启时(例如,重启PHP-FPM主进程)。
    • CLI模式:脚本执行结束后。
  • 主要工作
    1. 关闭扩展:调用所有已加载扩展的 PHP_MSHUTDOWN_FUNCTION(extension_name) 钩子函数。扩展必须在此阶段注销自己注册的所有函数、类、常量,并释放所有在MINIT中分配的持久资源
    2. 清理全局配置:销毁EG(ini_directives)哈希表,释放所有配置选项的内存。
    3. 卸载Zend引擎:清理编译器、执行器等核心组件的全局状态。
    4. 清理其他全局资源:释放所有其他在MINIT阶段分配的全局内存。
  • 特点:此阶段是确保整个PHP进程能够干净退出、无内存泄漏的最后一道关卡。

【参考链接】https://github.com/pangudashu/php7-internal/blob/master/1/base_process.md