资源数据类型
迄今为止, 你都是工作在非常基础的用户空间数据类型上, 字符串, 数值, TRUE/FALSE等值. 即便上一章你已经开始接触数组了, 但也只是收集这些基础数据类型的数组.
复杂的结构体
现实世界中, 你通常需要在更加复杂的数据集合下工作, 通常涉及到晦涩的结构体指针. 一个常见的晦涩的结构体指针示例就是stdio的文件描述符, 即便是在C语言中也只是一个指针.
#include <stdio.h> int main(void) { FILE *fd; fd = fopen("/home/jdoe/.plan", "r"); fclose(fd); return 0; }
stdio的文件描述符和其他多数文件描述符一致, 都像是一个书签. 你扩展的调用应用仅需要在feof(), fread(), fwrite(), fclose()这样的实现函数调用时传递这个值. 有时, 这个书签必须是用户空间代码可访问的; 因此, 就需要在标准的php变量或者说zval *中有表示它的方法.
这里就需要一种新的数据类型. RESOURCE数据类型在zval *中存储一个简单的整型值, 使用作为已注册资源的索引用来查找. 资源条目包含了资源索引所表示的内部数据类型, 以及存储资源数据的指针等信息.
定义资源类型
为了使注册的资源条目所包含的资源信息更加明确, 需要定义资源的类型. 首先在你的sample.c中已有的函数实现下增加下面的代码片段
static int le_sample_descriptor; PHP_MINIT_FUNCTION(sample) { le_sample_descriptor = zend_register_list_destructors_ex( NULL, NULL, PHP_SAMPLE_DESCRIPTOR_RES_NAME, module_number); return SUCCESS; }
接下来, 滚动到你的代码文件末尾, 修改sample_module_entry结构体, 将NULL, /* MINIT */一行替换为下面的内容. 就像你给这个结构中增加函数列表结构时一样, 你需要确认在这一行末尾保留一个逗号.
PHP_MINIT(sample), /* MINIT */
最后, 你需要在php_sample.h中定义PHP_SAMPLE_DESCRIPTOR_RES_NAME, 将下面的代码放到你的其他常量定义下面:
#define PHP_SAMPLE_DESCRIPTOR_RES_NAME "File Descriptor"
PHP_MINIT_FUNCTION()代表第1章"PHP生命周期"中介绍的4个特殊的启动和终止操作中的第一个, 关于生命周期, 在第12章"启动, 终止以及之间的几个关键点"和第13章"INI设置"中还将深入讨论.
这里需要知道的非常重要的一点是MINIT函数在你的扩展第一次加载时执行一次, 它会在所有请求到达之前被执行. 这里我们利用这个机会注册了析构函数, 不过它们是NULL值, 不过在通过一个唯一整型ID足以知道一个资源类型时, 你很快就会修改它.
注册资源
现在引擎已经知道了你要存储一些资源数据, 是时候给用户空间的代码一种方式去产生实际的资源了. 要做到这一点, 需要如下重新实现fopen()命令:
PHP_FUNCTION(sample_fopen) { FILE *fp; char *filename, *mode; int filename_len, mode_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &filename, &filename_len, &mode, &mode_len) == FAILURE) { RETURN_NULL(); } if (!filename_len || !mode_len) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid filename or mode length"); RETURN_FALSE; } fp = fopen(filename, mode); if (!fp) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to open %s using mode %s", filename, mode); RETURN_FALSE; } ZEND_REGISTER_RESOURCE(return_value, fp, le_sample_descriptor); }
为了让编译器知道什么是FILE *, 你需要包含stdio.h. 这可以放在sample.c中, 但是为了本章后面部分做准备, 我还是要求你放到php_sample.h中.
如果你对前面的章节付出了努力, 最后一行前面的所有内容都应该可以读懂. 这一行代码执行的任务是将fp指针存储到资源的索引中, 将它和MINIT中定义的类型关联起来, 并存储一个可用于查找的key到return_value中.
如果需要存储多于一个指针的值, 或者存储一个直接量, 则必须新分配一段内存用来存储数据, 接着将指向这段内存的指针注册为资源.
译注:
1. 资源数据类型的注册实际上是在list_destructors(Zend/zend_list.c中定义的静态全局变量HashTable)中插入一个新构建的zend_rsrc_list_dtors_entry结构体, 这个结构体描述了这个资源类型的信息.
2. 资源数据的注册(ZEND_REGISTER_RESOURCE)实际上是在EG(regular_list)中使用zend_hash_next_free_element()得到下一个数值下标, 作为资源的ID, 并将传入的资源指针(封装为zend_rsrc_list_entry结构体)存储到EG(regular_list)中这个下标对应的元素中.
3. EG(regular_list)的初始化是在请求初始化阶段完成的, 通过跟踪代码, 可以看到其函数调用流程如下: php_request_startup(main/main.c) --> zend_active(Zend/zend.c) --> init_compiler(Zend/zend_compile.c) --> zend_init_rsrc_list(Zend/zend_list.c). 通过观察zend_init_rsrc_list()函数可以看出EG(regular_list)的析构函数是list_entry_destructor(Zend/zend_list.c). 而list_entry_destructor()的逻辑是从list_destructors(上面第一步所述的静态全局变量)中查找要释放的资源对象类型的信息, 接着按照注册资源类型时所指定的析构器进行析构.
4. 按照上面几点, 可以很容易理清本章前面所述内容. 首先注册一个资源类型, 这个资源类型中包含了诸如所属模块编号, 析构器句柄这样的信息. 接着, 在创建具体的资源对象时, 将资源对象和资源类型做了一个关联.
以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索指针
, 结构体指针
, 函数
, 数据类型
, 类型
, 资源
, 一个
return_false
php 资源类型、php资源类型是什么、php 数据类型、php 数据类型转换、php 判断数据类型,以便于您获取更多的相关知识。