mike                                     Wed, 26 May 2010 07:24:37 +0000

Revision: http://svn.php.net/viewvc?view=revision&revision=299770

Log:
Added support for object references in recursive serialize() calls. FR #36424

Bug: http://bugs.php.net/36424 (Assigned) Keeping reference info through 
recursive calls to serialize()
      
Changed paths:
    U   php/php-src/trunk/NEWS
    U   php/php-src/trunk/ext/spl/spl_array.c
    U   php/php-src/trunk/ext/spl/spl_observer.c
    U   php/php-src/trunk/ext/spl/tests/bug49263.phpt
    U   php/php-src/trunk/ext/standard/basic_functions.h
    U   php/php-src/trunk/ext/standard/php_var.h
    A   php/php-src/trunk/ext/standard/tests/serialize/bug36424.phpt
    U   php/php-src/trunk/ext/standard/var.c
    U   php/php-src/trunk/ext/standard/var_unserializer.c
    U   php/php-src/trunk/ext/standard/var_unserializer.re

Modified: php/php-src/trunk/NEWS
===================================================================
--- php/php-src/trunk/NEWS	2010-05-26 06:35:07 UTC (rev 299769)
+++ php/php-src/trunk/NEWS	2010-05-26 07:24:37 UTC (rev 299770)
@@ -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)


Modified: php/php-src/trunk/ext/spl/spl_array.c
===================================================================
--- php/php-src/trunk/ext/spl/spl_array.c	2010-05-26 06:35:07 UTC (rev 299769)
+++ php/php-src/trunk/ext/spl/spl_array.c	2010-05-26 07:24:37 UTC (rev 299770)
@@ -59,23 +59,19 @@
 #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 @@
 /* }}} */

 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 @@
 	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 @@
 		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 @@
 }
 /* }}} */

-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,35 +1592,11 @@
 	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;
-}
-/* }}} */
+	PHP_VAR_SERIALIZE_DESTROY(var_hash);

-/* {{{ 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;
-	}
-
 	if (buf.c) {
 		RETURN_STRINGL(buf.c, buf.len, 0);
 	}
@@ -1636,47 +1604,32 @@
 	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);
+	spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(getThis() 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;
-		}
-	}
-}
-/* }}} */
-
-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 @@
 	++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 @@
 		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 @@
 	++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 @@
 	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 @@
 	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 @@
 	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;


Modified: php/php-src/trunk/ext/spl/spl_observer.c
===================================================================
--- php/php-src/trunk/ext/spl/spl_observer.c	2010-05-26 06:35:07 UTC (rev 299769)
+++ php/php-src/trunk/ext/spl/spl_observer.c	2010-05-26 07:24:37 UTC (rev 299770)
@@ -638,7 +638,7 @@
 	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 @@
 	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 @@
 	++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;
 	}

Modified: php/php-src/trunk/ext/spl/tests/bug49263.phpt
===================================================================
--- php/php-src/trunk/ext/spl/tests/bug49263.phpt	2010-05-26 06:35:07 UTC (rev 299769)
+++ php/php-src/trunk/ext/spl/tests/bug49263.phpt	2010-05-26 07:24:37 UTC (rev 299770)
@@ -19,7 +19,7 @@
 ?>
 ===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) {

Modified: php/php-src/trunk/ext/standard/basic_functions.h
===================================================================
--- php/php-src/trunk/ext/standard/basic_functions.h	2010-05-26 06:35:07 UTC (rev 299769)
+++ php/php-src/trunk/ext/standard/basic_functions.h	2010-05-26 07:24:37 UTC (rev 299770)
@@ -201,6 +201,14 @@

 	/* 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;

Modified: php/php-src/trunk/ext/standard/php_var.h
===================================================================
--- php/php-src/trunk/ext/standard/php_var.h	2010-05-26 06:35:07 UTC (rev 299769)
+++ php/php-src/trunk/ext/standard/php_var.h	2010-05-26 07:24:37 UTC (rev 299770)
@@ -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,30 +36,69 @@
 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);


Added: php/php-src/trunk/ext/standard/tests/serialize/bug36424.phpt
===================================================================
--- php/php-src/trunk/ext/standard/tests/serialize/bug36424.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/standard/tests/serialize/bug36424.phpt	2010-05-26 07:24:37 UTC (rev 299770)
@@ -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

Modified: php/php-src/trunk/ext/standard/var.c
===================================================================
--- php/php-src/trunk/ext/standard/var.c	2010-05-26 06:35:07 UTC (rev 299769)
+++ php/php-src/trunk/ext/standard/var.c	2010-05-26 07:24:37 UTC (rev 299770)
@@ -498,12 +498,18 @@
 			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 @@
 }
 /* }}} */

-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);
 }
 /* }}} */

Modified: php/php-src/trunk/ext/standard/var_unserializer.c
===================================================================
--- php/php-src/trunk/ext/standard/var_unserializer.c	2010-05-26 06:35:07 UTC (rev 299769)
+++ php/php-src/trunk/ext/standard/var_unserializer.c	2010-05-26 07:24:37 UTC (rev 299770)
@@ -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 @@

 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 @@
 		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_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 @@
 		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 @@
 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 @@

 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 @@
 {
 	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 @@
 		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 @@
 #define YYMARKER marker


-#line 198 "ext/standard/var_unserializer.re"
+#line 213 "ext/standard/var_unserializer.re"



@@ -396,7 +411,7 @@



-#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 @@
 	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 @@
 	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 @@
 	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 @@

 	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 @@
 	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 @@
 	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 @@
 	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 @@

 	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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 	}
 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 @@
 	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 @@
 	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 @@

 	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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@

 	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 @@
 	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 @@

 	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;

Modified: php/php-src/trunk/ext/standard/var_unserializer.re
===================================================================
--- php/php-src/trunk/ext/standard/var_unserializer.re	2010-05-26 06:35:07 UTC (rev 299769)
+++ php/php-src/trunk/ext/standard/var_unserializer.re	2010-05-26 07:24:37 UTC (rev 299770)
@@ -33,8 +33,11 @@

 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 @@
 		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_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 @@
 		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 @@
 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 @@

 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 @@
 {
 	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 @@
 		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++) {
-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to