PHP扩展开发指南

1.1     使用数组

曾讲到,PHP数组本质上就是个HashTable,因此访问数组就是对HashTable进行操作,Zend为我们提供的一组数组函数也只是对HashTable操作进行了简单包装而已。

来看创建数组,由于数组也是存在于zval里的,因此要先用MAKE_STD_ZVAL()宏创建一个zval,之后调用如下宏将其转化为一个空数组:

array_init(zval*)

接下来是朝数组中添加元素,这对关联数组元素和非关联数组元素要采用不同操作。

 

1.1.1 关联数组元素

关联数组采用char*作为key,zval*作为value,可以使用如下宏将已有的zval加入数组或者更新已有元素:

int add_assoc_zval(zval *arr, char *key, zval *value)

需要特别注意的是,Zend不会复制zval,只会简单的储存其指针,并且不关心任何引用计数,因此不能将其他变量的zval或者是栈上的zval传给它,只能用MAKE_STD_ZVAL()宏构建。

Zend为常用的类型定义了相应的API,以简化我们的操作:

add_assoc_long(zval *array, char *key, long n);
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);
add_assoc_stringl(zval *array, char *key, char *str, uint length, int duplicate);
add_assoc_null(zval *array, char *key);

当函数发现目标元素已经存在时,会首先递减其原zval的refcount,然后才插入新zval,这就保证了原zval引用信息的正确性。这种行为是通过HashTable.pDestructor(参见1.2.1)实现的,每次删除一个元素时,HashTable都将对被删元素调用这个函数指针,而数组为其HashTable设置的函数指针就是用来处理被删除zval的引用信息。

另外,查看这些函数的源代码可以发现一个有意思的现象,它们没有直接使用HashTable操作,而是使用变量符号表操作,可见关联数组和变量符号表就是一种东西。

Zend没有提供删除和获取数组元素的函数,此类操作只能使用HashTable函数或者是2.6节的变量符号表操作。

1.1.2非关联数组元素

非关联数组没有key,使用index作为hash,相应函数和上面关联数组的十分类似:

add_index_zval(zval *array, uint idx, zval *value);
add_index_long(zval *array, uint idx, long n);
add_index_bool(zval *array, uint idx, int b);
add_index_resource(zval *array, uint idx, int r);
add_index_double(zval *array, uint idx, double d);
add_index_string(zval *array, uint idx, char *str, int duplicate);
add_index_stringl(zval *array, uint idx, char *str, uint length, int duplicate);
add_index_null(zval *array, uint idx);

如果只是想插入值,而不指定index的话,可以使用如下函数:

add_next_index_zval(zval *array, zval *value);
add_next_index_long(zval *array, long n);
add_next_index_bool(zval *array, int b);
add_next_index_resource(zval *array, int r);
add_next_index_double(zval *array, double d);
add_next_index_string(zval *array, char *str, int duplicate);
add_next_index_stringl(zval *array, char *str, uint length, int duplicate);
add_next_index_null(zval *array);

1.2      使用资源

1.2.1  注册资源类型

1.1.1节曾经提到,所谓资源就是内部数据的handle(但是这句话并不全对),使用资源是比较简单的,首先是注册一个资源类型:

int zend_register_list_destructors_ex(
rsrc_dtor_func_t ld,
rsrc_dtor_func_t pld,
char *type_name,
int module_number);

第一个参数是函数指针,当资源不再被使用或者模块将被卸载时,Zend使用它来销毁资源,稍候再作介绍;第二个参数和第一个类似,只是它被用来销毁持久性资源(*);type_name是资源名称,用户可以使用var_dump函数来读取;module_number是模块号,在启动函数中可以获取该值。

注册过程其实就是将我们传入的参数放到一个内部数据结构,然后把这个数据结构放入一个没有使用key的HashTable里,该函数返回的值,也就是所谓“资源类型id”,其实就是HashTable的index。

1.2.1  注册资源

注册完资源类型后,就可以注册一个该类型的资源了:

1 ZEND_REGISTER_RESOURCE(
2 rsrc_result,
3 rsrc_pointer,
4 rsrc_type)

src_pointer是个指针类型,就是你的资源的handle, 通常是指向内部数据的指针,当然也可以是index或者其它标志符;rsrc_type是上面获取的资源类型id;rsrc_result是个已有的zval,注册完成后,资源的id就被放入该zval,同时其type也被设为IS_RESOURCE,通常是传入return_value,以将资源返回给用户。

在内部,Zend使用如下数据结构表示一个资源:

1 typedef struct _zend_rsrc_list_entry {
2     void *ptr;
3     int type;
4     int refcount;
5 } zend_rsrc_list_entry;

ptr和type就是我们在上面传入的参数;refcount是引用计数,由Zend维护,当引用减到0时,Zend会销毁该资源。不出所料的是,这个数据结构也被组织在一个HashTable里,并且没有使用key,仅仅使用index——这就是zval里存放的东西。现在资源的整个脉络已经清晰:通过zval可以获得资源id,通过资源id可以获得资源handle和资源类型id,通过资源类型id可以获得资源的销毁函数。 现在讲一下销毁函数:

1 typedef void (*rsrc_dtor_func_t)(
2 zend_rsrc_list_entry *rsrc
3 TSRMLS_DC);

rsrc是需要被销毁的资源,我们在函数的实现中可以通过它获得资源的handle,并且加以处理,比如释放内存块、关闭数据库连接或是关闭文件描述符等。

1.2.3  获取资源

当创建了资源后,用户通常都要调用创建者提供的函数来操作资源,此时我们需要从用户传入的zval中取出资源:

1 ZEND_FETCH_RESOURCE(
2 rsrc,  rsrc_type,
3 passed_id, default_id,
4 resource_type_name, resource_type)

首个参数用于接收handle值,第二个参数是handle值的类型,这个函数会扩展成“rsrc = (rsrc_type) zend_fetch_resource(…)”,因此应该保证rsrc是rsrc_type类型的;passed_id是用户传入的zval,这里使用zval**类型,函数从中取得资源id;default_id用来直接指定资源id,如果该值不是-1,则使用它,并且忽略passed_id,所以通常应该使用-1;resource_type_name是资源名称,当获取资源失败时,函数使用它来输出错误信息;resource_type是资源类型,如果取得的资源不是该类型的,则函数返回NULL,这用于防止用户传入一个其他类型资源的zval。

不过,这个宏确实比较难用,用其底层的宏反倒更加容易些:

1 zend_list_find(id, type)

id是要查找的资源id;type是int*类型,用于接收取出的资源的类型,可以用它来判断这是不是我们想要的资源;函数最后返回资源的handle,失败返回NULL。

1.2.4  维护引用计数

通常,当用户对资源类型的PHP变量执行赋值或是unset之类操作时,Zend会自动维护资源的引用计数。但有时,我们也需要手动进行,比如我们要复用一个数据库连接或者用户调用我们提供的close操作关闭一个文件,此时可以使用如下宏:

1 zend_list_addref(id)
2 zend_list_delete(id)

id是资源id,这两个宏分别增加和减少目标资源的引用计数,第二个宏还会在引用计数减到0时,调用先前注册的函数销毁资源。

posted on 2012-12-10 20:41  lsl8966  阅读(384)  评论(0编辑  收藏  举报