From 790bfba47fd8b204bb4b5d242015dd88c0c31109 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Wed, 15 Dec 2021 13:49:55 +0100 Subject: [PATCH] support multi sequence un/serialization --- ion.c | 7 +++++ ion.stub.php | 2 ++ ion_arginfo.h | 16 ++++++++++- ion_private.h | 75 +++++++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 88 insertions(+), 12 deletions(-) diff --git a/ion.c b/ion.c index b0730c5..4af7609 100644 --- a/ion.c +++ b/ion.c @@ -1311,9 +1311,12 @@ ZEND_METHOD(ion_Serializer_PHP, __construct) php_ion_serializer_php *obj = php_ion_obj(serializer_php, Z_OBJ_P(ZEND_THIS)); PTR_CHECK(obj); + obj->serializer.call_magic = true; + ZEND_PARSE_PARAMETERS_START(0, 3) Z_PARAM_OPTIONAL Z_PARAM_OBJ_OF_CLASS_OR_NULL(obj->opt, ce_Writer_Options) + Z_PARAM_BOOL(obj->serializer.multi_seq) Z_PARAM_BOOL(obj->serializer.call_magic) Z_PARAM_STR_OR_NULL(obj->serializer.call_custom) ZEND_PARSE_PARAMETERS_END(); @@ -1370,10 +1373,14 @@ ZEND_METHOD(ion_Serializer_PHP, serialize) ZEND_METHOD(ion_Unserializer_PHP, __construct) { php_ion_unserializer_php *obj = php_ion_obj(unserializer_php, Z_OBJ_P(ZEND_THIS)); + PTR_CHECK(obj); + + obj->unserializer.call_magic = true; ZEND_PARSE_PARAMETERS_START(0, 3) Z_PARAM_OPTIONAL Z_PARAM_OBJ_OF_CLASS_OR_NULL(obj->opt, ce_Reader_Options) + Z_PARAM_BOOL(obj->unserializer.multi_seq) Z_PARAM_BOOL(obj->unserializer.call_magic) Z_PARAM_STR_OR_NULL(obj->unserializer.call_custom) ZEND_PARSE_PARAMETERS_END(); diff --git a/ion.stub.php b/ion.stub.php index f3f81f3..4e85695 100644 --- a/ion.stub.php +++ b/ion.stub.php @@ -458,6 +458,7 @@ namespace ion\Serializer; class PHP implements \ion\Serializer { public function __construct( public readonly ?\ion\Writer\Options $writerOptions = null, + public readonly bool $multiSequence = false, public readonly bool $callMagicSerialize = true, public readonly ?string $callCustomSerialize = null, ) {} @@ -469,6 +470,7 @@ namespace ion\Unserializer; class PHP implements \ion\Unserializer { public function __construct( public readonly ?\ion\Reader\Options $readerOptions = null, + public readonly bool $multiSequence = true, public readonly bool $callMagicUnserialize = true, public readonly ?string $callCustomUnserialize = null, ){} diff --git a/ion_arginfo.h b/ion_arginfo.h index 4d5bc27..ad2a80e 100644 --- a/ion_arginfo.h +++ b/ion_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 5dc8abb809cd14ed4c542ca5114bd4ceda42d70b */ + * Stub hash: 4c467ccb7b924c30cc5471b9212f4bffe0b0ba31 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ion_serialize, 0, 1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, data, IS_MIXED, 0) @@ -428,6 +428,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ion_Serializer_PHP___construct, 0, 0, 0) ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, writerOptions, ion\\Writer\\Options, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, multiSequence, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, callMagicSerialize, _IS_BOOL, 0, "true") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, callCustomSerialize, IS_STRING, 1, "null") ZEND_END_ARG_INFO() @@ -438,6 +439,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ion_Unserializer_PHP___construct, 0, 0, 0) ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, readerOptions, ion\\Reader\\Options, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, multiSequence, _IS_BOOL, 0, "true") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, callMagicUnserialize, _IS_BOOL, 0, "true") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, callCustomUnserialize, IS_STRING, 1, "null") ZEND_END_ARG_INFO() @@ -1643,6 +1645,12 @@ static zend_class_entry *register_class_ion_Serializer_PHP(zend_class_entry *cla zend_declare_typed_property(class_entry, property_writerOptions_name, &property_writerOptions_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_writerOptions_class_ion_Writer_Options, 0, MAY_BE_NULL)); zend_string_release(property_writerOptions_name); + zval property_multiSequence_default_value; + ZVAL_UNDEF(&property_multiSequence_default_value); + zend_string *property_multiSequence_name = zend_string_init("multiSequence", sizeof("multiSequence") - 1, 1); + zend_declare_typed_property(class_entry, property_multiSequence_name, &property_multiSequence_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_multiSequence_name); + zval property_callMagicSerialize_default_value; ZVAL_UNDEF(&property_callMagicSerialize_default_value); zend_string *property_callMagicSerialize_name = zend_string_init("callMagicSerialize", sizeof("callMagicSerialize") - 1, 1); @@ -1673,6 +1681,12 @@ static zend_class_entry *register_class_ion_Unserializer_PHP(zend_class_entry *c zend_declare_typed_property(class_entry, property_readerOptions_name, &property_readerOptions_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_readerOptions_class_ion_Reader_Options, 0, MAY_BE_NULL)); zend_string_release(property_readerOptions_name); + zval property_multiSequence_default_value; + ZVAL_UNDEF(&property_multiSequence_default_value); + zend_string *property_multiSequence_name = zend_string_init("multiSequence", sizeof("multiSequence") - 1, 1); + zend_declare_typed_property(class_entry, property_multiSequence_name, &property_multiSequence_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_multiSequence_name); + zval property_callMagicUnserialize_default_value; ZVAL_UNDEF(&property_callMagicUnserialize_default_value); zend_string *property_callMagicUnserialize_name = zend_string_init("callMagicUnserialize", sizeof("callMagicUnserialize") - 1, 1); diff --git a/ion_private.h b/ion_private.h index 6aa8932..84b50b9 100644 --- a/ion_private.h +++ b/ion_private.h @@ -22,6 +22,7 @@ typedef struct php_ion_serializer { zend_string *call_custom; zend_bool call_magic; + zend_bool multi_seq; uint32_t level; HashTable *ids; @@ -44,6 +45,7 @@ typedef struct php_ion_unserializer { zend_string *call_custom; zend_bool call_magic; + zend_bool multi_seq; uint32_t level; HashTable *ids; @@ -1234,6 +1236,41 @@ static inline void php_ion_serialize_zval(php_ion_serializer *ser, zval *zv) php_ion_decl(serializer_php, Serializer_PHP, php_ion_serializer_php_dtor(obj)); +static inline void php_ion_serialize_ex(php_ion_serializer *ser, zval *zv) +{ + HashPosition pos; + HashTable *arr = NULL; + + if (ser->multi_seq) { + if (Z_TYPE_P(zv) != IS_ARRAY || !zend_array_is_list(Z_ARRVAL_P(zv))) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, IERR_INVALID_ARG, + "Expected a packed, consecutively numerically indexed array as argument to the multi sequence serializer"); + return; + } + + arr = Z_ARRVAL_P(zv); + + zend_hash_internal_pointer_reset_ex(arr, &pos); + zv = zend_hash_get_current_data_ex(arr, &pos); + } + + while (zv) { + /* start off with a global PHP annotation instead of repeating it all over the place */ + if (0 == php_ion_globals_serializer_step()) { + ION_STRING is; + ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, ZEND_STRL("PHP")))); + } + php_ion_serialize_zval(ser, zv); + php_ion_globals_serializer_exit(); + + if (!ser->multi_seq) { + break; + } + zend_hash_move_forward_ex(arr, &pos); + zv = zend_hash_get_current_data_ex(arr, &pos); + } +} + void php_ion_serialize(php_ion_serializer *ser, zval *zv, zval *return_value) { zend_object *zo_opt = NULL, *zo_ser = NULL; @@ -1261,14 +1298,9 @@ void php_ion_serialize(php_ion_serializer *ser, zval *zv, zval *return_value) ser->writer = writer->writer; ser->buffer = &writer->buffer.str; - /* start off with a global PHP annotation instead of repeating it all over the place */ - if (0 == php_ion_globals_serializer_step()) { - ION_STRING is; - ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, ZEND_STRL("PHP"))), - if (zo_ser) OBJ_RELEASE(zo_ser)); + if (!EG(exception)) { + php_ion_serialize_ex(ser, zv); } - php_ion_serialize_zval(ser, zv); - php_ion_globals_serializer_exit(); /* make sure to flush when done, else str.s might not contain everything until the writer is closed */ ion_writer_flush(ser->writer, NULL); @@ -1886,6 +1918,28 @@ unserialize_struct: ; php_ion_decl(unserializer_php, Unserializer_PHP, php_ion_unserializer_php_dtor(obj)); +static inline void php_ion_unserialize_ex(php_ion_unserializer *ser, zval *return_value) +{ + if (ser->multi_seq) { + array_init(return_value); + } + + do { + zval tmp; + ZVAL_NULL(&tmp); + php_ion_globals_unserializer_step(); + php_ion_unserialize_zval(ser, &tmp, NULL); + php_ion_globals_unserializer_exit(); + ION_CATCH(zval_ptr_dtor(&tmp)); + + if (!ser->multi_seq) { + RETURN_COPY_VALUE(&tmp); + } else if (ser->type != tid_EOF) { + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp); + } + } while (ser->type != tid_EOF); +} + void php_ion_unserialize(php_ion_unserializer *ser, zval *zdata, zval *return_value) { zend_object *zo_opt = NULL, *zo_ser = NULL; @@ -1930,11 +1984,10 @@ void php_ion_unserialize(php_ion_unserializer *ser, zval *zdata, zval *return_va php_ion_reader_ctor(reader); ser->reader = reader->reader; - ION_CATCH(); - php_ion_globals_unserializer_step(); - php_ion_unserialize_zval(ser, return_value, NULL); - php_ion_globals_unserializer_exit(); + if (!EG(exception)) { + php_ion_unserialize_ex(ser, return_value); + } OBJ_RELEASE(zo_reader); if (zo_opt) { -- 2.30.2