PHP扩展开发教程(总结)
PHP是一种解释型的语言,对于用户而言,我们精心的控制内存意味着easierprototyping和更少的崩溃!当我们深入到内核之后,所有的安全防线都已经被越过,最终还是要依赖于真正有责任心的软件工程师来保证系统的稳定运行。
1、线程安全宏定义
在TSRM/TSRM.h文件中有如下定义
#defineTSRMLS_FETCH() void***tsrm_ls=(void***)ts_resource_ex(0,NULL)
#defineTSRMLS_FETCH_FROM_CTX(ctx)void***tsrm_ls=(void***)ctx
#defineTSRMLS_SET_CTX(ctx) ctx=(void***)tsrm_ls
#defineTSRMG(id,type,element) (((type)(*((void***)tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element)
#defineTSRMLS_D void***tsrm_ls
#defineTSRMLS_DC ,TSRMLS_D
#defineTSRMLS_C tsrm_ls
#defineTSRMLS_CC ,TSRMLS_C
在ext/xsl/php_xsl.h有这么一段话
/*Ineveryutilityfunctionyouaddthatneedstousevariables.
inphp_xsl_globals,callTSRM_FETCH();afterdeclaringother.
variablesusedbythatfunction,orbetteryet,passinTSRMLS_CC
afterthelastfunctionargumentanddeclareyourutilityfunction
withTSRMLS_DCafterthelastdeclaredargument. Alwaysreferto
theglobalsinyourfunctionasXSL_G(variable). Youare.
encouragedtorenamethesemacrossomethingshorter,see
examplesinanyotherphpmoduledirectory.
*/
1.在方法定义时加上TSRMLS_D(如果方法没有参数用这个)或者TSRMLS_DC(有1个以上的参数)
2.在方法调用时用TSRMLS_C(如果方法没有参数用这个)或者TSRMLS_CC(有1个以上的参数)
应该可以这样理解
第一个后缀字母D表示定义,即D=Define,第一个后缀字母C表示调用,即C=Call,而第二个后缀字母C是不是表示逗号呢?C=Comma(逗号)
TSRMLS_D就是定义了,所以是 void***tsrm_ls
TSRMLS_DC是带逗号的定义,所以是,void***tsrm_ls
TSRMLS_C是调用,即tsrm_ls
TSRMLS_CC是调用并带逗号,即,tsrm_ls
所以一个是形参、一个是实参
可以这样使用
intphp_myext_action(intaction_id,char*messageTSRMLS_DC);
php_myext_action(42,"Themeaningoflife"TSRMLS_CC);
一般推荐使用tsrm_ls指针定义的方式来保证线程安全
TSRMLS_FETCH调用需要一定的处理时间。这在单次迭代中并不明显,但是随着你的线程数增多,随着你调用TSRMLS_FETCH()的点的增多,你的扩展就会显现出这个瓶颈。因此,请谨慎的使用它。注意:为了和c++编译器兼容,请确保将TSRMLS_FETCH()和所有变量定义放在给定块作用域的顶部(任何其他语句之前)。因为TSRMLS_FETCH()宏自身有多种不同的解析方式,因此最好将它作为变量定义的最后一行
2、PHP的生命周期
PHP的最多的两种运行模式是WEB模式、CLI模式,无论哪种模式,PHP工作原理都是一样的,作为一种SAPI运行。
1、当我们在终端敲入php这个命令的时候,它使用的是CLI。
它就像一个web服务器一样来支持php完成这个请求,请求完成后再重新把控制权交给终端。
2、当使用Apache作为宿主时,当一个请求到来时,PHP会来支持完成这个请求
PHP_MINIT_FUNCTION 初始化module时运行
PHP_MSHUTDOWN_FUNCTION 当module被卸载时运行
PHP_RINIT_FUNCTION 当一个REQUEST请求初始化时运行
PHP_RSHUTDOWN_FUNCTION 当一个REQUEST请求结束时运行
PHP_MINFO_FUNCTION 这个是设置phpinfo中这个模块的信息
PHP_GINIT_FUNCTION 初始化全局变量时
PHP_GSHUTDOWN_FUNCTION 释放全局变量时
比如PHP_GINIT_FUNCTION
PHP_GINIT_FUNCTION(test) { /**初始化全局变量*/ } //对应的C代码 voidzm_globals_ctor_test(zend_test_globals*test_globalsTSRMLS_DC) { /**初始化全局变量*/ } //在线程退出时,需要将之前自己申请的资源释放时,可以使用PHP_GSHUTDOWN_FUNCTION来注册析构函数。 PHP_GSHUTDOWN_FUNCTION(test) { /**清除全局变量*/ } //对应的C代码 voidzm_globals_dtor_test(zend_test_globals*test_globalsTSRMLS_DC) { /**清除全局变量*/ }
这里有一段代码,可以测试一下
intminit_time; PHP_MINIT_FUNCTION(test) { minit_time=time(NULL); returnSUCCESS; } PHP_MSHUTDOWN_FUNCTION(test) { FILE*fp=fopen("mshutdown.txt","a+"); fprintf(fp,"%ld\n",time(NULL)); fclose(fp); returnSUCCESS; } intrinit_time; PHP_RINIT_FUNCTION(test) { rinit_time=time(NULL); returnSUCCESS; } PHP_RSHUTDOWN_FUNCTION(test) { FILE*fp=fopen("rshutdown.txt","a+"); fprintf(fp,"%ld\n",time(NULL)); fclose(fp); returnSUCCESS; } PHP_MINFO_FUNCTION(test) { php_info_print_table_start(); php_info_print_table_header(,"moduleinfo","enabled"); php_info_print_table_end(); /*Removecommentsifyouhaveentriesinphp.ini DISPLAY_INI_ENTRIES(); */ } PHP_FUNCTION(test) { php_printf("%d",time_of_minit); php_printf("%d",time_of_rinit); return; }
3、段错误调试
Linux下的C程序常常会因为内存访问错误等原因造成segmentfault(段错误)此时如果系统coredump功能是打开的,那么将会有内存映像转储到硬盘上来,之后可以用gdb对core文件进行分析,还原系统发生段错误时刻的堆栈情况。这对于我们发现程序bug很有帮助。
使用ulimit-a可以查看系统core文件的大小限制;使用ulimit-c[kbytes]可以设置系统允许生成的core文件大小。
ulimit-c0不产生core文件
ulimit-c100设置core文件最大为100k
ulimit-cunlimited不限制core文件大小
步骤:
1、当发生段错误时,我们查看ulimit-a(corefilesize(blocks,-c)0)并没有文件,
2、设置:ulimit-cunlimited不限制core文件大小
3、运行程序,发生段错误时会自动记录在core中(php-fWorkWithArray.php)
4、ls-alcore.*在那个文件下(-rw-------1leconteleconte13926401-0622:31core.2065)
5、使用gdb运行程序和段错误记录的文件。(gdb./testcore.2065)
6、会提哪行有错。
很多系统默认的core文件大小都是0,我们可以通过在shell的启动脚本/etc/bashrc或者~/.bashrc等地方来加入ulimit-c命令来指定core文件大小,从而确保core文件能够生成。
除此之外,还可以在/proc/sys/kernel/core_pattern里设置core文件的文件名模板,详情请看core的官方man手册。
4、常见的变量操作宏
CG ->ComplierGlobal 编译时信息,包括函数表等(zend_globals_macros.h:32)
EG ->ExecutorGlobal 执行时信息(zend_globals_macros.h:43)
PG ->PHPCoreGlobal 主要存储php.ini中的信息
SG ->SAPIGlobal SAPI信息
1、SG 针对SAPI信息在main/SAPI.h文件中
typedefstruct_sapi_globals_struct{ void*server_context; sapi_request_inforequest_info; sapi_headers_structsapi_headers; intread_post_bytes; unsignedcharheaders_sent; structstatglobal_stat; char*default_mimetype; char*default_charset; HashTable*rfc1867_uploaded_files; longpost_max_size; intoptions; zend_boolsapi_started; doubleglobal_request_time; HashTableknown_post_content_types; zval*callback_func; zend_fcall_info_cachefci_cache; zend_boolcallback_run; }sapi_globals_struct;
看一下SG的定义
BEGIN_EXTERN_C()
#ifdefZTS
#defineSG(v)TSRMG(sapi_globals_id,sapi_globals_struct*,v)
SAPI_APIexternintsapi_globals_id;
#else
#defineSG(v)(sapi_globals.v)
externSAPI_APIsapi_globals_structsapi_globals;
#endif
SAPI_APIvoidsapi_startup(sapi_module_struct*sf);
SAPI_APIvoidsapi_shutdown(void);
SAPI_APIvoidsapi_activate(TSRMLS_D);
SAPI_APIvoidsapi_deactivate(TSRMLS_D);
SAPI_APIvoidsapi_initialize_empty_request(TSRMLS_D);
END_EXTERN_C()
成员都在sapi_globals_struct这里了
那么我么可以这样调用
SG(default_mimetype)
SG(request_info).request_uri
可以感受一下这么一段代码
staticintsapi_cgi_send_headers(sapi_headers_struct*sapi_headersTSRMLS_DC) { charbuf[SAPI_CGI_MAX_HEADER_LENGTH]; sapi_header_struct*h; zend_llist_positionpos; longrfc2616_headers=0; if(SG(request_info).no_headers==1){ returnSAPI_HEADER_SENT_SUCCESSFULLY; } if(SG(sapi_headers).http_response_code!=200){ intlen; len=sprintf(buf,"Status:%d\r\n",SG(sapi_headers).http_response_code); PHPWRITE_H(buf,len); } if(SG(sapi_headers).send_default_content_type){ char*hd; hd=sapi_get_default_content_type(TSRMLS_C); PHPWRITE_H("Content-type:",sizeof("Content-type:")-1); PHPWRITE_H(hd,strlen(hd)); PHPWRITE_H("\r\n",2); efree(hd); } h=zend_llist_get_first_ex(&sapi_headers->headers,&pos); while(h){ PHPWRITE_H(h->header,h->header_len); PHPWRITE_H("\r\n",2); h=zend_llist_get_next_ex(&sapi_headers->headers,&pos); } PHPWRITE_H("\r\n",2); returnSAPI_HEADER_SENT_SUCCESSFULLY; }
2、EG ExecutorGlobals
EG获取的是struct_zend_execution_globals结构体中的数据
struct_zend_execution_globals{ ... HashTablesymbol_table;/*全局作用域,如果没有进入函数内部,全局=活动*/ HashTable*active_symbol_table;/*活动作用域,当前作用域*/ ... }
通常,使用EG(symbol_table)获取的是全局作用域中的符号表,使用EG(active_symbol_table)获取的是当前作用域下的符号表
例如来定义$foo='bar'
zval*fooval;
MAKE_STD_ZVAL(fooval);
ZVAL_STRING(fooval,"bar",1);
ZEND_SET_SYMBOL(EG(active_symbol_table),"foo",fooval);
或者从符号表中查找$foo
zval**fooval;
if(zend_hash_find(&EG(symbol_table),"foo",sizeof("foo"),(void**)&fooval)==SUCCESS){
RETURN_STRINGL(Z_STRVAL_PP(fooval),Z_STRLEN_PP(fooval));
}else{
RETURN_FALSE;
}
上面的代码中,EG(active_symbol_table)==&EG(symbol_table)
3、CG()用来访问核心全局变量。(zend/zend_globals_macros.h)
4、PG()PHP全局变量。我们知道php.ini会映射一个或者多个PHP全局结构。(main/php_globals.h)
5、FG()文件全局变量。大多数文件I/O或相关的全局变量的数据流都塞进标准扩展出口结构。(ext/standard/file.h)
5、获取变量的类型和值
#defineZ_TYPE(zval) (zval).type
#defineZ_TYPE_P(zval_p) Z_TYPE(*zval_p)
#defineZ_TYPE_PP(zval_pp) Z_TYPE(**zval_pp)
比如获取一个变量的类型
voiddescribe_zval(zval*foo) { if(Z_TYPE_P(foo)==IS_NULL) { php_printf("这个变量的数据类型是:NULL"); } else { php_printf("这个变量的数据类型不是NULL,这种数据类型对应的数字是:%d",Z_TYPE_P(foo)); } }
有这么几种类型
#defineIS_NULL 0
#defineIS_LONG 1
#defineIS_DOUBLE 2
#defineIS_BOOL 3
#defineIS_ARRAY 4
#defineIS_OBJECT 5
#defineIS_STRING 6
#defineIS_RESOURCE7
#defineIS_CONSTANT8
#defineIS_CONSTANT_ARRAY 9
#defineIS_CALLABLE10
php_printf()函数是内核对printf()函数的一层封装,我们可以像使用printf()函数那样使用它,以一个P结尾的宏的参数大多是*zval型变量。此外获取变量类型的宏还有两个,分别是Z_TYPE和Z_TYPE_PP,前者的参数是zval型,而后者的参数则是**zval
比如gettype函数的实现
//开始定义php语言中的函数gettype PHP_FUNCTION(gettype) { //arg间接指向调用gettype函数时所传递的参数。是一个zval**结构 //所以我们要对他使用__PP后缀的宏。 zval**arg; //这个if的操作主要是让arg指向参数~ if(zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC,"Z",&arg)==FAILURE){ return; } //调用Z_TYPE_PP宏来获取arg指向zval的类型。 //然后是一个switch结构,RETVAL_STRING宏代表这gettype函数返回的字符串类型的值 switch(Z_TYPE_PP(arg)){ caseIS_NULL: RETVAL_STRING("NULL",1); break; caseIS_BOOL: RETVAL_STRING("boolean",1); break; caseIS_LONG: RETVAL_STRING("integer",1); break; caseIS_DOUBLE: RETVAL_STRING("double",1); break; caseIS_STRING: RETVAL_STRING("string",1); break; caseIS_ARRAY: RETVAL_STRING("array",1); break; caseIS_OBJECT: RETVAL_STRING("object",1); break; caseIS_RESOURCE: { char*type_name; type_name=zend_rsrc_list_get_rsrc_type(Z_LVAL_PP(arg)TSRMLS_CC); if(type_name){ RETVAL_STRING("resource",1); break; } } default: RETVAL_STRING("unknowntype",1); } }
获取变量的值,有这么多宏来获取
Z_LVAL()
Z_BVAL()
Z_DVAL()
Z_STRVAL()
Z_STRLEN()
Z_LVAL_P()
Z_BVAL_P()
Z_DVAL_P()
Z_STRVAL_P()
Z_STRLEN_P()
Z_LVAL_PP()
Z_BVAL_PP()
Z_DVAL_PP()
Z_STRVAL_PP()
Z_STRLEN_PP()
Z_ARRVAL()
Z_OBJ()
Z_OBJPROP()
Z_OBJCE()
Z_RESVAL()
Z_ARRVAL_P()
Z_OBJ_P()
Z_OBJPROP_P()
Z_OBJCE_P()
Z_RESVAL_P()
Z_ARRVAL_PP()
Z_OBJ_PP()
Z_OBJPROP_PP()
Z_OBJCE_PP()
Z_RESVAL_PP()
rot13函数的实现
PHP_FUNCTION(rot13) { zval**arg; char*ch,cap; inti; if(ZEND_NUM_ARGS()!=1||zend_get_parameters_ex(1,&arg)==FAILURE){ WRONG_PARAM_COUNT; } *return_value=**arg; zval_copy_ctor(return_value); convert_to_string(return_value); for(i=0,ch=return_value->value.str.val; i<return_value->value.str.len;i++,ch++){ cap=*ch&32; *ch&=~cap; *ch=((*ch>='A')&&(*ch<='Z')?((*ch-'A'+13)%26+'A'):*ch)|cap; } }
要获取变量的值,也应该使用Zend定义的宏进行访问。对于简单的标量数据类型、Boolean,long,double,使用Z_BVAL,Z_LVAL,Z_DVAL
voiddisplay_values(zvalboolzv,zval*longpzv,zval**doubleppzv) { if(Z_TYPE(boolzv)==IS_BOOL){ php_printf("Thevalueofthebooleanis:%s\n",Z_BVAL(boolzv)?"true":"false"); } if(Z_TYPE_P(longpzv)==IS_LONG){ php_printf("Thevalueofthelongis:%ld\n",Z_LVAL_P(longpzv)); } if(Z_TYPE_PP(doubleppzv)==IS_DOUBLE){ php_printf("Thevalueofthedoubleis:%f\n",Z_DVAL_PP(doubleppzv)); } }
对于字符串类型,因为它含有两个字段char*(Z_STRVAL)和int(Z_STRLEN),因此需要用两个宏来进行取值,因为需要二进制安全的输出这个字符串
voiddisplay_string(zval*zstr) { if(Z_TYPE_P(zstr)!=IS_STRING){ php_printf("Thewronngdatatypewaspassed!\n"); return; } PHPWRITE(Z_STRVAL_P(zstr),Z_STRLEN_P(zstr)); }
因为数组在zval中是以HashTable形式存在的,因此使用Z_ARRVAL()进行访问
voiddisplay_zval(zval*value) { switch(Z_TYPE_P(value)){ caseIS_NULL: /*如果是NULL,则不输出任何东西*/ break; caseIS_BOOL: /*如果是bool类型,并且true,则输出1,否则什么也不干*/ if(Z_BVAL_P(value)){ php_printf("1"); } break; caseIS_LONG: /*如果是long整型,则输出数字形式*/ php_printf("%ld",Z_LVAL_P(value)); break; caseIS_DOUBLE: /*如果是double型,则输出浮点数*/ php_printf("%f",Z_DVAL_P(value)); break; caseIS_STRING: /*如果是string型,则二进制安全的输出这个字符串*/ PHPWRITE(Z_STRVAL_P(value),Z_STRLEN_P(value)); break; caseIS_RESOURCE: /*如果是资源,则输出Resource#10格式的东东*/ php_printf("Resource#%ld",Z_RESVAL_P(value)); break; caseIS_ARRAY: /*如果是Array,则输出Array5个字母!*/ php_printf("Array"); break; caseIS_OBJECT: php_printf("Object"); break; default: /*Shouldneverhappeninpractice, *butit'sdangeroustomakeassumptions */ php_printf("Unknown"); break; } }
一些类型转换函数
ZEND_APIvoidconvert_to_long(zval*op);
ZEND_APIvoidconvert_to_double(zval*op);
ZEND_APIvoidconvert_to_null(zval*op);
ZEND_APIvoidconvert_to_boolean(zval*op);
ZEND_APIvoidconvert_to_array(zval*op);
ZEND_APIvoidconvert_to_object(zval*op);
ZEND_APIvoid_convert_to_string(zval*opZEND_FILE_LINE_DC);
6、常量的实例化
我们可以这样实例化
PHP_MINIT_FUNCTION(consts)//模块初始化时定义常量 { REGISTER_LONG_CONSTANT("CONSTS_MEANING_OF_LIFE",42,CONST_CS|CONST_PERSISTENT); REGISTER_DOUBLE_CONSTANT("CONSTS_PI",3.1415926,CONST_PERSISTENT); REGISTER_STRING_CONSTANT("CONSTS_NAME","leon",CONST_CS|CONST_PERSISTENT); } PHP_RINIT_FUNCTION(consts)//每次请求时定义常量 { charbuffer[40]; srand((int)time(NULL)); snprintf(buffer,sizeof(buffer),"%d",rand()); REGISTER_STRING_CONSTANT("CONSTS_RAND",estrdup(buffer),CONST_CS); returnSUCCESS; }
常见的宏
/*注册LONG类型常量*/
#defineREGISTER_LONG_CONSTANT(name,lval,flags) zend_register_long_constant((name),sizeof(name),(lval),(flags),module_numberTSRMLS_CC)
/*注册double类型常量*/
#defineREGISTER_DOUBLE_CONSTANT(name,dval,flags) zend_register_double_constant((name),sizeof(name),(dval),(flags),module_numberTSRMLS_CC)
/*注册STRING类型常量*/
#defineREGISTER_STRING_CONSTANT(name,str,flags) zend_register_string_constant((name),sizeof(name),(str),(flags),module_numberTSRMLS_CC)
/*注册STRING类型常量*/
#defineREGISTER_STRINGL_CONSTANT(name,str,len,flags) zend_register_stringl_constant((name),sizeof(name),(str),(len),(flags),module_numberTSRMLS_CC)
7、全局变量
#php-fpm生成POST|GET|COOKIE|SERVER|ENV|REQUEST|FILES全局变量的流程 php_cgi_startup()->php_module_startup()->php_startup_auto_globals()->保存变量到symbol_table符号表 php_cgi_startup()在fpm/fpm/fpm_main.c中定义 php_module_startup()在main/main.c中定义 php_startup_auto_globals()在main/php_variables.h中定义 zend_hash_update(&EG(symbol_table),"_GET",sizeof("_GET")+1,&vars,sizeof(zval*),NULL); /*读取$_SERVER变量*/ staticPHP_FUNCTION(print_server_vars){ zval**val; if(zend_hash_find(&EG(symbol_table),"_SERVER",sizeof("_SERVER"),(void**)&val)==SUCCESS){ RETURN_ZVAL(*val,1,0); }else{ RETURN_FALSE; } } /*读取$_SERVER[$name]*/ ZEND_BEGIN_ARG_INFO(print_server_var_arginfo,0) ZEND_ARG_INFO(0,"name") ZEND_END_ARG_INFO() staticPHP_FUNCTION(print_server_var){ char*name; intname_len; zval**val; HashTable*ht_vars=NULL; HashPositionpos; zval**ret_val; if(zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC,"|s!",&name,&name_len)==FAILURE){ RETURN_NULL(); } if(zend_hash_find(&EG(symbol_table),"_SERVER",sizeof("_SERVER"),(void**)&val)==SUCCESS){ ht_vars=Z_ARRVAL_PP(val); //此处需传入大于name长度+1的值,因为字符串值后面需要'\0' if(zend_hash_find(ht_vars,name,name_len+1,(void**)&ret_val)==SUCCESS){RETURN_STRING(Z_STRVAL_PP(ret_val),0); }else{ RETURN_NULL(); } }else{ RETURN_NULL(); } }
8、包装第三方库
配置(config.m4)
SEARCH_PATH="/usr/local/usr"#lib搜索的目录 SEARCH_FOR="/include/curl/curl.h"#lib头文件的路径 iftest-r$PHP_LIBS/$SEARCH_FOR;then LIBS_DIR=$PHP_LIBS else#searchdefaultpathlist AC_MSG_CHECKING([forlibsfilesindefaultpath]) foriin$SEARCH_PATH;do iftest-r$i/$SEARCH_FOR;then LIBS_DIR=$i#搜索到的lib的路径 AC_MSG_RESULT(foundin$i) fi done fi /*验证lib是否存在*/ iftest-z"$LIBS_DIR";then AC_MSG_RESULT([notfound]) AC_MSG_ERROR([Pleasereinstallthelibsdistribution]) fi /*编译的时候添加lib的include目录,-I/usr/include*/ PHP_ADD_INCLUDE($LIBS_DIR/include) LIBNAME=curl#lib名称 LIBSYMBOL=curl_version#lib的一个函数,用来PHP_CHECK_LIBRARY验证lib /*验证lib*/ PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, [ PHP_ADD_LIBRARY_WITH_PATH($LIBNAME,$LIBS_DIR/$PHP_LIBDIR,LIBS_SHARED_LIBADD)#编译的时候链接lib,-llibcurl AC_DEFINE(HAVE_LIBSLIB,1,[]) ],[ AC_MSG_ERROR([wronglibslibversionorlibnotfound]) ],[ -L$LIBS_DIR/$PHP_LIBDIR-lm ]) PHP_SUBST(LIBS_SHARED_LIBADD)
9、用于返回的宏
//这些宏都定义在Zend/zend_API.h文件里
#defineRETVAL_RESOURCE(l) ZVAL_RESOURCE(return_value,l)
#defineRETVAL_BOOL(b) ZVAL_BOOL(return_value,b)
#defineRETVAL_NULL() ZVAL_NULL(return_value)
#defineRETVAL_LONG(l) ZVAL_LONG(return_value,l)
#defineRETVAL_DOUBLE(d) ZVAL_DOUBLE(return_value,d)
#defineRETVAL_STRING(s,duplicate) ZVAL_STRING(return_value,s,duplicate)
#defineRETVAL_STRINGL(s,l,duplicate) ZVAL_STRINGL(return_value,s,l,duplicate)
#defineRETVAL_EMPTY_STRING() ZVAL_EMPTY_STRING(return_value)
#defineRETVAL_ZVAL(zv,copy,dtor) ZVAL_ZVAL(return_value,zv,copy,dtor)
#defineRETVAL_FALSE ZVAL_BOOL(return_value,0)
#defineRETVAL_TRUE ZVAL_BOOL(return_value,1)
#defineRETURN_RESOURCE(l) {RETVAL_RESOURCE(l);return;}
#defineRETURN_BOOL(b) {RETVAL_BOOL(b);return;}
#defineRETURN_NULL() {RETVAL_NULL();return;}
#defineRETURN_LONG(l) {RETVAL_LONG(l);return;}
#defineRETURN_DOUBLE(d) {RETVAL_DOUBLE(d);return;}
#defineRETURN_STRING(s,duplicate) {RETVAL_STRING(s,duplicate);return;}
#defineRETURN_STRINGL(s,l,duplicate){RETVAL_STRINGL(s,l,duplicate);return;}
#defineRETURN_EMPTY_STRING() {RETVAL_EMPTY_STRING();return;}
#defineRETURN_ZVAL(zv,copy,dtor) {RETVAL_ZVAL(zv,copy,dtor);return;}
#defineRETURN_FALSE {RETVAL_FALSE;return;}
#defineRETURN_TRUE {RETVAL_TRUE;return;}
其实,除了这些标量类型,还有很多php语言中的复合类型我们需要在函数中返回,如数组和对象,我们可以通过RETVAL_ZVAL与RETURN_ZVAL来操作它们
10、hashTable的遍历函数
//基于longkey的操作函数
zval*v3;
MAKE_STD_ZVAL(v3);
ZVAL_STRING(v3,"value3",1);
zend_hash_index_update(names,0,&v3,sizeof(zval*),NULL);//按数字索引键更新HashTable元素的值
zval**v4;
zend_hash_index_find(names,1,&v4);//按数字索引获取HashTable元素的值
php_printf("v4:");
PHPWRITE(Z_STRVAL_PP(v4),Z_STRLEN_PP(v4));
php_printf("\n");
ulongidx;
idx=zend_hash_index_exists(names,10);//按数字索引查找HashTable,如果找到返回1,反之则返回0
zend_hash_index_del(names,2); //按数字索引删除HashTable元素
//hashTable的遍历函数
zend_hash_internal_pointer_reset(names);//初始化hash指针
zend_hash_internal_pointer_reset_ex(names,&pos);//初始化hash指针,并付值给pos
zend_hash_get_current_data(names,(void**)&val);//获取当前hash存储值,datashouldbecasttovoid**,ie:(void**)&data
zend_hash_get_current_data_ex(names,(void**)&val,&pos)==SUCCESS;//获取当前hash存储值
zend_hash_get_current_key(names,&key,&klen,&index,0)==HASH_KEY_IS_LONG
zend_hash_get_current_key_ex(names,&key,&klen,&index,0,&pos)==HASH_KEY_IS_LONG;//读取hashtable当前的KEY,返回值会有两种HASH_KEY_IS_LONG|HASH_KEY_IS_STRING,分别对应array("value"),array("key"=>"value")两种hashtable
zend_hash_move_forward(names);
zend_hash_move_forward_ex(names,&pos);//hash指针移至下一位
//HashTable长度
php_printf("%*carray(%d){\n",depth*2,'',zend_hash_num_elements(Z_ARRVAL_P(zv))
一个简单的函数
functionhello_array_strings($arr){ if(!is_array($arr))returnNULL; printf("Thearraypassedcontains%delements",count($arr)); foreach($arras$data){ if(is_string($data))echo"$data"; } }
PHP内核实现
PHP_FUNCTION(hello_array_strings) { zval*arr,**data; HashTable*arr_hash; HashPositionpointer; intarray_count; if(zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC,"a",&arr)==FAILURE){ RETURN_NULL(); } arr_hash=Z_ARRVAL_P(arr); array_count=zend_hash_num_elements(arr_hash); php_printf("Thearraypassedcontains%delements",array_count); for(zend_hash_internal_pointer_reset_ex(arr_hash,&pointer);zend_hash_get_current_data_ex(arr_hash,(void**)&data,&pointer)==SUCCESS;zend_hash_move_forward_ex(arr_hash,&pointer)){ if(Z_TYPE_PP(data)==IS_STRING){ PHPWRITE(Z_STRVAL_PP(data),Z_STRLEN_PP(data)); php_printf(""); } } RETURN_TRUE; }
以上所述就是本文给大家介绍的PHP扩展开发教程,希望大家喜欢。