转载请附上本文地址: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就好了。
参数讲解:
参数 | 讲解 |
---|---|
HashTable | Zend使用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到了。
如果你觉得有收获~可以关注我的公众号【咖啡色的羊驼】~第一时间收到我的分享和知识梳理~