[Seis desarrollo de extensión de php7] operación zval

Generar varios tipos de zval
PHP7 transfiere el recuento de referencia de la variable al valor específico, por lo que zval se usa más como un formato de transmisión unificado y, en muchos casos, solo se usa temporalmente, como pasar parámetros durante las llamadas a funciones. Los datos finales required es El zval que lleva zval, la función ya no se preocupa por el zval después de obtener zend_value del zval, y el zval puede asignarse directamente en la pila. Después de asignar zval, necesitamos establecerlo en el tipo que necesitamos y establecer su zend_value. Las macros de la serie ZVAL_XXX () definidas en PHP se utilizan para hacer esto. El primer parámetro z de estas macros es el puntero del zval que se va a establecer El siguiente es el zend_value que se va a establecer.

ZVAL_UNDEF(z)    zval被销毁
ZVAL_NULL(z)    zval设置为NULL
ZVAL_FALSE(z)    设置为false
ZVAL_TRUE(z)    设置为true
ZVAL_BOOL(z, b)    设置为布尔型,b为IS_TRUE、IS_FALSE,与上面两个等价
ZVAL_LONG(z, l)    设置为整形,l类型为zend_long  如:zval z; ZVAL_LONG(&z, 88);
ZVAL_DOUBLE(z, d)    设置为浮点型,d类型为double
ZVAL_STR(z, s)    设置字符串,将z的value设置为s,s类型为zend_string*,不会增加s的refcount,支持interned strings
ZVAL_NEW_STR(z, s)    同ZVAL_STR(z, s),s为普通字符串,不支持interned strings
ZVAL_STR_COPY(z, s)    将s拷贝到z的value,s类型为zend_string*,同ZVAL_STR(z,s),这里会增加s的refcount
ZVAL_ARR(z, a)    设置为数组,a类型为zend_array*
ZVAL_NEW_ARR(z)    新分配一个数组,主动分配一个zend_array
ZVAL_NEW_PERSISTENT_ARR(z)    创建持久化数组,通过malloc分配,需要手动释放
ZVAL_OBJ(z, o)    设置为对象,o类型为zend_object*
ZVAL_RES(z, r)    设置为资源,r类型为zend_resource*
ZVAL_NEW_RES(z, h, p, t)    新创建一个资源,h为资源handle,t为type,p为资源ptr指向结构
ZVAL_REF(z, r)    设置为引用,r类型为zend_reference*
ZVAL_NEW_EMPTY_REF(z)    新创建一个空引用,没有设置具体引用的value


Obtener el valor y tipo de
zval. El tipo de zval se obtiene a través de dos macros: Z_TYPE (zval) y Z_TYPE_P (zval *). Este valor se toma como zval.u1.v.type, pero no solo modifica este tipo cuando configurándolo, pero establezca typeinfo, porque zval tiene otros indicadores que deben establecerse, como si se debe usar el recuento de referencias, si se puede recolectar basura, si se puede copiar, etc.

Escriba un hello_typeof () similar a gettype () para obtener el tipo de la variable:

PHP_FUNCTION(hello_typeof)
{
    zval *userval = NULL;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &userval) == FAILURE) {
        RETURN_NULL();
    }
    switch (Z_TYPE_P(userval)) {
        case IS_NULL:
            RETVAL_STRING("NULL");
            break;
 
        case IS_TRUE:
            RETVAL_STRING("true");
            break;
 
        case IS_FALSE:
            RETVAL_STRING("false");
            break;
 
        case IS_LONG:
            RETVAL_STRING("integer");
            break;
 
        case IS_DOUBLE:
            RETVAL_STRING("double");
            break;
 
        case IS_STRING:
            RETVAL_STRING("string");
            break;
 
        case IS_ARRAY:
            RETVAL_STRING("array");
            break;
 
        case IS_OBJECT:
            RETVAL_STRING("object");
            break;
 
        case IS_RESOURCE:
            RETVAL_STRING("resource");
            break;
 
        default:
            RETVAL_STRING("unknown type");
    }
}


Obtenga el valor de diferentes tipos de zval

