global声明建立引用而非复制变量,函数内对$var的操作直接作用于全局同名变量;递归调用间共享内存,易致状态残留与并发冲突,应优先使用参数传递或引用传参保障隔离性。

global 声明只是建立引用,不是复制变量
PHP 中 global $var 并不会创建新变量,而是让函数内对 $var 的读写直接作用于全局作用域的同名变量。这意味着:递归调用之间共享同一块内存地址,上一层修改了 $var,下一层拿到的就是已被改过的值。
常见错误现象:
- 第一次调用
getFiles()正常返回 12 个文件,第二次调用却返回 24 个(重复累加) - 递归遍历目录时,
$arr越来越大,且跨请求残留(尤其在 CLI 持续运行或 FastCGI 复用进程时)
根本原因不是语法错,而是设计误判——把“每次独立调用需隔离状态”当成了“一次调用中多层共享状态”。
static 变量在递归中也不清零,但作用域更可控
static $arr = [] 确实比 global 少一层命名污染,但它仍属于函数级静态存储:变量只在首次执行时初始化,之后所有调用(包括递归嵌套和后续顶层调用)都复用同一个数组实例。
使用场景有限制:
- 适合单次调用完成全部递归(比如一次
getFiles('logs')),且不介意该函数不可重入 - 不适合 Web 请求中多次调用同一函数(如 API 接口反复触发),因为
static不随请求生命周期自动销毁 - 无法用于并发环境(多个请求同时进入同一函数,
static数组被交叉写入)
示例中看似“两次调用都输出 1–9”,只是因为 static $i 在第二次调用前没被重置;但若函数返回后又被另一个请求调用,$i 就从上次结束处继续——这在 CLI 下难察觉,在 FPM 下极易出错。
真正安全的递归必须靠参数传递或引用传参
要让每次递归调用拥有独立状态,唯一可靠方式是把状态作为显式参数带进去。PHP 支持两种主流做法:
- 值传递:适用于小数据(如索引、计数器),每层递归获得副本,互不影响。例如
factorial($n)中$n-1是新值 - 引用传递:适用于大数据结构(如收集结果的数组),避免拷贝开销,且能保证所有层级操作同一容器。需明确加
&,如traverse($node, &$result)
关键细节:
- 引用传参必须在调用前初始化变量:
$result = [];,否则traverse($node, $result)会报Undefined variable - 不要混用
global和引用参数——既传参又改全局,逻辑混乱且难以调试 - 对象本身默认按引用语义传递(不需
&),但修改对象属性 ≠ 修改变量指向,这点常被忽略
为什么你看到的“能跑通”的例子其实很脆弱
很多博客示例用 global $arr 或 static $arr 实现了文件遍历,看起来没问题,是因为:
- 测试时只跑了一次,没验证重复调用
- 运行在 CLI 单次脚本中,进程退出后变量自动释放
- 没考虑并发、长连接、FPM worker 复用等真实部署场景
真正容易被忽略的点是:PHP 的 global 和 static 都不具备“请求局部性”。它们的状态可能跨越多个 HTTP 请求、多个用户会话、甚至不同模块的调用链。一旦业务变复杂,这种隐式状态就成了定时炸弹。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











