本文目录导读:

PHP 的启动流程可以分为 几个关键阶段,从服务器(如 Apache、Nginx、CLI)接收到请求开始,到 PHP 引擎完成初始化、执行脚本并返回响应,下面是详细的步骤解析:
PHP 的模块化启动模式
在深入了解流程前,先明确 PHP 运行时的两种模式:
| 运行模式 | 说明 |
|---|---|
| 模块模式(如 Apache mod_php) | PHP 作为 Web 服务器模块,随服务器进程启动,仅在首次请求时初始化。 |
| CGI/FastCGI(如 Nginx + FPM) | 每个请求 → 创建独立 PHP 进程(或复用进程池),处理完请求后保持进程存活。 |
PHP 启动的核心流程(以 Apache mod_php 为例)
阶段 1:模块加载(服务器启动时)
- Apache 加载
mod_php模块。 - 调用
PHP_MINIT_FUNCTION(Module Init):- 解析
php.ini。 - 注册 Zend 引擎。
- 加载内置扩展(如
date、pdo、mbstring等)。 - 分配全局资源(如内存管理器、错误处理栈)。
- 解析
阶段 2:请求初始化(收到 HTTP 请求后)
- 调用
PHP_RINIT_FUNCTION(Request Init):- 创建新的请求上下文。
- 初始化全局变量(
$_SERVER、$_REQUEST等)。 - 重置输出缓冲机制。
- 加载用户配置(如
.htaccess中的php_value)。
阶段 3:脚本执行
- 词法分析:PHP 代码 → tokens(
zlex)。 - 语法分析:tokens → 抽象语法树(AST)。
- 编译:AST → opcode(中间指令,类似字节码)。
- 执行:Zend 虚拟机逐条执行 opcode。
- 遇到函数/类定义时延迟编译。
- 遇到
include/require→ 插入新的文件,递归编译执行。
阶段 4:请求结束
- 调用
PHP_RSHUTDOWN_FUNCTION(Request Shutdown):- 清空请求级变量。
- 关闭打开的数据库连接(未持久化的)。
- 执行
register_shutdown_function()注册的回调。 - 输出缓冲区内容(如
ob_flush()后发送到客户端)。 - 销毁请求上下文。
阶段 5:进程/线程的持续运行
- 如果是
mod_php的 Worker 模式:进程不退出,等待下一个请求 → 返回阶段 2。 - 如果是 FPM:请求处理完后,Worker 进程继续监听新请求。
经典扩展初始化宏(C 语言级别)
PHP 扩展通过以下宏标记其生命周期:
// MINIT: 模块加载时调用一次(如注册类常量、ini 条目)
PHP_MINIT_FUNCTION(my_ext) { ... }
// RINIT: 每个请求开始时调用(如分配请求级缓存)
PHP_RINIT_FUNCTION(my_ext) { ... }
// RSHUTDOWN: 每个请求结束时调用
PHP_RSHUTDOWN_FUNCTION(my_ext) { ... }
// MSHUTDOWN: 模块卸载时调用(Apache 停止或 FastCGI 进程关闭)
PHP_MSHUTDOWN_FUNCTION(my_ext) { ... }
详细的 CLI 启动流程(单次运行)
当你执行 php -f script.php 时:
加载 PHP 二进制文件
2. 调用 sapi_startup()
├── 初始化 SAPI 全局变量(如 CLI 模式)
3. 调用 php_module_startup()
├── 解析 php.ini
├── PHP_MINIT (所有注册扩展)
└── 设置 zend 引擎
4. 调用 php_request_startup()
├── PHP_RINIT (扩展)
├── 初始化全局变量
├── 设置输出缓冲
5. 编译、执行 script.php(opcode 编译 → 执行)
6. 执行完毕,调用 php_request_shutdown()
├── PHP_RSHUTDOWN (扩展)
├── 刷新输出缓冲
├── 释放请求级内存
7. 调用 php_module_shutdown()
├── PHP_MSHUTDOWN (扩展)
├── 释放全局资源
8. 退出进程,返回 0
关键优化机制
- opcode 缓存(如 OPcache):
- 编译后的 opcode 被缓存到共享内存。
- 后续请求直接复用,跳过编译阶段(阶段 3 中的词法/语法/编译,只剩执行)。
- 持久连接:
- 数据库 PDO/MySQL 连接在
RSHUTDOWN时可以选择不销毁,复用于下一个请求。
- 数据库 PDO/MySQL 连接在
- 懒加载:
类和函数定义仅在首次调用时编译(避免每次请求都加载所有代码)。
流程图(简洁版)
服务器启动(Apache)
└─ 加载 mod_php
└─ PHP_MINIT (全局 ini, 扩展注册)
│
请求到达
├─ PHP_RINIT (请求级初始化, 全局变量)
├─ 编译脚本为 opcode (若 OPcache 未命中)
├─ Zend 虚拟机执行 opcode
└─ PHP_RSHUTDOWN (清理, 输出结果)
│
下一个请求 → 重复 RINIT → 执行 → RSHUTDOWN
关键要点总结
| 概念 | 说明 |
|---|---|
| MINIT | 只运行一次,用于全局静态资源初始化。 |
| RINIT | 每个请求运行一次,用于隔离请求数据。 |
| RSHUTDOWN | 清理请求资源,释放内存。 |
| MSHUTDOWN | 进程退出前运行,释放全局资源。 |
| OPcache | 避免重复编译,节省 CPU。 |
如果你需要我绘制更详细的调用栈图或解释某个特定 SAPI(如 FPM)的启动差异,可以直接告诉我。