发布时间:2015-05-27 19:22:42作者:知识屋
php中的资源数据类型可以说是php作为胶水语言的一个重要组成部分。在扩展中使用资源类型,可以使你的程序既可以与第三方类库中的自定义数据类型的指针相互联系,也可以与用户空间的数据进行交互。使用起来十分的方便。
首先来看一下资源在php内核中的结构:
typedef struct _zend_rsrc_list_entry{ void *ptr; int type; int refcount;}zend_rsrc_list_entry;
每一个资源都是由这个结构所实现的。
首先来看下资源复合的结构:
在c语言中有stdio的文件描述符,通过这个描述符可以进行一系列的文件操作。
#includec语言中的stdio的文件描述符是与每个打开的文件相匹配的一个变量,表示了一个FILE类型的指针。利用这个文件描述符可以进行诸如fwrite、fopen在内的各类文件操作。这样一个FILE类型的指针变量,如果想在php中同样的使用,就要用到资源了。int main(void){ FILE *fd; fd = fopen("/home/jdoe/.plan", "r"); fclose(fd); return 0;}
php中的资源类型使用一个整数来表示资源的句柄。同时利用这个整数可以通过内核访问到资源实际的内存指针位置。
如果想要使用资源,就必须先定义资源类型
首先声明资源:
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;}
zend_module_entry sample_module_entry = { //创建一个入口 #if ZEND_MODULE_API_NO >= 20010901 //这个是一个版本号 STANDARD_MODULE_HEADER, #endif PHP_SAMPLE_EXTNAME, php_sample_functions, /* Functions 这里是把php_function加入到Zend中去*/ PHP_MINIT(sample), /* MINIT */ NULL, /* MSHUTDOWN */ NULL, /* RINIT */ NULL, /* RSHUTDOWN */ NULL, /* MINFO */ #if ZEND_MODULE_API_NO >= 20010901 PHP_SAMPLE_EXTVER, #endif STANDARD_MODULE_PROPERTIES};
接下来就需要注册资源:
zend engine已经知道你在存储资源数据,现在需要给用户空间的代码一种产生资源的方式。所以重新定义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);//注册一个文件句柄的资源,这就算是把你的int整数和文件句柄这一资源给联系起来了。}有下面两点需要注意:资源并不局限于文件句柄,我们可以申请一块内存,并注册指向它的指针为资源。资源可以对应任意类型的数据,当然包括自己定义的结构体。这使得对自定义类型的操作非常的方便
PHP_FUNCTION(sample_fwrite){ FILE *fp; zval *file_resource;//文件资源,php函数写入的时候用的是指针。 char *data; int data_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &file_resource, &data, &data_len) == FAILURE ) { RETURN_NULL(); } /* 使用zval*去验证资源类型,同时从查找表中获取相应的指针存储在fp中 */ZEND_FETCH_RESOURCE(fp, FILE*, &file_resource, -1, PHP_SAMPLE_DESCRIPTOR_RES_NAME, le_sample_descriptor); /* Write the data, and * return the number of bytes which were * successfully written to the file */ RETURN_LONG(fwrite(data, 1, data_len, fp));}先看一下ZEBD_FETCH_RESOURCE()函数:
#define ZEND_FETCH_RESOURCE(rsrc, rsrc_type, passed_id, default_id, resource_type_name, resource_type) rsrc = (rsrc_type) zend_fetch_resource(passed_id TSRMLS_CC, default_id, resource_type_name, NULL, 1, resource_type); ZEND_VERIFY_RESOURCE(rsrc);在我们的例子中:
fp = (FILE*) zend_fetch_resource(&file_descriptor TSRMLS_CC, -1, PHP_SAMPLE_DESCRIPTOR_RES_NAME, NULL, 1, le_sample_descriptor);if (!fp) { RETURN_FALSE;}从用户空间中输入了file_resource,作为资源类型。然后这个file_resource作为输入参数传给ZEND_FETCH_RESOURCE,这个函数会去管理所有资源的哈希表中拉相应的数据。然后存储到相应的资源指针中,这里是fp。第二个参数FILE*指定了指针的类型,-1是default_id,然后最后两个参数给出资源的名称以及当时注册这个类型的资源时的静态整数le_sample_descriptor。
这个函数与zend_hash_find函数相比,一致的地方在于都是从哈希表中拉取数据,不同之处在于Fetch的时候还会进行额外的数据类型检查。确保拉到正确的数据类型。
在宏定义中的ZEND_VERIFY_RESOURCE如果检测到类型错误就会自动返回。
另外一种从资源变量ID中得到指针的方式是zend_list_find():
PHP_FUNCTION(sample_fwrite){ FILE *fp; zval *file_resource; char *data; int data_len, rsrc_type; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &file_resource, &data, &data_len) == FAILURE ) { RETURN_NULL(); } fp = (FILE*)zend_list_find(Z_RESVAL_P(file_resource), &rsrc_type); if (!fp || rsrc_type != le_sample_descriptor) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid resource provided"); RETURN_FALSE; } RETURN_LONG(fwrite(data, 1, data_len, fp));}两种方法都可以用来抓取资源指针。
在完成了资源的利用之后,就需要销毁资源了:
销毁资源的时候,仅仅把资源句柄unset是不够的,还必须回收相应的内存。在文件操作的例子中,就必须还要进行fclose操作:
重新回到当时声明资源的函数:
le_sample_descriptor = zend_register_list_destructors_ex(//注册用来回收的函数 NULL, NULL, PHP_SAMPLE_DESCRIPTOR_RES_NAME, module_number);
le_sample_descriptor = zend_register_list_destructors_ex( php_sample_descriptor_dtor, NULL, PHP_SAMPLE_DESCRIPTOR_RES_NAME, module_number);
static void php_sample_descriptor_dtor( zend_rsrc_list_entry *rsrc TSRMLS_DC){ FILE *fp = (FILE*)rsrc->ptr; fclose(fp);}
第二个参数则是用在一个类似于长连接类型的资源上,也就是这个资源创建后会一直在内存中,而不会在request结束时被释放掉。会在web服务器进程终止时调用。
为了避免下面这种情况所造成的文件句柄没有被正确返回的情况,还是需要一个显式的sample_fclose这种强制析构函数:
PHP_FUNCTION(sample_fclose){ FILE *fp; zval *file_resource; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &file_resource) == FAILURE ) { RETURN_NULL(); } /* While it's not necessary to actually fetch the * FILE* resource, performing the fetch provides * an opportunity to verify that we are closing * the correct resource type. */ ZEND_FETCH_RESOURCE(fp, FILE*, &file_resource, -1, PHP_SAMPLE_DESCRIPTOR_RES_NAME, le_sample_descriptor); /* Force the resource into self-destruct mode */zend_hash_index_del(&EG(regular_list), Z_RESVAL_P(file_resource)); RETURN_TRUE;}
如何对PHP文件进行加密方法 PHP实现加密的几种方式介绍
php生成圆角图片的方法 电脑中php怎么生成圆角图片教程
用PHP构建一个留言本方法步骤 php怎么实现留言板功能
php中三元运算符用法 php中的三元运算符使用说明
php文件如何怎么打开方式介绍 php文件用什么打开方法
PHP怎么插入数据库方法步骤 php编程怎么导入数据库教程
如何安装PHPstorm并配置方法教程 phpstorm安装后要进行哪些配置
PHP 获取远程文件大小的3种解决方法 如何用PHP获取远程大文件的大小
20个实用PHP实例代码 php接口开发实例代码详细介绍
如何架设PHP服务器方法步骤 怎么搭建php服务器简单教程