Z_LVAL(zval)、Z_LVAL_P(zval_p)    返回zend_long
Z_DVAL(zval)、Z_DVAL_P(zval_p)    返回double
Z_STR(zval)、Z_STR_P(zval_p)    返回zend_string*
Z_STRVAL(zval)、Z_STRVAL_P(zval_p)    返回char*,即:zend_string->val
Z_STRLEN(zval)、Z_STRLEN_P(zval_p)    获取字符串长度
Z_STRHASH(zval)、Z_STRHASH_P(zval_p)    获取字符串的哈希值
Z_ARR(zval)、Z_ARR_P(zval_p)、Z_ARRVAL(zval)、Z_ARRVAL_P(zval_p)    返回zend_array*
Z_OBJ(zval)、Z_OBJ_P(zval_p)    返回zend_object*
Z_OBJ_HT(zval)、Z_OBJ_HT_P(zval_p)    返回对象的zend_object_handlers,即zend_object->handlers
Z_OBJ_HANDLER(zval, hf)、Z_OBJ_HANDLER_P(zv_p, hf)    获取对象各操作的handler指针,hf为write_property、read_property等,注意:这个宏取到的为只读,不要试图
修改这个值(如:Z_OBJ_HANDLER(obj, write_property) = xxx;),因为对象的handlers成员前加了const修饰符
Z_OBJCE(zval)、Z_OBJCE_P(zval_p)    返回对象的zend_class_entry*
Z_OBJPROP(zval)、Z_OBJPROP_P(zval_p)    获取对象的成员数组
Z_RES(zval)、Z_RES_P(zval_p)    返回zend_resource*
Z_RES_HANDLE(zval),Z_RES_HANDLE_P(zval_p)    返回资源handle
Z_RES_TYPE(zval)、Z_RES_TYPE_P(zval_p)    返回资源type
Z_RES_VAL(zval)、Z_RES_VAL_P(zval_p)    返回资源ptr
Z_REF(zval)、Z_REF_P(zval_p)    返回zend_reference*
Z_REFVAL(zval)、Z_REFVAL_P(zval_p)    返回引用的zval*


Operaciones de matriz Las
matrices se utilizan como variables para transportar otras variables. La implementación interna utiliza la conocida HashTable. Para crear una matriz que se devolverá a PPHP, la forma más sencilla:

Agregue un valor del tipo especificado a una matriz indexada numéricamente

$arr = array();    => array_init(arr);    初始化一个新数组
$arr[] = NULL;     add_next_index_null(arr);     
$arr[] = 42;     add_next_index_long(arr, 42);    
$arr[] = true;     add_next_index_bool(arr, 1);    
$arr[] = 3.14;     add_next_index_double(arr, 3.14);    
$arr[] = 'foo';  add_next_index_string(arr, "foo", 1);    
$arr[] = $myvar; add_next_index_zval(arr, myvar);    


Agregue un valor del tipo especificado al índice numérico especificado en la matriz

$arr[0] = NULL;        add_index_null(arr, 0);     
$arr[1]= 42;            add_index_long(arr, 1, 42);    
$arr[2] = true;        add_index_bool(arr, 2, 1);    
$arr[3] = 3.14;        add_index_double(arr, 3, 3.14);    
$arr[4] = 'foo';    add_index_string(arr, 4, "foo", 1);    
$arr[5] = $myvar;    add_index_zval(arr, 5, myvar);    


 
Agregue un valor del tipo especificado a la matriz de índices asociativos

$arr['abc'] = NULL;        add_assoc_null(arr, "abc");    
$arr['def'] = 711;        add_assoc_long(arr, "def", 711);     
$arr['ghi'] = true;        add_assoc_bool(arr, "ghi", 1);    
$arr['jkl'] = 1.44;        add_assoc_double(arr, "jkl", 1.44);    
$arr['mno'] = 'baz';    add_assoc_string(arr, "mno", "baz", 1);    
$arr['pqr'] = $myvar;    add_assoc_zval(arr, "pqr", myvar);


Manipulación de cuerdas

//创建zend_string
zend_string *zend_string_init(const char *str, size_t len, int persistent);
 
//字符串复制,只增加引用
zend_string *zend_string_copy(zend_string *s);
 
//字符串拷贝,硬拷贝
zend_string *zend_string_dup(zend_string *s, int persistent);
 
//将字符串按len大小重新分配,会减少s的refcount,返回新的字符串
zend_string *zend_string_realloc(zend_string *s, size_t len, int persistent);
 
//延长字符串,与zend_string_realloc()类似,不同的是len不能小于s的长度
zend_string *zend_string_extend(zend_string *s, size_t len, int persistent);
 
//截断字符串,与zend_string_realloc()类似,不同的是len不能大于s的长度
zend_string *zend_string_truncate(zend_string *s, size_t len, int persistent);
 
//获取字符串refcount
uint32_t zend_string_refcount(const zend_string *s);
 
//增加字符串refcount
uint32_t zend_string_addref(zend_string *s);
 
//减少字符串refcount
uint32_t zend_string_delref(zend_string *s);
 
//释放字符串,减少refcount,为0时销毁
void zend_string_release(zend_string *s);
 
//销毁字符串,不管引用计数是否为0
void zend_string_free(zend_string *s);
 
//比较两个字符串是否相等,区分大小写,memcmp()
zend_bool zend_string_equals(zend_string *s1, zend_string *s2);
 
//比较两个字符串是否相等,不区分大小写
#define zend_string_equals_ci(s1, s2) \
(ZSTR_LEN(s1) == ZSTR_LEN(s2) && !zend_binary_strcasecmp(ZSTR_VAL(s1)
, ZSTR_LEN(s1), ZSTR_VAL(s2), ZSTR_LEN(s2)))
...


