一个函数的执行结果要返回给调用者,除了使用return功能,还有一种办法,那就是以引用的形式传递参数,然后在内部修改这个参数的值。前一种方法往往只能返回一个值,如果我们的函数执行结果具有多种数据,便需要把这些数据打包到一个数组、类等复合类型的变量中才能得以实现;但后一种方法相比而言就简单一些了。

运行时传递引用:Call-time Pass-by-ref

标题有点绕口,其实很简单,功能如以下php代码所示:

function byref_calltime($a) {

$a = '(modified by ref!)';

}

$foo = 'I am a string';

//使用&传递引用

byref_calltime(&$foo);

echo $foo;

//输出'(modified by ref!)'

?>

我们在传递参数的时候使用&操作符,便可以传递$foo变量的引用过去,而不是copy一份。当我们在函数内核修改这个参数时,函数外部的$foo也跟着被一起修改了。同样的功能我们如何在扩展里实现呢,其实很简单,请看下面的源码:

ZEND_FUNCTION(byref_calltime)

{

zval *a;

//我们我接收的参数传给zval *a;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &a) == FAILURE)

{

RETURN_NULL();

}

//如果a不是以应用的方式传递的。

if (!a->is_ref__gc)

{

return;

}

//将a转成字符串

convert_to_string(a);

//更改数据

ZVAL_STRING(a," (modified by ref!)",1);

return;

}

编译时的传递引用Compile-time Pass-by-ref

如果每一次都在调用函数时候都对参数加一个&符号真是太罗嗦了,有没有一个简单的办法呢,比如在定义函数的时候便声明这个参数是引用形式的,而不用用户自己加&符号表示引用,而由内核来完成这步操作?这个功能是有的,我们在PHP语言中可以这样实现。

// 在定义函数参数的时候加了引用符

function byref_compiletime(&$a) {

$a = ' (modified by ref!)';

}

$foo = 'I am a string';

//这个地方我们没有加&引用符

byref_compiletime($foo);

echo $foo;

//输出 (modified by ref!)

?>

上面的代码中,我们只是把引用符号从函数调用里转移到函数定义里。此功能在扩展里面实现的话就颇费周折了,我们需要提前为它定义一个arginfo结构体来向内核通知此函数的这个特定行为。添加此函数到module_entry里需要这样:

ZEND_FE(byref_compiletime, byref_compiletime_arginfo)

byref_compiletime_arginfo十一个arginfo结构体,我们在前面的章节中已经用过一次了。

在Zend Engine 2 (PHP5+)中,arginfo的数据是由多个zend_arg_info结构体构成的数组,数组的每一个成员即每一个zend_arg_info结构体处理函数的一个参数。zend_arg_info结构体的定义如下:

typedef struct _zend_arg_info {

const char *name;/* 参数的名称*/

zend_uint name_len;/* 参数名称的长度*/

const char *class_name;/* 类名 */

zend_uint class_name_len;/* 类名长度*/

zend_bool array_type_hint;/* 数组类型提示 */

zend_bool allow_null;/* 是否允许为NULL */

zend_bool pass_by_reference;/* 是否引用传递 */

zend_bool return_reference;/* 返回值是否为引用形式 */

int required_num_args; /* 必要参数的数量 */

} zend_arg_info;

生成zend_arg_info结构的数组比较繁琐,为了方便PHP扩展开发者,内核已经准备好了相应的宏来专门处理此问题,首先先用一个宏函数来生成头部,然后用第二个宏生成具体的数据,最后用一个宏生成尾部代码。

#define ZEND_BEGIN_ARG_INFO(name, pass_rest_by_reference)ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, ZEND_RETURN_VALUE, -1)

#define ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args)\

