写时复制
下面的代码不应该修改变量 $y
,但却修改了。
php -r '$x=$y=[5]; test_scale_ref($x, 2); var_dump($x, $y);'
array(1) {
[0]=>
int(10)
}
array(1) {
[0]=>
int(10)
}
这是一个错误。出现这种情况的原因是,我们在就地更新某个值时,没有考虑其引用计数器。如果引用计数器大于 1,同一值就会被其他地方引用。我们必须将其 "分离"(或执行写时复制)。
这可以通过一些宏来实现:
-
SEPARATE_STRING(zv) - 如果 PHP 字符串的引用计数器大于 1,将执行写入时复制。
-
SEPARATE_ARRAY(zv) - 如果 PHP 数组的引用计数器大于 1,将执行写入时复制。
在我们的例子中,我们只需要 "分离" 数组,因为 zend_string_safe_realloc() 会考虑引用计数,并在写入时执行复制。解决方法很简单:
} else if (Z_TYPE_P(x) == IS_ARRAY) {
zval *val;
SEPARATE_ARRAY(x);
ZEND_HASH_FOREACH_VAL(Z_ARR_P(x), val) {
if (do_scale_ref(val, factor) != SUCCESS) {
下面的代码修正后的执行结果。
php -r '$x=$y=[5]; test_scale_ref($x, 2); var_dump($x, $y);'
array(1) {
[0]=>
int(10)
}
array(1) {
[0]=>
int(5)
}