整理目录
This commit is contained in:
237
docs/note/learn/PHP扩展开发/02-函数.md
Normal file
237
docs/note/learn/PHP扩展开发/02-函数.md
Normal file
@ -0,0 +1,237 @@
|
||||
---
|
||||
title: PHP扩展开发(二)-函数
|
||||
---
|
||||
|
||||
> 弄好骨架之后,我们得给我们的扩展增加些 php 能够调用的函数,这里我们使用 vscode 进行开发
|
||||
|
||||
## 开发环境
|
||||
|
||||
> 给我们的 vscode 装好扩展,然后配置一下 include 路径
|
||||
|
||||

|
||||
|
||||
点击,生成一个配置文件,我的配置如下,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
|
||||
|
||||
参数个数,一般这样填就好了
|
Reference in New Issue
Block a user