summaryrefslogtreecommitdiff
path: root/ext/standard/var.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/standard/var.c')
-rw-r--r--ext/standard/var.c85
1 files changed, 62 insertions, 23 deletions
diff --git a/ext/standard/var.c b/ext/standard/var.c
index fcee7a9cc6..16b9fffba1 100644
--- a/ext/standard/var.c
+++ b/ext/standard/var.c
@@ -666,6 +666,36 @@ static inline zend_bool php_var_serialize_class_name(smart_str *buf, zval *struc
}
/* }}} */
+static HashTable *php_var_serialize_collect_names(HashTable *src, uint32_t count, zend_bool incomplete) /* {{{ */ {
+ zval *val;
+ HashTable *ht;
+ zend_string *key, *name;
+
+ ALLOC_HASHTABLE(ht);
+ zend_hash_init(ht, count, NULL, NULL, 0);
+ ZEND_HASH_FOREACH_STR_KEY_VAL(src, key, val) {
+ if (incomplete && strcmp(ZSTR_VAL(key), MAGIC_MEMBER) == 0) {
+ continue;
+ }
+ if (Z_TYPE_P(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(val);
+ if (zend_hash_exists(ht, name)) {
+ php_error_docref(NULL, E_NOTICE,
+ "\"%s\" is returned from __sleep multiple times", ZSTR_VAL(name));
+ zend_string_release(name);
+ continue;
+ }
+ zend_hash_add_empty_element(ht, name);
+ 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) /* {{{ */
{
uint32_t count;
@@ -686,37 +716,29 @@ static void php_var_serialize_class(smart_str *buf, zval *struc, zval *retval_pt
}
} else {
count = 0;
+ ht = NULL;
}
- smart_str_append_unsigned(buf, count);
- smart_str_appendl(buf, ":{", 2);
-
if (count > 0) {
- zend_string *key;
- zval *d, *val;
+ zval *d;
zval nval, *nvalp;
zend_string *name;
- HashTable *propers;
+ HashTable *names, *propers;
+
+ names = php_var_serialize_collect_names(ht, count, incomplete_class);
+
+ smart_str_append_unsigned(buf, zend_hash_num_elements(names));
+ smart_str_appendl(buf, ":{", 2);
ZVAL_NULL(&nval);
nvalp = &nval;
+ propers = Z_OBJPROP_P(struc);
- ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, val) {
- if (incomplete_class && strcmp(ZSTR_VAL(key), MAGIC_MEMBER) == 0) {
- continue;
- }
-
- if (Z_TYPE_P(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(val);
- propers = Z_OBJPROP_P(struc);
+ ZEND_HASH_FOREACH_STR_KEY(names, name) {
if ((d = zend_hash_find(propers, name)) != NULL) {
if (Z_TYPE_P(d) == IS_INDIRECT) {
d = Z_INDIRECT_P(d);
if (Z_TYPE_P(d) == IS_UNDEF) {
- zend_string_release(name);
continue;
}
}
@@ -769,10 +791,14 @@ static void php_var_serialize_class(smart_str *buf, zval *struc, zval *retval_pt
php_var_serialize_intern(buf, nvalp, var_hash);
}
}
- zend_string_release(name);
} ZEND_HASH_FOREACH_END();
+ smart_str_appendc(buf, '}');
+
+ zend_hash_destroy(names);
+ FREE_HASHTABLE(names);
+ } else {
+ smart_str_appendl(buf, "0:{}", 4);
}
- smart_str_appendc(buf, '}');
}
/* }}} */
@@ -864,7 +890,7 @@ again:
return;
}
- if (ce && ce != PHP_IC_ENTRY && zend_hash_str_exists(&ce->function_table, "__sleep", sizeof("__sleep")-1)) {
+ if (ce != PHP_IC_ENTRY && zend_hash_str_exists(&ce->function_table, "__sleep", sizeof("__sleep")-1)) {
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);
@@ -931,6 +957,10 @@ again:
php_var_serialize_string(buf, ZSTR_VAL(key), ZSTR_LEN(key));
}
+ 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))
@@ -1039,7 +1069,7 @@ PHP_FUNCTION(unserialize)
if (!php_var_unserialize_ex(return_value, &p, p + buf_len, &var_hash, class_hash)) {
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
- if(class_hash) {
+ if (class_hash) {
zend_hash_destroy(class_hash);
FREE_HASHTABLE(class_hash);
}
@@ -1050,8 +1080,17 @@ PHP_FUNCTION(unserialize)
}
RETURN_FALSE;
}
+ /* We should keep an reference to return_value to prevent it from being dtor
+ in case nesting calls to unserialize */
+ var_push_dtor(&var_hash, return_value);
+
+ /* Ensure return value is a value */
+ if (Z_ISREF_P(return_value)) {
+ zend_unwrap_reference(return_value);
+ }
+
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
- if(class_hash) {
+ if (class_hash) {
zend_hash_destroy(class_hash);
FREE_HASHTABLE(class_hash);
}