zend_read_property的rv参数作用

本文基于的PHP版本为7.3.12

我们在开发PHP扩展的时候,经常会要去读取对象的属性,一般来说就是使用zend_read_property这个函数来完成。这个函数的原型如下:

1
ZEND_API zval *zend_read_property(zend_class_entry *scope, zval *object, const char *name, size_t name_length, zend_bool silent, zval *rv);

这个函数的返回值很容易理解,就是这个属性对应的value值。那么,最后一个参数zval *rv有什么用呢?

这个参数是给动态属性来用的。我们知道,当访问一个PHP对象的动态属性的时候,是会去调用这个对象的__get魔术方法。而动态属性它的内存是不在zend_object上面的,它是通过__get魔术方法来得到的。而普通的属性它的内存是在zend_object上面的,所以,当我们去访问普通的属性的时候,可以直接返回一个zval

既然访问动态属性是通过调用__get魔术方法来实现的,那么,类似于zend_call_method一样,我们需要去设置zend_fcall_info::retval。当函数调用结束的时候,返回值就会存放在这个zval中。这样,我们就可以取到动态属性的值了。

核心的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
static void zend_std_call_getter(zend_object *zobj, zend_string *prop_name, zval *retval) /* {{{ */
{
zend_class_entry *ce = zobj->ce;
zend_class_entry *orig_fake_scope = EG(fake_scope);
zend_fcall_info fci;
zend_fcall_info_cache fcic;
zval member;

EG(fake_scope) = NULL;

/* __get handler is called with one argument:
property name

it should return whether the call was successful or not
*/

ZVAL_STR(&member, prop_name);

fci.size = sizeof(fci);
fci.object = zobj;
fci.retval = retval;
fci.param_count = 1;
fci.params = &member;
fci.no_separation = 1;
ZVAL_UNDEF(&fci.function_name); /* Unused */

fcic.function_handler = ce->__get;
fcic.called_scope = ce;
fcic.object = zobj;

zend_call_function(&fci, &fcic);

EG(fake_scope) = orig_fake_scope;
}

当访问PHP对象的动态属性的时候,就会去调用这个函数。而这个函数的retval就是我们zend_read_propertyrv参数。