From 9489cf82b53b09c56a1380bc0a4eee82a60f3ade Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 12 Mar 2017 22:59:57 +0100 Subject: [PATCH 1/3] More __sleep() refactoring Decouple handling of __sleep() from remaining serialization logic completely. In particular, create an explicit HT with the properties that are to be serialized. To compensate, remove the previously existing names HT and perform duplicate property handling on the fly. A side benefit of doing it this way is that duplicate properties will be detected even if they only clash after name mangling. --- .../sleep_duplicate_props_via_mangling.phpt | Bin 0 -> 384 bytes ext/standard/var.c | 290 +++++++++--------- 2 files changed, 143 insertions(+), 147 deletions(-) create mode 100644 ext/standard/tests/serialize/sleep_duplicate_props_via_mangling.phpt diff --git a/ext/standard/tests/serialize/sleep_duplicate_props_via_mangling.phpt b/ext/standard/tests/serialize/sleep_duplicate_props_via_mangling.phpt new file mode 100644 index 0000000000000000000000000000000000000000..13541f872b042415a465e25a5f070a0bac9ee372 GIT binary patch literal 384 zcmYk2(M!WH5XSd>f5j0tXu-mU`jV=9kPZZyf_)I2jN0DjL7Ijno#5#IZqiLOFUcMG zzWd#mWo7YFW*Ka^P7#?TMN7W7n)M!=-YBjsPiSms$a*G+2d=QK^j?J@kY%ff$0EpY zmgds{)T(k0OL88MfZ-U+hiEX3j~q_D{v+7puGh8aPUGL`lP2m;5cnoG^XRsjN4>Z} zG2ZX_Xf(h8Fm-r~jx1M-zbVo5HO^0SI8=7q^sPyzE>y6*3%JGWb8%m;0`a=@T$B7K zP374h*_{EX!JX~eUqozs<=H4g&uz$pHO?G{6+#$name), ZSTR_LEN(ce->name), + ZSTR_VAL(name), ZSTR_LEN(name), ce->type & ZEND_INTERNAL_CLASS); + if (php_var_serialize_try_add_sleep_prop(ht, props, priv_name, name) == SUCCESS) { + zend_string_release(name); + zend_string_release(priv_name); + continue; + } + zend_string_release(priv_name); + + prot_name = zend_mangle_property_name( + "*", 1, ZSTR_VAL(name), ZSTR_LEN(name), ce->type & ZEND_INTERNAL_CLASS); + if (php_var_serialize_try_add_sleep_prop(ht, props, prot_name, name) == SUCCESS) { + zend_string_release(name); + zend_string_release(prot_name); + continue; + } + zend_string_release(prot_name); + + php_error_docref(NULL, E_NOTICE, + "\"%s\" returned as member variable from __sleep() but does not exist", ZSTR_VAL(name)); + zend_hash_add(ht, name, &EG(uninitialized_zval)); + zend_string_release(name); } ZEND_HASH_FOREACH_END(); + + return ht; } /* }}} */ -static void php_var_serialize_class(smart_str *buf, zval *struc, zval *retval_ptr, php_serialize_data_t var_hash) /* {{{ */ +static HashTable *php_var_serialize_get_props(zval *obj, zend_bool *is_temp) { - zend_class_entry *ce = Z_OBJCE_P(struc); - HashTable names, *propers; - zval nval; - zend_string *name; + zend_class_entry *ce = Z_OBJCE_P(obj); + HashTable *ht; + if (ce != PHP_IC_ENTRY && zend_hash_str_exists(&ce->function_table, "__sleep", sizeof("__sleep")-1)) { + zval retval; - php_var_serialize_class_name(buf, struc); - php_var_serialize_collect_names(&names, HASH_OF(retval_ptr)); + if (php_var_serialize_call_sleep(&retval, obj) == FAILURE) { + return NULL; + } - smart_str_append_unsigned(buf, zend_hash_num_elements(&names)); - smart_str_appendl(buf, ":{", 2); + *is_temp = 1; + ht = php_var_serialize_get_sleep_props(obj, HASH_OF(&retval)); + zval_ptr_dtor(&retval); - ZVAL_NULL(&nval); - propers = Z_OBJPROP_P(struc); + return ht; + } - ZEND_HASH_FOREACH_STR_KEY(&names, name) { - zend_string *prot_name, *priv_name; + *is_temp = 0; + return Z_OBJPROP_P(obj); +} - zval *val = zend_hash_find(propers, name); - if (val != NULL) { - if (Z_TYPE_P(val) == IS_INDIRECT) { - val = Z_INDIRECT_P(val); - if (Z_TYPE_P(val) == IS_UNDEF) { - goto undef_prop; - } +static void php_var_serialize_nested_data(smart_str *buf, zval *struc, HashTable *ht, uint32_t count, zend_bool incomplete_class, php_serialize_data_t var_hash) /* {{{ */ +{ + smart_str_append_unsigned(buf, count); + smart_str_appendl(buf, ":{", 2); + if (count > 0) { + zend_string *key; + zval *data; + zend_ulong index; + + ZEND_HASH_FOREACH_KEY_VAL_IND(ht, index, key, data) { + if (incomplete_class && strcmp(ZSTR_VAL(key), MAGIC_MEMBER) == 0) { + continue; } - php_var_serialize_string(buf, ZSTR_VAL(name), ZSTR_LEN(name)); - php_var_serialize_intern(buf, val, var_hash); - continue; - } - - priv_name = zend_mangle_property_name( - ZSTR_VAL(ce->name), ZSTR_LEN(ce->name), ZSTR_VAL(name), ZSTR_LEN(name), ce->type & ZEND_INTERNAL_CLASS); - val = zend_hash_find(propers, priv_name); - if (val != NULL) { - if (Z_TYPE_P(val) == IS_INDIRECT) { - val = Z_INDIRECT_P(val); - if (Z_ISUNDEF_P(val)) { - zend_string_free(priv_name); - goto undef_prop; - } + if (!key) { + php_var_serialize_long(buf, index); + } else { + php_var_serialize_string(buf, ZSTR_VAL(key), ZSTR_LEN(key)); } - php_var_serialize_string(buf, ZSTR_VAL(priv_name), ZSTR_LEN(priv_name)); - zend_string_free(priv_name); - php_var_serialize_intern(buf, val, var_hash); - continue; - } - zend_string_free(priv_name); + if (Z_ISREF_P(data) && Z_REFCOUNT_P(data) == 1) { + data = Z_REFVAL_P(data); + } - prot_name = zend_mangle_property_name( - "*", 1, ZSTR_VAL(name), ZSTR_LEN(name), ce->type & ZEND_INTERNAL_CLASS); - val = zend_hash_find(propers, prot_name); - if (val != NULL) { - if (Z_TYPE_P(val) == IS_INDIRECT) { - val = Z_INDIRECT_P(val); - if (Z_TYPE_P(val) == IS_UNDEF) { - zend_string_free(prot_name); - goto undef_prop; + /* we should still add element even if it's not OK, + * since we already wrote the length of the array before */ + if ((Z_TYPE_P(data) == IS_ARRAY && Z_TYPE_P(struc) == IS_ARRAY && Z_ARR_P(data) == Z_ARR_P(struc)) + || (Z_TYPE_P(data) == IS_ARRAY && Z_ARRVAL_P(data)->u.v.nApplyCount > 1) + ) { + smart_str_appendl(buf, "N;", 2); + } else { + if (Z_TYPE_P(data) == IS_ARRAY && ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(data))) { + Z_ARRVAL_P(data)->u.v.nApplyCount++; + } + php_var_serialize_intern(buf, data, var_hash); + if (Z_TYPE_P(data) == IS_ARRAY && ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(data))) { + Z_ARRVAL_P(data)->u.v.nApplyCount--; } } - - php_var_serialize_string(buf, ZSTR_VAL(prot_name), ZSTR_LEN(prot_name)); - zend_string_free(prot_name); - php_var_serialize_intern(buf, val, var_hash); - continue; - } - zend_string_free(prot_name); - -undef_prop: - php_var_serialize_string(buf, ZSTR_VAL(name), ZSTR_LEN(name)); - php_var_serialize_intern(buf, &nval, var_hash); - php_error_docref(NULL, E_NOTICE, - "\"%s\" returned as member variable from __sleep() but does not exist", ZSTR_VAL(name)); - } ZEND_HASH_FOREACH_END(); + } ZEND_HASH_FOREACH_END(); + } smart_str_appendc(buf, '}'); - - zend_hash_destroy(&names); } /* }}} */ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash) /* {{{ */ { zend_long var_already; - HashTable *myht; if (EG(exception)) { return; @@ -861,6 +896,10 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_ case IS_OBJECT: { zend_class_entry *ce = Z_OBJCE_P(struc); + HashTable *ht; + zend_bool is_temp, incomplete_class; + uint32_t count; + zval tmp; if (ce->serialize != NULL) { /* has custom handler */ @@ -887,86 +926,43 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_ return; } - if (ce != PHP_IC_ENTRY && zend_hash_str_exists(&ce->function_table, "__sleep", sizeof("__sleep")-1)) { - zval retval, tmp; - ZVAL_COPY(&tmp, struc); - - if (php_var_serialize_call_sleep(&retval, &tmp) == FAILURE) { - if (!EG(exception)) { - /* we should still add element even if it's not OK, - * since we already wrote the length of the array before */ - smart_str_appendl(buf, "N;", 2); - } - zval_ptr_dtor(&tmp); - return; - } + /* Make sure the object is not freed by __sleep() or similar. */ + ZVAL_COPY(&tmp, struc); - php_var_serialize_class(buf, &tmp, &retval, var_hash); - zval_ptr_dtor(&retval); + ht = php_var_serialize_get_props(&tmp, &is_temp); + if (!ht) { + if (!EG(exception)) { + /* we should still add element even if it's not OK, + * since we already wrote the length of the array before */ + smart_str_appendl(buf, "N;", 2); + } zval_ptr_dtor(&tmp); return; } - /* fall-through */ - } - case IS_ARRAY: { - uint32_t i; - zend_bool incomplete_class = 0; - if (Z_TYPE_P(struc) == IS_ARRAY) { - smart_str_appendl(buf, "a:", 2); - myht = Z_ARRVAL_P(struc); - i = zend_array_count(myht); - } else { - incomplete_class = php_var_serialize_class_name(buf, struc); - myht = Z_OBJPROP_P(struc); + incomplete_class = php_var_serialize_class_name(buf, &tmp); /* count after serializing name, since php_var_serialize_class_name * changes the count if the variable is incomplete class */ - i = zend_array_count(myht); - if (i > 0 && incomplete_class) { - --i; + count = zend_array_count(ht); + if (count > 0 && incomplete_class) { + --count; } - } - smart_str_append_unsigned(buf, i); - smart_str_appendl(buf, ":{", 2); - if (i > 0) { - zend_string *key; - zval *data; - zend_ulong index; - ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, data) { - - if (incomplete_class && strcmp(ZSTR_VAL(key), MAGIC_MEMBER) == 0) { - continue; - } - - if (!key) { - php_var_serialize_long(buf, index); - } else { - php_var_serialize_string(buf, ZSTR_VAL(key), ZSTR_LEN(key)); - } + php_var_serialize_nested_data( + buf, &tmp, ht, count, incomplete_class, var_hash); - if (Z_ISREF_P(data) && Z_REFCOUNT_P(data) == 1) { - data = Z_REFVAL_P(data); - } - - /* we should still add element even if it's not OK, - * since we already wrote the length of the array before */ - if ((Z_TYPE_P(data) == IS_ARRAY && Z_TYPE_P(struc) == IS_ARRAY && Z_ARR_P(data) == Z_ARR_P(struc)) - || (Z_TYPE_P(data) == IS_ARRAY && Z_ARRVAL_P(data)->u.v.nApplyCount > 1) - ) { - smart_str_appendl(buf, "N;", 2); - } else { - if (Z_TYPE_P(data) == IS_ARRAY && ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(data))) { - Z_ARRVAL_P(data)->u.v.nApplyCount++; - } - php_var_serialize_intern(buf, data, var_hash); - if (Z_TYPE_P(data) == IS_ARRAY && ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(data))) { - Z_ARRVAL_P(data)->u.v.nApplyCount--; - } - } - } ZEND_HASH_FOREACH_END(); + if (is_temp) { + zend_hash_destroy(ht); + FREE_HASHTABLE(ht); + } + zval_ptr_dtor(&tmp); + return; } - smart_str_appendc(buf, '}'); + case IS_ARRAY: { + HashTable *ht = Z_ARRVAL_P(struc); + uint32_t count = zend_array_count(ht); + smart_str_appendl(buf, "a:", 2); + php_var_serialize_nested_data(buf, struc, ht, count, 0, var_hash); return; } case IS_REFERENCE: From 5ffde2bf1d2e409574820a2d16090bc2996b827a Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 13 Mar 2017 17:53:37 +0100 Subject: [PATCH 2/3] Introduce get_serialize_properties() handler The signature is intentionally the same as for get_debug_info(), as these can commonly share an implementation. --- Zend/zend_object_handlers.c | 125 +++++++++++++++++++++++++++++++++ Zend/zend_object_handlers.h | 4 ++ ext/standard/var.c | 135 +++--------------------------------- 3 files changed, 137 insertions(+), 127 deletions(-) diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 66de460a309e6..03acbed9f6336 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1701,6 +1701,130 @@ int zend_std_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function **f } /* }}} */ +static int serialize_call_sleep(zval *retval, zval *struc) /* {{{ */ +{ + zval fname; + int res; + + ZVAL_STRINGL(&fname, "__sleep", sizeof("__sleep") - 1); + res = call_user_function_ex(CG(function_table), struc, &fname, retval, 0, 0, 1, NULL); + zval_dtor(&fname); + + if (res == FAILURE || Z_ISUNDEF_P(retval)) { + zval_ptr_dtor(retval); + return FAILURE; + } + + if (!HASH_OF(retval)) { + zval_ptr_dtor(retval); + zend_error(E_NOTICE, "serialize(): __sleep should return an array only containing the names of instance-variables to serialize"); + return FAILURE; + } + + return SUCCESS; +} +/* }}} */ + +static int serialize_try_add_sleep_prop(HashTable *ht, HashTable *props, zend_string *name, zend_string *error_name) /* {{{ */ +{ + zval *val = zend_hash_find(props, name); + if (val == NULL) { + return FAILURE; + } + + if (Z_TYPE_P(val) == IS_INDIRECT) { + val = Z_INDIRECT_P(val); + if (Z_TYPE_P(val) == IS_UNDEF) { + return FAILURE; + } + } + + if (!zend_hash_add(ht, name, val)) { + zend_error(E_NOTICE, "serialize(): \"%s\" is returned from __sleep multiple times", + ZSTR_VAL(error_name)); + return SUCCESS; + } + + Z_TRY_ADDREF_P(val); + return SUCCESS; +} +/* }}} */ + +static HashTable *serialize_get_sleep_props(zval *struc, HashTable *sleep_retval) /* {{{ */ +{ + zend_class_entry *ce = Z_OBJCE_P(struc); + HashTable *ht, *props = Z_OBJPROP_P(struc); + zval *name_val; + + ALLOC_HASHTABLE(ht); + zend_hash_init(ht, zend_hash_num_elements(sleep_retval), NULL, ZVAL_PTR_DTOR, 0); + + ZEND_HASH_FOREACH_VAL(sleep_retval, name_val) { + zend_string *name, *prot_name, *priv_name; + + if (Z_TYPE_P(name_val) != IS_STRING) { + zend_error(E_NOTICE, "serialize(): __sleep should return an array only containing the names of instance-variables to serialize."); + } + + name = zval_get_string(name_val); + + if (serialize_try_add_sleep_prop(ht, props, name, name) == SUCCESS) { + zend_string_release(name); + continue; + } + + priv_name = zend_mangle_property_name( + ZSTR_VAL(ce->name), ZSTR_LEN(ce->name), + ZSTR_VAL(name), ZSTR_LEN(name), ce->type & ZEND_INTERNAL_CLASS); + if (serialize_try_add_sleep_prop(ht, props, priv_name, name) == SUCCESS) { + zend_string_release(name); + zend_string_release(priv_name); + continue; + } + zend_string_release(priv_name); + + prot_name = zend_mangle_property_name( + "*", 1, ZSTR_VAL(name), ZSTR_LEN(name), ce->type & ZEND_INTERNAL_CLASS); + if (serialize_try_add_sleep_prop(ht, props, prot_name, name) == SUCCESS) { + zend_string_release(name); + zend_string_release(prot_name); + continue; + } + zend_string_release(prot_name); + + zend_error(E_NOTICE, "serialize(): \"%s\" returned as member variable from __sleep() but does not exist", ZSTR_VAL(name)); + zend_hash_add(ht, name, &EG(uninitialized_zval)); + + zend_string_release(name); + } ZEND_HASH_FOREACH_END(); + + return ht; +} +/* }}} */ + +ZEND_API HashTable *zend_std_get_serialize_properties(zval *obj, int *is_temp) /* {{{ */ +{ + zend_class_entry *ce = Z_OBJCE_P(obj); + HashTable *ht; + if (zend_hash_str_exists(&ce->function_table, "__sleep", sizeof("__sleep")-1)) { + zval retval; + + if (serialize_call_sleep(&retval, obj) == FAILURE) { + return NULL; + } + + *is_temp = 1; + ht = serialize_get_sleep_props(obj, HASH_OF(&retval)); + zval_ptr_dtor(&retval); + + return ht; + } + + *is_temp = 0; + return Z_OBJPROP_P(obj); +} +/* }}} */ + ZEND_API zend_object_handlers std_object_handlers = { 0, /* offset */ @@ -1732,6 +1856,7 @@ ZEND_API zend_object_handlers std_object_handlers = { zend_std_get_gc, /* get_gc */ NULL, /* do_operation */ NULL, /* compare */ + zend_std_get_serialize_properties, /* get_serialize_properties */ }; /* diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h index ecb4f6e1a00b1..7d70d872f3bb9 100644 --- a/Zend/zend_object_handlers.h +++ b/Zend/zend_object_handlers.h @@ -121,6 +121,8 @@ typedef HashTable *(*zend_object_get_gc_t)(zval *object, zval **table, int *n); typedef int (*zend_object_do_operation_t)(zend_uchar opcode, zval *result, zval *op1, zval *op2); +typedef HashTable *(*zend_object_get_serialize_properties_t)(zval *object, int *is_temp); + struct _zend_object_handlers { /* offset of real object header (usually zero) */ int offset; @@ -153,6 +155,7 @@ struct _zend_object_handlers { zend_object_get_gc_t get_gc; zend_object_do_operation_t do_operation; zend_object_compare_zvals_t compare; + zend_object_get_serialize_properties_t get_serialize_properties; }; extern ZEND_API zend_object_handlers std_object_handlers; @@ -170,6 +173,7 @@ ZEND_API HashTable *zend_std_get_properties(zval *object); ZEND_API HashTable *zend_std_get_debug_info(zval *object, int *is_temp); ZEND_API int zend_std_cast_object_tostring(zval *readobj, zval *writeobj, int type); ZEND_API void zend_std_write_property(zval *object, zval *member, zval *value, void **cache_slot); +ZEND_API HashTable *zend_std_get_serialize_properties(zval *object, int *is_temp); ZEND_API void rebuild_object_properties(zend_object *zobj); ZEND_API int zend_check_private(union _zend_function *fbc, zend_class_entry *ce, zend_string *function_name); diff --git a/ext/standard/var.c b/ext/standard/var.c index 6f1abf9ff32f5..36fc7b53728e4 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -671,131 +671,6 @@ static inline zend_bool php_var_serialize_class_name(smart_str *buf, zval *struc } /* }}} */ -static int php_var_serialize_call_sleep(zval *retval, zval *struc) /* {{{ */ -{ - zval fname; - int res; - - ZVAL_STRINGL(&fname, "__sleep", sizeof("__sleep") - 1); - BG(serialize_lock)++; - res = call_user_function_ex(CG(function_table), struc, &fname, retval, 0, 0, 1, NULL); - BG(serialize_lock)--; - zval_dtor(&fname); - - if (res == FAILURE || Z_ISUNDEF_P(retval)) { - zval_ptr_dtor(retval); - return FAILURE; - } - - if (!HASH_OF(retval)) { - zval_ptr_dtor(retval); - php_error_docref(NULL, E_NOTICE, "__sleep should return an array only containing the names of instance-variables to serialize"); - return FAILURE; - } - - return SUCCESS; -} -/* }}} */ - -static int php_var_serialize_try_add_sleep_prop(HashTable *ht, HashTable *props, zend_string *name, zend_string *error_name) { - zval *val = zend_hash_find(props, name); - if (val == NULL) { - return FAILURE; - } - - if (Z_TYPE_P(val) == IS_INDIRECT) { - val = Z_INDIRECT_P(val); - if (Z_TYPE_P(val) == IS_UNDEF) { - return FAILURE; - } - } - - if (!zend_hash_add(ht, name, val)) { - php_error_docref(NULL, E_NOTICE, - "\"%s\" is returned from __sleep multiple times", ZSTR_VAL(error_name)); - return SUCCESS; - } - - Z_TRY_ADDREF_P(val); - return SUCCESS; -} - -static HashTable *php_var_serialize_get_sleep_props(zval *struc, HashTable *sleep_retval) /* {{{ */ -{ - zend_class_entry *ce = Z_OBJCE_P(struc); - HashTable *ht, *props = Z_OBJPROP_P(struc); - zval *name_val; - - ALLOC_HASHTABLE(ht); - zend_hash_init(ht, zend_hash_num_elements(sleep_retval), NULL, ZVAL_PTR_DTOR, 0); - - ZEND_HASH_FOREACH_VAL(sleep_retval, name_val) { - zend_string *name, *prot_name, *priv_name; - - if (Z_TYPE_P(name_val) != IS_STRING) { - php_error_docref(NULL, E_NOTICE, - "__sleep should return an array only containing the names of instance-variables to serialize."); - } - - name = zval_get_string(name_val); - - if (php_var_serialize_try_add_sleep_prop(ht, props, name, name) == SUCCESS) { - zend_string_release(name); - continue; - } - - priv_name = zend_mangle_property_name( - ZSTR_VAL(ce->name), ZSTR_LEN(ce->name), - ZSTR_VAL(name), ZSTR_LEN(name), ce->type & ZEND_INTERNAL_CLASS); - if (php_var_serialize_try_add_sleep_prop(ht, props, priv_name, name) == SUCCESS) { - zend_string_release(name); - zend_string_release(priv_name); - continue; - } - zend_string_release(priv_name); - - prot_name = zend_mangle_property_name( - "*", 1, ZSTR_VAL(name), ZSTR_LEN(name), ce->type & ZEND_INTERNAL_CLASS); - if (php_var_serialize_try_add_sleep_prop(ht, props, prot_name, name) == SUCCESS) { - zend_string_release(name); - zend_string_release(prot_name); - continue; - } - zend_string_release(prot_name); - - php_error_docref(NULL, E_NOTICE, - "\"%s\" returned as member variable from __sleep() but does not exist", ZSTR_VAL(name)); - zend_hash_add(ht, name, &EG(uninitialized_zval)); - - zend_string_release(name); - } ZEND_HASH_FOREACH_END(); - - return ht; -} -/* }}} */ - -static HashTable *php_var_serialize_get_props(zval *obj, zend_bool *is_temp) -{ - zend_class_entry *ce = Z_OBJCE_P(obj); - HashTable *ht; - if (ce != PHP_IC_ENTRY && zend_hash_str_exists(&ce->function_table, "__sleep", sizeof("__sleep")-1)) { - zval retval; - - if (php_var_serialize_call_sleep(&retval, obj) == FAILURE) { - return NULL; - } - - *is_temp = 1; - ht = php_var_serialize_get_sleep_props(obj, HASH_OF(&retval)); - zval_ptr_dtor(&retval); - - return ht; - } - - *is_temp = 0; - return Z_OBJPROP_P(obj); -} - static void php_var_serialize_nested_data(smart_str *buf, zval *struc, HashTable *ht, uint32_t count, zend_bool incomplete_class, php_serialize_data_t var_hash) /* {{{ */ { smart_str_append_unsigned(buf, count); @@ -897,7 +772,8 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_ case IS_OBJECT: { zend_class_entry *ce = Z_OBJCE_P(struc); HashTable *ht; - zend_bool is_temp, incomplete_class; + int is_temp; + zend_bool incomplete_class; uint32_t count; zval tmp; @@ -929,7 +805,12 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_ /* Make sure the object is not freed by __sleep() or similar. */ ZVAL_COPY(&tmp, struc); - ht = php_var_serialize_get_props(&tmp, &is_temp); + BG(serialize_lock)++; + ht = Z_OBJ_HT_P(&tmp)->get_serialize_properties + ? Z_OBJ_HT_P(&tmp)->get_serialize_properties(&tmp, &is_temp) + : zend_std_get_serialize_properties(&tmp, &is_temp); + BG(serialize_lock)--; + if (!ht) { if (!EG(exception)) { /* we should still add element even if it's not OK, From 430e0f2c72962ed0064c65993382317803c2a15f Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 13 Mar 2017 19:08:42 +0100 Subject: [PATCH 3/3] Use get_serialize_properties in wddx serialization --- ext/wddx/wddx.c | 72 +++++++++++++++---------------------------------- 1 file changed, 22 insertions(+), 50 deletions(-) diff --git a/ext/wddx/wddx.c b/ext/wddx/wddx.c index a905f59fd5646..6890d747f2584 100644 --- a/ext/wddx/wddx.c +++ b/ext/wddx/wddx.c @@ -447,13 +447,13 @@ static void php_wddx_serialize_unset(wddx_packet *packet) static void php_wddx_serialize_object(wddx_packet *packet, zval *obj) { /* OBJECTS_FIXME */ - zval *ent, fname, *varname; - zval retval; + zval *ent; zend_string *key; zend_ulong idx; char tmp_buf[WDDX_BUF_LEN]; - HashTable *objhash, *sleephash; + HashTable *objhash; zend_class_entry *ce; + int is_temp; PHP_CLASS_ATTRIBUTES; PHP_SET_CLASS_ATTRIBUTES(obj); @@ -464,54 +464,23 @@ static void php_wddx_serialize_object(wddx_packet *packet, zval *obj) return; } - ZVAL_STRING(&fname, "__sleep"); - /* - * We try to call __sleep() method on object. It's supposed to return an - * array of property names to be serialized. - */ - if (call_user_function_ex(CG(function_table), obj, &fname, &retval, 0, 0, 1, NULL) == SUCCESS) { - if (!Z_ISUNDEF(retval) && (sleephash = HASH_OF(&retval))) { - PHP_CLASS_ATTRIBUTES; - - PHP_SET_CLASS_ATTRIBUTES(obj); - - php_wddx_add_chunk_static(packet, WDDX_STRUCT_S); - snprintf(tmp_buf, WDDX_BUF_LEN, WDDX_VAR_S, PHP_CLASS_NAME_VAR); - php_wddx_add_chunk(packet, tmp_buf); - php_wddx_add_chunk_static(packet, WDDX_STRING_S); - php_wddx_add_chunk_ex(packet, ZSTR_VAL(class_name), ZSTR_LEN(class_name)); - php_wddx_add_chunk_static(packet, WDDX_STRING_E); - php_wddx_add_chunk_static(packet, WDDX_VAR_E); - - objhash = Z_OBJPROP_P(obj); - - ZEND_HASH_FOREACH_VAL(sleephash, varname) { - if (Z_TYPE_P(varname) != IS_STRING) { - php_error_docref(NULL, E_NOTICE, "__sleep should return an array only containing the names of instance-variables to serialize."); - continue; - } - - if ((ent = zend_hash_find(objhash, Z_STR_P(varname))) != NULL) { - php_wddx_serialize_var(packet, ent, Z_STR_P(varname)); - } - } ZEND_HASH_FOREACH_END(); - - php_wddx_add_chunk_static(packet, WDDX_STRUCT_E); - } - } else { - php_wddx_add_chunk_static(packet, WDDX_STRUCT_S); - snprintf(tmp_buf, WDDX_BUF_LEN, WDDX_VAR_S, PHP_CLASS_NAME_VAR); - php_wddx_add_chunk(packet, tmp_buf); - php_wddx_add_chunk_static(packet, WDDX_STRING_S); - php_wddx_add_chunk_ex(packet, ZSTR_VAL(class_name), ZSTR_LEN(class_name)); - php_wddx_add_chunk_static(packet, WDDX_STRING_E); - php_wddx_add_chunk_static(packet, WDDX_VAR_E); + php_wddx_add_chunk_static(packet, WDDX_STRUCT_S); + snprintf(tmp_buf, WDDX_BUF_LEN, WDDX_VAR_S, PHP_CLASS_NAME_VAR); + php_wddx_add_chunk(packet, tmp_buf); + php_wddx_add_chunk_static(packet, WDDX_STRING_S); + php_wddx_add_chunk_ex(packet, ZSTR_VAL(class_name), ZSTR_LEN(class_name)); + php_wddx_add_chunk_static(packet, WDDX_STRING_E); + php_wddx_add_chunk_static(packet, WDDX_VAR_E); - objhash = Z_OBJPROP_P(obj); + objhash = Z_OBJ_HT_P(obj)->get_serialize_properties + ? Z_OBJ_HT_P(obj)->get_serialize_properties(obj, &is_temp) + : zend_std_get_serialize_properties(obj, &is_temp); + if (objhash) { ZEND_HASH_FOREACH_KEY_VAL(objhash, idx, key, ent) { if (ent == obj) { continue; } + if (key) { const char *class_name, *prop_name; size_t prop_name_len; @@ -527,13 +496,16 @@ static void php_wddx_serialize_object(wddx_packet *packet, zval *obj) zend_string_release(key); } } ZEND_HASH_FOREACH_END(); - php_wddx_add_chunk_static(packet, WDDX_STRUCT_E); + + if (is_temp) { + zend_hash_destroy(objhash); + FREE_HASHTABLE(objhash); + } } - PHP_CLEANUP_CLASS_ATTRIBUTES(); + php_wddx_add_chunk_static(packet, WDDX_STRUCT_E); - zval_ptr_dtor(&fname); - zval_ptr_dtor(&retval); + PHP_CLEANUP_CLASS_ATTRIBUTES(); } /* }}} */