PHP 7 Разделение zval правильно
Я пытаюсь отделить zval
используя PHP 7.x, так что я немного запутался. Мне кажется, я обнаружил некоторые потенциально опасные ошибки в некоторых моих старых расширениях.
Я хочу обновить свойство объекта, который является массивом:
this->foo = [];
this->foo["a"]["b"]["c"] = "d";
PHP массивы пересчитаны, поэтому, когда несколько zval
с относятся к тому же zend_array
, он не будет скопирован, однако его счет будет увеличен. Таким образом, вот моя упрощенная функция для достижения этого с моими комментариями:
int multiple_array_offset_update(
zval *object,
const char *property,
zend_uint property_length,
zval *value,
const char *types,
int types_length,
int types_count,
...
) {
va_list ap;
zval tmp_arr;
int separated = 0;
// I'm going to update object->property
// exit if `object` is not object
if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
return FAILURE;
}
// Here tmp_arr has refcount = 2
my_func_read_property(&tmp_arr, object, property, property_length);
/** Separation only when refcount > 1 */
if (Z_REFCOUNTED(tmp_arr)) {
if (Z_REFCOUNT(tmp_arr) > 1) {
if (!Z_ISREF(tmp_arr)) {
zval new_zv;
ZVAL_DUP(&new_zv, &tmp_arr);
// After ZVAL_DUP
// new_zv refcount = 1
// tmp_arr refcount = 2
ZVAL_COPY_VALUE(&tmp_arr, &new_zv);
// After ZVAL_COPY_VALUE
// new_zv refcount = 1
// tmp_arr refcount = 1
Z_TRY_DELREF(new_zv);
// After Z_TRY_DELREF
// new_zv refcount = 0
// tmp_arr refcount = 0
// #1
// Should I use Z_TRY_DELREF?
// or
// ZVAL_NULL(&new_zv);
// zval_ptr_dtor(&new_zv);
separated = 1;
}
}
} else {
// #2
// Is it possible?
// What scenario should I use here?
}
/** Convert the value to array if not is an array */
if (Z_TYPE(tmp_arr) != IS_ARRAY) {
if (separated) {
convert_to_array(&tmp_arr);
} else {
array_init(&tmp_arr);
separated = 1;
}
// #3
// Should I remove decrement refcount here?
Z_DELREF(tmp_arr);
}
va_start(ap, types_count);
// Here is HT_ASSERT_RC1() which is invoked before performing any modification on
// a zend_array in all hashtable APIs defined in this source file.
my_func_array_update_multi_ex(&tmp_arr, value, types, types_length, types_count, ap);
va_end(ap);
if (separated) {
my_func_update_property_zval(object, property, property_length, &tmp_arr);
}
// #4.
// Should I use Z_TRY_DELREF(tmp_arr) here
// Or just ZVAL_NULL && zval_ptr_dtor
return SUCCESS;
}
Начиная с PHP 7.2, есть HT_ASSERT_RC1(
) макрос, определенный в zend_hash.c, который вызывается перед выполнением любой модификации zend_array
во всех хеш-таблицах API, определенных в этом исходном файле.
Я просто хочу знать, что я делаю не так. Я улавливаю утверждение в HT_ASSERT_RC1
потому что в:
my_func_array_update_multi_ex(&tmp_arr, value, types, types_length, types_count, ap);
tmp_arr
Звал имеет refcount = 0
,
На самом деле у меня есть 4 вопроса, которые я указал непосредственно в комментариях (отмечены #1
-#4
).