Skip to content

Commit d71355c

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 1e14b7a commit d71355c

8 files changed

+110
-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
@@ -1203,8 +1203,7 @@ ZEND_FUNCTION(get_object_vars)
12031203
continue;
12041204
}
12051205

1206-
ZEND_ASSERT(key);
1207-
if (zend_check_property_access(zobj, key) == FAILURE) {
1206+
if (zend_check_property_slot_access(zobj, value) == FAILURE) {
12081207
continue;
12091208
}
12101209
unmangle = 1;

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) {
@@ -573,6 +606,32 @@ ZEND_API int zend_check_property_access(zend_object *zobj, zend_string *prop_inf
573606
}
574607
/* }}} */
575608

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

Zend/zend_object_handlers.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,30 @@ ZEND_API zend_function *zend_get_call_trampoline_func(zend_class_entry *ce, zend
207207

208208
ZEND_API uint32_t *zend_get_property_guard(zend_object *zobj, zend_string *member);
209209

210+
ZEND_API struct _zend_property_info **zend_build_properties_info_table(zend_class_entry *ce);
211+
ZEND_API int zend_check_property_info_access(struct _zend_property_info *prop_info);
212+
213+
static inline struct _zend_property_info **zend_get_properties_info_table(zend_class_entry *ce)
214+
{
215+
ZEND_ASSERT(ce->default_properties_count != 0);
216+
if (ce->properties_info_table) {
217+
return ce->properties_info_table;
218+
}
219+
220+
return zend_build_properties_info_table(ce);
221+
}
222+
223+
static inline struct _zend_property_info *zend_get_property_info_for_slot(zend_object *obj, zval *slot)
224+
{
225+
struct _zend_property_info **table = zend_get_properties_info_table(obj->ce);
226+
return table[slot - obj->properties_table];
227+
}
228+
229+
static inline int zend_check_property_slot_access(zend_object *obj, zval *slot) /* {{{ */
230+
{
231+
return zend_check_property_info_access(zend_get_property_info_for_slot(obj, slot));
232+
}
233+
210234
#define zend_free_trampoline(func) do { \
211235
if ((func) == &EG(trampoline)) { \
212236
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
@@ -5880,7 +5880,7 @@ ZEND_VM_C_LABEL(fe_fetch_r_exit):
58805880
value = Z_INDIRECT_P(value);
58815881
value_type = Z_TYPE_INFO_P(value);
58825882
if (EXPECTED(value_type != IS_UNDEF)
5883-
&& EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) {
5883+
&& EXPECTED(zend_check_property_slot_access(Z_OBJ_P(array), value) == SUCCESS)) {
58845884
break;
58855885
}
58865886
} else {
@@ -6030,7 +6030,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR)
60306030
value = Z_INDIRECT_P(value);
60316031
value_type = Z_TYPE_INFO_P(value);
60326032
if (EXPECTED(value_type != IS_UNDEF)
6033-
&& EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) {
6033+
&& EXPECTED(zend_check_property_slot_access(Z_OBJ_P(array), value) == SUCCESS)) {
60346034
break;
60356035
}
60366036
} else {

Zend/zend_vm_execute.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21364,7 +21364,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZE
2136421364
value = Z_INDIRECT_P(value);
2136521365
value_type = Z_TYPE_INFO_P(value);
2136621366
if (EXPECTED(value_type != IS_UNDEF)
21367-
&& EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) {
21367+
&& EXPECTED(zend_check_property_slot_access(Z_OBJ_P(array), value) == SUCCESS)) {
2136821368
break;
2136921369
}
2137021370
} else {
@@ -21514,7 +21514,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z
2151421514
value = Z_INDIRECT_P(value);
2151521515
value_type = Z_TYPE_INFO_P(value);
2151621516
if (EXPECTED(value_type != IS_UNDEF)
21517-
&& EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) {
21517+
&& EXPECTED(zend_check_property_slot_access(Z_OBJ_P(array), value) == SUCCESS)) {
2151821518
break;
2151921519
}
2152021520
} else {

ext/standard/http.c

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,26 @@ 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);
6167

62-
zend_object *zobj = Z_OBJ_P(type);
63-
if (zend_check_property_access(zobj, key) != SUCCESS) {
68+
if (type) {
69+
const char *tmp;
70+
if (zend_check_property_slot_access(Z_OBJ_P(type), zdata) != SUCCESS) {
6471
/* private or protected property access outside of the class */
6572
continue;
6673
}
6774
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);
7175
}
72-
} else {
73-
prop_name = NULL;
74-
prop_len = 0;
7576
}
7677

7778
ZVAL_DEREF(zdata);

0 commit comments

Comments
 (0)