static const zend_arg_info name[] = {\

{ NULL, 0, NULL, 0, 0, 0, pass_rest_by_reference, return_reference, required_num_args },

#define ZEND_ARG_INFO(pass_by_ref, name){ #name, sizeof(#name)-1, NULL, 0, 0, 0, pass_by_ref, 0, 0 },

#define ZEND_ARG_PASS_INFO(pass_by_ref){ NULL, 0, NULL, 0, 0, 0, pass_by_ref, 0, 0 },

#define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, sizeof(#name)-1, #classname, sizeof(#classname)-1, 0, allow_null, pass_by_ref, 0, 0 },

#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) { #name, sizeof(#name)-1, NULL, 0, 1, allow_null, pass_by_ref, 0, 0 },

#define ZEND_END_ARG_INFO()};

//这里我们先看

ZEND_BEGIN_ARG_INFO(name, pass_rest_by_reference)

ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference,required_num_args)

这两个宏函数的前两个参数的含义是一样的,name便是这个zend_arg_info数组变量的名字,这里我们定义它为:byref_compiletime_arginfo。pass_rest_by_reference如果被赋值为1,则代表着所有的参数默认都是需要以引用的方式传递的(在arginfo中单独声明的除外)。而对于ZEND_BEGIN_ARG_INFO_EX的后两个参数:

name和pass_rest_by_reference的含义同上。

return_reference:声明这个函数的返回值需要以引用的形式返回,这个参数已经在前面章节用过了。

required_num_args:函数被调用时,传递参数至少为前N个函数(也就是后面参数都有默认值),当设置为-1时,必须传递所有参数

接下来让我们看生成具体数据的宏:

ZEND_ARG_PASS_INFO(by_ref)

//强制所有参数使用引用的方式传递

ZEND_ARG_INFO(by_ref, name)

//如果by_ref为1,则名称为name的参数必须以引用的方式传递,

ZEND_ARG_ARRAY_INFO(by_ref, name, allow_null)

ZEND_ARG_OBJ_INFO(by_ref, name, classname, allow_null)

这两个宏实现了类型绑定,也就是说我们在传递某个参数时,必须是数组类型或者某个类的实例。如果最后的参数为真,则除了绑定的数据类型,还可以传递一个NULL数据。

//我们组合起来使用:

ZEND_BEGIN_ARG_INFO(byref_compiletime_arginfo, 0)

ZEND_ARG_PASS_INFO(1)

ZEND_END_ARG_INFO()

为了使我们的扩展能够兼容PHP4,还需要使用#ifdef进行特殊处理。

#ifdef ZEND_ENGINE_2

ZEND_BEGIN_ARG_INFO(byref_compiletime_arginfo, 0)

ZEND_ARG_PASS_INFO(1)

ZEND_END_ARG_INFO()

#else /* ZE 1 */

static unsigned char byref_compiletime_arginfo[] = { 1, BYREF_FORCE };

#endif

我们copy一份ZEND_FUNCTION(byref_calltime)的实现,并重名成ZEND_FUNCTION(byref_compiletime)就行了。或者直接弄个ZEND_FALIAS就行了:

ZEND_FE(byref_compiletime,byref_calltime,byref_compiletime_arginfo)

延伸阅读

此文章所在专题列表如下:

