--- title: PHP扩展开发(二)-函数 --- > 弄好骨架之后,我们得给我们的扩展增加些 php 能够调用的函数,这里我们使用 vscode 进行开发 ## 开发环境 > 给我们的 vscode 装好扩展,然后配置一下 include 路径 ![](img/02-%E5%87%BD%E6%95%B0.assets/2018-09-21-14-20-32-%E7%9A%84%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE-300x132.png) 点击,生成一个配置文件,我的配置如下,php 的路径看自己的来定,我这里是宝塔安装的,路径为:**/www/server/php/72/include/php**,主要是自动提示 ```json { "configurations": [ { "name": "Linux", "includePath": [ "${workspaceFolder}/**", "/www/server/php/72/include/php", "/www/server/php/72/include/php/main", "/www/server/php/72/include/php/Zend", "/www/server/php/72/include/php/TSRM" ], "defines": [], "compilerPath": "/usr/bin/gcc", "cStandard": "c11", "cppStandard": "c++17", "intelliSenseMode": "clang-x64" } ], "version": 4 } ``` ## 开发 ### 返回和输出 > 假设我这里想添加一个输出一些东西的函数 这里用到了三个宏,**PHP_FUNCTION**,**RETURN\_\***,**PHP_FE** php_printf 是 php 提供的一个输出函数 ```c PHP_FUNCTION(study_ext_print) { php_printf("我是输出到页面的内容\n"); RETURN_STRING("学习PHP扩展开发~~"); } /* }}} */ /* {{{ study_functions[] * * Every user visible function must have an entry in study_functions[]. */ const zend_function_entry study_functions[] = { PHP_FE(confirm_study_compiled, NULL) /* For testing, remove later. */ PHP_FE(study_ext_print, NULL) /* 学习插件输出 */ PHP_FE_END /* Must be the last line in study_functions[] */ }; /* }}} */ ``` 然后再 make,sudo make install #### 调试跑一次 修改我们目录下的`study.php`,在最后写一行,我们刚刚编写的这个函数 ```php echo study_ext_print()."\n"; ``` 输出,成功 ``` huanl@huanl-CN15S:/www/server/php/72/src/ext/study$ php study.php Functions available in the test extension: confirm_study_compiled study_ext_print Congratulations! You have successfully modified ext/study/config.m4. Module study is now compiled into PHP. 我是输出到页面的内容 学习PHP扩展开发~~ ``` #### 这里再说下这三个宏 ##### PHP_FUNCTION 使用这个宏会将我们的函数最终定义成如下的形式 ```cpp void zif_study_ext_print(zend_execute_data *execute_data, zval *return_value) ``` ~~官网上的是 php5.3 的版本,我这里是 php7,所以是这样,如果有什么错误,还望指出~~ 然后因为这里的返回值是 void,所以在我们写函数的时候`return`不能带值 ##### RETURN\_\* 这个宏一看就知道是 php 给我们的返回值,除了我上面所写的`RETURN_STR`外还有其他的类型 ```c #define RETURN_BOOL(b) { RETVAL_BOOL(b); return; } #define RETURN_NULL() { RETVAL_NULL(); return;} #define RETURN_LONG(l) { RETVAL_LONG(l); return; } #define RETURN_DOUBLE(d) { RETVAL_DOUBLE(d); return; } #define RETURN_STR(s) { RETVAL_STR(s); return; } #define RETURN_INTERNED_STR(s) { RETVAL_INTERNED_STR(s); return; } #define RETURN_NEW_STR(s) { RETVAL_NEW_STR(s); return; } #define RETURN_STR_COPY(s) { RETVAL_STR_COPY(s); return; } #define RETURN_STRING(s) { RETVAL_STRING(s); return; } #define RETURN_STRINGL(s, l) { RETVAL_STRINGL(s, l); return; } #define RETURN_EMPTY_STRING() { RETVAL_EMPTY_STRING(); return; } #define RETURN_RES(r) { RETVAL_RES(r); return; } #define RETURN_ARR(r) { RETVAL_ARR(r); return; } #define RETURN_OBJ(r) { RETVAL_OBJ(r); return; } #define RETURN_ZVAL(zv, copy, dtor) { RETVAL_ZVAL(zv, copy, dtor); return; } #define RETURN_FALSE { RETVAL_FALSE; return; } #define RETURN_TRUE { RETVAL_TRUE; return; } ``` ##### PHP_FE 这个宏帮助我们生成一个和 php 函数相关的结构体 ```c //宏如下,PHP_替换成了ZEND_ #define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_internal_arg_info)-1), flags }, #define ZEND_FE(name, arg_info) ZEND_FENTRY(name, ZEND_FN(name), arg_info, 0) //感觉就是帮我们省事了,不需要我们去重写结构体,上面那些结构体也是 typedef struct _zend_function_entry { const char *fname;//我们的php函数名 zif_handler handler;//相当于再调用一次PHP_FUNCTION,c中函数的指针 const struct _zend_internal_arg_info *arg_info;//参数的信息,就上一个函数来说,我们是NULL uint32_t num_args;//参数个数 uint32_t flags;//flags这里是0 } zend_function_entry; ``` ### 参数 > 假设来一个两数相加的函数,这时候我们就需要传送参数了 > 参考:[https://wiki.php.net/rfc/fast_zpp](https://wiki.php.net/rfc/fast_zpp) 我的 c 代码如下 ```cpp //定义参数结构 ZEND_BEGIN_ARG_INFO(add_param,0) ZEND_ARG_INFO(0,num1) ZEND_ARG_INFO(0,num2) ZEND_END_ARG_INFO() //函数 PHP_FUNCTION(study_add) { long long num1=0,num2=0; if(zend_parse_parameters(ZEND_NUM_ARGS() , "ll", &num1,&num2)==FAILURE){ RETURN_LONG(-1) } RETURN_LONG(num1+num2) } /* }}} */ /* {{{ study_functions[] * * Every user visible function must have an entry in study_functions[]. */ const zend_function_entry study_functions[] = { PHP_FE(confirm_study_compiled, NULL) /* For testing, remove later. */ PHP_FE(study_ext_print, NULL) /* 学习插件输出 */ PHP_FE(study_add, add_param) /* 两数相加 */ PHP_FE_END /* Must be the last line in study_functions[] */ }; ``` ```php echo "study_add:".study_add(10,123456); ``` #### 宏 ##### ZEND_BEGIN_ARG_INFO,ZEND_ARG_INFO,ZEND_END_ARG_INFO 定义参数,我把源码贴上来 ```cpp #define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args) \ static const zend_internal_arg_info name[] = { \ { (const char*)(zend_uintptr_t)(required_num_args), 0, return_reference, 0 }, #define ZEND_BEGIN_ARG_INFO(name, _unused) \ ZEND_BEGIN_ARG_INFO_EX(name, 0, ZEND_RETURN_VALUE, -1) #define ZEND_END_ARG_INFO() }; #define ZEND_ARG_INFO(pass_by_ref, name) { #name, 0, pass_by_ref, 0}, ``` 那么通过这些宏的转换,变成了这样 ```cpp static const zend_internal_arg_info add_param[] = { { (const char*)(zend_uintptr_t)(-1), 0, 0, 0 }, { "num1", 0, 0, 0}, { "num2", 0, 0, 0}, }; ``` #### zend_parse_parameters 获取参数 ```cpp //声明 ZEND_API int zend_parse_parameters(int num_args, const char *type_spec, ...); ``` 第一个参数是我们要获取的参数的个数,第二个是 参数的格式化字符串,后面的是变量指针 | 数据类型 | 字符 | c 对应类型 | | -------- | ---- | ----------- | | Boolean | b | zend_bool | | Long | l | long | | Double | d | double | | String | s | char\*, int | | Resource | r | zval\* | | Array | a | zval\* | | Object | o | zval\* | | zval | z | zval\* | #### ZEND_NUM_ARGS 参数个数,一般这样填就好了