diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index 8f2e03b5329..88bb87f9ae5 100755 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -25,7 +25,10 @@ #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" +#include "ext/standard/php_var.h" +#include "ext/standard/php_smart_str.h" #include "zend_interfaces.h" +#include "zend_API.h" #include "zend_exceptions.h" #include "php_spl.h" @@ -53,7 +56,7 @@ PHPAPI zend_class_entry *spl_ce_RecursiveArrayIterator; #define SPL_ARRAY_IS_SELF 0x02000000 #define SPL_ARRAY_USE_OTHER 0x04000000 #define SPL_ARRAY_INT_MASK 0xFFFF0000 -#define SPL_ARRAY_CLONE_MASK 0x03000007 +#define SPL_ARRAY_CLONE_MASK 0x030FFFFF typedef struct _spl_array_object { zend_object std; @@ -69,7 +72,7 @@ typedef struct _spl_array_object { zend_class_entry* ce_get_iterator; } spl_array_object; -static inline HashTable *spl_array_get_hash_table(spl_array_object* intern, int check_std_props TSRMLS_DC) { +static inline HashTable *spl_array_get_hash_table(spl_array_object* intern, int check_std_props TSRMLS_DC) { /* {{{ */ if ((intern->ar_flags & SPL_ARRAY_IS_SELF) != 0) { return intern->std.properties; } else if ((intern->ar_flags & SPL_ARRAY_USE_OTHER) && (check_std_props == 0 || (intern->ar_flags & SPL_ARRAY_STD_PROP_LIST) == 0) && Z_TYPE_P(intern->array) == IS_OBJECT) { @@ -80,7 +83,7 @@ static inline HashTable *spl_array_get_hash_table(spl_array_object* intern, int } else { return HASH_OF(intern->array); } -} +} /* }}} */ SPL_API int spl_hash_verify_pos(spl_array_object * intern TSRMLS_DC) /* {{{ */ { @@ -553,7 +556,6 @@ SPL_METHOD(Array, offsetSet) spl_array_write_dimension_ex(0, getThis(), index, value TSRMLS_CC); } /* }}} */ - void spl_array_iterator_append(zval *object, zval *append_value TSRMLS_DC) /* {{{ */ { spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC); @@ -600,7 +602,7 @@ SPL_METHOD(Array, offsetUnset) spl_array_unset_dimension_ex(0, getThis(), index TSRMLS_CC); } /* }}} */ -/* {{ proto array ArrayObject::getArrayCopy() U +/* {{{ proto array ArrayObject::getArrayCopy() U proto array ArrayIterator::getArrayCopy() U Return a copy of the contained array */ SPL_METHOD(Array, getArrayCopy) @@ -759,11 +761,11 @@ static int spl_array_next(spl_array_object *intern TSRMLS_DC) /* {{{ */ } } /* }}} */ -/* define an overloaded iterator structure */ +/* {{{ define an overloaded iterator structure */ typedef struct { zend_user_iterator intern; spl_array_object *object; -} spl_array_it; +} spl_array_it; /* }}} */ static void spl_array_it_dtor(zend_object_iterator *iter TSRMLS_DC) /* {{{ */ { @@ -892,7 +894,7 @@ static void spl_array_it_rewind(zend_object_iterator *iter TSRMLS_DC) /* {{{ */ } /* }}} */ -/* iterator handler table */ +/* {{{ iterator handler table */ zend_object_iterator_funcs spl_array_it_funcs = { spl_array_it_dtor, spl_array_it_valid, @@ -900,7 +902,7 @@ zend_object_iterator_funcs spl_array_it_funcs = { spl_array_it_get_current_key, spl_array_it_move_forward, spl_array_it_rewind -}; +}; /* }}} */ zend_object_iterator *spl_array_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC) /* {{{ */ { @@ -1201,6 +1203,7 @@ SPL_METHOD(Array, count) RETURN_LONG(count); } /* }}} */ +/* {{{ static void spl_array_method */ static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, char *fname, int fname_len, int use_arg) { spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(getThis() TSRMLS_CC); @@ -1220,45 +1223,41 @@ static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, char *fname, int fnam } else { zend_call_method(NULL, NULL, NULL, fname, fname_len, &return_value, 1, &tmp, NULL TSRMLS_CC); } -} +} /* }}} */ +/* {{{ SPL_ARRAY_METHOD */ #define SPL_ARRAY_METHOD(cname, fname, use_arg) \ SPL_METHOD(cname, fname) \ { \ spl_array_method(INTERNAL_FUNCTION_PARAM_PASSTHRU, #fname, sizeof(#fname)-1, use_arg); \ } -/* {{{ proto int ArrayObject::asort() U +/* proto int ArrayObject::asort() U proto int ArrayIterator::asort() U Sort the entries by values. */ SPL_ARRAY_METHOD(Array, asort, 0) -/* }}} */ -/* {{{ proto int ArrayObject::ksort() U +/* proto int ArrayObject::ksort() U proto int ArrayIterator::ksort() U Sort the entries by key. */ SPL_ARRAY_METHOD(Array, ksort, 0) -/* }}} */ -/* {{{ proto int ArrayObject::uasort(callback cmp_function) U +/* proto int ArrayObject::uasort(callback cmp_function) U proto int ArrayIterator::uasort(callback cmp_function) U Sort the entries by values user defined function. */ SPL_ARRAY_METHOD(Array, uasort, 1) -/* }}} */ -/* {{{ proto int ArrayObject::uksort(callback cmp_function) U +/* proto int ArrayObject::uksort(callback cmp_function) U proto int ArrayIterator::uksort(callback cmp_function) U Sort the entries by key using user defined function. */ SPL_ARRAY_METHOD(Array, uksort, 1) -/* }}} */ -/* {{{ proto int ArrayObject::natsort() U +/* proto int ArrayObject::natsort() U proto int ArrayIterator::natsort() U Sort the entries by values using "natural order" algorithm. */ SPL_ARRAY_METHOD(Array, natsort, 0) -/* }}} */ -/* {{{ proto int ArrayObject::natcasesort() U +/* proto int ArrayObject::natcasesort() U proto int ArrayIterator::natcasesort() U Sort the entries by key using case insensitive "natural order" algorithm. */ SPL_ARRAY_METHOD(Array, natcasesort, 0) @@ -1324,11 +1323,11 @@ void spl_array_iterator_key(zval *object, zval *return_value TSRMLS_DC) /* {{{ * } /* }}} */ -/* {{{ proto mixed|NULL ArrayIterator::key() U +/* {{{ proto mixed|NULL ArrayIterator::key() U Return current array key */ SPL_METHOD(Array, key) { - spl_array_iterator_key(getThis(), return_value TSRMLS_CC); + spl_array_iterator_key(getThis(), return_value TSRMLS_CC); } /* }}} */ @@ -1430,6 +1429,153 @@ 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); + HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC); + zval **entry, members, *pmembers; + HashPosition pos; + php_serialize_data_t var_hash; + smart_str buf = {0}; + + if (!aht) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array"); + return; + } + + PHP_VAR_SERIALIZE_INIT(var_hash); + + /* storage */ + smart_str_appendl(&buf, "x:i:", 4); + smart_str_append_long(&buf, (intern->ar_flags & SPL_ARRAY_CLONE_MASK)); + smart_str_appendc(&buf, ';'); + + if (!(intern->ar_flags & SPL_ARRAY_IS_SELF)) { + php_var_serialize(&buf, &intern->array, &var_hash TSRMLS_CC); + smart_str_appendc(&buf, ';'); + } + + /* members */ + smart_str_appendl(&buf, "m:", 2); + INIT_PZVAL(&members); + 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 */ + + /* done */ + PHP_VAR_SERIALIZE_DESTROY(var_hash); + + if (buf.c) { + RETURN_STRINGL(buf.c, buf.len, 0); + } + + 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); + + zstr buf; + unsigned int buf_len; + UChar *p, *s; + zend_uchar buf_type; + + php_unserialize_data_t var_hash; + zval *pentry, *pmembers, *pflags = NULL; + long flags; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &buf, &buf_len, &buf_type) == 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 = (buf_type == IS_UNICODE ? buf.u : (UChar *)buf.s); + + PHP_VAR_UNSERIALIZE_INIT(var_hash); + + if (*p!= 'x' || *++p != ':') { + goto outexcept; + } + ++p; + + ALLOC_INIT_ZVAL(pflags); + 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; + } + + --p; /* for ';' */ + flags = Z_LVAL_P(pflags); + zval_ptr_dtor(&pflags); + /* flags needs to be verified and we also need to verify whether the next + * thing we get is ';'. After that we require an 'm' or somethign else + * where 'm' stands for members and anything else should be an array. If + * neither 'a' or 'm' follows we have an error. */ + + if (*p != ';') { + goto outexcept; + } + ++p; + + if (*p!='m') { + if (*p!='a') { + goto outexcept; + } + intern->ar_flags &= ~SPL_ARRAY_CLONE_MASK; + 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)) { + goto outexcept; + } + } + if (*p != ';') { + goto outexcept; + } + ++p; + + /* members */ + if (*p!= 'm' || *++p != ':') { + goto outexcept; + } + ++p; + + ALLOC_INIT_ZVAL(pmembers); + if (!php_var_unserialize(&pmembers, &p, s + buf_len, &var_hash TSRMLS_CC)) { + zval_ptr_dtor(&pmembers); + goto outexcept; + } + + /* copy members */ + zend_hash_copy(intern->std.properties, Z_ARRVAL_P(pmembers), (copy_ctor_func_t) zval_add_ref, (void *) NULL, sizeof(zval *)); + zval_ptr_dtor(&pmembers); + + /* 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_type == IS_UNICODE ? buf.u : (UChar *)buf.s)), buf_len); + return; + +} /* }}} */ + +/* {{{ ZEND_BEGIN_ARG_INFO */ static ZEND_BEGIN_ARG_INFO(arginfo_array___construct, 0) ZEND_ARG_INFO(0, array) @@ -1473,10 +1619,16 @@ ZEND_END_ARG_INFO() static ZEND_BEGIN_ARG_INFO(arginfo_array_uXsort, 0) - ZEND_ARG_INFO(0, cmp_function ) + ZEND_ARG_INFO(0, cmp_function) ZEND_END_ARG_INFO(); -static const zend_function_entry spl_funcs_ArrayObject[] = { +static +ZEND_BEGIN_ARG_INFO(arginfo_array_unserialize, 0) + ZEND_ARG_INFO(0, serialized) +ZEND_END_ARG_INFO(); +/* }}} */ + +static const zend_function_entry spl_funcs_ArrayObject[] = { /* {{{ */ SPL_ME(Array, __construct, arginfo_array___construct, ZEND_ACC_PUBLIC) SPL_ME(Array, offsetExists, arginfo_array_offsetGet, ZEND_ACC_PUBLIC) SPL_ME(Array, offsetGet, arginfo_array_offsetGet, ZEND_ACC_PUBLIC) @@ -1493,15 +1645,17 @@ static const zend_function_entry spl_funcs_ArrayObject[] = { SPL_ME(Array, uksort, arginfo_array_uXsort, ZEND_ACC_PUBLIC) SPL_ME(Array, natsort, NULL, ZEND_ACC_PUBLIC) SPL_ME(Array, natcasesort, NULL, ZEND_ACC_PUBLIC) + SPL_ME(Array, unserialize, arginfo_array_unserialize, ZEND_ACC_PUBLIC) + SPL_ME(Array, serialize, NULL, ZEND_ACC_PUBLIC) /* ArrayObject specific */ SPL_ME(Array, getIterator, NULL, ZEND_ACC_PUBLIC) SPL_ME(Array, exchangeArray, arginfo_array_exchangeArray, ZEND_ACC_PUBLIC) SPL_ME(Array, setIteratorClass, arginfo_array_setIteratorClass, ZEND_ACC_PUBLIC) SPL_ME(Array, getIteratorClass, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} -}; +}; /* }}} */ -static const zend_function_entry spl_funcs_ArrayIterator[] = { +static const zend_function_entry spl_funcs_ArrayIterator[] = { /* {{{ */ SPL_ME(Array, __construct, arginfo_array___construct, ZEND_ACC_PUBLIC) SPL_ME(Array, offsetExists, arginfo_array_offsetGet, ZEND_ACC_PUBLIC) SPL_ME(Array, offsetGet, arginfo_array_offsetGet, ZEND_ACC_PUBLIC) @@ -1518,6 +1672,8 @@ static const zend_function_entry spl_funcs_ArrayIterator[] = { SPL_ME(Array, uksort, arginfo_array_uXsort, ZEND_ACC_PUBLIC) SPL_ME(Array, natsort, NULL, ZEND_ACC_PUBLIC) SPL_ME(Array, natcasesort, NULL, ZEND_ACC_PUBLIC) + SPL_ME(Array, unserialize, arginfo_array_unserialize, ZEND_ACC_PUBLIC) + SPL_ME(Array, serialize, NULL, ZEND_ACC_PUBLIC) /* ArrayIterator specific */ SPL_ME(Array, rewind, NULL, ZEND_ACC_PUBLIC) SPL_ME(Array, current, NULL, ZEND_ACC_PUBLIC) @@ -1526,13 +1682,13 @@ static const zend_function_entry spl_funcs_ArrayIterator[] = { SPL_ME(Array, valid, NULL, ZEND_ACC_PUBLIC) SPL_ME(Array, seek, arginfo_array_seek, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} -}; +}; /* }}} */ -static const zend_function_entry spl_funcs_RecursiveArrayIterator[] = { +static const zend_function_entry spl_funcs_RecursiveArrayIterator[] = { /* {{{ */ SPL_ME(Array, hasChildren, NULL, ZEND_ACC_PUBLIC) SPL_ME(Array, getChildren, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} -}; +}; /* }}} */ /* {{{ PHP_MINIT_FUNCTION(spl_array) */ PHP_MINIT_FUNCTION(spl_array) @@ -1540,6 +1696,7 @@ PHP_MINIT_FUNCTION(spl_array) REGISTER_SPL_STD_CLASS_EX(ArrayObject, spl_array_object_new, spl_funcs_ArrayObject); REGISTER_SPL_IMPLEMENTS(ArrayObject, Aggregate); REGISTER_SPL_IMPLEMENTS(ArrayObject, ArrayAccess); + REGISTER_SPL_IMPLEMENTS(ArrayObject, Serializable); memcpy(&spl_handler_ArrayObject, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); spl_handler_ArrayObject.clone_obj = spl_array_object_clone; @@ -1561,6 +1718,7 @@ PHP_MINIT_FUNCTION(spl_array) REGISTER_SPL_IMPLEMENTS(ArrayIterator, Iterator); REGISTER_SPL_IMPLEMENTS(ArrayIterator, ArrayAccess); REGISTER_SPL_IMPLEMENTS(ArrayIterator, SeekableIterator); + REGISTER_SPL_IMPLEMENTS(ArrayIterator, Serializable); memcpy(&spl_handler_ArrayIterator, &spl_handler_ArrayObject, sizeof(zend_object_handlers)); spl_ce_ArrayIterator->get_iterator = spl_array_get_iterator;