Skip to content

Commit 5371206

Browse files
committed
Implement mechanism for finding prop_info for property slot
Currently there is no efficient way of finding the property_info which corresponds to a given property slot. This patch implements such a mechanism, by storing an array of property_infos in offset order on the class. This structure is lazily initialized when it is needed, though we could also compute it during inheritance. This patch only uses it to optimize visibility checks during foreach (and get_object_vars etc). We avoid having to look up the property by name and can directly check the accessibility. We're also interested in having this mapping to handle some edge cases in the typed properties implementation.
1 parent 76c8d79 commit 5371206

File tree

9 files changed

+116
-18
lines changed

9 files changed

+116
-18
lines changed

Zend/zend.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ struct _zend_class_entry {
135135
HashTable properties_info;
136136
HashTable constants_table;
137137

138+
struct _zend_property_info **properties_info_table;
139+
138140
zend_function *constructor;
139141
zend_function *destructor;
140142
zend_function *clone;

Zend/zend_builtin_functions.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,8 +1198,7 @@ ZEND_FUNCTION(get_object_vars)
11981198
continue;
11991199
}
12001200

1201-
ZEND_ASSERT(key);
1202-
if (zend_check_property_access(zobj, key) == FAILURE) {
1201+
if (zend_check_property_slot_access(zobj, value) == FAILURE) {
12031202
continue;
12041203
}
12051204
unmangle = 1;

Zend/zend_compile.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1622,6 +1622,7 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify
16221622

16231623
ce->default_properties_count = 0;
16241624
ce->default_static_members_count = 0;
1625+
ce->properties_info_table = NULL;
16251626

16261627
if (nullify_handlers) {
16271628
ce->constructor = NULL;

Zend/zend_object_handlers.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,39 @@
5858
called, we cal __call handler.
5959
*/
6060

61+
ZEND_API zend_property_info **zend_build_properties_info_table(zend_class_entry *ce)
62+
{
63+
zend_property_info *prop;
64+
zend_property_info **table = pemalloc(
65+
sizeof(zend_property_info *) * ce->default_properties_count,
66+
ce->type == ZEND_INTERNAL_CLASS
67+
);
68+
69+
ZEND_ASSERT(ce->properties_info_table == NULL);
70+
ZEND_ASSERT(ce->default_properties_count != 0);
71+
ce->properties_info_table = table;
72+
73+
if (ce->parent && ce->parent->default_properties_count != 0) {
74+
zend_property_info **parent_table = zend_get_properties_info_table(ce->parent);
75+
memcpy(
76+
table, parent_table,
77+
sizeof(zend_property_info *) * ce->parent->default_properties_count
78+
);
79+
80+
/* Child did not add any new properties, we are done */
81+
if (ce->default_properties_count == ce->parent->default_properties_count) {
82+
return table;
83+
}
84+
}
85+
86+
ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) {
87+
if (prop->ce == ce && (prop->flags & ZEND_ACC_STATIC) == 0) {
88+
table[OBJ_PROP_TO_NUM(prop->offset)] = prop;
89+
}
90+
} ZEND_HASH_FOREACH_END();
91+
return table;
92+
}
93+
6194
ZEND_API void rebuild_object_properties(zend_object *zobj) /* {{{ */
6295
{
6396
if (!zobj->properties) {
@@ -571,6 +604,32 @@ ZEND_API int zend_check_property_access(zend_object *zobj, zend_string *prop_inf
571604
}
572605
/* }}} */
573606

607+
ZEND_API int zend_check_property_info_access(zend_property_info *prop_info) /* {{{ */
608+
{
609+
zend_class_entry *scope;
610+
if (prop_info->flags & ZEND_ACC_PUBLIC) {
611+
return SUCCESS;
612+
}
613+
614+
if (UNEXPECTED(EG(fake_scope))) {
615+
scope = EG(fake_scope);
616+
} else {
617+
scope = zend_get_executed_scope();
618+
}
619+
620+
if (prop_info->ce == scope) {
621+
return SUCCESS;
622+
}
623+
624+
if ((prop_info->flags & ZEND_ACC_PROTECTED)
625+
&& is_protected_compatible_scope(prop_info->ce, scope)) {
626+
return SUCCESS;
627+
}
628+
629+
return FAILURE;
630+
}
631+
/* }}} */
632+
574633
static void zend_property_guard_dtor(zval *el) /* {{{ */ {
575634
uint32_t *ptr = (uint32_t*)Z_PTR_P(el);
576635
if (EXPECTED(!(((zend_uintptr_t)ptr) & 1))) {

Zend/zend_object_handlers.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,32 @@ ZEND_API HashTable *zend_get_properties_for(zval *obj, zend_prop_purpose purpose
242242
} \
243243
} while (0)
244244

245+
ZEND_API struct _zend_property_info **zend_build_properties_info_table(zend_class_entry *ce);
246+
ZEND_API int zend_check_property_info_access(struct _zend_property_info *prop_info);
247+
248+
static inline struct _zend_property_info **zend_get_properties_info_table(zend_class_entry *ce)
249+
{
250+
ZEND_ASSERT(ce->default_properties_count != 0);
251+
if (ce->properties_info_table) {
252+
return ce->properties_info_table;
253+
}
254+
255+
return zend_build_properties_info_table(ce);
256+
}
257+
258+
static inline struct _zend_property_info *zend_get_property_info_for_slot(zend_object *obj, zval *slot)
259+
{
260+
struct _zend_property_info **table = zend_get_properties_info_table(obj->ce);
261+
intptr_t prop_num = slot - obj->properties_table;
262+
ZEND_ASSERT(prop_num >= 0 && prop_num < obj->ce->default_properties_count);
263+
return table[prop_num];
264+
}
265+
266+
static inline int zend_check_property_slot_access(zend_object *obj, zval *slot) /* {{{ */
267+
{
268+
return zend_check_property_info_access(zend_get_property_info_for_slot(obj, slot));
269+
}
270+
245271
#define zend_free_trampoline(func) do { \
246272
if ((func) == &EG(trampoline)) { \
247273
EG(trampoline).common.function_name = NULL; \

Zend/zend_opcode.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,10 @@ ZEND_API void destroy_zend_class(zval *zv)
284284
_destroy_zend_class_traits_info(ce);
285285
}
286286

287+
if (ce->properties_info_table) {
288+
efree(ce->properties_info_table);
289+
}
290+
287291
break;
288292
case ZEND_INTERNAL_CLASS:
289293
if (ce->default_properties_table) {
@@ -339,6 +343,9 @@ ZEND_API void destroy_zend_class(zval *zv)
339343
if (ce->num_interfaces > 0) {
340344
free(ce->interfaces);
341345
}
346+
if (ce->properties_info_table) {
347+
free(ce->properties_info_table);
348+
}
342349
free(ce);
343350
break;
344351
}

Zend/zend_vm_def.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5876,7 +5876,7 @@ ZEND_VM_C_LABEL(fe_fetch_r_exit):
58765876
value = Z_INDIRECT_P(value);
58775877
value_type = Z_TYPE_INFO_P(value);
58785878
if (EXPECTED(value_type != IS_UNDEF)
5879-
&& EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) {
5879+
&& EXPECTED(zend_check_property_slot_access(Z_OBJ_P(array), value) == SUCCESS)) {
58805880
break;
58815881
}
58825882
} else {
@@ -6026,7 +6026,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR)
60266026
value = Z_INDIRECT_P(value);
60276027
value_type = Z_TYPE_INFO_P(value);
60286028
if (EXPECTED(value_type != IS_UNDEF)
6029-
&& EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) {
6029+
&& EXPECTED(zend_check_property_slot_access(Z_OBJ_P(array), value) == SUCCESS)) {
60306030
break;
60316031
}
60326032
} else {

Zend/zend_vm_execute.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21352,7 +21352,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZE
2135221352
value = Z_INDIRECT_P(value);
2135321353
value_type = Z_TYPE_INFO_P(value);
2135421354
if (EXPECTED(value_type != IS_UNDEF)
21355-
&& EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) {
21355+
&& EXPECTED(zend_check_property_slot_access(Z_OBJ_P(array), value) == SUCCESS)) {
2135621356
break;
2135721357
}
2135821358
} else {
@@ -21502,7 +21502,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z
2150221502
value = Z_INDIRECT_P(value);
2150321503
value_type = Z_TYPE_INFO_P(value);
2150421504
if (EXPECTED(value_type != IS_UNDEF)
21505-
&& EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) {
21505+
&& EXPECTED(zend_check_property_slot_access(Z_OBJ_P(array), value) == SUCCESS)) {
2150621506
break;
2150721507
}
2150821508
} else {

ext/standard/http.c

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,29 @@ PHPAPI int php_url_encode_hash_ex(HashTable *ht, smart_str *formstr,
5353
}
5454
arg_sep_len = strlen(arg_sep);
5555

56-
ZEND_HASH_FOREACH_KEY_VAL_IND(ht, idx, key, zdata) {
57-
/* handling for private & protected object properties */
56+
ZEND_HASH_FOREACH_KEY_VAL(ht, idx, key, zdata) {
5857
if (key) {
59-
if (ZSTR_VAL(key)[0] == '\0' && type != NULL) {
60-
const char *tmp;
58+
prop_name = ZSTR_VAL(key);
59+
prop_len = ZSTR_LEN(key);
60+
} else {
61+
prop_name = NULL;
62+
prop_len = 0;
63+
}
64+
65+
if (Z_TYPE_P(zdata) == IS_INDIRECT) {
66+
zdata = Z_INDIRECT_P(zdata);
67+
if (Z_ISUNDEF_P(zdata)) {
68+
continue;
69+
}
6170

62-
zend_object *zobj = Z_OBJ_P(type);
63-
if (zend_check_property_access(zobj, key) != SUCCESS) {
71+
if (type) {
72+
const char *tmp;
73+
if (zend_check_property_slot_access(Z_OBJ_P(type), zdata) != SUCCESS) {
6474
/* private or protected property access outside of the class */
6575
continue;
6676
}
6777
zend_unmangle_property_name_ex(key, &tmp, &prop_name, &prop_len);
68-
} else {
69-
prop_name = ZSTR_VAL(key);
70-
prop_len = ZSTR_LEN(key);
7178
}
72-
} else {
73-
prop_name = NULL;
74-
prop_len = 0;
7579
}
7680

7781
ZVAL_DEREF(zdata);

0 commit comments

Comments
 (0)