summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS2
-rwxr-xr-xext/spl/spl_array.c220
-rwxr-xr-xext/spl/spl_observer.c12
-rw-r--r--ext/spl/tests/bug49263.phpt2
-rw-r--r--ext/standard/basic_functions.h8
-rw-r--r--ext/standard/php_var.h64
-rw-r--r--ext/standard/tests/serialize/bug36424.phpt72
-rw-r--r--ext/standard/var.c10
-rw-r--r--ext/standard/var_unserializer.c105
-rw-r--r--ext/standard/var_unserializer.re41
10 files changed, 291 insertions, 245 deletions
diff --git a/NEWS b/NEWS
index 2cbc7fde9a..5d2da7e96e 100644
--- a/NEWS
+++ b/NEWS
@@ -42,6 +42,8 @@
- Added scalar typehinting. (Ilia, Derick)
- Added support for JSON_NUMERIC_CHECK option in json_encode() that converts
numeric strings to integers. (Ilia)
+- Added support for object references in recursive serialize() calls. FR #36424.
+ (Mike)
- default_charset if not specified is now UTF-8 instead of ISO-8859-1. (Rasmus)
diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c
index f98e8fa16e..81ec0e092a 100755
--- a/ext/spl/spl_array.c
+++ b/ext/spl/spl_array.c
@@ -59,23 +59,19 @@ PHPAPI zend_class_entry *spl_ce_RecursiveArrayIterator;
#define SPL_ARRAY_CLONE_MASK 0x0300FFFF
typedef struct _spl_array_object {
- zend_object std;
- zval *array;
- zval *retval;
- HashPosition pos;
- ulong pos_h;
- int ar_flags;
- int is_self;
- zend_function *fptr_offset_get;
- zend_function *fptr_offset_set;
- zend_function *fptr_offset_has;
- zend_function *fptr_offset_del;
- zend_function *fptr_count;
- zend_function *fptr_serialize;
- zend_function *fptr_unserialize;
- zend_class_entry *ce_get_iterator;
- php_serialize_data_t *serialize_data;
- php_unserialize_data_t *unserialize_data;
+ zend_object std;
+ zval *array;
+ zval *retval;
+ HashPosition pos;
+ ulong pos_h;
+ int ar_flags;
+ int is_self;
+ zend_function *fptr_offset_get;
+ zend_function *fptr_offset_set;
+ zend_function *fptr_offset_has;
+ zend_function *fptr_offset_del;
+ zend_function *fptr_count;
+ zend_class_entry* ce_get_iterator;
HashTable *debug_info;
} spl_array_object;
@@ -161,8 +157,6 @@ static void spl_array_object_free_storage(void *object TSRMLS_DC)
/* }}} */
zend_object_iterator *spl_array_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC);
-int spl_array_serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC);
-int spl_array_unserialize(zval **object, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC);
/* {{{ spl_array_object_new_ex */
static zend_object_value spl_array_object_new_ex(zend_class_entry *class_type, spl_array_object **obj, zval *orig, int clone_orig TSRMLS_DC)
@@ -182,8 +176,6 @@ static zend_object_value spl_array_object_new_ex(zend_class_entry *class_type, s
object_properties_init(&intern->std, class_type);
intern->ar_flags = 0;
- intern->serialize_data = NULL;
- intern->unserialize_data = NULL;
intern->debug_info = NULL;
intern->ce_get_iterator = spl_ce_ArrayIterator;
if (orig) {
@@ -250,14 +242,6 @@ static zend_object_value spl_array_object_new_ex(zend_class_entry *class_type, s
if (intern->fptr_count->common.scope == parent) {
intern->fptr_count = NULL;
}
- zend_hash_find(&class_type->function_table, "serialize", sizeof("serialize"), (void **) &intern->fptr_serialize);
- if (intern->fptr_serialize->common.scope == parent) {
- intern->fptr_serialize = NULL;
- }
- zend_hash_find(&class_type->function_table, "unserialize", sizeof("unserialize"), (void **) &intern->fptr_unserialize);
- if (intern->fptr_unserialize->common.scope == parent) {
- intern->fptr_unserialize = NULL;
- }
}
/* Cache iterator functions if ArrayIterator or derived. Check current's */
/* cache since only current is always required */
@@ -1567,27 +1551,35 @@ SPL_METHOD(Array, getChildren)
}
/* }}} */
-smart_str spl_array_serialize_helper(spl_array_object *intern, php_serialize_data_t *var_hash_p TSRMLS_DC) { /* {{{ */
+/* {{{ proto string ArrayObject::serialize()
+ Serialize the object */
+SPL_METHOD(Array, serialize)
+{
+ zval *object = getThis();
+ spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
zval members, *pmembers;
+ php_serialize_data_t var_hash;
smart_str buf = {0};
zval *flags;
if (!aht) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
- return buf;
+ return;
}
+ PHP_VAR_SERIALIZE_INIT(var_hash);
+
MAKE_STD_ZVAL(flags);
ZVAL_LONG(flags, (intern->ar_flags & SPL_ARRAY_CLONE_MASK));
/* storage */
smart_str_appendl(&buf, "x:", 2);
- php_var_serialize(&buf, &flags, var_hash_p TSRMLS_CC);
+ php_var_serialize(&buf, &flags, &var_hash TSRMLS_CC);
zval_ptr_dtor(&flags);
if (!(intern->ar_flags & SPL_ARRAY_IS_SELF)) {
- php_var_serialize(&buf, &intern->array, var_hash_p TSRMLS_CC);
+ php_var_serialize(&buf, &intern->array, &var_hash TSRMLS_CC);
smart_str_appendc(&buf, ';');
}
@@ -1600,34 +1592,10 @@ smart_str spl_array_serialize_helper(spl_array_object *intern, php_serialize_dat
Z_ARRVAL(members) = intern->std.properties;
Z_TYPE(members) = IS_ARRAY;
pmembers = &members;
- php_var_serialize(&buf, &pmembers, var_hash_p TSRMLS_CC); /* finishes the string */
+ php_var_serialize(&buf, &pmembers, &var_hash TSRMLS_CC); /* finishes the string */
/* done */
- return buf;
-}
-/* }}} */
-
-/* {{{ proto string ArrayObject::serialize()
- Serialize the object */
-SPL_METHOD(Array, serialize)
-{
- zval *object = getThis();
- spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
- int was_in_serialize = intern->serialize_data != NULL;
- smart_str buf;
-
- if (!was_in_serialize) {
- intern->serialize_data = emalloc(sizeof(php_serialize_data_t));
- PHP_VAR_SERIALIZE_INIT(*intern->serialize_data);
- }
-
- buf = spl_array_serialize_helper(intern, intern->serialize_data TSRMLS_CC);
-
- if (!was_in_serialize) {
- PHP_VAR_SERIALIZE_DESTROY(*intern->serialize_data);
- efree(intern->serialize_data);
- intern->serialize_data = NULL;
- }
+ PHP_VAR_SERIALIZE_DESTROY(var_hash);
if (buf.c) {
RETURN_STRINGL(buf.c, buf.len, 0);
@@ -1636,47 +1604,32 @@ SPL_METHOD(Array, serialize)
RETURN_NULL();
} /* }}} */
-int spl_array_serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC) /* {{{ */
+/* {{{ proto void ArrayObject::unserialize(string serialized)
+ * unserialize the object
+ */
+SPL_METHOD(Array, unserialize)
{
- spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
-
- if (intern->fptr_serialize) {
- int retval;
- php_serialize_data_t *before;
-
- before = intern->serialize_data;
- intern->serialize_data = (php_serialize_data_t *)data;
-
- retval = zend_user_serialize(object, buffer, buf_len, data TSRMLS_CC);
-
- intern->serialize_data = before;
-
- return retval;
- } else {
- smart_str buf;
-
- buf = spl_array_serialize_helper(intern, (php_serialize_data_t *)data TSRMLS_CC);
-
- if (buf.c) {
- *buffer = (unsigned char*)estrndup(buf.c, buf.len);
- *buf_len = buf.len;
- efree(buf.c);
- return SUCCESS;
- } else {
- return FAILURE;
- }
- }
-}
-/* }}} */
+ spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
-void spl_array_unserialize_helper(spl_array_object *intern, const unsigned char *buf, int buf_len, php_unserialize_data_t *var_hash_p TSRMLS_DC) /* {{{ */
-{
+ char *buf;
+ int buf_len;
const unsigned char *p, *s;
+ php_unserialize_data_t var_hash;
zval *pmembers, *pflags = NULL;
long flags;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) {
+ return;
+ }
+
+ if (buf_len == 0) {
+ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Empty serialized string cannot be empty");
+ return;
+ }
/* storage */
- s = p = buf;
+ s = p = (const unsigned char*)buf;
+ PHP_VAR_UNSERIALIZE_INIT(var_hash);
if (*p!= 'x' || *++p != ':') {
goto outexcept;
@@ -1684,7 +1637,7 @@ void spl_array_unserialize_helper(spl_array_object *intern, const unsigned char
++p;
ALLOC_INIT_ZVAL(pflags);
- if (!php_var_unserialize(&pflags, &p, s + buf_len, var_hash_p TSRMLS_CC) || Z_TYPE_P(pflags) != IS_LONG) {
+ if (!php_var_unserialize(&pflags, &p, s + buf_len, &var_hash TSRMLS_CC) || Z_TYPE_P(pflags) != IS_LONG) {
zval_ptr_dtor(&pflags);
goto outexcept;
}
@@ -1710,7 +1663,7 @@ void spl_array_unserialize_helper(spl_array_object *intern, const unsigned char
intern->ar_flags |= flags & SPL_ARRAY_CLONE_MASK;
zval_ptr_dtor(&intern->array);
ALLOC_INIT_ZVAL(intern->array);
- if (!php_var_unserialize(&intern->array, &p, s + buf_len, var_hash_p TSRMLS_CC)) {
+ if (!php_var_unserialize(&intern->array, &p, s + buf_len, &var_hash TSRMLS_CC)) {
goto outexcept;
}
}
@@ -1726,7 +1679,7 @@ void spl_array_unserialize_helper(spl_array_object *intern, const unsigned char
++p;
ALLOC_INIT_ZVAL(pmembers);
- if (!php_var_unserialize(&pmembers, &p, s + buf_len, var_hash_p TSRMLS_CC)) {
+ if (!php_var_unserialize(&pmembers, &p, s + buf_len, &var_hash TSRMLS_CC)) {
zval_ptr_dtor(&pmembers);
goto outexcept;
}
@@ -1739,80 +1692,17 @@ void spl_array_unserialize_helper(spl_array_object *intern, const unsigned char
zval_ptr_dtor(&pmembers);
/* done reading $serialized */
+
+ PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
return;
outexcept:
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Error at offset %ld of %d bytes", (long)((char*)p - (char *)buf), buf_len);
+ PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
+ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Error at offset %ld of %d bytes", (long)((char*)p - buf), buf_len);
return;
-}
-/* }}} */
-
-/* {{{ proto void ArrayObject::unserialize(string serialized)
- Unserialize the object */
-SPL_METHOD(Array, unserialize)
-{
- char *buf;
- int buf_len;
- spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
- int was_in_unserialize = intern->unserialize_data != NULL;
-
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) {
- return;
- }
-
- if (buf_len == 0) {
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Empty serialized string cannot be empty");
- return;
- }
-
- if (!was_in_unserialize) {
- intern->unserialize_data = emalloc(sizeof(php_unserialize_data_t));
- PHP_VAR_UNSERIALIZE_INIT(*intern->unserialize_data);
- }
-
- spl_array_unserialize_helper(intern, (const unsigned char *)buf, buf_len, intern->unserialize_data TSRMLS_CC);
-
- if (!was_in_unserialize) {
- PHP_VAR_UNSERIALIZE_DESTROY(*intern->unserialize_data);
- efree(intern->unserialize_data);
- intern->unserialize_data = NULL;
- }
} /* }}} */
-int spl_array_unserialize(zval **object, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC)
-{
- spl_array_object *intern;
-
- object_init_ex(*object, ce);
- intern = (spl_array_object*)zend_object_store_get_object(*object TSRMLS_CC);
-
- if (intern->fptr_unserialize) {
- zval *zdata;
- php_unserialize_data_t *before;
- MAKE_STD_ZVAL(zdata);
- ZVAL_STRINGL(zdata, (char *)buf, buf_len, 1);
-
- before = intern->unserialize_data;
- intern->unserialize_data = (php_unserialize_data_t *)data;
-
- zend_call_method_with_1_params(object, ce, &ce->unserialize_func, "unserialize", NULL, zdata);
-
- intern->unserialize_data = before;
-
- zval_ptr_dtor(&zdata);
- } else {
- spl_array_unserialize_helper(intern, buf, buf_len, (php_unserialize_data_t *)data TSRMLS_CC);
- }
-
- if (EG(exception)) {
- return FAILURE;
- } else {
- return SUCCESS;
- }
-}
-/* }}} */
-
/* {{{ arginfo and function tbale */
ZEND_BEGIN_ARG_INFO(arginfo_array___construct, 0)
ZEND_ARG_INFO(0, array)
@@ -1928,8 +1818,6 @@ PHP_MINIT_FUNCTION(spl_array)
REGISTER_SPL_IMPLEMENTS(ArrayObject, Aggregate);
REGISTER_SPL_IMPLEMENTS(ArrayObject, ArrayAccess);
REGISTER_SPL_IMPLEMENTS(ArrayObject, Serializable);
- spl_ce_ArrayObject->serialize = spl_array_serialize;
- spl_ce_ArrayObject->unserialize = spl_array_unserialize;
memcpy(&spl_handler_ArrayObject, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
spl_handler_ArrayObject.clone_obj = spl_array_object_clone;
@@ -1952,8 +1840,6 @@ PHP_MINIT_FUNCTION(spl_array)
REGISTER_SPL_IMPLEMENTS(ArrayIterator, ArrayAccess);
REGISTER_SPL_IMPLEMENTS(ArrayIterator, SeekableIterator);
REGISTER_SPL_IMPLEMENTS(ArrayIterator, Serializable);
- spl_ce_ArrayIterator->serialize = spl_array_serialize;
- spl_ce_ArrayIterator->unserialize = spl_array_unserialize;
memcpy(&spl_handler_ArrayIterator, &spl_handler_ArrayObject, sizeof(zend_object_handlers));
spl_ce_ArrayIterator->get_iterator = spl_array_get_iterator;
diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c
index 1e09ca98b9..4b62f9e1e6 100755
--- a/ext/spl/spl_observer.c
+++ b/ext/spl/spl_observer.c
@@ -638,7 +638,7 @@ SPL_METHOD(SplObjectStorage, serialize)
spl_SplObjectStorage *intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
spl_SplObjectStorageElement *element;
- zval members, *pmembers;
+ zval members, *pmembers, *flags;
HashPosition pos;
php_serialize_data_t var_hash;
smart_str buf = {0};
@@ -646,9 +646,11 @@ SPL_METHOD(SplObjectStorage, serialize)
PHP_VAR_SERIALIZE_INIT(var_hash);
/* storage */
- smart_str_appendl(&buf, "x:i:", 4);
- smart_str_append_long(&buf, zend_hash_num_elements(&intern->storage));
- smart_str_appendc(&buf, ';');
+ smart_str_appendl(&buf, "x:", 2);
+ MAKE_STD_ZVAL(flags);
+ ZVAL_LONG(flags, zend_hash_num_elements(&intern->storage));
+ php_var_serialize(&buf, &flags, &var_hash TSRMLS_CC);
+ zval_ptr_dtor(&flags);
zend_hash_internal_pointer_reset_ex(&intern->storage, &pos);
@@ -716,7 +718,7 @@ SPL_METHOD(SplObjectStorage, unserialize)
++p;
ALLOC_INIT_ZVAL(pcount);
- if (!php_var_unserialize(&pcount, &p, s + buf_len, NULL TSRMLS_CC) || Z_TYPE_P(pcount) != IS_LONG) {
+ if (!php_var_unserialize(&pcount, &p, s + buf_len, &var_hash TSRMLS_CC) || Z_TYPE_P(pcount) != IS_LONG) {
zval_ptr_dtor(&pcount);
goto outexcept;
}
diff --git a/ext/spl/tests/bug49263.phpt b/ext/spl/tests/bug49263.phpt
index 14d0950f12..2075577ba8 100644
--- a/ext/spl/tests/bug49263.phpt
+++ b/ext/spl/tests/bug49263.phpt
@@ -19,7 +19,7 @@ var_dump(unserialize($ss));
?>
===DONE===
--EXPECTF--
-C:16:"SplObjectStorage":113:{x:i:2;O:8:"stdClass":0:{},a:2:{s:4:"prev";i:2;s:4:"next";O:8:"stdClass":0:{}};r:4;,a:1:{s:4:"prev";r:1;};m:a:0:{}}
+C:16:"SplObjectStorage":113:{x:i:2;O:8:"stdClass":0:{},a:2:{s:4:"prev";i:2;s:4:"next";O:8:"stdClass":0:{}};r:6;,a:1:{s:4:"prev";r:3;};m:a:0:{}}
object(SplObjectStorage)#2 (1) {
["storage":"SplObjectStorage":private]=>
array(2) {
diff --git a/ext/standard/basic_functions.h b/ext/standard/basic_functions.h
index b7b5264de8..4498e6cf8f 100644
--- a/ext/standard/basic_functions.h
+++ b/ext/standard/basic_functions.h
@@ -201,6 +201,14 @@ typedef struct _php_basic_globals {
/* var.c */
zend_class_entry *incomplete_class;
+ struct {
+ void *var_hash;
+ unsigned level;
+ } serialize;
+ struct {
+ void *var_hash;
+ unsigned level;
+ } unserialize;
/* url_scanner_ex.re */
url_adapt_state_ex_t url_adapt_state_ex;
diff --git a/ext/standard/php_var.h b/ext/standard/php_var.h
index ed03e3f2eb..e74680c548 100644
--- a/ext/standard/php_var.h
+++ b/ext/standard/php_var.h
@@ -21,6 +21,7 @@
#ifndef PHP_VAR_H
#define PHP_VAR_H
+#include "ext/standard/basic_functions.h"
#include "ext/standard/php_smart_str_public.h"
PHP_FUNCTION(var_dump);
@@ -35,29 +36,68 @@ PHPAPI void php_var_dump(zval **struc, int level TSRMLS_DC);
PHPAPI void php_var_export(zval **struc, int level TSRMLS_DC);
PHPAPI void php_debug_zval_dump(zval **struc, int level TSRMLS_DC);
-/* typdef HashTable php_serialize_data_t; */
-#define php_serialize_data_t HashTable
+typedef HashTable* php_serialize_data_t;
struct php_unserialize_data {
void *first;
void *first_dtor;
};
-typedef struct php_unserialize_data php_unserialize_data_t;
+typedef struct php_unserialize_data* php_unserialize_data_t;
PHPAPI void php_var_serialize(smart_str *buf, zval **struc, php_serialize_data_t *var_hash TSRMLS_DC);
PHPAPI int php_var_unserialize(zval **rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash TSRMLS_DC);
-#define PHP_VAR_SERIALIZE_INIT(var_hash) \
- zend_hash_init(&(var_hash), 10, NULL, NULL, 0)
-#define PHP_VAR_SERIALIZE_DESTROY(var_hash) \
- zend_hash_destroy(&(var_hash))
+#define PHP_VAR_SERIALIZE_INIT(var_hash_ptr) \
+do { \
+ if (BG(serialize).level) { \
+ (var_hash_ptr) = BG(serialize).var_hash; \
+ ++BG(serialize).level; \
+ } else { \
+ ALLOC_HASHTABLE(var_hash_ptr); \
+ zend_hash_init((var_hash_ptr), 10, NULL, NULL, 0); \
+ BG(serialize).var_hash = (var_hash_ptr); \
+ BG(serialize).level = 1; \
+ } \
+} while(0)
-#define PHP_VAR_UNSERIALIZE_INIT(var_hash) \
- (var_hash).first = 0; \
- (var_hash).first_dtor = 0
-#define PHP_VAR_UNSERIALIZE_DESTROY(var_hash) \
- var_destroy(&(var_hash))
+#define PHP_VAR_SERIALIZE_DESTROY(var_hash_ptr) \
+do { \
+ if (BG(serialize).level) { \
+ if (!--BG(serialize).level) { \
+ zend_hash_destroy(BG(serialize).var_hash); \
+ FREE_HASHTABLE(BG(serialize).var_hash); \
+ BG(serialize).var_hash = NULL; \
+ } \
+ } else { \
+ zend_hash_destroy((var_hash_ptr)); \
+ } \
+} while (0)
+
+#define PHP_VAR_UNSERIALIZE_INIT(var_hash_ptr) \
+do { \
+ if (BG(unserialize).level) { \
+ (var_hash_ptr) = BG(unserialize).var_hash; \
+ ++BG(unserialize).level; \
+ } else { \
+ (var_hash_ptr) = ecalloc(1, sizeof(struct php_unserialize_data)); \
+ BG(unserialize).var_hash = (var_hash_ptr); \
+ BG(unserialize).level = 1; \
+ } \
+} while (0)
+
+#define PHP_VAR_UNSERIALIZE_DESTROY(var_hash_ptr) \
+do { \
+ if (BG(unserialize).level) { \
+ if (!--BG(unserialize).level) { \
+ var_destroy(&(var_hash_ptr)); \
+ efree((var_hash_ptr)); \
+ BG(unserialize).var_hash = NULL; \
+ } \
+ } else { \
+ var_destroy(&(var_hash_ptr)); \
+ } \
+} while (0)
PHPAPI void var_replace(php_unserialize_data_t *var_hash, zval *ozval, zval **nzval);
PHPAPI void var_destroy(php_unserialize_data_t *var_hash);
diff --git a/ext/standard/tests/serialize/bug36424.phpt b/ext/standard/tests/serialize/bug36424.phpt
new file mode 100644
index 0000000000..c62be01758
--- /dev/null
+++ b/ext/standard/tests/serialize/bug36424.phpt
@@ -0,0 +1,72 @@
+--TEST--
+Bug #36424 - Serializable interface breaks object references
+--FILE--
+<?php
+
+echo "-TEST\n";
+
+class a implements Serializable {
+ function serialize() {
+ return serialize(get_object_vars($this));
+ }
+ function unserialize($s) {
+ foreach (unserialize($s) as $p=>$v) {
+ $this->$p=$v;
+ }
+ }
+}
+class b extends a {}
+class c extends b {}
+
+$c = new c;
+$c->a = new a;
+$c->a->b = new b;
+$c->a->b->c = $c;
+$c->a->c = $c;
+$c->a->b->a = $c->a;
+$c->a->a = $c->a;
+
+$s = serialize($c);
+printf("%s\n", $s);
+
+$d = unserialize($s);
+
+var_dump(
+ $d === $d->a->b->c,
+ $d->a->a === $d->a,
+ $d->a->b->a === $d->a,
+ $d->a->c === $d
+);
+
+print_r($d);
+
+echo "Done\n";
+
+?>
+--EXPECTF--
+%aTEST
+C:1:"c":108:{a:1:{s:1:"a";C:1:"a":81:{a:3:{s:1:"b";C:1:"b":30:{a:2:{s:1:"c";r:1;s:1:"a";r:3;}}s:1:"c";r:1;s:1:"a";r:3;}}}}
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+c Object
+(
+ [a] => a Object
+ (
+ [b] => b Object
+ (
+ [c] => c Object
+ *RECURSION*
+ [a] => a Object
+ *RECURSION*
+ )
+
+ [c] => c Object
+ *RECURSION*
+ [a] => a Object
+ *RECURSION*
+ )
+
+)
+Done
diff --git a/ext/standard/var.c b/ext/standard/var.c
index 7e6f6def94..6c5ae27f79 100644
--- a/ext/standard/var.c
+++ b/ext/standard/var.c
@@ -498,12 +498,18 @@ static inline int php_add_var_hash(HashTable *var_hash, zval *var, void *var_old
var_no = -1;
zend_hash_next_index_insert(var_hash, &var_no, sizeof(var_no), NULL);
}
+#if 0
+ fprintf(stderr, "- had var (%d): %lu\n", Z_TYPE_P(var), **(ulong**)var_old);
+#endif
return FAILURE;
}
/* +1 because otherwise hash will think we are trying to store NULL pointer */
var_no = zend_hash_num_elements(var_hash) + 1;
zend_hash_add(var_hash, p, len, &var_no, sizeof(var_no), NULL);
+#if 0
+ fprintf(stderr, "+ add var (%d): %lu\n", Z_TYPE_P(var), var_no);
+#endif
return SUCCESS;
}
/* }}} */
@@ -814,9 +820,9 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, HashTable *var
}
/* }}} */
-PHPAPI void php_var_serialize(smart_str *buf, zval **struc, HashTable *var_hash TSRMLS_DC) /* {{{ */
+PHPAPI void php_var_serialize(smart_str *buf, zval **struc, php_serialize_data_t *var_hash TSRMLS_DC) /* {{{ */
{
- php_var_serialize_intern(buf, *struc, var_hash TSRMLS_CC);
+ php_var_serialize_intern(buf, *struc, *var_hash TSRMLS_CC);
smart_str_0(buf);
}
/* }}} */
diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c
index 1ed2710f1f..4ccc2b322a 100644
--- a/ext/standard/var_unserializer.c
+++ b/ext/standard/var_unserializer.c
@@ -1,4 +1,4 @@
-/* Generated by re2c 0.13.5 on Mon Apr 12 10:11:22 2010 */
+/* Generated by re2c 0.13.5 on Wed May 26 09:11:18 2010 */
#line 1 "ext/standard/var_unserializer.re"
/*
+----------------------------------------------------------------------+
@@ -35,8 +35,11 @@ typedef struct {
static inline void var_push(php_unserialize_data_t *var_hashx, zval **rval)
{
- var_entries *var_hash = var_hashx->first, *prev = NULL;
-
+ var_entries *var_hash = (*var_hashx)->first, *prev = NULL;
+#if 0
+ fprintf(stderr, "var_push(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
+#endif
+
while (var_hash && var_hash->used_slots == VAR_ENTRIES_MAX) {
prev = var_hash;
var_hash = var_hash->next;
@@ -47,8 +50,8 @@ static inline void var_push(php_unserialize_data_t *var_hashx, zval **rval)
var_hash->used_slots = 0;
var_hash->next = 0;
- if (!var_hashx->first)
- var_hashx->first = var_hash;
+ if (!(*var_hashx)->first)
+ (*var_hashx)->first = var_hash;
else
prev->next = var_hash;
}
@@ -58,8 +61,11 @@ static inline void var_push(php_unserialize_data_t *var_hashx, zval **rval)
static inline void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval)
{
- var_entries *var_hash = var_hashx->first_dtor, *prev = NULL;
-
+ var_entries *var_hash = (*var_hashx)->first_dtor, *prev = NULL;
+#if 0
+ fprintf(stderr, "var_push_dtor(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
+#endif
+
while (var_hash && var_hash->used_slots == VAR_ENTRIES_MAX) {
prev = var_hash;
var_hash = var_hash->next;
@@ -70,8 +76,8 @@ static inline void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval)
var_hash->used_slots = 0;
var_hash->next = 0;
- if (!var_hashx->first_dtor)
- var_hashx->first_dtor = var_hash;
+ if (!(*var_hashx)->first_dtor)
+ (*var_hashx)->first_dtor = var_hash;
else
prev->next = var_hash;
}
@@ -83,7 +89,10 @@ static inline void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval)
PHPAPI void var_replace(php_unserialize_data_t *var_hashx, zval *ozval, zval **nzval)
{
long i;
- var_entries *var_hash = var_hashx->first;
+ var_entries *var_hash = (*var_hashx)->first;
+#if 0
+ fprintf(stderr, "var_replace(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(nzval));
+#endif
while (var_hash) {
for (i = 0; i < var_hash->used_slots; i++) {
@@ -98,8 +107,11 @@ PHPAPI void var_replace(php_unserialize_data_t *var_hashx, zval *ozval, zval **n
static int var_access(php_unserialize_data_t *var_hashx, long id, zval ***store)
{
- var_entries *var_hash = var_hashx->first;
-
+ var_entries *var_hash = (*var_hashx)->first;
+#if 0
+ fprintf(stderr, "var_access(%ld): %ld\n", var_hash?var_hash->used_slots:-1L, id);
+#endif
+
while (id >= VAR_ENTRIES_MAX && var_hash && var_hash->used_slots == VAR_ENTRIES_MAX) {
var_hash = var_hash->next;
id -= VAR_ENTRIES_MAX;
@@ -118,7 +130,10 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
{
void *next;
long i;
- var_entries *var_hash = var_hashx->first;
+ var_entries *var_hash = (*var_hashx)->first;
+#if 0
+ fprintf(stderr, "var_destroy(%ld)\n", var_hash?var_hash->used_slots:-1L);
+#endif
while (var_hash) {
next = var_hash->next;
@@ -126,7 +141,7 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
var_hash = next;
}
- var_hash = var_hashx->first_dtor;
+ var_hash = (*var_hashx)->first_dtor;
while (var_hash) {
for (i = 0; i < var_hash->used_slots; i++) {
@@ -190,7 +205,7 @@ static char *unserialize_str(const unsigned char **p, size_t *len, size_t maxlen
#define YYMARKER marker
-#line 198 "ext/standard/var_unserializer.re"
+#line 213 "ext/standard/var_unserializer.re"
@@ -396,7 +411,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
-#line 400 "ext/standard/var_unserializer.c"
+#line 415 "ext/standard/var_unserializer.c"
{
YYCTYPE yych;
static const unsigned char yybm[] = {
@@ -456,9 +471,9 @@ yy2:
yych = *(YYMARKER = ++YYCURSOR);
if (yych == ':') goto yy95;
yy3:
-#line 722 "ext/standard/var_unserializer.re"
+#line 737 "ext/standard/var_unserializer.re"
{ return 0; }
-#line 462 "ext/standard/var_unserializer.c"
+#line 477 "ext/standard/var_unserializer.c"
yy4:
yych = *(YYMARKER = ++YYCURSOR);
if (yych == ':') goto yy89;
@@ -501,13 +516,13 @@ yy13:
goto yy3;
yy14:
++YYCURSOR;
-#line 716 "ext/standard/var_unserializer.re"
+#line 731 "ext/standard/var_unserializer.re"
{
/* this is the case where we have less data than planned */
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unexpected end of serialized data");
return 0; /* not sure if it should be 0 or 1 here? */
}
-#line 511 "ext/standard/var_unserializer.c"
+#line 526 "ext/standard/var_unserializer.c"
yy16:
yych = *++YYCURSOR;
goto yy3;
@@ -537,7 +552,7 @@ yy20:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 599 "ext/standard/var_unserializer.re"
+#line 614 "ext/standard/var_unserializer.re"
{
size_t len, len2, len3, maxlen;
long elements;
@@ -654,7 +669,7 @@ yy20:
return object_common2(UNSERIALIZE_PASSTHRU, elements);
}
-#line 658 "ext/standard/var_unserializer.c"
+#line 673 "ext/standard/var_unserializer.c"
yy25:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -679,7 +694,7 @@ yy27:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 591 "ext/standard/var_unserializer.re"
+#line 606 "ext/standard/var_unserializer.re"
{
INIT_PZVAL(*rval);
@@ -687,7 +702,7 @@ yy27:
return object_common2(UNSERIALIZE_PASSTHRU,
object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR));
}
-#line 691 "ext/standard/var_unserializer.c"
+#line 706 "ext/standard/var_unserializer.c"
yy32:
yych = *++YYCURSOR;
if (yych == '+') goto yy33;
@@ -708,7 +723,7 @@ yy34:
yych = *++YYCURSOR;
if (yych != '{') goto yy18;
++YYCURSOR;
-#line 571 "ext/standard/var_unserializer.re"
+#line 586 "ext/standard/var_unserializer.re"
{
long elements = parse_iv(start + 2);
/* use iv() not uiv() in order to check data range */
@@ -728,7 +743,7 @@ yy34:
return finish_nested_data(UNSERIALIZE_PASSTHRU);
}
-#line 732 "ext/standard/var_unserializer.c"
+#line 747 "ext/standard/var_unserializer.c"
yy39:
yych = *++YYCURSOR;
if (yych == '+') goto yy40;
@@ -749,7 +764,7 @@ yy41:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 542 "ext/standard/var_unserializer.re"
+#line 557 "ext/standard/var_unserializer.re"
{
size_t len, maxlen;
char *str;
@@ -778,7 +793,7 @@ yy41:
ZVAL_STRINGL(*rval, str, len, 0);
return 1;
}
-#line 782 "ext/standard/var_unserializer.c"
+#line 797 "ext/standard/var_unserializer.c"
yy46:
yych = *++YYCURSOR;
if (yych == '+') goto yy47;
@@ -799,7 +814,7 @@ yy48:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 514 "ext/standard/var_unserializer.re"
+#line 529 "ext/standard/var_unserializer.re"
{
size_t len, maxlen;
char *str;
@@ -827,7 +842,7 @@ yy48:
ZVAL_STRINGL(*rval, str, len, 1);
return 1;
}
-#line 831 "ext/standard/var_unserializer.c"
+#line 846 "ext/standard/var_unserializer.c"
yy53:
yych = *++YYCURSOR;
if (yych <= '/') {
@@ -915,7 +930,7 @@ yy61:
}
yy63:
++YYCURSOR;
-#line 504 "ext/standard/var_unserializer.re"
+#line 519 "ext/standard/var_unserializer.re"
{
#if SIZEOF_LONG == 4
use_double:
@@ -925,7 +940,7 @@ use_double:
ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL));
return 1;
}
-#line 929 "ext/standard/var_unserializer.c"
+#line 944 "ext/standard/var_unserializer.c"
yy65:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -984,7 +999,7 @@ yy73:
yych = *++YYCURSOR;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 489 "ext/standard/var_unserializer.re"
+#line 504 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
INIT_PZVAL(*rval);
@@ -999,7 +1014,7 @@ yy73:
return 1;
}
-#line 1003 "ext/standard/var_unserializer.c"
+#line 1018 "ext/standard/var_unserializer.c"
yy76:
yych = *++YYCURSOR;
if (yych == 'N') goto yy73;
@@ -1026,7 +1041,7 @@ yy79:
if (yych <= '9') goto yy79;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 462 "ext/standard/var_unserializer.re"
+#line 477 "ext/standard/var_unserializer.re"
{
#if SIZEOF_LONG == 4
int digits = YYCURSOR - start - 3;
@@ -1053,7 +1068,7 @@ yy79:
ZVAL_LONG(*rval, parse_iv(start + 2));
return 1;
}
-#line 1057 "ext/standard/var_unserializer.c"
+#line 1072 "ext/standard/var_unserializer.c"
yy83:
yych = *++YYCURSOR;
if (yych <= '/') goto yy18;
@@ -1061,24 +1076,24 @@ yy83:
yych = *++YYCURSOR;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 455 "ext/standard/var_unserializer.re"
+#line 470 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
INIT_PZVAL(*rval);
ZVAL_BOOL(*rval, parse_iv(start + 2));
return 1;
}
-#line 1072 "ext/standard/var_unserializer.c"
+#line 1087 "ext/standard/var_unserializer.c"
yy87:
++YYCURSOR;
-#line 448 "ext/standard/var_unserializer.re"
+#line 463 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
INIT_PZVAL(*rval);
ZVAL_NULL(*rval);
return 1;
}
-#line 1082 "ext/standard/var_unserializer.c"
+#line 1097 "ext/standard/var_unserializer.c"
yy89:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -1101,7 +1116,7 @@ yy91:
if (yych <= '9') goto yy91;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 425 "ext/standard/var_unserializer.re"
+#line 440 "ext/standard/var_unserializer.re"
{
long id;
@@ -1124,7 +1139,7 @@ yy91:
return 1;
}
-#line 1128 "ext/standard/var_unserializer.c"
+#line 1143 "ext/standard/var_unserializer.c"
yy95:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -1147,7 +1162,7 @@ yy97:
if (yych <= '9') goto yy97;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 404 "ext/standard/var_unserializer.re"
+#line 419 "ext/standard/var_unserializer.re"
{
long id;
@@ -1168,9 +1183,9 @@ yy97:
return 1;
}
-#line 1172 "ext/standard/var_unserializer.c"
+#line 1187 "ext/standard/var_unserializer.c"
}
-#line 724 "ext/standard/var_unserializer.re"
+#line 739 "ext/standard/var_unserializer.re"
return 0;
diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re
index 689fd90cd6..089ffcae9f 100644
--- a/ext/standard/var_unserializer.re
+++ b/ext/standard/var_unserializer.re
@@ -33,8 +33,11 @@ typedef struct {
static inline void var_push(php_unserialize_data_t *var_hashx, zval **rval)
{
- var_entries *var_hash = var_hashx->first, *prev = NULL;
-
+ var_entries *var_hash = (*var_hashx)->first, *prev = NULL;
+#if 0
+ fprintf(stderr, "var_push(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
+#endif
+
while (var_hash && var_hash->used_slots == VAR_ENTRIES_MAX) {
prev = var_hash;
var_hash = var_hash->next;
@@ -45,8 +48,8 @@ static inline void var_push(php_unserialize_data_t *var_hashx, zval **rval)
var_hash->used_slots = 0;
var_hash->next = 0;
- if (!var_hashx->first)
- var_hashx->first = var_hash;
+ if (!(*var_hashx)->first)
+ (*var_hashx)->first = var_hash;
else
prev->next = var_hash;
}
@@ -56,8 +59,11 @@ static inline void var_push(php_unserialize_data_t *var_hashx, zval **rval)
static inline void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval)
{
- var_entries *var_hash = var_hashx->first_dtor, *prev = NULL;
-
+ var_entries *var_hash = (*var_hashx)->first_dtor, *prev = NULL;
+#if 0
+ fprintf(stderr, "var_push_dtor(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
+#endif
+
while (var_hash && var_hash->used_slots == VAR_ENTRIES_MAX) {
prev = var_hash;
var_hash = var_hash->next;
@@ -68,8 +74,8 @@ static inline void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval)
var_hash->used_slots = 0;
var_hash->next = 0;
- if (!var_hashx->first_dtor)
- var_hashx->first_dtor = var_hash;
+ if (!(*var_hashx)->first_dtor)
+ (*var_hashx)->first_dtor = var_hash;
else
prev->next = var_hash;
}
@@ -81,7 +87,10 @@ static inline void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval)
PHPAPI void var_replace(php_unserialize_data_t *var_hashx, zval *ozval, zval **nzval)
{
long i;
- var_entries *var_hash = var_hashx->first;
+ var_entries *var_hash = (*var_hashx)->first;
+#if 0
+ fprintf(stderr, "var_replace(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(nzval));
+#endif
while (var_hash) {
for (i = 0; i < var_hash->used_slots; i++) {
@@ -96,8 +105,11 @@ PHPAPI void var_replace(php_unserialize_data_t *var_hashx, zval *ozval, zval **n
static int var_access(php_unserialize_data_t *var_hashx, long id, zval ***store)
{
- var_entries *var_hash = var_hashx->first;
-
+ var_entries *var_hash = (*var_hashx)->first;
+#if 0
+ fprintf(stderr, "var_access(%ld): %ld\n", var_hash?var_hash->used_slots:-1L, id);
+#endif
+
while (id >= VAR_ENTRIES_MAX && var_hash && var_hash->used_slots == VAR_ENTRIES_MAX) {
var_hash = var_hash->next;
id -= VAR_ENTRIES_MAX;
@@ -116,7 +128,10 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
{
void *next;
long i;
- var_entries *var_hash = var_hashx->first;
+ var_entries *var_hash = (*var_hashx)->first;
+#if 0
+ fprintf(stderr, "var_destroy(%ld)\n", var_hash?var_hash->used_slots:-1L);
+#endif
while (var_hash) {
next = var_hash->next;
@@ -124,7 +139,7 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
var_hash = next;
}
- var_hash = var_hashx->first_dtor;
+ var_hash = (*var_hashx)->first_dtor;
while (var_hash) {
for (i = 0; i < var_hash->used_slots; i++) {