PHP扩展开发(二)---函数

弄好骨架之后,我们得给我们的扩展增加些php能够调用的函数,这里我们使用vscode进行开发

开发环境

给我们的vscode装好扩展,然后配置一下include路径

《PHP扩展开发(二)---函数》
点击,生成一个配置文件,我的配置如下,php的路径看自己的来定,我这里是宝塔安装的,路径为:/www/server/php/72/include/php,主要是自动提示

{
    "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提供的一个输出函数

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,在最后写一行,我们刚刚编写的这个函数

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

使用这个宏会将我们的函数最终定义成如下的形式

void zif_study_ext_print(zend_execute_data *execute_data, zval *return_value)

官网上的是php5.3的版本,我这里是php7,所以是这样,如果有什么错误,还望指出
然后因为这里的返回值是void,所以在我们写函数的时候return不能带值

RETURN_*

这个宏一看就知道是php给我们的返回值,除了我上面所写的RETURN_STR外还有其他的类型

#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函数相关的结构体

//宏如下,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

我的c代码如下

//定义参数结构
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[] */
};
echo "study_add:".study_add(10,123456);

ZEND_BEGIN_ARG_INFO,ZEND_ARG_INFO,ZEND_END_ARG_INFO

定义参数,我把源码贴上来

#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},

那么通过这些宏的转换,变成了这样

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

获取参数

//声明
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

参数个数,一般这样填就好了

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.