内容均以php5.6.14为例.

从一个封装 uniqid 的例子来讲。

/* {{{ wrapper of uniqid */
PHP_FUNCTION(fox)
{    // #1.zval *prefix, *more = NULL;zval function, *params[2] = {0};// #2.if ( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|z", &prefix, &more) == FAILURE ) {RETURN_FALSE;}params[0] = prefix;if (more) {params[1] = more;}// #3.ZVAL_STRING(&function, "uniqid", 0);// #4.if ( call_user_function(EG(function_table), NULL, &function, return_value, ZEND_NUM_ARGS(), params TSRMLS_CC) == FAILURE ) {if (return_value) {zval_dtor(return_value);}zend_error(E_WARNING, "%s() calling %s() failed.", get_active_function_name(TSRMLS_C), Z_STRVAL(function));RETURN_FALSE;}RETURN_STRING(Z_STRVAL_P(return_value), 0);
}
/* }}} */

#1.

zval 不赋值默认是非空,不要随意给 声明的 zval 赋值为 NULL,除非你知道自己在干什么,比方用在判断是否有传参;

如果你想对可选的参数使用默认值 farwish,可以像下面这样 (非用于上例):

if (more == NULL) {MAKE_STD_ZVAL(more);Z_STRVAL_P(more) = "farwish";Z_STRLEN_P(more) = strlen("farwish");Z_TYPE_P(more) = IS_STRING;
}
params[1] = more;

还有别忘了 call_user_function 中的参数个数就不能再用 ZEND_NUM_ARGS(),写固定值 2 就可以了。

#2.

接收的参数类型必须用双引号包裹,为了避免其它地方也遇到这种错误,最好后面统一都用双引号。

如果接收的参数含 char *name 类型的, 别忘了要有 uint *len 跟在它后面传入。

#3. #4.

如果开头声明的是 zval *function, 并且 ZVAL_STRING 赋值 和 call_user_function 的调用都传 function, 编译能通过, 但是使用会segmentaion fault;测试证明, ZVAL_STRING 第一个参数一定是指向 zval 的地址, 而不是简单的传 zval *, 因为宏中做了 zval *__z = (z) 这么一件事, 如果 z 已经是指针, 那么值就不对了.

./Zend/zend_execute_API.c:575

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

zval function_name, retval_ptr;

Thats all.

开发文档:https://github.com/farwish/php-core-hack

Link:http://www.cnblogs.com/farwish/p/5635429.html

[php-src]扩展中封装业务与 call_user_function 的使用建议相关推荐

  1. Delphi中DLL封装业务逻辑的实现

      三层结构是开发C/S或B/S系统经常采用的策略,这种分层方式将系统分为用户服务.业务服务和数据服务三部分,能够解决客户端与服务器结构维护成本问题.改善客户端与服务器结构延展性问题:解决应用逻辑重复 ...

  2. OEA 中的业务控制器设计模式

    对于业务逻辑的组织,个人认为,最好是使用 DDD(<Domain Driven Design>) 的方式.DDD 使用领域模型来表达实体间的关系,同时在应用层使用 Service 来组织各 ...

  3. MySQL:尽量不要用存储过程封装业务逻辑

    本篇文章讨论并不是:不要使用存储过程,因为有些事情还是要存储过程来完成,不可能不用.而是关于:"业务逻辑是不是要封装在存储过程中实现,这样子php.java等就是调用存储过程". ...

  4. 减少存储过程封装业务逻辑-web开发与传统软件开发的思维模式不同

    转:减少存储过程封装业务逻辑-web开发与传统软件开发的思维模式不同 本篇文章讨论并不是:不要使用存储过程,因为有些事情还是要存储过程来完成,不可能不用.而是关于:"业务逻辑是不是要封装在存 ...

  5. vue2项目中封装echarts地图的比较优雅的方式

    以前写过 vue项目中封装echarts的比较优雅的方式,大屏可视化里面,除了数据图表很常用,显示省市地图区域也是很常用到的,这是姐妹篇. 区域地图选区域时,需要弹窗展示数据,样式是各种各样的,各种排 ...

  6. Android 中封装优雅的 MediaPlayer 音频播放器,支持多个播放器

    Android 中封装优雅的 MediaPlayer 音频播放器,支持多个播放器实例的示例: public class AudioPlayer implements MediaPlayer.OnPre ...

  7. main.js中封装全局登录函数

    main.js中封装全局登录函数 1. 在 main.js 中封装全局登录函数 通过 vue 对象的原型扩展,可以扩展一个函数,这样这个函数就可以在 每一个界面通过类似指向对象的方式,去访问这个函数. ...

  8. 【SSH进阶之路】一步步重构MVC实现Struts框架——封装业务逻辑和跳转路径(四)...

    目录: [SSH进阶之路]Struts基本原理 + 实现简单登录(二) [SSH进阶之路]一步步重构MVC实现Struts框架--从一个简单MVC开始(三) [SSH进阶之路]一步步重构MVC实现St ...

  9. go get 的不再src目录中_GO语言基础进阶教程:包的使用

    Go语言使用包(package)这种语法元素来组织源码,所有语法可见性均定义在package这个级别,与Java .python等语言相比,这算不上什么创新,但与C传统的include相比,则是显得& ...

最新文章

  1. 用SDM架构Cisco IOS ***图文详解全攻略(一)——easy ***
  2. pandas drop 删除行和列的方法
  3. mysql 高并发加锁_Mysql高并发加锁事务处理
  4. 2021-01-22 使用 Docker 打包 Python 项目
  5. Java注解全面解析
  6. 计算机处理信息的过程按先后顺序可表述为,信息加工理论按照信息处理的先后顺序将记忆区分为三个阶段系统;感觉记忆、()长时记忆。...
  7. LINUX yum用法
  8. Android开发学习——基础学习
  9. 测绘计算机编程在在道路中的应用,CASIO系列编程计算器在公路测量中的应用.doc...
  10. python循环语句笔记
  11. R语言生存分析之COX比例风险模型构建及亚组森林图绘制示例
  12. java-word模板导出
  13. 前端常见的浏览器兼容性问题及解决方案
  14. 【服务器数据恢复】断电导致raid信息丢失的磁盘阵列数据恢复案例
  15. redis中字符串(String)类型常见命令操作 (附有示例)
  16. nginx配置反向代理和负载均衡完结篇
  17. No.8 CA证书和SSH服务
  18. SwiftUI基础之Text格式化显示小数specifier
  19. 山东大学项目实训十六——可控音乐变压器Controllable Music Transformer
  20. c语言结构体世界杯,世界杯冷知识 | 12座球场的结构巡礼

热门文章

  1. python读取大数据量xml_[C#]_[使用微软OpenXmlSDK (OpenXmlReader)读取xlsx表格] 读取大数据量100万条数据Excel文件解决方案...
  2. LeetCode 426. 将二叉搜索树转化为排序的双向链表
  3. 【J2EE】第四章 SpringMVC
  4. vue 使用element 菜单与tab页联动
  5. 清结算系统的一些思考
  6. html学习文档-3、HTML元素
  7. pl/sql developer执行光标所在行
  8. 微信公众号开发入门教程第一篇
  9. 257. Binary Tree Paths
  10. android SQLite数据库(转)