技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> PHP --> PHP内核介绍及扩展开发指南―高级主题

PHP内核介绍及扩展开发指南―高级主题

浏览:3562次  出处信息

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 注册资源

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

ZEND_REGISTER_RESOURCE(
rsrc_result,
rsrc_pointer,
rsrc_type)

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

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

typedef struct _zend_rsrc_list_entry {
	void *ptr;
	int type;
	int refcount;
} zend_rsrc_list_entry;

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

     现在讲一下销毁函数:

typedef void (*rsrc_dtor_func_t)(
zend_rsrc_list_entry *rsrc
TSRMLS_DC);

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

1.2.3 获取资源

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

ZEND_FETCH_RESOURCE(
rsrc,  rsrc_type,
passed_id, default_id,
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。

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

zend_list_find(id, type)

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

1.2.4 维护引用计数

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

zend_list_addref(id)
zend_list_delete(id)

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

建议继续学习:

  1. linux内核研究笔记(一)内存管理 – page介绍    (阅读:8505)
  2. PHP内核介绍及扩展开发指南―Extensions 的编写    (阅读:4614)
  3. chrome扩展应用开发教程之开发chrome应用基础    (阅读:4660)
  4. 我的内核配置文件    (阅读:3669)
  5. Linux内核协议栈对于timewait状态的处理    (阅读:3664)
  6. 深入理解PHP原理之扩展载入过程    (阅读:3427)
  7. chrome扩展应用开发教程之调试和打包上线    (阅读:3519)
  8. 编写python的C语言扩展    (阅读:3371)
  9. PHP内核介绍及扩展开发指南―基础知识    (阅读:3361)
  10. Mediawiki扩展编写实战    (阅读:3159)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1