13. Copy on Write
The following code shouldn’t modify variable $y but it does.
php -r ‘$x=$y=[5]; test_scale_ref($x, 2); var_dump($x, $y);’
array(1) {
[0]=>
int(10)
}
array(1) {
[0]=>
int(10)
}
This is a bug. And it occurs because we update some value in-place without taking into account its reference-counter. In case it’s greater than 1, the same value is referenced from some other place. And we have to “separate” it (or perform a Copy-on-Write).
This may be done using few macros:
- SEPARATE_STRING(zv) – this will perform a copy-on-write for PHP string if its reference-counter is above 1.
- SEPARATE_ARRAY(zv) – this will perform a copy-on-write for PHP array if its reference-counter is above 1.
In our example, we need to “separate” only arrays, because zend_string_safe_realloc() takes care about reference-counting and performs copy-on-write itself. The fix is simple:
} 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) {