
PHP 中数组默认按值传递(写时复制),修改函数内副本不影响原始数组;对象则因存储的是标识符(句柄),属性修改天然反映到原实例——这是语言底层模型决定的,无法绕过 & 实现数组的“伪引用”行为。
php 中数组默认按值传递(写时复制),修改函数内副本不影响原始数组;对象则因存储的是标识符(句柄),属性修改天然反映到原实例——这是语言底层模型决定的,无法绕过 `&` 实现数组的“伪引用”行为。
在 PHP 开发中,一个高频困惑是:为什么向函数传入对象后能直接修改其属性,而传入数组却必须显式加 & 才能就地变更? 这并非设计疏漏,而是由 PHP 对不同类型采用的不同内存模型所决定。
? 核心机制:数组是“值语义”,对象是“句柄语义”
-
数组(及字符串、整型等标量):
PHP 将其视为值类型。传参时传递的是 ZVAL 结构的副本(PHP 7+ 启用写时复制优化:仅当发生写操作如 $arr['k'] = 'v' 或 array_push() 时才真正分离内存)。因此以下代码中,$array 在 test2() 内被扩展,但原始变量完全不受影响:public function test1() { $array = ['test' => 1, 'foo' => 'bar']; $this->test2($array); // 传的是副本(逻辑上) var_dump($array); // 仍为原数组,无 'steps' } public function test2($array) { foreach (range(1, 10) as $step) { $array['steps'][$step] = true; // 触发写时复制 → 操作副本 } } -
对象(PHP 5+):
变量中存储的并非对象数据本身,而是指向对象的唯一标识符(object handle)。传参时复制的是这个轻量级句柄,两个变量因此指向同一堆内存中的对象实体。所以即使按“值传递”声明,修改属性(如 $obj->name = 'new')也必然作用于原始对象:class Config { public $host = 'localhost'; } public function test3() { $cfg = new Config(); $this->test4($cfg); // 传句柄副本,但指向同一对象 var_dump($cfg->host); // 输出 'updated' ✅ } public function test4($cfg) { $cfg->host = 'updated'; // 修改共享对象的属性 }
⚠️ 注意:这不是引用传递(&),而是“按标识符值传递”。若想在函数内替换整个对象实例(如 $cfg = new Logger();),才需 function test4(&$cfg) —— 否则新赋值仅改变局部变量指向。
❌ 无法规避 & 的根本原因
你提出的诉求——“不加 &,也不用返回赋值,让数组像对象一样自动反映修改”——在 PHP 语言层面是不可能的。原因如下:
- PHP 的语法和运行时严格区分值类型与对象句柄类型;
- 数组没有内置的“引用透明性”,其行为由 ZVAL 的 refcount 和 is_ref 标志控制;
- 尝试用 $$, eval, 或封装类(如 ArrayObject)模拟,均无法改变基础数组变量的传递语义;
- ArrayObject 虽可被引用传递,但它本质是对象,其内部数组仍遵循值传递规则,且引入额外抽象层,违背“简单数组”的初衷。
✅ 推荐实践:清晰、安全、符合惯用法
| 场景 | 推荐方式 | 示例 |
|---|---|---|
| 仅读取/计算数组 | 值传递(默认) | function filterActive(array $users): array { ... } |
| 需原地修改大数组 | 显式引用传递(&$arr) | function normalizeKeys(&$arr): void { foreach ($arr as &$v) { ... } unset($v); } |
| 需多处修改或链式调用 | 返回新数组(函数式) | function withSteps(array $arr): array { return [...$arr, 'steps' => [...]]; } |
| 避免 foreach 引用残留 | 循环后立即 unset($v) | foreach ($arr as &$item) { $item .= '!'; } unset($item); |
此外,务必注意:
立即学习“PHP免费学习笔记(深入)”;
- 调用时禁止写 &$array:PHP 5.4+ 已废弃,PHP 8.0+ 直接报错;
- 不能对表达式传引用:modify(&$config['host']) 非法,须先 &$host = $config['host']; modify($host);;
- 引用会延长生命周期:长任务中未 unset() 引用变量,可能导致内存泄漏。
? 总结
PHP 数组的“非自动引用”不是缺陷,而是明确的设计选择:它保障了函数副作用的可见性与可控性。与其寻求绕过 & 的技巧,不如拥抱语言特性——用引用传递表达明确的就地修改意图,用返回值表达不可变转换逻辑。这种清晰的契约,正是写出可维护、可测试、团队友好的 PHP 代码的基石。



















