c扩展调用php的函数(调用实现php函数的c函数)
上一次是写的c扩展调用c的标准函数,但是只能调用头文件中申明的函数,今天来说下c扩展调用实现php函数的c函数,比方说,c扩展要用到php中ip2long这个函数,但是c不可能去php中调用,肯定是去调用实现php函数的c函数。那么c扩展如何调用c内核对php的API呢?
这里要用到一个函数: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);
第一个参数是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考虑到大部分情况下,我们都是从用户那获得参数,然后再调用call_user_function_ex的,这样就可以不作处理直接把用户参数传给该函数。当然,我们也可以手动创建一个string型zval传给它。
retval_ptr_ptr用于获取函数的返回值,Zend执行完指定的函数后,它就将返回值的指针填充到这里。这个容器的空间函数会自动帮你申请,所以我们无需手动申请,但在事后这个容器空间的销毁释放工作得由我们自己(使用 zval_dtor())来做。
param_count和params用于指定函数的参数,param_count是一个标识参数个数的整数,params[] 是一个包含具体参数的数组。
no_separation用于指定是否在必要时执行zval分离,这在写入非引用zval时发生。应该总是将其设为0,表示执行zval分离,否则可能破坏数据。
symbol_table用于指定目标函数的active_symbol_table,通常应该使用NULL,这样Zend会为目标函数生成一个空的符号表。下面来看一个具体例子:
我在c扩展中要使用php函数中的ip2long函数,那么调用方法
unsigned long ip2longs(const char* ip){
zval *funname,*ret_ptr = NULL,*args,**params[1],*args_2; //如果有多个参数,比方说两个参数,就继续定义变量,看红色部分,如果继续加参数,按红色的步骤继续,定义变量,赋值,放入数组,参数个数也要变
MAKE_STD_ZVAL(funname); //创建变量
ZVAL_STRING(funname, "ip2long", 1); //设置好zval的类型和值,第二个参数就是我们要调用的ip2long函数 ,这两个方法不了解的可以查看http://www.cunmou.com/phpbook/2.3.md
MAKE_STD_ZVAL(args);
ZVAL_STRING(args,ip,1);
MAKE_STD_ZVAL(args_2);
ZVAL_LONG(args_2,123); //这是第二个参数,创建变量并赋值,整形只有两个参数,别的类型可以上网查
params[0] = &args; //把参数放入数组中
params[1] = &args_2; // 放入数组
call_user_function_ex(EG(function_table), NULL, funname, &ret_ptr, 2, params, 0, EG(active_symbol_table)); //调用函数,第5个参数代表参数个数我们这是2
zval_ptr_dtor(&ret_ptr); //销毁手动创建的空间
return ret_ptr->value.lval; //获取返回的值,因为函数返回是一个long型的,所以取lval
}
//该函数是php可以直接调用的函数。功能是用来判断某个ip是否在内网中
PHP_FUNCTION(is_intranet) {
char *ip;
int ip_length=0;
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"s",&ip,&ip_length) == FAILURE){
RETURN_NULL();
}
unsigned long ip_long = ip2longs(ip); //调用声明的函数
if(ip_long == ip2longs("127.0.0.1") || (ip_long > ip2longs("10.0.0.0") && ip_long <= ip2longs("10.255.255.255")) ||
(ip_long >= ip2longs("172.16.0.0") && ip_long <= ip2longs("172.31.255.255")) ||
(ip_long >= ip2longs("192.168.0.0") && ip_long <= ip2longs("192.168.255.255"))
) {
RETURN_BOOL(1);
} else {
RETURN_BOOL(0);
}
}
这里在多说下zval的结构,这些是自己平时学习总结的,在此拿出来和大家分享:
zvalue_value value;
unsigned char type;
unsigned char is_ref;
short refcount;
};
变量的实际值,具体来说是一个zvalue_value的联合体(union):
long lval; /* long value */
double dval; /* double value */
struct { /* string */
char *val;
int len;
} str;
HashTable *ht; /* hash table value,used for array */
zend_object_value obj; /* object */
zvalue_value结构的说明如下:
dval 如果变量类型为 IS_DOUBLE 就用这个属性值
str 如果变量类型为 IS_STRING 就访问这个属性值。它的字段 len 表示这个字符串的长度,字段 val 则指向该字符串。由于 Zend 使用的是 C 风格的字符串,因此字符串的长度就必须把字符串末尾的结束符 0×00 也计算在内
ht 如果变量类型为数组,那这个 ht 就指向数组的哈希表入口
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z",
&uservar) == FAILURE) {
RETURN_NULL();
IS_LONG 是一个(长)整数
IS_DOUBLE 是一个双精度的浮点数
IS_STRING 是一个字符串
IS_ARRAY 是一个数组
IS_OBJECT 是一个对象
IS_BOOL 是一个布尔值
IS_RESOURCE 是一个资源(关于资源的讨论,我们以后会在适当的时候讨论到它)
c扩展调用php的函数(调用实现php函数的c函数)的更多相关文章
- 应用层open(read、write、close)怎样调用驱动open(read、write、close)函数的?
应用层open(read.write.close)怎样调用驱动open(read.write.close)函数的? 华清远见2014-09-29 北京海淀区 张俊浩 三大数据结构关系图
- 正则表达式去除字符串左右空格函数 调用方法是,str.Trim();
正则表达式去除字符串左右空格函数 调用方法是,str.Trim(); String.prototype.Trim = function() { return this.replace(/(^\s*)| ...
- Python函数的定义、参数传入与函数的调用
作为计算机代码的一种抽象方式,函数在Python中扮演了极为重要的角色.今天给大家介绍Python函数的定义.参数的传入以及调用方式.其中函数参数的传入方式为本节重点内容.Python函数的参数形式包 ...
- JS高级---函数中的this的指向,函数的不同调用方式
函数中的this的指向 普通函数中的this是谁?-----window 对象.方法中的this是谁?----当前的实例对象 定时器方法中的this是谁?----window 构造函数中的this是谁 ...
- C语言内自定义汇编函数&调用约定
探究如何在C语言里直接自写汇编函数 裸函数 裸函数与普通函数的区别 普通函数在经过编译器编译时,编译器自动生成保护现场,恢复现场等反汇编代码 当我们想要自己实现函数内部的汇编代码时,就可以告诉汇编器不 ...
- js调用php和php调用js的方法举例
js调用php和php调用js的方法举例1 JS方式调用PHP文件并取得php中的值 举一个简单的例子来说明: 如在页面a.html中用下面这句调用: <script type="te ...
- C#.NET 大型企业信息化系统集成快速开发平台 4.2 版本 - 外部服务调用、内部服务调用优化,面向服务化的
现在的信息系统越来越复杂,越来越庞大,不仅需要内部是一个整体,而且还需要提供很多对外的服务调用. 1:别人如何调用最方便?用不同的开发语言调用.例如app.手持设备.服务器.2:服务的返回状态是什么样 ...
- js调用.net后台事件、后台调用前台以及js调用服务器控件
1. javaScript函数中执行C#代码中的函数: 方法一:间接触发后台代码 1.首先建立一个服务端控件按钮命名为btn1,双击进入后台将调用或处理的内容写入btn1_click中; 2.在前台写 ...
- 由ASP.NET所谓前台调用后台、后台调用前台想到HTTP——实践篇(二)
在由ASP.NET所谓前台调用后台.后台调用前台想到HTTP——理论篇中描述了一下ASP.NET新手的三个问题及相关的HTTP协议内容,在由ASP.NET所谓前台调用后台.后台调用前台想到HTTP—— ...
- 委托(delegate)的三种调用方式:同步调用,异步调用,异步回调(转载)
下面为即将被调用的方法: public delegate int AddHandler(int a,int b); public class 加法类 { public static int Add(i ...
随机推荐
- 【Go】http server 性能测试
node秒java几街!pathon秒node几街 !go秒pathon几街! ab测试性能极佳的 package main import ( "fmt" "html/t ...
- 关于qt5在win7下发布 & 打包
QT5 发布时,莫过于依赖动态链接库(dll) , 但是,QT5的动态链接库貌似都有2套 ,例如 Qt5Core (针对realese) , Qt5Cored (针对debug) ,凡事末尾带d的都是 ...
- Ubuntu14.04下中山大学锐捷上网设置
Ubuntu14.04下中山大学锐捷上网设置 打开终端后的初始目录是 -,Ubuntu安装完毕默认路径,不是的请自行先运行cd ~ 非斜体字命令行方法,斜体字是图形管理方法,二选一即可 记得善用Tab ...
- 【转】 java自定义注解
java注解是附加在代码中的一些元信息,用于一些工具在编译.运行时进行解析和使用,起到说明.配置的功能. 注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用.包含在 java.lang.annot ...
- tomcat配置及使用 环境变量设置
Tomcat的配置及测试: 第一步:下载tomcat,然后解压到任意盘符 第二步:配置系统环境变量 我这里是tomcat5.5,解压到的D盘 (路径为: D:\Program Files\tomcat ...
- UML类图关系-转
1.关联 双向关联: C1-C2:指双方都知道对方的存在,都可以调用对方的公共属性和方法. 在GOF的设计模式书上是这样描述的:虽然在分析阶段这种关系是适用的,但我们觉得它对于描述设计模式内的类关系来 ...
- c++ uuid生成法则
http://www.jb51.net/LINUXjishu/39614.html CentOS #include <uuid/uuid.h> 找不到文件解决方法: sudo yum in ...
- SQL SERVER调优常用方法
说起SQL SERVER的调优,我想大伙也很想知道这方面的知识.本人也正在探索的路上,大家有什么好的意见,欢迎一起探讨.研究.博取众人之长,才能扬长避短.本文中的内容主要是摘自<程序员的SQL金 ...
- java文件操作(输出目录、查看磁盘符)
问题描述: java操作文件,所有硬盘中所有文件路径 问题解决: (1)查看所有磁盘文件 注: 如上所示,使用接口 File.listRoots()可以返回所有磁盘文件,通过f ...
- powerdesigner使用之——从“概念模型”到“物理模型”
现实问题在计算机上的解决,需要我们从现实问题中抽象出实体模型,然后再将实体模型对应到数据库关系表中. 例如,我们在思考学生选课,这件事情上,实体模型就是“学生”和“课程”两个 此时,我们使用power ...