PHP之foreach引用填坑
起因
看到一篇文章描(就是它)述了PHP的一个语法特点,用文字来描述就是如果对一个数组进行foreach
引用遍历过后在对这个数组使用同样的变量进行一次普通foreach
遍历会导致这个数组的值发生变化。
我这个描述可能还是不太清楚,直接上代码:
可以看到在循环当中没有任何代码,所以两次打印出来的数据肯定应该是一模一样的,然而结果总是让人意外:
我当时就惊呆了!还能这样的?
文章中的解释只有一句话:因为数组最后一个元素的 $value 引用在 foreach 循环之后仍会保留,在第二个循环的时候实际上是对之前的指针不断的赋值。
看着这句话想了半天愣是没想明白,然后又找到了PHP关于foreach
的文档:PHP 手册=>语言参考=>流程控制
文档里面也只是给了个警告提示:Warning:数组最后一个元素的 $value 引用在 foreach 循环之后仍会保留。建议使用 unset() 来将其销毁。
原谅我,我真的没看懂…本来都不想去思考这个了,反正我平时也不会有这种骚操作,但是作为一个phper1,我的内心深处告诉我不能就这样算了,PHP的问题都搞不清楚还怎么在PHP界立足,世界上最好的语言都搞不好怎么去学习其他语言,想到这里我的手又不由自主的打开了搜索引擎…
经过
在我的一通搜索中终于找到了一篇文章:PHP foreach使用 &引用 的坑:
很认真的看了几遍,似乎有些眉目了,就是说在第一次foreach
循环结束以后里面的变量$a
并没有被销毁,而是保留了下来,并且依旧是一个引用2 变量。
明白了这个并没有解决我心中的疑虑:就算变量$a
没有被销毁,那么在第二次的foreach
也要被覆盖的,第一次循环$arr[2]
被覆盖成了1
,第二次循环$arr[2]
被覆盖成了2
,第三次循环$arr[2]
被覆盖成了4
,循环结束$arr[2]
不还是4
吗,咋打印出来就变成了2呢?
确实想不通,我在第二次循环中把$arr[2]
打印出来看一看,来验证一次:
结果:
真是搞不懂第三次咋就变了,这个时候切记心浮气躁,静下心来捋一捋整个流程,从第一次循环结束开始:
前面说第一次循环结束后的的变量$a
是引用1 ,怎么证明呢?那多简单,把变量$a
的类型打印出来看下不就知道了,找了下有个gettype()
函数,我们来试试:
看样子这不是我想要的结果…只有再去搜索一番了。
果然让我发现了一个新的函数xdebug_debug_zval()
,这个函数需要装Xdebug
扩展,关于这个函数的介绍可以看一下PHP官网:引用计数基本知识
这个函数不仅会打印出来变量的类型和值,还会有两个额外信息:is_ref
和refcount
,is_ref
表示这个变量是否为引用变量,refcount
表示指向这个zval变量容器的变量(也称符号即symbol)个数。我们只需要看is_ref
就可以了:
结果:
从结果中可以看到is_ref
为1
即true
表示变量$a
确实是引用变量,然后我们继续分析foreach
循环过程中的数据变化:
- 把
$arr[0]
的值1
赋值给$a
,这时$a
的引用即$arr[2]
的值为1
; - 把
$arr[1]
的值2
赋值给$a
,这时$a
的引用即$arr[2]
的值为2
; - 把
$arr[2]
的值(注意了,第二步$arr[2]
的值已经被覆盖为2
)2
赋值给$a
,这时$a
的引用即$arr[2]
的值为2
; - 此时
$arr
的值就是[1, 2, 2]
了。
总结
经过这么一遍梳理总算是搞清楚怎么回事了,其实就是在第二次循环中通过赋值给引用变量改变了被循环数组的值,导致原数组值的变化,现在也理解了官方文档中那句警告是什么意思了。