diff --git a/Zend/zend.h b/Zend/zend.h index d6d427ccdbed4..fb4f8dc8f8293 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -135,6 +135,8 @@ struct _zend_class_entry { HashTable properties_info; HashTable constants_table; + struct _zend_property_info **properties_info_table; + zend_function *constructor; zend_function *destructor; zend_function *clone; diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 81a4ee76986dc..f9c3cddc79e4e 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2743,6 +2743,7 @@ ZEND_API zend_class_entry *zend_register_internal_class_ex(zend_class_entry *cla if (parent_ce) { zend_do_inheritance(register_class, parent_ce); + zend_build_properties_info_table(register_class); } return register_class; } @@ -3715,10 +3716,20 @@ ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, z property_info->offset = property_info_ptr->offset; zval_ptr_dtor(&ce->default_properties_table[OBJ_PROP_TO_NUM(property_info->offset)]); zend_hash_del(&ce->properties_info, name); + + ZEND_ASSERT(ce->type == ZEND_INTERNAL_CLASS); + ZEND_ASSERT(ce->properties_info_table != NULL); + ce->properties_info_table[OBJ_PROP_TO_NUM(property_info->offset)] = property_info; } else { property_info->offset = OBJ_PROP_TO_OFFSET(ce->default_properties_count); ce->default_properties_count++; ce->default_properties_table = perealloc(ce->default_properties_table, sizeof(zval) * ce->default_properties_count, ce->type == ZEND_INTERNAL_CLASS); + + /* For user classes this is handled during linking */ + if (ce->type == ZEND_INTERNAL_CLASS) { + ce->properties_info_table = perealloc(ce->properties_info_table, sizeof(zend_property_info *) * ce->default_properties_count, 1); + ce->properties_info_table[ce->default_properties_count - 1] = property_info; + } } ZVAL_COPY_VALUE(&ce->default_properties_table[OBJ_PROP_TO_NUM(property_info->offset)], property); } diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 364d478ab73f3..dc8d0c3dde9fc 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1198,8 +1198,7 @@ ZEND_FUNCTION(get_object_vars) continue; } - ZEND_ASSERT(key); - if (zend_check_property_access(zobj, key) == FAILURE) { + if (zend_check_property_slot_access(zobj, value) == FAILURE) { continue; } unmangle = 1; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index daaf00c9b1386..481a3bd13f17a 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1622,6 +1622,7 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify ce->default_properties_count = 0; ce->default_static_members_count = 0; + ce->properties_info_table = NULL; if (nullify_handlers) { ce->constructor = NULL; @@ -6331,6 +6332,7 @@ void zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */ CG(zend_lineno) = decl->end_lineno; ce->ce_flags |= ZEND_ACC_LINKED; zend_do_inheritance(ce, parent_ce); + zend_build_properties_info_table(ce); if ((ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) == ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) { zend_verify_abstract_class(ce); } @@ -6342,6 +6344,7 @@ void zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */ } else { if (EXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ce) != NULL)) { zend_string_release(lcname); + zend_build_properties_info_table(ce); ce->ce_flags |= ZEND_ACC_LINKED; return; } diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 9a4efed9b9e81..378100dc73a2d 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -782,6 +782,42 @@ static void do_inherit_class_constant(zend_string *name, zend_class_constant *pa } /* }}} */ +void zend_build_properties_info_table(zend_class_entry *ce) +{ + zend_property_info **table, *prop; + if (ce->default_properties_count == 0) { + return; + } + + ZEND_ASSERT(ce->properties_info_table == NULL); + if (ce->type == ZEND_USER_CLASS) { + ce->properties_info_table = table = zend_arena_alloc(&CG(arena), + sizeof(zend_property_info *) * ce->default_properties_count); + } else { + ce->properties_info_table = table = pemalloc( + sizeof(zend_property_info *) * ce->default_properties_count, 1); + } + + if (ce->parent && ce->parent->default_properties_count != 0) { + zend_property_info **parent_table = ce->parent->properties_info_table; + memcpy( + table, parent_table, + sizeof(zend_property_info *) * ce->parent->default_properties_count + ); + + /* Child did not add any new properties, we are done */ + if (ce->default_properties_count == ce->parent->default_properties_count) { + return; + } + } + + ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) { + if (prop->ce == ce && (prop->flags & ZEND_ACC_STATIC) == 0) { + table[OBJ_PROP_TO_NUM(prop->offset)] = prop; + } + } ZEND_HASH_FOREACH_END(); +} + ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce) /* {{{ */ { zend_property_info *property_info; @@ -1936,6 +1972,8 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_class_entry *parent) if ((ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) == ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) { zend_verify_abstract_class(ce); } + + zend_build_properties_info_table(ce); } /* }}} */ diff --git a/Zend/zend_inheritance.h b/Zend/zend_inheritance.h index d6cca74389b3d..fe85aea65022b 100644 --- a/Zend/zend_inheritance.h +++ b/Zend/zend_inheritance.h @@ -31,6 +31,7 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_class_entry *parent_ void zend_verify_abstract_class(zend_class_entry *ce); void zend_check_deprecated_constructor(const zend_class_entry *ce); +void zend_build_properties_info_table(zend_class_entry *ce); END_EXTERN_C() diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 6773f99704347..82959e7fda2ea 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -571,6 +571,32 @@ ZEND_API int zend_check_property_access(zend_object *zobj, zend_string *prop_inf } /* }}} */ +ZEND_API int zend_check_property_info_access(zend_property_info *prop_info) /* {{{ */ +{ + zend_class_entry *scope; + if (prop_info->flags & ZEND_ACC_PUBLIC) { + return SUCCESS; + } + + if (UNEXPECTED(EG(fake_scope))) { + scope = EG(fake_scope); + } else { + scope = zend_get_executed_scope(); + } + + if (prop_info->ce == scope) { + return SUCCESS; + } + + if ((prop_info->flags & ZEND_ACC_PROTECTED) + && is_protected_compatible_scope(prop_info->ce, scope)) { + return SUCCESS; + } + + return FAILURE; +} +/* }}} */ + static void zend_property_guard_dtor(zval *el) /* {{{ */ { uint32_t *ptr = (uint32_t*)Z_PTR_P(el); if (EXPECTED(!(((zend_uintptr_t)ptr) & 1))) { diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h index 3d89783727b29..b43279ae932ec 100644 --- a/Zend/zend_object_handlers.h +++ b/Zend/zend_object_handlers.h @@ -242,6 +242,21 @@ ZEND_API HashTable *zend_get_properties_for(zval *obj, zend_prop_purpose purpose } \ } while (0) +ZEND_API int zend_check_property_info_access(struct _zend_property_info *prop_info); + +static inline struct _zend_property_info *zend_get_property_info_for_slot(zend_object *obj, zval *slot) +{ + struct _zend_property_info **table = obj->ce->properties_info_table; + intptr_t prop_num = slot - obj->properties_table; + ZEND_ASSERT(prop_num >= 0 && prop_num < obj->ce->default_properties_count); + return table[prop_num]; +} + +static inline int zend_check_property_slot_access(zend_object *obj, zval *slot) /* {{{ */ +{ + return zend_check_property_info_access(zend_get_property_info_for_slot(obj, slot)); +} + #define zend_free_trampoline(func) do { \ if ((func) == &EG(trampoline)) { \ EG(trampoline).common.function_name = NULL; \ diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index f348b0b5892e3..9f9fee5ce13c1 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -339,6 +339,9 @@ ZEND_API void destroy_zend_class(zval *zv) if (ce->num_interfaces > 0) { free(ce->interfaces); } + if (ce->properties_info_table) { + free(ce->properties_info_table); + } free(ce); break; } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 36fb9a1779b4c..8ee520527dc9d 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5876,7 +5876,7 @@ ZEND_VM_C_LABEL(fe_fetch_r_exit): value = Z_INDIRECT_P(value); value_type = Z_TYPE_INFO_P(value); if (EXPECTED(value_type != IS_UNDEF) - && EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) { + && EXPECTED(zend_check_property_slot_access(Z_OBJ_P(array), value) == SUCCESS)) { break; } } else { @@ -6026,7 +6026,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR) value = Z_INDIRECT_P(value); value_type = Z_TYPE_INFO_P(value); if (EXPECTED(value_type != IS_UNDEF) - && EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) { + && EXPECTED(zend_check_property_slot_access(Z_OBJ_P(array), value) == SUCCESS)) { break; } } else { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 03d767247b662..ebb852f603a22 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -21352,7 +21352,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZE value = Z_INDIRECT_P(value); value_type = Z_TYPE_INFO_P(value); if (EXPECTED(value_type != IS_UNDEF) - && EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) { + && EXPECTED(zend_check_property_slot_access(Z_OBJ_P(array), value) == SUCCESS)) { break; } } else { @@ -21502,7 +21502,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z value = Z_INDIRECT_P(value); value_type = Z_TYPE_INFO_P(value); if (EXPECTED(value_type != IS_UNDEF) - && EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) { + && EXPECTED(zend_check_property_slot_access(Z_OBJ_P(array), value) == SUCCESS)) { break; } } else { diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index 4db2c34fbcd12..7e7350a5b1f65 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -279,6 +279,16 @@ static void zend_class_copy_ctor(zend_class_entry **pce) /* constants table */ zend_hash_clone_constants(&ce->constants_table, &old_ce->constants_table); + if (ce->properties_info_table) { + int i; + ce->properties_info_table = ARENA_REALLOC(ce->properties_info_table); + for (i = 0; i < ce->default_properties_count; i++) { + if (IN_ARENA(ce->properties_info_table[i])) { + ce->properties_info_table[i] = ARENA_REALLOC(ce->properties_info_table[i]); + } + } + } + if (ce->num_interfaces) { zend_class_name *interface_names; diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index f6ed2fc91877e..da99c9d55ae78 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -653,6 +653,19 @@ static void zend_file_cache_serialize_class(zval *zv, SERIALIZE_STR(ce->info.user.doc_comment); zend_file_cache_serialize_hash(&ce->properties_info, script, info, buf, zend_file_cache_serialize_prop_info); + if (ce->properties_info_table) { + uint32_t i; + zend_property_info **table; + + SERIALIZE_PTR(ce->properties_info_table); + table = ce->properties_info_table; + UNSERIALIZE_PTR(table); + + for (i = 0; i < ce->default_properties_count; i++) { + SERIALIZE_PTR(table[i]); + } + } + if (ce->num_interfaces) { uint32_t i; zend_class_name *interface_names; @@ -1294,6 +1307,15 @@ static void zend_file_cache_unserialize_class(zval *zv, zend_file_cache_unserialize_hash(&ce->properties_info, script, buf, zend_file_cache_unserialize_prop_info, NULL); + if (ce->properties_info_table) { + uint32_t i; + UNSERIALIZE_PTR(ce->properties_info_table); + + for (i = 0; i < ce->default_properties_count; i++) { + UNSERIALIZE_PTR(ce->properties_info_table[i]); + } + } + if (ce->num_interfaces) { uint32_t i; diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 6b3413324c62f..67ba23a2adbaa 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -778,6 +778,19 @@ static void zend_persist_class_entry(zval *zv) zend_hash_persist(&ce->properties_info, zend_persist_property_info); HT_FLAGS(&ce->properties_info) &= (HASH_FLAG_INITIALIZED | HASH_FLAG_STATIC_KEYS); + if (ce->properties_info_table) { + int i; + + size_t size = sizeof(zend_property_info *) * ce->default_properties_count; + memcpy(ZCG(arena_mem), ce->properties_info_table, size); + ce->properties_info_table = ZCG(arena_mem); + ZCG(arena_mem) = (void*)((char*)ZCG(arena_mem) + ZEND_ALIGNED_SIZE(size)); + + for (i = 0; i < ce->default_properties_count; i++) { + ce->properties_info_table[i] = zend_shared_alloc_get_xlat_entry(ce->properties_info_table[i]); + } + } + if (ce->num_interfaces) { uint32_t i = 0; diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index fcd5db58a7e9d..f60e9495a9dcc 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -343,6 +343,10 @@ static void zend_persist_class_entry_calc(zval *zv) zend_hash_persist_calc(&ce->properties_info, zend_persist_property_info_calc); + if (ce->properties_info_table) { + ADD_ARENA_SIZE(sizeof(zend_property_info *) * ce->default_properties_count); + } + if (ce->num_interfaces) { uint32_t i; diff --git a/ext/standard/http.c b/ext/standard/http.c index 293258b1aafc1..08729f8a9ad89 100644 --- a/ext/standard/http.c +++ b/ext/standard/http.c @@ -53,25 +53,29 @@ PHPAPI int php_url_encode_hash_ex(HashTable *ht, smart_str *formstr, } arg_sep_len = strlen(arg_sep); - ZEND_HASH_FOREACH_KEY_VAL_IND(ht, idx, key, zdata) { - /* handling for private & protected object properties */ + ZEND_HASH_FOREACH_KEY_VAL(ht, idx, key, zdata) { if (key) { - if (ZSTR_VAL(key)[0] == '\0' && type != NULL) { - const char *tmp; + prop_name = ZSTR_VAL(key); + prop_len = ZSTR_LEN(key); + } else { + prop_name = NULL; + prop_len = 0; + } + + if (Z_TYPE_P(zdata) == IS_INDIRECT) { + zdata = Z_INDIRECT_P(zdata); + if (Z_ISUNDEF_P(zdata)) { + continue; + } - zend_object *zobj = Z_OBJ_P(type); - if (zend_check_property_access(zobj, key) != SUCCESS) { + if (type) { + const char *tmp; + if (zend_check_property_slot_access(Z_OBJ_P(type), zdata) != SUCCESS) { /* private or protected property access outside of the class */ continue; } zend_unmangle_property_name_ex(key, &tmp, &prop_name, &prop_len); - } else { - prop_name = ZSTR_VAL(key); - prop_len = ZSTR_LEN(key); } - } else { - prop_name = NULL; - prop_len = 0; } ZVAL_DEREF(zdata);