当前位置: 首页 > 后端技术 > PHP

PHP进阶之路——PHP7使用资源包装第三方扩展

时间:2023-03-30 02:47:23 PHP

PHP扩展开发篇,我已经更新到《TIPI》在阅读下面的内容之前,我们假设你已经对PHP7的基本数据结构有一个大概的了解,这是阅读后面内容的前提。我们分为两部分:第一,实现一个自定义的文件操作扩展,用于文件的打开、读取、写入和关闭;然后分析每个操作背后的实现原理,部分实现将与PHP5.3使用的资源进行对比分析。通过原型生成扩展骨架首先进入源代码目录的ext目录,添加原型文件进行文件操作[shell][root@localhostphp-src-php-7.0.3]#cdext/[root@localhostext]#vimtipi_file.proto编辑为[shell]resourcefile_open(stringfilename,stringmode)stringfile_read(resourcefilehandle,intsize)boolfile_write(resourcefilehandle,stringbuffer)boolfile_close(resourcefilehandle)然后生成框架,如前所述,但是,我们不会详细介绍先大致了解一下,这样后面看的时候,思路可能会清晰很多。注册资源类型识别注册资源类型API[c]ZEND_APIintzend_register_list_destructors_ex(rsrc_dtor_func_tld,rsrc_dtor_func_tpld,constchar*type_name,intmodule_number)参数描述了ld释放资源时调用的函数。pld用于释放跨请求持续存在的持久资源的函数。type_name描述性类型名称的字符串别名。module_number在引擎内部使用,已经定义。例如,它已经定义在PHP_FUNCTION宏中。此API返回资源类型ID。此id应作为全局变量存储在扩展中,以便在必要时可以将其传递给其他资源API。添加资源释放回调函数。该方法表示释放该类资源时需要关闭打开的文件描述符。[c]staticvoidtipi_file_dtor(zend_resource*rsrcTSRMLS_DC){FILE*fp=(FILE*)rsrc->ptr;fclose(fp);}我们发现这个函数的参数类型是zend_resource。这是PHP7中新增的数据结构,在PHP5中为zend_rsrc_list_entry,具体细节留待后面分析。在PHP_MINIT_FUNCTION中注册资源类型我们知道,在PHP的生命周期中,当PHP加载时,PHP_MINIT_FUNCTION(模块启动函数)会被引擎调用。这允许引擎在初始化后执行资源类型、注册INI变量等操作。那么我们这里需要通过zend_register_list_destructors_ex在PHP_MINIT_FUNCTION中注册资源类型。[c]PHP_MINIT_FUNCTION(tipi_file){/*如果您有INI条目,请取消注释这些行REGISTER_INI_ENTRIES();*/le_tipi_file=zend_register_list_destructors_ex(tipi_file_dtor,NULL,TIPI_FILE_TYPE,module_number);returnNOPETILESUCCITESShasbeendefinedinfrontofwhich_PI_PI;},是扩展的别名。有关详细信息,请参阅本节开头给出的完整代码示例。在注册资源之前,注册一个新的资源类型,然后需要注册一个该类型的资源。注册资源API删除了PHP7中原有的ZEND_REGISTER_RESOURCE宏,直接使用zend_register_resource函数type在自定义file_open函数中实现资源注册[c]PHP_FUNCTION(file_open){char*filename=NULL;字符*模式=NULL;intargc=ZEND_NUM_ARGS();size_t文件名_len;size_tmode_len;#ifndefFAST_ZPPif(zend_parse_parameters(argcTSRMLS_CC,"ss",&filename,&filename_len,&mode,&mode_len)==FAILURE)return;#elseZEND_PARSE_PARAMETERS_START(2,2)Z_PARAM_STRING(filename,filename_len)Z_PARAM_STRING(mode,mode_len)ZEND_PARSE_PARAMETERS_END(使用VCWD宏替换标准C文件操作函数FILE*fp=VCWD_FOPEN(filename,mode);if(fp==NULL){RETURN_FALSE;}RETURN_RES(zend_register_resource(fp,le_tipi_file));}RETURN_RES宏的作用是将返回的zend_resource添加到zval中,最终的zval作为值返回。也就是说,这个函数的返回值是一个zval指针。RETURN_RES(zend_register_resource(fp,le_tipi_file))会将返回值的value.res设置为fp并将u1.type_info设置为IS_RESOURCE_EX。大家根据源码可以很直观的理解,这里就不贴代码详细解释了。使用资源API删除了PHP7中原有的ZEND_FETCH_RESOURCE宏,直接使用函数zend_fetch_resource,解析方式也变得简单很多,效率比PHP5高很多,后面我们会在表格中进行分析比较绘图。[c]ZEND_APIvoid*zend_fetch_resource(zend_resource*res,constchar*resource_type_name,intresource_type)参数说明res资源指针resource_type_namestring别名resource_type该类型资源类型id解析资源实现当我们要实现文件读取时,你仍然需要使用原来的fread函数,所以这里需要使用zend_fetch_resource将zend_resource解析为资源包裹的原始FILE*指针。[c]PHP_FUNCTION(file_read){intargc=ZEND_NUM_ARGS();int文件句柄_id=-1;zend_long大小;zval*文件句柄=NULL;文件*fp=NULL;炭*结果;size_tbytes_read;#ifndefFAST_ZPPif(zend_parse_parameters(argcTSRMLS_CC,"rl",&filehandle,&size)==FAILURE)return;#elseZEND_PARSE_PARAMETERS_START(2,2)Z_PARAM_RESOURCE(filehandle)Z_PARAM_LONG(size)ZEND_PARSE_PARAMETERS_END();#endif如果((fp=(FILE*)_resource(Z_RES_P(filehandle),TIPI_FILE_TYPE,le_tipi_file))==NULL){RETURN_FALSE;}结果=(char*)emalloc(size+1);bytes_read=fread(结果,1,大小,fp);结果[bytes_read]='\0';RETURN_STRING(result,0);}这里需要说明一下,脚本自动生成的扩展代码仍然使用了ZEND_FETCH_RESOURCE,这是一个BUG,因为自动生成的脚本(ext/skeleton/create_stubs)还没有更新然而。类似文件的写入操作也很相似,这里不再复制代码。资源删除资源删除API[c]ZEND_APIintzend_list_close(zend_resource*res)传入要删除的资源即可。API看似很简单,但实际上做了很多工作,后面会详细讲解原理分析。资源删除的实现我们需要在函数file_close[c]PHP_FUNCTION(file_close){intargc=ZEND_NUM_ARGS();中调用资源删除API;int文件句柄_id=-1;zval*文件句柄=NULL;#ifndefFAST_ZPPif(zend_parse_parameters(argcTSRMLS_CC,"r",&filehandle)==FAILURE)return;#elseZEND_PARSE_PARAMETERS_START(1,1)Z_PARAM_RESOURCE(filehandle)ZEND_PARSE_PARAMETERS_END();#endifzend_list_close(Z_RES_P(filehandle));RETURN_TRUE;}编译、安装和测试编译后的代码请参考本章第一节,这里不做说明,先说测试环节。直接使用php脚本进行测试,不用逐个函数编写测试样例,修改tipi_file.php文件即可。[php]$fp=file_open("./CREDITS","r+");var_dump($fp);var_dump(file_read($fp,6));var_dump(file_write($fp,"zhoumengakng"));var_dump(文件关闭($fp));然后通过命令行执行[shell]php7-d"extension=tipi_file.so"tipi_file.php