今天过了个不一样的儿童节,遇到了一个线上问题,各种刷数据进行解决。 暂不表如何避免类似问题,确实有很多不健壮的地方。单单看其中的技术问题,追了下源码,记录如下。
对原始问题进行了抽象,预期通过unset 将数组置空,结果呢,可以看下面的执行。 使用的php 版本为7.2. 具体代码以及输出如下
为了进一步验证问题,将$r 扩充为5个元素, 加入一些输出。
基于上面的输出对问题总结一下,预期输出的是个空数组,结果最后输出的是实际长度为2的,包含4个元素的数组。 初步猜测中间由于unset,所以跳跃了元素。
首先看下官方文档,然后赤落落的不建议这么使用,但是没有告诉我们为森么。
没关系看下源码,首先看下unset的逻辑;通过gdb 发现执行落到了 unset_dim_array 下的 zend_hash_del,和
看gbb的结果,很正常的将数据清理掉了。
为了对比array_walk中的不同,直接unset也调试了下,发现最后也是用来zend_hash_del. 应该和unset关系不大。
上面看起来很太平, 那应该问题出现在 array_walk的 遍历。 继续。关键的代码分为两处。
(1)会记录下下次要使用的pos
(2)设置pos & target_hash, 此处使用的是 Z_ARRVAL_P , 取出的数据仍旧是原数组, (zval).value.arr. 这也就解释了为什么取出的数组元素只少了一个。因为每次都会重新赋值。
所以结合代码来看,所谓的跳并不是真的跳跃,而是元素被删除后,对应原来的pos拿到的数据已经变了。
有问题还是要基于现象大胆假设,小心求证,对不理解的地方看一下源码,6.1很快乐^-^。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END