From: Etienne Kneuss Date: Mon, 25 Aug 2008 18:38:23 +0000 (+0000) Subject: Fix #45826 (Custom serialization) X-Git-Tag: BEFORE_HEAD_NS_CHANGE~581 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b2dd15897aaefe80721a64f1c6567521fc58c603;p=php Fix #45826 (Custom serialization) --- diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index 91e9f24da8..aa1507b308 100755 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -60,18 +60,22 @@ 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; - 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; + zend_object std; + zval *array; + zval *retval; + HashPosition pos; + 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; } spl_array_object; static inline HashTable *spl_array_get_hash_table(spl_array_object* intern, int check_std_props TSRMLS_DC) { /* {{{ */ @@ -125,6 +129,8 @@ 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, int *type, zstr *buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC); +int spl_array_unserialize(zval **object, zend_class_entry *ce, int type, const zstr 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) @@ -144,6 +150,8 @@ static zend_object_value spl_array_object_new_ex(zend_class_entry *class_type, s zend_hash_copy(intern->std.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); intern->ar_flags = 0; + intern->serialize_data = NULL; + intern->unserialize_data = NULL; intern->ce_get_iterator = spl_ce_ArrayIterator; if (orig) { spl_array_object *other = (spl_array_object*)zend_object_store_get_object(orig TSRMLS_CC); @@ -209,6 +217,14 @@ 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 */ @@ -1442,32 +1458,27 @@ SPL_METHOD(Array, getChildren) } /* }}} */ -/* {{{ 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); +smart_str spl_array_serialize_helper(spl_array_object *intern, php_serialize_data_t *var_hash_p TSRMLS_DC) { /* {{{ */ 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; + return buf; } - 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:i:", 4); - smart_str_append_long(&buf, (intern->ar_flags & SPL_ARRAY_CLONE_MASK)); - smart_str_appendc(&buf, ';'); + smart_str_appendl(&buf, "x:", 2); + php_var_serialize(&buf, &flags, var_hash_p TSRMLS_CC); + zval_ptr_dtor(&flags); if (!(intern->ar_flags & SPL_ARRAY_IS_SELF)) { - php_var_serialize(&buf, &intern->array, &var_hash TSRMLS_CC); + php_var_serialize(&buf, &intern->array, var_hash_p TSRMLS_CC); smart_str_appendc(&buf, ';'); } @@ -1477,43 +1488,81 @@ SPL_METHOD(Array, serialize) Z_ARRVAL(members) = intern->std.properties; Z_TYPE(members) = IS_ARRAY; pmembers = &members; - php_var_serialize(&buf, &pmembers, &var_hash TSRMLS_CC); /* finishes the string */ + php_var_serialize(&buf, &pmembers, var_hash_p TSRMLS_CC); /* finishes the string */ + + 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); + } - /* done */ - PHP_VAR_SERIALIZE_DESTROY(var_hash); + 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); } - RETURN_NULL(); + RETURN_NULL(); } /* }}} */ -/* {{{ proto void ArrayObject::unserialize(string serialized) U - * unserialize the object - */ -SPL_METHOD(Array, unserialize) -{ - spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(getThis() TSRMLS_CC); - - char *buf; - int buf_len; +int spl_array_serialize(zval *object, int *type, zstr *buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC) { /* {{{ */ + 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, type, 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->s = estrndup(buf.c, buf.len); + *buf_len = buf.len; + *type = IS_STRING; + efree(buf.c); + return SUCCESS; + } else { + return FAILURE; + } + } +} +/* }}} */ + +void spl_array_unserialize_helper(spl_array_object *intern, const unsigned char *buf, zend_uint buf_len, php_unserialize_data_t *var_hash_p TSRMLS_DC) { /* {{{ */ 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; - } - - s = p = (const unsigned char*)buf; - PHP_VAR_UNSERIALIZE_INIT(var_hash); + s = p = buf; if (*p!= 'x' || *++p != ':') { goto outexcept; @@ -1521,7 +1570,7 @@ SPL_METHOD(Array, unserialize) ++p; ALLOC_INIT_ZVAL(pflags); - if (!php_var_unserialize(&pflags, &p, s + buf_len, &var_hash TSRMLS_CC) || Z_TYPE_P(pflags) != IS_LONG) { + if (!php_var_unserialize(&pflags, &p, s + buf_len, var_hash_p TSRMLS_CC) || Z_TYPE_P(pflags) != IS_LONG) { zval_ptr_dtor(&pflags); goto outexcept; } @@ -1547,7 +1596,7 @@ SPL_METHOD(Array, unserialize) 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 TSRMLS_CC)) { + if (!php_var_unserialize(&intern->array, &p, s + buf_len, var_hash_p TSRMLS_CC)) { goto outexcept; } } @@ -1563,7 +1612,7 @@ SPL_METHOD(Array, unserialize) ++p; ALLOC_INIT_ZVAL(pmembers); - if (!php_var_unserialize(&pmembers, &p, s + buf_len, &var_hash TSRMLS_CC)) { + if (!php_var_unserialize(&pmembers, &p, s + buf_len, var_hash_p TSRMLS_CC)) { zval_ptr_dtor(&pmembers); goto outexcept; } @@ -1574,15 +1623,93 @@ SPL_METHOD(Array, unserialize) /* done reading $serialized */ - PHP_VAR_UNSERIALIZE_DESTROY(var_hash); return; outexcept: - 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 - (long)buf), buf_len); + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Error at offset %ld of %d bytes", (long)((char*)p - (char *)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, int type, const zstr 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_ZSTRL(zdata, type, 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 { + if (type == IS_STRING) { + spl_array_unserialize_helper(intern, (unsigned char *)buf.s, buf_len, (php_unserialize_data_t *)data TSRMLS_CC); + } else { + unsigned char *bufc = (unsigned char*)zend_unicode_to_ascii(buf.u, buf_len TSRMLS_CC); + if (!bufc) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Binary or ASCII-Unicode string expected, non-ASCII-Unicode string received"); + return FAILURE; + } + + spl_array_unserialize_helper(intern, bufc, buf_len, (php_unserialize_data_t *)data TSRMLS_CC); + + efree(bufc); + } + } + + if (EG(exception)) { + return FAILURE; + } else { + return SUCCESS; + } +} +/* }}} */ /* {{{ ZEND_BEGIN_ARG_INFO */ static @@ -1706,6 +1833,8 @@ 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; @@ -1728,6 +1857,8 @@ 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/tests/bug45826.phpt b/ext/spl/tests/bug45826.phpt new file mode 100644 index 0000000000..4573ab2a33 --- /dev/null +++ b/ext/spl/tests/bug45826.phpt @@ -0,0 +1,88 @@ +--TEST-- +ArrayObject/ArrayIterator : serialization +--FILE-- +append($y); +$o->append($y); +$o->append($o); + +var_dump($o[0] === $o[1]); +var_dump($o[2] === $o); + +$s1 = serialize($o); +$s2 = $o->serialize(); +var_dump($s1); +var_dump($s2); + +$o1 =unserialize($s1); + +var_dump($o1[0] === $o1[1]); +var_dump($o1[2] === $o1); + +$o2 = new ArrayObject(); +$o2->unserialize($s2); + +var_dump($o2[0] === $o2[1]); +var_dump($o2[2] !== $o2); +var_dump($o2[2][2] === $o2[2]); + +echo "#### Extending ArrayObject\n"; +unset($o,$x,$s1,$s2,$o1,$o2); +class ArrayObject2 extends ArrayObject { + public function serialize() { + return parent::serialize(); + } + + public function unserialize($s) { + return parent::unserialize($s); + } +} + +$o = new ArrayObject2(); +$y = new StdClass; +$o->append($y); +$o->append($y); +$o->append($o); + +var_dump($o[0] === $o[1]); +var_dump($o[2] === $o); + +$s1 = serialize($o); +$s2 = $o->serialize(); +var_dump($s1); +var_dump($s2); + +$o1 =unserialize($s1); + +var_dump($o1[0] === $o1[1]); +var_dump($o1[2] === $o1); + +$o2 = new ArrayObject2(); +$o2->unserialize($s2); + +var_dump($o2[0] === $o2[1]); +var_dump($o2[2] !== $o2); +var_dump($o2[2][2] === $o2[2]); +?> +--EXPECT-- +bool(true) +bool(true) +unicode(84) "C:11:"ArrayObject":60:{x:i:0;a:3:{i:0;O:8:"stdClass":0:{}i:1;r:4;i:2;r:1;};m:a:0:{}}" +string(125) "x:i:0;a:3:{i:0;O:8:"stdClass":0:{}i:1;r:3;i:2;C:11:"ArrayObject":45:{x:i:0;a:3:{i:0;r:3;i:1;r:3;i:2;r:5;};m:a:0:{}}};m:a:0:{}" +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +#### Extending ArrayObject +bool(true) +bool(true) +unicode(85) "C:12:"ArrayObject2":60:{x:i:0;a:3:{i:0;O:8:"stdClass":0:{}i:1;r:4;i:2;r:1;};m:a:0:{}}" +string(126) "x:i:0;a:3:{i:0;O:8:"stdClass":0:{}i:1;r:3;i:2;C:12:"ArrayObject2":45:{x:i:0;a:3:{i:0;r:3;i:1;r:3;i:2;r:5;};m:a:0:{}}};m:a:0:{}" +bool(true) +bool(true) +bool(true) +bool(true) +bool(true)