回忆起曾经的一次面试题,当时回答的很简略,现在静下来仔细梳理了一下这道题。
PHP的执行流程?
我当时说了
-> HTTP 请求
-> PHP 解释器
-> 词法分析
-> 语法分析
-> 字节码生成
-> 执行
-> 输出
细细梳理下来,里面应该包含几个大的阶段节点,内里再细分出子节点
-
客户端请求
客户端(例如浏览器)通过 HTTP/HTTPS 请求向服务器发起对 PHP 文件的访问请求。
2.Web服务器接收请求并与PHP通信
服务器(通常是 Apache、Nginx 或 IIS)接收到客户端请求,判断该请求是 PHP 文件时,会将请求交由 PHP 引擎处理
3.PHP引擎处理
a. 加载代码
PHP 引擎从磁盘读取 .php 文件的内容。
b. 词法分析
PHP 引擎通过词法分析器将源代码分解为一系列的词法单元,这些 Tokens 是程序的最小语法单位,例如关键字、变量名、操作符。
c. 语法解析
语法解析器会根据 PHP 的语法规则,将词法单元(Tokens)组织成抽象语法树(AST,Abstract Syntax Tree)。
d. 编译为中间代码
PHP 将 AST 转换为中间代码(Opcode),这些是低级指令,可以被虚拟机直接执行。
e. 执行中间代码
Zend 引擎通过 Zend 虚拟机逐条执行中间代码,完成逻辑处理。 -
PHP输出生成
PHP 的输出缓冲区会捕获 echo 或直接输出的内容,经过处理后生成完整的 HTML 文档或其他类型的内容。
5.服务器返回响应
生成的内容被传递回服务器,由服务器通过 HTTP 响应发送给客户端浏览器。
6.客户端渲染
浏览器接收到服务器的响应后,解析 HTML、CSS、JavaScript 等内容并渲染页面。
上面是从执行流程上来说明的,但同样PHP生命周期概念也是基于此
-
请求初始化阶段
这一阶段的主要是准备执行环境。根据运行模式(如CLI或Web Server),初始化相应的环境变量和配置:加载配置:从 php.ini 文件中读取配置。 分配资源:初始化内存分配器和资源管理器。 请求参数解析:解析输入参数(如GET、POST、COOKIE等)。 扩展加载:加载动态链接扩展(如MySQL、GD库等)。 环境初始化:设置与运行模式相关的环境,如CGI模式或FastCGI模式。
-
脚本解析与编译阶段
PHP脚本文件解析并转化为中间代码(Opcode):词法分析和语法分析:将PHP脚本代码解析为抽象语法树(AST)。 编译:将AST转换为中间代码(Opcode)。 优化:对生成的Opcode进行优化(如常量折叠、无效代码移除)。 存储:如果启用了Opcache,则缓存编译后的Opcode以提升后续请求的性能。
-
脚本执行阶段
执行编译后的Opcode,以完成实际的逻辑:Opcode执行:由Zend虚拟机逐条解释执行Opcode。 函数与类调用:根据脚本中的逻辑调用函数、类方法或使用扩展功能。 I/O操作:如数据库查询、文件读写、网络通信等。 错误处理:处理运行时错误(如语法错误、未定义变量等)。
-
请求结束阶段
在脚本执行完成后,PHP会清理资源并释放内存:输出内容:将生成的输出内容发送到客户端。 资源释放:关闭打开的文件、数据库连接、网络连接等。 销毁变量:销毁用户定义的变量和对象,调用析构函数。 垃圾回收:触发Zend引擎的垃圾回收机制,回收循环引用的内存。
-
终止阶段
这是PHP生命周期的最后阶段,PHP引擎退出时完成:模块终止:卸载所有加载的扩展模块。 引擎关闭:释放Zend引擎占用的资源。 进程终止:如果是Web模式,PHP-FPM或Web服务器进程将等待下一个请求。
PS: 模式特定行为
PHP的生命周期在不同运行模式下略有不同:
CLI模式:
PHP在命令行启动并直接运行脚本。
生命周期是从启动到脚本执行结束。
Web服务器模式(如Apache/Nginx):
使用模块(mod_php)或PHP-FPM来处理HTTP请求。
生命周期与HTTP请求相对应,每次请求都会经历完整的生命周期。
在FastCGI模式下,PHP-FPM进程会保持运行,处理多个请求,减少初始化开销。
总结
PHP生命周期的每个阶段都有特定的任务:
初始化:准备运行环境。
解析与编译:将代码转化为中间代码。
执行:完成业务逻辑。
清理:释放资源并输出结果。
终止:关闭进程或等待新请求。
这是一个完整的PHP流程,从执行流程与生命周期两种角度,但里面还有一些更细力度的交互,很多都值得单独开一章说明,后面我再详细单独列举,例如PHP与Web服务器的通信方式SAPI,四层架构体系等等。