菜鸟学php扩展 之 实战如何在扩展里调第三方函数(标准库/原生/自己写的)(六)

转载请附上本文地址:http://blog.csdn.net/u011957758/article/details/72873567

前言

经过上文菜鸟学php扩展 之 详解php扩展的参数返回(五) 的笔记,基本上创建一个扩展的基础技能get到了。
本文主要想做下以下三个问题的笔记:
1.如何调用php标准库的函数?
2.如何调用php原生的函数?
3.如何调用php写的函数?

正文

如何调用php标准库的函数?

实战场景:想写一个简单的获取token的函数。token由任意前缀+随机数组成。

重点:随机数部分联想到了php有实现了,可直接调用。

引入php标准库头文件

搜索rand函数定义的文件:

grep -rn "PHP_FUNCTION(RAND)" ./ext

找到在/ext/standard/rand.c里头。我们要找的是头文件,故进一份分析,rand函数主要核心方法是调用了php_rand函数,发现原来是需要引入:

 #include "php_rand.h"

是不是这个头文件里头声明的函数都可以调用呢?

答案是no,只能调用php_rand.h 头文件声明的PHPAPI的函数。

使用标准库的函数

如果不知道怎么使用可以这么分析:在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到了

函数的实现

需要先添加PHP_FE(get_token, NULL) 注册函数(不懂请移步菜鸟学php扩展 之 hello world(一)),后实现函数。

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;	

执行文件得到:test12

至此调用php标准库的技能get到了。

如何调用php原生的函数?

实战场景:还是想写一个简单的获取token的函数。token由任意前缀+随机数组成。

重点:随机数部分用php原生的函数mt_rand实现。那么c扩展怎么调用原生的php函数呢?

详解重点方法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就好了。

参数讲解:

参数讲解
HashTableZend使用HashTable来存储PHP函数,function_table用于指定从哪个HashTable中获取函数。通常应该用CG(function_table),展开就是compiler_globals.function_table,compiler_globals是一个用来存储编译器数据的全局数据结构(与其对应的还有个EG宏,即executor_globals,它用来存储执行器数据)。compiler_globals.function_table里面存储了所有我们可以在PHP页面里面调用的函数,包括Zend内建函数、PHP标准库函数、模块导出的函数以及用户使用PHP代码定义的函数。
object_pp一个对象,当指定该值时,Zend会从对象的函数表中获取函数,总是设为NULL。
function_name必须是string型的zval,存储我们希望调用的函数的名称。为什么使用zval而不是直接用char*,是因为Zend考虑到大部分情况下,我们都是从用户那获得参数,当然也可以手动创建一个string型的zval传给它。
retval_ptr函数的返回值,Zend执行完指定的函数后,它就将返回值的指针填充到这里。申请空间MAKE_STD_ZVAL,事后这个容器空间的销毁释放工作得由zval_ptr_dtor(&ret_ptr)来做。
param_count标识参数个数的整数
params包含具体参数的数组

函数的实现

需要先添加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;	

执行文件得到:test212

至此调用php原生函数的技能get到了。

如何调用php写的函数?

实战场景:还是想写一个简单的获取token的函数。token由任意前缀+随机数组成。

重点:随机数部分用我们自己写的php函数来实现。那么c扩展怎么调用自己写的php函数呢?

重点函数

当然还是call_user_function

函数的实现

需要先添加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");	

执行文件得到:test782

至此调用自己写的php函数的技能get到了。

如果你觉得有收获~可以关注我的公众号【咖啡色的羊驼】~第一时间收到我的分享和知识梳理~
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值