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

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

相关推荐
BingoGo1 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack1 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo2 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack2 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack3 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo3 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack4 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理4 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
QQ5110082855 天前
python+springboot+django/flask的校园资料分享系统
spring boot·python·django·flask·node.js·php
WeiXin_DZbishe5 天前
基于django在线音乐数据采集的设计与实现-计算机毕设 附源码 22647
javascript·spring boot·mysql·django·node.js·php·html5