欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

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

程序员文章站 2022-05-16 11:54:01
...
阅读本节前如对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