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).

0 ответов

Другие вопросы по тегам