美文网首页
PHP拓展开发 - 在扩展里调第三方函数

PHP拓展开发 - 在扩展里调第三方函数

作者: 半亩房顶 | 来源:发表于2019-05-03 23:35 被阅读0次

参考原文:
https://blog.csdn.net/u011957758/article/details/72873567

如何调用php标准库的函数

实战场景:想写一个简单的获取token的函数。token由任意前缀+随机数组成。
重点:随机数部分联想到了php有实现了,可直接调用。

1、引入php标准库头文件

搜索rand函数定义的文件:
grep -rn "PHP_FUNCTION(RAND)" ./ext
找到在/ext/standard/rand.c里头。我们要找的是头文件,故进一份分析,rand函数主要核心方法是调用了php_rand函数,发现原来是需要引入:
#include "php_rand.h"

是不是这个头文件里头声明的函数都可以调用呢?
答案是no,只能调用php_rand.h 头文件声明的PHPAPI的函数。

2、使用标准库的函数

如果不知道怎么使用可以这么分析:在PHP_FUNCTION(rand)函数中,找到调用的例子:

PHP_FUNCTION(rand)
{
    long min;
    long max;
    long number;
    int  argc = ZEND_NUM_ARGS();

    if (argc != 0 && zend_parse_parameters(argc TSRMLS_CC, "ll", &min, &max) == FAILURE)
        return;

    number = php_rand(TSRMLS_C);//TSRMLS_C代表线程安全,此处可直接使用,用的时候照着那个传就行。如果参数是别的,需要找参数的来源,在源文件中搜索或者参数是php调用的时候传的,找到参数的意义,就好了。

    if (argc == 2) {
        RAND_RANGE(number, min, max, PHP_RAND_MAX);
    }

    RETURN_LONG(number);
}

可以发现关键的实现代码是:

number = php_rand(TSRMLS_C);
RAND_RANGE(number, min, max, PHP_RAND_MAX);

好了,调用技能get到了

3、函数的实现

需要先添加PHP_FE(get_token, NULL) 注册函数,后实现函数。

PHP_FUNCTION(get_token) {
    // 1.定义参数
    long min = 1;
    long max = 100;
    char *prefix;
    int prefix_len;
    long number;
    char sz[10];

    // 2.接收token的前缀
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &prefix, &prefix_len) == FAILURE) {
        return;
    }   

    // 3.获取随机数
    number = php_rand(TSRMLS_C);
    RAND_RANGE(number, min, max, PHP_RAND_MAX);

    // 4.随机数转成字符串,方便拼接,否则会报make时候会有错误提示。
    sprintf(sz, "%d", number);

    // 5.拼接前缀与随机数
    strcat(prefix, sz);

    // 6.返回结果
    RETURN_STRING(prefix, 1); 
}

重新走一波编译安装流程(phpize && make && make install && /etc/init.d/php-fpm restart)

测试:

<?php
$token = get_token('test');
echo $token;   

至此,调用php标准库done。

如何调用php原生的函数

实战场景:还是想写一个简单的获取token的函数。token由任意前缀+随机数组成。
重点:随机数部分用php原生的函数mt_rand实现。那么c扩展怎么调用原生的php函数呢?

1、详解重点方法call_user_function

官方的定义:

ZEND_API int call_user_function(HashTable *function_table, zval **object_pp, zval *function_name, zval *retval_ptr, zend_uint param_count, zval *params[] TSRMLS_DC);

ZEND_API int call_user_function_ex(HashTable *function_table, zval **object_pp, zval *function_name, zval **retval_ptr_ptr, zend_uint param_count, zval **params[], int no_separation, HashTable *symbol_table TSRMLS_DC);

竟然有两个函数,通过查看源码./Zend/zend_execute_API.c

是的,call_user_function封装了call_user_function_ex,直接使用call_user_function就好了。

参数讲解:


2、函数的实现

需要先添加PHP_FE(get_token_b, NULL) 注册函数.

//定义一个将整形转成zval型的函数(在查询鸟哥怎么用call_user_function的时候,发现的函数,原先是字符串变zval型,给稍微改了下整形变zval型)
static zval *_change_long_zval(long num)
{
    zval *ret;

    MAKE_STD_ZVAL(ret);

    if (num) {
        ZVAL_LONG(ret, num); 
    } else {
        ZVAL_NULL(ret);
    }

    return ret;
}


PHP_FUNCTION(get_token_b) {
    long min; // 随机数最小值
    long max; // 随机数最大值
    char *prefix;    // 前缀
    int prefix_len;  // 前缀长度
    char sz[10];     // 长整型转字符串时候用到
    zval *func_name; // 函数名字
    zval *retval;    // 返回值
    zval *params[1]; // 装参数的数组

    // 获取参数
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sll", &prefix, &prefix_len, &min, &max) == FAILURE) {
        return;
    }

    // 申请一个内存
    MAKE_STD_ZVAL(func_name);

    // 赋值要调用的函数
    ZVAL_STRING(func_name,"mt_rand", 1);

    // 设置参数
    params[0] = _change_long_zval(min);
    params[1] = _change_long_zval(max);

    // 申请返回值的内存空间
    MAKE_STD_ZVAL(retval);

    // 调用函数
    if(call_user_function(CG(function_table),NULL,func_name,retval,2,params TSRMLS_DC)==FAILURE){
        return;
    }

    // 转字符串
    sprintf(sz, "%d", Z_LVAL_P(retval));

    // 拼接返回结果
    strcat(prefix, sz);
    RETURN_STRING(prefix, 1);
}

测试:

<?php
$token = get_token_b('test',1,1000);
echo $token;   

调用php原生函数done。

如何调用php写的函数

实战场景:还是想写一个简单的获取token的函数。token由任意前缀+随机数组成。
重点:随机数部分用我们自己写的php函数来实现。那么c扩展怎么调用自己写的php函数呢?

1、重点函数

当然还是call_user_function

2、函数的实现

需要先添加PHP_FE(get_token_c, NULL) 注册函数.

PHP_FUNCTION(get_token_c) {
    zval *func_name;
    zval *args;
    zval *retval;
    zval *params[1];

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &func_name, &args) == FAILURE) {
        return;
    }   

    if (Z_TYPE_P(func_name) != IS_STRING){
        return;
    }   
    params[0] = args;

    MAKE_STD_ZVAL(retval);
    if(call_user_function(CG(function_table),NULL,func_name,retval,1,params TSRMLS_DC)==FAILURE){
        return;
    }

    RETURN_STRING(Z_STRVAL_P(retval),1);
}

测试:

function get_token_demo($prefix){
    return "token :".$prefix.mt_rand(1,1000);
}
echo get_token_c("get_token_demo","test");  

至此,三种调用都已经

以上

欢迎大家关注我的公众号


半亩房顶

相关文章

网友评论

      本文标题:PHP拓展开发 - 在扩展里调第三方函数

      本文链接:https://www.haomeiwen.com/subject/fdftoqtx.html