php 加载完成后在执行函数,PHP内核探索:引用与函数执行相关推荐

  1. html等待图片全部加载,imgLoad等待图片资源加载完成后执行函数(图片预加载)...

    修改自一个2013年写的js判断图片资源加载完成后再执行函数的代码: 当时的代码片断需要手动加入需要确定加载完的图片名称到数组,比较麻烦,修改为插件后,不需要手动添加文件名称,只需对需要确定加载完成的 ...

  2. vue组件加载完成之后执行方法_vuejs页面加载完成后执行函数

    module.exports = { data: function(){ return { memberQrcodeState: false } }, components: {memberQrcod ...

  3. html动态加载js方法,原生JS实现动态加载js文件并在加载成功后执行回调函数的方法...

    本文实例讲述了原生JS实现动态加载js文件并在加载成功后执行回调函数的方法.分享给大家供大家参考,具体如下: 有的时候需要动态加载一个javascript文件,并且在加载成功后执行回调函数(例如文件中 ...

  4. js 和jQuery(自动执行函数)立即执行函数和页面加载完后执行函数写法

    js 立即执行函数的写法. js 立即执行函数只能用于匿名函数,如果声明了函数名是不可以用立即执行的,通常在函数表达式后加一对小括号()用于立即执行 如果想让函数不被调用的情况下,立即自动执行,需要在 ...

  5. JS,两种在页面加载完成后自动执行的方法(ready,onload)

    JS,两种在页面加载完成后自动执行的方法 1.jQuery的ready事件,需要引入jQuery的包才能使用,表示文档结构已经加载完成(不包含图片等非文字媒体文件): ready可以多次调用,可以绑定 ...

  6. JS--页面加载完毕后执行

    原文网址:JS--页面加载完毕后执行_IT利刃出鞘的博客-CSDN博客 简介 说明 本文用示例介绍JavaScript如何在页面加载完毕之后执行函数. 页面加载完毕主要有两个事件: DOMConten ...

  7. 动态加载JS后执行后续文件

    在正常的加载过程中,js的加载都是同步的,也就是在加载过程中,浏览器会阻塞接下来的内容的加载. 但有时候我们需要加载完JS后,执行某个函数.这时候我们就要用到动态加载,动态加载是异步的,如果我们在后边 ...

  8. jQuery:等页面DOM加载完毕后再执行代码

    等着页面DOM加载完毕后再执行代码 第一种方式(比较麻烦,不常用): $(document).ready(function(){ - }) 第二种方式(常用): $(function(){ - }) ...

  9. html5页面加载执行动作,页面加载完成后执行JS的5种方式

    在js和jquery使用中,常用到页面加载完成后执行某一方法.经过整理,大概是五种方式.javascript 1.jQuery的$( function(){} );html 2.jQuery的$(do ...

  10. js页面加载完成后自动执行指定方法

    1.要执行的代码是在DOM元素被加载完成的情况下执行,同一个页面可以反复调用该方法,会按照先后顺序依次执行 //jquery提供的完整方法 $(document).ready(function () ...

最新文章

  1. Ubuntu18.04运行ORB_SLAM2
  2. rn php,rn怎样在PHP的正则表达式中匹配到?
  3. 美国半导体十年计划中的NO.1,模拟硬件究竟有什么价值?
  4. 机器学习服务第一梯队都有谁?权威研究机构Forrester发布最新报告
  5. 科普云计算知识,迎接云计算大会
  6. mysql using filesort_mysql using filesort Using temporary
  7. 在一个解决方案中用C#测试调用C++ DLL
  8. Tesseract-OCR 字符识别-样书训练
  9. android progressdialog 样式,android之修改系统自带ProgressDialog样式
  10. MyBatis之快速入门
  11. Cocos2d-x 3 X CMake MinGW版本编译运行
  12. Swift 4.1带来条件一致性等语言上的提升
  13. 基于内容的图像检索概述
  14. C++之const类成员变量,const成员函数
  15. 【jQwidgets】简单封装示例
  16. PreScan 教程:0. PreScan与Matlab连接
  17. Springboot Mybatis MySQL读写分离及事物配置
  18. 《刷新:重新发现商业与未来》读后感
  19. Acrobat如何将PDF拆分为多个文档
  20. 中缀转后缀表达式并计算

热门文章

  1. html - 好友列表 - 头像为名字最后一个字
  2. 循环右移, c = c ~(~0 (32-n))
  3. 利用fixed把div浮动在底部
  4. 现在的游戏都是java吗_Java程序员:工作还是游戏,是该好好衡量一下了
  5. python量化交易:筹码分布(2)
  6. 超棒的离线文档阅读器:Zeal
  7. 山科大web开发————表格的制作(个人简历)
  8. Python入门程序【十二】
  9. PADS LogicPADS Layout软件常用的快捷键
  10. android 10.0 更换壁纸加载慢滑动卡顿的解决