Recuento de referencias
Al operar variables relacionadas con el espacio de usuario de PHP en la extensión, debe considerar si necesita sumar o restar el recuento de referencias, como en el siguiente ejemplo:

function test($arr){
    return $arr;
}
$a = array(1,2);
$b = test($a);


Si la función test () se implementa como una función interna, esta función acepta un parámetro de matriz pasado por el espacio de usuario de PHP, y luego lo devuelve y lo asigna a otra variable en el espacio de usuario de PHP. En este momento, debe aumentar el refcount de la matriz entrante, debido a que esta matriz es asignada por el espacio de usuario de PHP, refcount = 1 antes de que se llame a la función, es equivalente al parámetro asignado a la función cuando se pasa a la función interna, por lo que refcount aumenta de 1 a 2. Esta vez el aumento es cuando se ejecuta la función y se libera el parámetro Se restará. Después de regresar y asignar a $ b, hay dos variables que apuntan a esta matriz en este momento, por lo que la función interna necesita para aumentar el recuento de referencia, y la referencia agregada es para el valor de retorno. test () se traduce en una función interna:
 

PHP_FUNCTION(test)
{
    zval *arr;
    if(zend_parse_parameters(ZEND_NUM_ARGS(), "a", &arr) == FAILURE){
    RETURN_FALSE;
    }
    //如果注释掉下面这句将导致core dumped
    Z_TRY_ADDREF_P(arr);
    RETURN_ARR(Z_ARR_P(arr));
}


Entonces, ¿bajo qué circunstancias debería considerar establecer un recuento de referencia?

(1) Asignación de variable: la asignación de variable es la situación más común. Un tipo de variable que utiliza el recuento de referencias tiene refcount = 1 durante la asignación inicial. Si esta variable se asigna posteriormente a otras variables, su referencia se incrementará en consecuencia. Count
(2) Array operación: si inserta una variable en una matriz, debe aumentar el recuento de referencias de esta variable, si desea eliminar un elemento de la matriz, debe reducir su referencia en consecuencia
(3) Llamada de función: los parámetros de paso en realidad se pueden considerar como asignación de Variable ordinaria, asigne la variable en el espacio de llamada a la variable en el espacio de función llamado. Cuando la función regrese, la variable en el espacio de función se destruirá y la referencia al parámetro se reducirá en este momento. El kernel completa dos procesos y no es necesario que se amplíen.
Atributo de miembro de procesamiento (4): cuando se asigna una variable al atributo de miembro del objeto, es necesario aumentar el recuento de referencias
. Las siguientes macros se definen en PHP como referencia operaciones de conteo:

//获取引用数:pz类型为zval*
#define Z_REFCOUNT_P(pz) zval_refcount_p(pz)
//设置引用数
#define Z_SET_REFCOUNT_P(pz, rc) zval_set_refcount_p(pz, rc)
//增加引用
#define Z_ADDREF_P(pz) zval_addref_p(pz)
//减少引用
#define Z_DELREF_P(pz) zval_delref_p(pz)
#define Z_REFCOUNT(z) Z_REFCOUNT_P(&(z))
#define Z_SET_REFCOUNT(z, rc) Z_SET_REFCOUNT_P(&(z), rc)
#define Z_ADDREF(z) Z_ADDREF_P(&(z))
#define Z_DELREF(z) Z_DELREF_P(&(z))
 
//只对使用了引用计数的变量类型增加引用,建议使用这个
#define Z_TRY_ADDREF_P(pz) do { \
if (Z_REFCOUNTED_P((pz))) { \
Z_ADDREF_P((pz)); \
} \
} while (0)
 
#define Z_TRY_DELREF_P(pz) do { \
if (Z_REFCOUNTED_P((pz))) { \
Z_DELREF_P((pz)); \
} \
} while (0)
 
#define Z_TRY_ADDREF(z) Z_TRY_ADDREF_P(&(z))
#define Z_TRY_DELREF(z) Z_TRY_DELREF_P(&(z))


Estos tipos de operaciones de macro son todos zval o zval *. Si necesita manipular el recuento de referencia de un valor específico, puede usar las siguientes macros:

//直接获取zend_value的引用,可以直接通过这个宏修改value的refcount
#define GC_REFCOUNT(p) (p)->gc.refcount


También hay varias macros de uso común:

//判断zval是否用到引用计数机制
#define Z_REFCOUNTED(zval) ((Z_TYPE_FLAGS(zval) & IS_TYPE_REFCO
UNTED) != 0)
#define Z_REFCOUNTED_P(zval_p) Z_REFCOUNTED(*(zval_p))
//根据zval获取value的zend_refcounted头部
#define Z_COUNTED(zval) (zval).value.counted
#define Z_COUNTED_P(zval_p) Z_COUNTED(*(zval_p))


 

Supongo que te gusta

Origin blog.csdn.net/zhangge3663/article/details/115173780
Recomendado
Clasificación