PHP扩展如何获取私有属性的名字

本篇文章基于PHP7.4.10

我们的测试脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

class Foo
{
public $a;

private $b;

public function __construct()
{
$this->a = 1;
$this->b = 1;
}
}

$foo = new Foo;

printAllAttributeKeys($foo);

其中printAllAttributeKeys是我们要编写的一个扩展函数,用来打印对象所有的属性名字。

因为PHP属性是存在一个哈希表里面的,所以我们可以进行如下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static PHP_FUNCTION(printAllAttributeKeys) {
zend_array *properties;
zval *zobj;
zend_ulong num;
zend_string *key;
zval *val;

ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1)
Z_PARAM_OBJECT(zobj)
ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);

#if PHP_VERSION_ID >= 70400
properties = zend_get_properties_for(zobj, ZEND_PROP_PURPOSE_VAR_EXPORT);
#else
if (Z_OBJ_HANDLER_P(zobj, get_properties)) {
properties = Z_OBJPROP_P(zobj);
}
#endif

ZEND_HASH_FOREACH_KEY_VAL_IND(properties, num, key, val) {
printf("%s\n", ZSTR_VAL(key));
} ZEND_HASH_FOREACH_END();
}

执行结果如下:

1
2
3
php test.php
a

可以发现,只打印出了a。实际上,对于b这个属性,它在zend_string里的存储内容为:

1
\0Foo\0b

如果我们要打印出私有属性,我们可以作如下操作:

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
const char *get_property_name(zend_string *property_name) {
const char *class_name, *_property_name;
size_t _property_name_len;

zend_unmangle_property_name_ex(property_name, &class_name, &_property_name, &_property_name_len);

return _property_name;
}

static PHP_FUNCTION(printAllAttributeKeys) {
zend_array *properties;
zval *zobj;
zend_ulong num;
zend_string *key;
zval *val;

ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1)
Z_PARAM_OBJECT(zobj)
ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);

#if PHP_VERSION_ID >= 70400
properties = zend_get_properties_for(zobj, ZEND_PROP_PURPOSE_VAR_EXPORT);
#else
if (Z_OBJ_HANDLER_P(zobj, get_properties)) {
properties = Z_OBJPROP_P(zobj);
}
#endif

ZEND_HASH_FOREACH_KEY_VAL_IND(properties, num, key, val) {
printf("%s\n", get_property_name(key));
} ZEND_HASH_FOREACH_END();
}

对属性key这个zend_string调用zend_unmangle_property_name_ex即可获取到私有属性的名字。

执行结果如下:

1
2
3
php test.php
a
b