Laravel中each闭包需传&$list引用才可修改外部变量的原因
PHP闭包use语法传值/传引用差异的底层原理
PHP的函数作用域默认是隔离的,闭包(匿名函数)不能直接访问外层作用域的变量,必须通过use关键字显式声明要继承的外层变量。use默认传值、加&传引用的行为差异,底层逻辑完全不一样:
- 默认值传递(不带&标记)
不带&声明的继承变量,会在闭包定义的瞬间,把外层变量的值做一次浅拷贝,存到闭包自己的独立作用域里。从PHP底层的zval(PHP存储变量的核心结构体)层面说,闭包内的变量和外层原变量是完全独立的两个变量容器,闭包里对变量的赋值、修改只会作用在自己持有的副本上,根本碰不到外层的原变量。
这里有个很容易踩的坑:如果继承的是对象类型变量(比如你示例里的$request、$user),PHP里对象变量存的只是指向对象实例的标识符,值拷贝的时候复制的只是这个标识符,副本和原变量会指向同一个对象实例。这种情况下,你在闭包里改对象属性、调用对象的修改型方法(比如$user->name = 'test')是能生效的,但要是你在闭包里直接给变量重新赋值(比如$user = new User()),外层的$user不会有任何变化。 - 引用传递(带&标记)
加了&声明的继承变量,不会做任何值拷贝,闭包内部直接持有外层原变量的zval内存地址,相当于闭包里的变量和外层原变量指向同一个变量容器。不管你在闭包里是修改变量内容,还是给变量重新赋值,操作的都是同一个实体,外层变量会同步所有变更。
对应你贴的代码场景:
你初始化的$list是Collection对象,而Collection的大部分链式方法(比如merge、map、filter、flatMap这类)都会返回全新的Collection实例,不会修改原Collection本身。如果你在闭包里写$list = $list->merge($val)这类逻辑,本质是给$list变量重新赋值,要是不用&做引用传递,你改的只是闭包内的$list副本,外层的$list始终是最开始那个空集合,最后dd($list)当然拿不到预期结果。
要是你闭包里只写$list->push($val)这类直接修改原Collection内部元素的逻辑,就算不带&也能正常跑,但只要涉及给$list变量本身重新赋值的操作,就必须用引用传递才能同步修改外层的变量。
内容的提问来源于stack exchange,提问作者eggsloveoats
相关产品推荐
相关产品推荐

