[ PHP 内核与扩展开发系列] 函数返回值:引用参数与函数的执行结果

栏目: PHP · 发布时间: 8年前

内容简介:[ PHP 内核与扩展开发系列] 函数返回值:引用参数与函数的执行结果

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

运行时传递引用

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

<?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;
}

编译时传递引用

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

<?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 (PHP 5+) 中, 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_arginfopass_rest_by_reference 如果被赋值为 1 ,则代表着所有的参数默认都是需要以引用的方式传递的(在arginfo中单独声明的除外)。而对于 ZEND_BEGIN_ARG_INFO_EX 的后两个参数:

  • 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()

为了使我们的扩展能够兼容 PHP 4,还需要使用 #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_FALIAS(byref_compiletime, byref_calltime, byref_compiletime_arginfo)

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Single Page Web Applications

Single Page Web Applications

Michael Mikowski、Josh Powell / Manning Publications / 2013-9-30 / USD 44.99

Code for most web sites mostly runs on the server. When a user clicks on a link, the site reacts slowly because the browser sends information to the server and the server sends it back again before di......一起来看看 《Single Page Web Applications》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

html转js在线工具
html转js在线工具

html转js在线工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具