php扩展开发-变量
我们在php中用到的变量,在底层的C语言代码里是一个结构体,由四个成员组成
typedef struct _zval_struct { zvalue_value value; /* 变量的值,也是一个结构体 */ zend_uint refcount__gc; /* 变量的引用计数 typedef unsigned int zend_uint */ zend_uchar type; /* 变量的类型 typedef unsigned char zend_uchar */ zend_uchar is_ref__gc; /* 是否引用 typedef unsigned char zend_uchar*/
} zval;
typedef union _zvalue_value { long lval; /* 长整型,存储整数,bool,资源类型 */ double dval; /* 浮点型,存储小数 */ struct { char *val; int len; /* */ } str; /* 字符串,val是字符串指针,len是字符串长度 */ HashTable *ht; /* hashtable 即PHP数组 */ zend_object_value obj; /* php对象 */ } zvalue_value;
php变量的类型,即zval的type成员,一共有8种
类型 | zvalue_value中存储的成员 | 说明 |
---|---|---|
IS_NULL |
不存储值 | NULL |
IS_LONG |
lval | 整型 |
IS_DOUBLE |
dval | 浮点型 |
IS_BOOL |
lval | 布尔 |
IS_RESOURCE | lval | 资源 |
IS_STRING | str | 字符串 |
IS_ARRAY |
ht | 数组 |
IS_OBJECT |
obj | 对象 |
这些类型都是宏定义,在Zend/zend.h中可以查到
1 #define IS_NULL 0 2 #define IS_LONG 1 3 #define IS_DOUBLE 2 4 #define IS_BOOL 3 5 #define IS_ARRAY 4 6 #define IS_OBJECT 5 7 #define IS_STRING 6 8 #define IS_RESOURCE 7
通常我们不会直接使用php变量的成员,例如zval->type或zvalue_value->lval,为了代码的兼容性,zend给我们提供了很多的API方便我们操作变量
宏定义原型 | 获取变量 | 描述 |
---|---|---|
zend_uchar Z_TYPE(zval zv) |
type | 返回变量类型 |
long Z_LVAL(zval zv) |
value.lval | 返回zvalue_value的lval成员 |
zend_bool Z_BVAL(zval zv) |
value.lval | 返回zvalue_value的lval成员,并且转换成zend_bool类型 |
double Z_DVAL(zval zv) |
value.dval | |
long Z_RESVAL(zval zv) |
value.lval | 返回zvalue_value的lval成员,此时的type是IS_RESOURCE |
char* Z_STRVAL(zval zv) |
value.str.val | 返回字符串的值 |
int Z_STRLEN(zval zv) |
value.str.len | 返回字符串的长度 |
HashTable* Z_ARRVAL(zval zv) |
value.ht | 返回hashtable即数组 |
zend_object_value Z_OBJVAL(zval zv) |
value.obj | returns object value |
uint Z_OBJ_HANDLE(zval zv) |
value.obj.handle | returns the object handle for object value |
zend_object_handlers* Z_OBJ_HT_P(zval zv) |
value.obj.handlers | returns the handler table for object value |
zend_class_entry* Z_OBJCE(zval zv) |
value.obj | returns the class entry for object value |
HashTable* Z_OBJPROP(zval zv) |
value.obj | returns the properties of object value |
HashTable* Z_OBJPROP(zval zv) |
value.obj | returns the properties of object value |
HashTable* Z_OBJDEBUG(zval zv) |
value.obj | if an object has the get_debug_info handler set, it is called, else Z_OBJPROP is called |
以上这些API其实就是宏定义,上面列出的每个宏同时都有另外两种类似的定义,以Z_TYPE为例
#define Z_TYPE(zval) (zval).type //参数是zval
#define Z_TYPE_P(zval_p) Z_TYPE(*zval_p) //参数是zval的指针
#define Z_TYPE_PP(zval_pp) Z_TYPE(**zval_pp) //参数是zval的指针的指针
以上这些宏定义,在Zend/zend_operators.h中可以查到
1 #define Z_LVAL(zval) (zval).value.lval 2 #define Z_BVAL(zval) ((zend_bool)(zval).value.lval) 3 #define Z_DVAL(zval) (zval).value.dval 4 #define Z_STRVAL(zval) (zval).value.str.val 5 #define Z_STRLEN(zval) (zval).value.str.len 6 #define Z_ARRVAL(zval) (zval).value.ht 7 #define Z_AST(zval) (zval).value.ast 8 #define Z_OBJVAL(zval) (zval).value.obj 9 #define Z_OBJ_HANDLE(zval) Z_OBJVAL(zval).handle 10 #define Z_OBJ_HT(zval) Z_OBJVAL(zval).handlers 11 #define Z_OBJCE(zval) zend_get_class_entry(&(zval) TSRMLS_CC) 12 #define Z_OBJPROP(zval) Z_OBJ_HT((zval))->get_properties(&(zval) TSRMLS_CC) 13 #define Z_OBJ_HANDLER(zval, hf) Z_OBJ_HT((zval))->hf 14 #define Z_RESVAL(zval) (zval).value.lval 15 #define Z_OBJDEBUG(zval,is_tmp) (Z_OBJ_HANDLER((zval),get_debug_info)?Z_OBJ_HANDLER((zval),get_debug_info)(&(zval),&is_tmp TSRMLS_CC):(is_tmp=0,Z_OBJ_HANDLER((zval),get_properties)?Z_OBJPROP(zval):NULL)) 16 17 #define Z_LVAL_P(zval_p) Z_LVAL(*zval_p) 18 #define Z_BVAL_P(zval_p) Z_BVAL(*zval_p) 19 #define Z_DVAL_P(zval_p) Z_DVAL(*zval_p) 20 #define Z_STRVAL_P(zval_p) Z_STRVAL(*zval_p) 21 #define Z_STRLEN_P(zval_p) Z_STRLEN(*zval_p) 22 #define Z_ARRVAL_P(zval_p) Z_ARRVAL(*zval_p) 23 #define Z_AST_P(zval_p) Z_AST(*zval_p) 24 #define Z_OBJPROP_P(zval_p) Z_OBJPROP(*zval_p) 25 #define Z_OBJCE_P(zval_p) Z_OBJCE(*zval_p) 26 #define Z_RESVAL_P(zval_p) Z_RESVAL(*zval_p) 27 #define Z_OBJVAL_P(zval_p) Z_OBJVAL(*zval_p) 28 #define Z_OBJ_HANDLE_P(zval_p) Z_OBJ_HANDLE(*zval_p) 29 #define Z_OBJ_HT_P(zval_p) Z_OBJ_HT(*zval_p) 30 #define Z_OBJ_HANDLER_P(zval_p, h) Z_OBJ_HANDLER(*zval_p, h) 31 #define Z_OBJDEBUG_P(zval_p,is_tmp) Z_OBJDEBUG(*zval_p,is_tmp) 32 33 #define Z_LVAL_PP(zval_pp) Z_LVAL(**zval_pp) 34 #define Z_BVAL_PP(zval_pp) Z_BVAL(**zval_pp) 35 #define Z_DVAL_PP(zval_pp) Z_DVAL(**zval_pp) 36 #define Z_STRVAL_PP(zval_pp) Z_STRVAL(**zval_pp) 37 #define Z_STRLEN_PP(zval_pp) Z_STRLEN(**zval_pp) 38 #define Z_ARRVAL_PP(zval_pp) Z_ARRVAL(**zval_pp) 39 #define Z_AST_PP(zval_p) Z_AST(**zval_p) 40 #define Z_OBJPROP_PP(zval_pp) Z_OBJPROP(**zval_pp) 41 #define Z_OBJCE_PP(zval_pp) Z_OBJCE(**zval_pp) 42 #define Z_RESVAL_PP(zval_pp) Z_RESVAL(**zval_pp) 43 #define Z_OBJVAL_PP(zval_pp) Z_OBJVAL(**zval_pp) 44 #define Z_OBJ_HANDLE_PP(zval_p) Z_OBJ_HANDLE(**zval_p) 45 #define Z_OBJ_HT_PP(zval_p) Z_OBJ_HT(**zval_p) 46 #define Z_OBJ_HANDLER_PP(zval_p, h) Z_OBJ_HANDLER(**zval_p, h) 47 #define Z_OBJDEBUG_PP(zval_pp,is_tmp) Z_OBJDEBUG(**zval_pp,is_tmp)
如果你是初始变量赋值,或者需要同时改变变量的类型,你可以直接使用以下这些宏定义函数
#define ZVAL_RESOURCE(z, l) do { \//资源类型,值为l zval *__z = (z); \ Z_LVAL_P(__z) = l; \ Z_TYPE_P(__z) = IS_RESOURCE;\ } while (0) #define ZVAL_BOOL(z, b) do { \//布尔类型 zval *__z = (z); \ Z_LVAL_P(__z) = ((b) != 0); \ Z_TYPE_P(__z) = IS_BOOL; \ } while (0) #define ZVAL_NULL(z) { \//NULL Z_TYPE_P(z) = IS_NULL; \ } #define ZVAL_LONG(z, l) { \//整型 zval *__z = (z); \ Z_LVAL_P(__z) = l; \ Z_TYPE_P(__z) = IS_LONG; \ } #define ZVAL_DOUBLE(z, d) { \//浮点型 zval *__z = (z); \ Z_DVAL_P(__z) = d; \ Z_TYPE_P(__z) = IS_DOUBLE; \ } #define ZVAL_STRING(z, s, duplicate) do { \//字符串,duplicate表示是否赋值一份字符串再赋值 const char *__s=(s); \ zval *__z = (z); \ Z_STRLEN_P(__z) = strlen(__s); \ if (UNEXPECTED(Z_STRLEN_P(__z) < 0)) { \ zend_error(E_ERROR, "String size overflow"); \ } \ Z_STRVAL_P(__z) = (duplicate?estrndup(__s, Z_STRLEN_P(__z)):(char*)__s);\ Z_TYPE_P(__z) = IS_STRING; \ } while (0) #define ZVAL_STRINGL(z, s, l, duplicate) do { \//字符串,l表示字符串的长度 const char *__s=(s); int __l=l; \ zval *__z = (z); \ Z_STRLEN_P(__z) = __l; \ Z_STRVAL_P(__z) = (duplicate?estrndup(__s, __l):(char*)__s);\ Z_TYPE_P(__z) = IS_STRING; \ } while (0) #define ZVAL_EMPTY_STRING(z) do { \//设置空字符串 zval *__z = (z); \ Z_STRLEN_P(__z) = 0; \ Z_STRVAL_P(__z) = STR_EMPTY_ALLOC();\ Z_TYPE_P(__z) = IS_STRING; \ } while (0) #define ZVAL_ZVAL(z, zv, copy, dtor) do { \//赋值一个zval变量,copy表示是否硬拷贝zval中的数据,dtor表示是否对zv尝试unset zval *__z = (z); \ zval *__zv = (zv); \ ZVAL_COPY_VALUE(__z, __zv); \ if (copy) { \ zval_copy_ctor(__z); \ } \ if (dtor) { \ if (!copy) { \ ZVAL_NULL(__zv); \ } \ zval_ptr_dtor(&__zv); \ } \ } while (0) #define ZVAL_FALSE(z) ZVAL_BOOL(z, 0)//布尔值TRUE #define ZVAL_TRUE(z) ZVAL_BOOL(z, 1)//布尔值FALSE
接着我们来看一下变量的创建,复制和销毁的相关内容和操作的API
宏定义 | 说明 |
---|---|
ALLOC_ZVAL(zval* pzval) |
为变量分配空间 |
ALLOC_INIT_ZVAL(zval* pzval) |
为变量分配空间,并且初始化变量为NULL类型 |
MAKE_STD_ZVAL(zval* pzval) |
为变量分配空间,并且设置refcount__gc = 1,is_ref__gc = 0
|
ZVAL_COPY_VALUE(zval* dst, zval* src) |
复制变量,dst的value和type分别等于src的value和type |
INIT_PZVAL_COPY(zval* dst, zval*dst) |
先执行ZVAL_COPY_VALUE,然后设置dst的refcount__gc = 1,is_ref__gc = 0 |
SEPARATE_ZVAL(zval** ppzval) |
如果*ppzval的refcount__gc > 1, 则创建一个新的变量*zvalnew,用INIT_PZVAL_COPY函数来复制原变量,最后把ppzval指向新的变量 |
SEPARATE_ZVAL_IF_NOT_REF(zval** ppzval) |
如果*ppzval不是一个引用(is_ref_gc = 0) , 则执行SEPARATE_ZVAL(zval** ppzval) |
SEPARATE_ZVAL_TO_MAKE_IS_REF(zval** ppzval) |
如果*ppzval不是一个引用 Z_SET_ISREF_PP(zval** ppzval)设置is_ref__gc = 0 |
COPY_PZVAL_TO_ZVAL(zval dst, zval** src) |
results in dst being a copy of src without affecting the refcount of src |
MAKE_COPY_ZVAL(zval** src, zval* dst) |
performs INIT_PZVAL_COPY, then zval_copy_ctor on the new zval |
void zval_copy_ctor(zval* pzval) |
把pzval的数据复制一份出来。 |
void zval_ptr_dtor(zval* pzval) |
把pzval->refcount__gc减1,如果refcount__gc此时等于0,则销毁pzval指向的变量回收空间。 |
FREE_ZVAL(zval* pzval) |
free(pzval)回收空间 |
//zend.h #define INIT_PZVAL(z) \ (z)->refcount__gc = 1; \ (z)->is_ref__gc = 0; #define INIT_ZVAL(z) z = zval_used_for_init; #define ALLOC_INIT_ZVAL(zp) \ ALLOC_ZVAL(zp); \ INIT_ZVAL(*zp); #define MAKE_STD_ZVAL(zv) \ ALLOC_ZVAL(zv); \ INIT_PZVAL(zv); #define PZVAL_IS_REF(z) Z_ISREF_P(z) #define ZVAL_COPY_VALUE(z, v) \ do { \ (z)->value = (v)->value; \ Z_TYPE_P(z) = Z_TYPE_P(v); \ } while (0) #define INIT_PZVAL_COPY(z, v) \ do { \ ZVAL_COPY_VALUE(z, v); \ Z_SET_REFCOUNT_P(z, 1); \ Z_UNSET_ISREF_P(z); \ } while (0) #define SEPARATE_ZVAL(ppzv) \ do { \ if (Z_REFCOUNT_PP((ppzv)) > 1) { \ zval *new_zv; \ Z_DELREF_PP(ppzv); \ ALLOC_ZVAL(new_zv); \ INIT_PZVAL_COPY(new_zv, *(ppzv)); \ *(ppzv) = new_zv; \ zval_copy_ctor(new_zv); \ } \ } while (0) #define SEPARATE_ZVAL_IF_NOT_REF(ppzv) \ if (!PZVAL_IS_REF(*ppzv)) { \ SEPARATE_ZVAL(ppzv); \ } #define SEPARATE_ZVAL_TO_MAKE_IS_REF(ppzv) \ if (!PZVAL_IS_REF(*ppzv)) { \ SEPARATE_ZVAL(ppzv); \ Z_SET_ISREF_PP((ppzv)); \ } #define COPY_PZVAL_TO_ZVAL(zv, pzv) \ (zv) = *(pzv); \ if (Z_REFCOUNT_P(pzv)>1) { \ zval_copy_ctor(&(zv)); \ Z_DELREF_P((pzv)); \ } else { \ FREE_ZVAL(pzv); \ } \ INIT_PZVAL(&(zv)); #define MAKE_COPY_ZVAL(ppzv, pzv) \ INIT_PZVAL_COPY(pzv, *(ppzv)); \ zval_copy_ctor((pzv)); #define REPLACE_ZVAL_VALUE(ppzv_dest, pzv_src, copy) { \ int is_ref, refcount; \ \ SEPARATE_ZVAL_IF_NOT_REF(ppzv_dest); \ is_ref = Z_ISREF_PP(ppzv_dest); \ refcount = Z_REFCOUNT_PP(ppzv_dest); \ zval_dtor(*ppzv_dest); \ ZVAL_COPY_VALUE(*ppzv_dest, pzv_src); \ if (copy) { \ zval_copy_ctor(*ppzv_dest); \ } \ Z_SET_ISREF_TO_PP(ppzv_dest, is_ref); \ Z_SET_REFCOUNT_PP(ppzv_dest, refcount); \ } #define SEPARATE_ARG_IF_REF(varptr) \ if (PZVAL_IS_REF(varptr)) { \ zval *original_var = varptr; \ ALLOC_ZVAL(varptr); \ INIT_PZVAL_COPY(varptr, original_var); \ zval_copy_ctor(varptr); \ } else { \ Z_ADDREF_P(varptr); \ }
除了获取类型和值之外,还有一些操作跟l变量的refcount__gc和is_ref__gc相关
宏定义原型 | 描述 |
---|---|
zend_uint Z_REFCOUNT(zval zv) |
返回refcount__gc的值 |
zend_uint Z_SET_REFCOUNT(zval zv) |
设置zval变量的refcount__gc并返回 |
zend_uint Z_ADDREF(zval zv) |
++zval->refcount__gc并返回 |
zend_uint Z_DELREF(zval zv) |
--zval->refcount__gc并返回 |
zend_bool Z_ISREF(zval zv) |
返回zval->is_ref__gc |
void Z_UNSET_ISREF(zval zv) |
set is_ref__gc to 0 |
void Z_SET_ISREF(zval zv) |
set is_ref__gc to 1 |
void Z_SET_ISREF_TO(zval zv, zend_uchar to) |
set is_ref__gc to to |
这些宏定义也同样有*_P或*_PP的版本,可以在Zend/zend.h中查看
1 #define Z_REFCOUNT_PP(ppz) Z_REFCOUNT_P(*(ppz)) 2 #define Z_SET_REFCOUNT_PP(ppz, rc) Z_SET_REFCOUNT_P(*(ppz), rc) 3 #define Z_ADDREF_PP(ppz) Z_ADDREF_P(*(ppz)) 4 #define Z_DELREF_PP(ppz) Z_DELREF_P(*(ppz)) 5 #define Z_ISREF_PP(ppz) Z_ISREF_P(*(ppz)) 6 #define Z_SET_ISREF_PP(ppz) Z_SET_ISREF_P(*(ppz)) 7 #define Z_UNSET_ISREF_PP(ppz) Z_UNSET_ISREF_P(*(ppz)) 8 #define Z_SET_ISREF_TO_PP(ppz, isref) Z_SET_ISREF_TO_P(*(ppz), isref) 9 10 #define Z_REFCOUNT_P(pz) zval_refcount_p(pz) 11 #define Z_SET_REFCOUNT_P(pz, rc) zval_set_refcount_p(pz, rc) 12 #define Z_ADDREF_P(pz) zval_addref_p(pz) 13 #define Z_DELREF_P(pz) zval_delref_p(pz) 14 #define Z_ISREF_P(pz) zval_isref_p(pz) 15 #define Z_SET_ISREF_P(pz) zval_set_isref_p(pz) 16 #define Z_UNSET_ISREF_P(pz) zval_unset_isref_p(pz) 17 #define Z_SET_ISREF_TO_P(pz, isref) zval_set_isref_to_p(pz, isref) 18 19 #define Z_REFCOUNT(z) Z_REFCOUNT_P(&(z)) 20 #define Z_SET_REFCOUNT(z, rc) Z_SET_REFCOUNT_P(&(z), rc) 21 #define Z_ADDREF(z) Z_ADDREF_P(&(z)) 22 #define Z_DELREF(z) Z_DELREF_P(&(z)) 23 #define Z_ISREF(z) Z_ISREF_P(&(z)) 24 #define Z_SET_ISREF(z) Z_SET_ISREF_P(&(z)) 25 #define Z_UNSET_ISREF(z) Z_UNSET_ISREF_P(&(z)) 26 #define Z_SET_ISREF_TO(z, isref) Z_SET_ISREF_TO_P(&(z), isref)
我们会在扩展的开发过程中,频繁用到这些zend提供的API操作,你不需要把它记下来,随着开发的进行,你将会慢慢习惯这些API的使用。我们在开发的过程中,经常还需要转换变量的类型,可以使用下面这些函数。
//把zval *pzval的变量转换其它类型
void convert_to_long(zval* pzval)//整型 void convert_to_double(zval* pzval)//浮点 void convert_to_long_base(zval* pzval, int base)//整型,base表示进制,2,8,10,16等 void convert_to_null(zval* pzval)//NULL void convert_to_boolean(zval* pzval)//布尔 void convert_to_array(zval* pzval)//数组 void convert_to_object(zval* pzval)//对象 void convert_object_to_type(zval* pzval, convert_func_t converter)//根据回调函数转换成对象 convert_func_t functions should have the prototype (void) (zval* pzval)//接上,回调函数的原型