2024-03-22 17:42:41 +08:00

238 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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[] */
};
/* }}} */
```
然后再 makesudo 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_FUNCTIONc中函数的指针
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_INFOZEND_ARG_INFOZEND_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
参数个数,一般这样填就好了