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

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

内容简介:[ 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)

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

查看所有标签

猜你喜欢:

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

Scalable Internet Architectures

Scalable Internet Architectures

Theo Schlossnagle / Sams Publishing / 2006-7-31 / USD 49.99

As a developer, you are aware of the increasing concern amongst developers and site architects that websites be able to handle the vast number of visitors that flood the Internet on a daily basis. Sc......一起来看看 《Scalable Internet Architectures》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

URL 编码/解码
URL 编码/解码

URL 编码/解码

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具