时间:2021-07-01 10:21:17 帮助过:13人阅读
变量的作用是存放数据,由于PHP中变量不但保存着值还保存着类型,所以不但要为变量赋值,同时还要为变量设置类型。
1) 长整型(整型)类型变量:PHP内核中整数全部是长整型的(long),其值就保存在之前讲过的PHP变量zval结构的联合体value中的lval字段中,相应的类型为IS_LONG,赋值的代码如下:
zval *new_var;
MAKE_STD_ZVAL(new_var);
new_var->value.lval = 12;
new_var->type = IS_LONG;
但为了兼容性最好使用ZVAL_LONG宏赋值:
zval *new_var;
MAKE_STD_ZVAL(new_var);
ZVAL_LONG(new_var,12);
上面的例子跟代码效果相同;
2) 双精度(浮点数)类型变量:同长整型赋值差别只在变量类型为IS_DOUBLE,ZVAL_DOUBLE(new_var,12.56);
3) 字符串类型变量:除了保存字符串的值外,还需要保存字符串的长度(提供给strlen()等函数使用)。字符串的值保存在zval结构的value联合体的str结构体中的val字段中,而字符串长度保存在str结构体中的len字段,并且相应的类型设置为IS_STRING;另外值得注意的是用来保存字符串值的内在块应该使用ZEND引擎内存管理函数去申请这样可以避免自己管理这些内存还可以让ZEND引擎处理起来更方便,赋值代码如下:
zval *new_var;
char *str = "this is n new string variable";
MAKE_STD_ZVAL(new_var);
new_var->value.str.len = strlen(str);
new_var->value.str.val = estrdup(str); //estrdup是ZEND引擎的内存管理函数
new_var->type = IS_STRING;
使用ZEND_STRING宏来赋值:ZVAL_STRING(new_var,str,1);这里第三个参数指明该字符串是否需要被复制(使用ZEND引擎内存管理函数),当设置为1时,将会复制这二个参数指向的字符串,设置为0时,直接把val字段指向这二个参数指向的字符串(可理解为引用赋值)。
如果只想取字符串的一部分或者已经知道字符串的长度进行赋值,可使用宏ZEND_STRINGL(zval,string,length,duplicate)完成这项工作,参数length就是指定字符串的长度,ZVAL_STRINGL宏要比ZVAL_STRING宏快,其定义如下:
#define ZVAL_STRINGL(z,s,l,duplicate) { \
char *_s = (s); int _l = l; \
(z)->value.str.len = _l; \
(z)->value.str.val = (duplicate ? estrndup(_s,_l) : _s); \
(z)->type =IS_STRING; \
}
从定义可看出字符串的长度(len字段)被直接设置成length参数的值所以不再用strlen()去计算字符串的长度,如果想创建一个空字符串可将其长度设置为0,并且把empty_string作为字符串的值即可
new_var->value.str.len = 0;
new_var->value.str.val = empty_string;
new_var->type = IS_STRING;或者使用ZVAL_EMPTY_STRING宏完成:
ZVAL_EMPTY_STRING(new_string); //这里没有漏掉参数确定不是ZVAL_EMPTY_STRING(new_var,new_string);?
4) 布尔类型变量:布尔类型的赋值和整型的赋值差不多,区别在于把数据类型字段设置为IS_BOOL,并且lval字段的值只能是0或1,ZVAL_BOOL宏代码如下:ZVAL_BOOL(new_var,1);
5) 数组类型变量:数组在PHP中扮演了重要的角色,PHP的强大可以说是因为数组的灵活,在内核中数组是使用哈希表存储的(HashTable);在为变量赋值数组时,先要创建一个HashTable,然后将其保存在zval.val容器的ht字段中,Zend引擎提供了一个简单的接口array_init()来完成这项工作,代码如下:
zval *new_var;
MAKE_STD_ZVAL(new_var);
array_init(new_array);
上面的代码相当于
创建一个空的数组之后就可往里面添加元素了,有 平行线铁API可供使用,下面列出可使用的所有API(成功时都返回SUCCESS,失败时都返回FAILURE):
关联数组的API:
add_assoc_long(zval *array,char *key,long n); //相当于$array["key"]=10;
add_assoc_unset(zval *array,char *key); //$array["key"] = NULL;
add_assoc_bool(zval *array,char *key,int b);
add_assoc_resource(zval *array,char *key,int r);
add_assoc_double(zval *array,char *key,double d);
add_assoc_string(zval *array,char *key,char *str,int duplicate); //$array["key"] = "string";
add_assoc_stringl(zval *array,char *key,char *str,uinit length,int duplicate); //添加指定长度的字符串
add_assoc_zval(zval *array,char *key,zval *value); //添加一个zval结构,这在添加另外一个数组、对象或流等数据时很有用,相当于$array["key"] = $value;
索引数组的API:索引数组和关联数组很相似,差别在于下标是字符串还是整型,所以把上面的原型中所有的char *key换成unit idx就是索引数组的API了,就不再列出了。
上面提到的API只是抽象HashTable的API函数而已,也可以直接使用HashTable的API函数进行操作,如添加一个元素到数组sk 使用zend_hash_update()函数进行操作。使用API添加一个元素到数组的例子:
zval array,element;
char *key = "key for search";
char *value = "value_for_element";
MAKE_STD_ZVAL(array);
MAKE_STD_ZVAL(element);
array_init(array);
ZVAL_STRING(element,value,1);
add_assoc_zval(array,key,element);
6) 对象类型变量:在讲解变量赋予对象类型的值之前,先要了解PHP在对象跟数组的关系:对象可转化成数组(此过程不可逆,因为对象的functions会丢失),对象的属性和数组是可相互转换的。对象的函数等应该是保存在一个HashTable中?这个问题留到了解PHP内核中的函数的时候再来解决。
创建一个对象的API代码如下:
zval *new_object;
MAKE_STD_ZVAL(new_object);
if(object_init(new_object)!=SUCCESS) {
RETURN_NULL();
}
Zend引擎为对象添加属性的API如下:
add_property_long(zval *object,char
key,long l);
object,char *key);add_property_bool(zval *object,char *key,int b);
add_property_resource(zval *object,char *key,long r);
add_property_double(zval *object,char *key,double d);
add_property_string(zval *object,char *key,char *str,int duplicate);
add_property_stringl(zval *object,char *key,char *str,uint length,int duplicate);
add_property_zval(zval *object,char *key,zval *container);
如添加一个名字为“name”类型为字符串的属性如下:
zval *new_object;
MAKE_STD_ZVAL(new_object);
if(object_init(new_object)!=SUCCESS) {
RETURN_NULL();
}
add_property_string(new_object,"name","James",1);
7) 资源类型变量:创建资源类型的变量比创建其他类型的变量烦琐一点,严格来说,资源不是数据类型,它是一个可以维护任何数据类型的抽象(就像C语言中的指针)。所有的资源都是保存在一个Zend内部的资源列表中,列表中的每人资源都有一个指向实际数据的指针,如果对PHP内核足够了解的话,可以直接访问这些资源,不过为了兼容性和安全性还是建议使用Zend引擎提供的API访问它们。
当资源失去所有的引用的时候就会触发相应的析构函数,而这个析构函数是有资源自己的提供的,原因是zend引擎不能管理资源的实际数据,所以为了防止内存泄漏就必须提供析构函数释放这些内存。
数据库连接和文件操作符是PHP资源类型的,另外也可以保存自定义的数据结构等其他的类型的数据为资源类型。
使用zend_register_list_destructors_ex()函数可以用来注册一个资源变量的析构函数,该函数返回一个资源的句柄,资源句柄的作用就是把资源与资源的析构函数相关联,以下是zend_register_list_destructors_ex()函数的定义:
ZEND_API int_zend_register_list_destructors_ex(rsrc_dtor_func_t ld,rsrc_dtor_func_t pld,char *type_name,int module_number);其参数说明如下:
ld:普通资源的析构函数,
pld:持久化资源的析构函数,
type_name:为资源指定一个名称,在PHP内部为某个资源类型起个名字是好习惯,用户调用var_dump($resource)时就可取得该资源的名称。
module_number:在模块的PHP_MINIT_FUNCTION函数中会自动定义,因此可将其忽略。
ld和pld必须要至少提供一个,另外一个析构函数可以简单的设为NULL.
资源的析构函数原型必须如下定义:void resource_destruction_handler(zend_rsrc_entry *rsrc TSRMLS_DC);
参数rsrc是一个指向zend_rsrc_list_entry结构体的指针:
typedef struct _zend_rsrc_list_entry {
void *ptr; //用于保存资源的真正数据的地址,一般在析构函数中释放ptr字段指向的内存
int type;
int refcount;
} zend_rsrc_list_entry;
例如定义一个链表数据结构如下:
typedef struct _ListNode {
struct _ListNode *next;
void *data;
} ListNode;
然后资源变量保存的就是这个链表的头指针,析构函数可以这样编写:
void list_destroy_handler(zend_rsrc_list_entry
rsrc TSRMLS_DC) {
ListNode current,next;
)rsrc->ptr; //rsrc->ptr就是一个指针类型,这里是指针类型转换会把ptr所指向的资源转换成ListNode的链表资源?while(current) {
next = current->next;
free(current);
current = next;
}
}
这样就可以释放整个链表的内存。
while