首页  >  文章  >  后端开发  >  《PHP核心技术与最佳实践》在PHP扩展中为变量赋值

《PHP核心技术与最佳实践》在PHP扩展中为变量赋值

WBOY
WBOY原创
2016-06-20 12:35:28983浏览

阅读本节前如对PHP中变量的结构还不了解请前往:PHP内核中的变量

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

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn