变量的作用是存放数据,由于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
