ensure proper default initialization
authorMichael Wallner <mike@php.net>
Wed, 22 Dec 2021 15:50:22 +0000 (16:50 +0100)
committerMichael Wallner <mike@php.net>
Wed, 22 Dec 2021 15:50:22 +0000 (16:50 +0100)
ion.c
ion.stub.php
ion_arginfo.h
ion_private.h
tests/Serializer/PHP.phpt [new file with mode: 0644]
tests/Symbol/Table/Shared.phpt
tests/Unserializer/PHP.phpt [new file with mode: 0644]
tests/serialize/custom.phpt [new file with mode: 0644]

diff --git a/ion.c b/ion.c
index 762ea91da0cef433295df028646753a8628bbd70..f6fb4d367e04abd4f88b132a98556a747aba90ea 100644 (file)
--- a/ion.c
+++ b/ion.c
@@ -630,17 +630,26 @@ static ZEND_METHOD(ion_Reader_Options, __construct)
                Z_PARAM_BOOL(skip_validation)
        ZEND_PARSE_PARAMETERS_END();
 
-       opt->opt.context_change_notifier = EMPTY_READER_CHANGE_NOTIFIER;
        if (opt->cb) {
+               zval zcb;
+               ZVAL_OBJ(&zcb, opt->cb);
+               zend_fcall_info_init(&zcb, 0, &opt->ccn.fci, &opt->ccn.fcc, NULL, NULL);
+               opt->opt.context_change_notifier.context = &opt->ccn;
                update_property_obj(&opt->std, ZEND_STRL("onContextChange"), opt->cb);
+       } else {
+               zend_update_property_null(NULL, &opt->std, ZEND_STRL("onContextChange"));
        }
        if (opt->cat) {
                update_property_obj(&opt->std, ZEND_STRL("catalog"), opt->cat);
                opt->opt.pcatalog = php_ion_obj(catalog, opt->cat)->cat;
+       } else {
+               zend_update_property_null(NULL, &opt->std, ZEND_STRL("catalog"));
        }
        if (opt->dec_ctx) {
                update_property_obj(&opt->std, ZEND_STRL("decimalContext"), opt->dec_ctx);
                opt->opt.decimal_context = &php_ion_obj(decimal_ctx, opt->dec_ctx)->ctx;
+       } else {
+               zend_update_property_null(NULL, &opt->std, ZEND_STRL("decimalContext"));
        }
        zend_update_property_bool(opt->std.ce, &opt->std, ZEND_STRL("returnSystemValues"),
                opt->opt.return_system_values = ret_sys_val);
@@ -648,7 +657,7 @@ static ZEND_METHOD(ion_Reader_Options, __construct)
                opt->opt.new_line_char = ch_nl);
        zend_update_property_long(opt->std.ce, &opt->std, ZEND_STRL("maxContainerDepth"),
                opt->opt.max_container_depth = max_depth);
-       zend_update_property_long(opt->std.ce, &opt->std, ZEND_STRL("maxAnnotationCount"),
+       zend_update_property_long(opt->std.ce, &opt->std, ZEND_STRL("maxAnnotations"),
                opt->opt.max_annotation_count = max_ann);
        zend_update_property_long(opt->std.ce, &opt->std, ZEND_STRL("maxAnnotationBuffered"),
                opt->opt.max_annotation_buffered = max_ann_buf);
@@ -1246,16 +1255,20 @@ static ZEND_METHOD(ion_Writer_Options, __construct)
        if (obj->cat) {
                update_property_obj(&obj->std, ZEND_STRL("catalog"), obj->cat);
                obj->opt.pcatalog = php_ion_obj(catalog, obj->cat)->cat;
+       } else {
+               zend_update_property_null(NULL, &obj->std, ZEND_STRL("catalog"));
        }
        if (obj->dec_ctx) {
                update_property_obj(&obj->std, ZEND_STRL("decimalContext"), obj->dec_ctx);
                obj->opt.decimal_context = &php_ion_obj(decimal_ctx, obj->dec_ctx)->ctx;
+       } else {
+               zend_update_property_null(NULL, &obj->std, ZEND_STRL("decimalContext"));
        }
        zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("outputBinary"),
                        obj->opt.output_as_binary = binary);
-       zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("comactFloats"),
+       zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("compactFloats"),
                        obj->opt.compact_floats = compact_floats);
-       zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("excapeNonAscii"),
+       zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("escapeNonAscii"),
                        obj->opt.escape_all_non_ascii = escape);
        zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("prettyPrint"),
                        obj->opt.pretty_print = pretty);
@@ -1659,7 +1672,7 @@ static ZEND_METHOD(ion_Serializer_PHP, __construct)
 
        obj->serializer.call_magic = true;
 
-       ZEND_PARSE_PARAMETERS_START(0, 3)
+       ZEND_PARSE_PARAMETERS_START(0, 4)
                Z_PARAM_OPTIONAL
                Z_PARAM_OBJ_OF_CLASS_OR_NULL(obj->opt, ce_Writer_Options)
                Z_PARAM_BOOL(obj->serializer.multi_seq)
@@ -1723,7 +1736,7 @@ static ZEND_METHOD(ion_Unserializer_PHP, __construct)
 
        obj->unserializer.call_magic = true;
 
-       ZEND_PARSE_PARAMETERS_START(0, 3)
+       ZEND_PARSE_PARAMETERS_START(0, 4)
                Z_PARAM_OPTIONAL
                Z_PARAM_OBJ_OF_CLASS_OR_NULL(obj->opt, ce_Reader_Options)
                Z_PARAM_BOOL(obj->unserializer.multi_seq)
index 8f4be35681c3e472ba3a877eb0888ec1e44012b2..f317abc2a95618d079cccc40172a0662fe3f3561 100644 (file)
@@ -557,7 +557,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 $multiSequence = false,
         public readonly bool $callMagicUnserialize = true,
         public readonly ?string $callCustomUnserialize = null,
     ){}
index 0f38dcc7d8ad7029971ae537e7a10de5a91c7063..2f02cec872e48a3967ee035fd7a320f17aff3930 100644 (file)
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: c45868fbad8a734e7511f0c4e4991d10b5cc4aa7 */
+ * Stub hash: 7322502dd0c48787538abe7e2fa1894b20a736ba */
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_ion_Symbol_Table_PHP, 0, 0, ion\\Symbol\\Table, 0)
 ZEND_END_ARG_INFO()
@@ -533,7 +533,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, multiSequence, _IS_BOOL, 0, "false")
        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()
index 1bc1c1518dab1c59af7a3f0032a8e28374a193c1..0831df39c032acf5fc1426cbb1bec74ad13d8b67 100644 (file)
@@ -379,18 +379,20 @@ static inline void php_ion_globals_unserializer_dtor(void)
        } \
 } while (0)
 
-#define PTR_CHECK(ptr, ...) do { \
+#define PTR_CHECK_RETURN(ret, ptr, ...) do { \
        if (!(ptr)) { \
                zend_throw_error(NULL, "Uninitialized object"); \
                __VA_ARGS__; \
-               return; \
+               return ret; \
        } \
 } while (0)
+#define PTR_CHECK(ptr, ...) PTR_CHECK_RETURN(, ptr, __VA_ARGS__)
 
-#define OBJ_CHECK(obj, ...) do { \
-       PTR_CHECK(obj, __VA_ARGS__); \
-       PTR_CHECK(*((void **)obj), __VA_ARGS__); \
+#define OBJ_CHECK_RETURN(ret, obj, ...) do { \
+       PTR_CHECK_RETURN(ret, obj, __VA_ARGS__); \
+       PTR_CHECK_RETURN(ret, *((void **)obj), __VA_ARGS__); \
 } while (0)
+#define OBJ_CHECK(obj, ...) OBJ_CHECK_RETURN(, obj, __VA_ARGS__)
 
 static inline ION_STRING *ion_string_from_zend(ION_STRING *is, const zend_string *zs)
 {
@@ -987,12 +989,33 @@ static inline void php_ion_catalog_symbol_table_zval(php_ion_catalog *obj, ION_S
 
 php_ion_decl(catalog, Catalog, php_ion_catalog_dtor(obj));
 
+typedef struct php_ion_reader_options_ccn_ctx {
+       zend_object *obj;
+       zend_fcall_info fci;
+       zend_fcall_info_cache fcc;
+} php_ion_reader_options_ccn_ctx;
+
 typedef struct php_ion_reader_options {
        ION_READER_OPTIONS opt;
+       php_ion_reader_options_ccn_ctx ccn;
        zend_object *cat, *dec_ctx, *cb, std;
 } php_ion_reader_options;
 
-php_ion_decl(reader_options, Reader_Options);
+static inline void php_ion_reader_options_dtor(php_ion_reader_options *obj)
+{
+       if (obj->cb) {
+               zend_fcall_info_args_clear(&obj->ccn.fci, true);
+       }
+}
+
+php_ion_decl(reader_options, Reader_Options, php_ion_reader_options_dtor(obj));
+
+static inline zend_object *php_ion_reader_options_new(void)
+{
+       zend_object *obj = create_ion_Reader_Options(NULL);
+       zend_call_known_instance_method_with_0_params(obj->ce->constructor, obj, NULL);
+       return obj;
+}
 
 typedef struct php_ion_reader {
        ION_READER *reader;
@@ -1039,26 +1062,31 @@ static inline iERR php_ion_reader_stream_handler(struct _ion_user_stream *user)
 
 static inline iERR on_context_change(void *context, ION_COLLECTION *imports)
 {
+       iERR e = IERR_OK;
+
        if (context) {
-               php_ion_reader *obj = php_ion_obj(reader, context);
-               (void) obj;
+               php_ion_reader_options_ccn_ctx *ctx = context;
+
+               zval zobj;
+               ZVAL_OBJ(&zobj, ctx->obj);
+               zend_fcall_info_argn(&ctx->fci, 1, &zobj);
+               if (SUCCESS != zend_fcall_info_call(&ctx->fci, &ctx->fcc, NULL, NULL)) {
+                       e = IERR_INTERNAL_ERROR;
+               }
+               zend_fcall_info_args_clear(&ctx->fci, false);
        }
-       fprintf(stderr, "%s\n", __FUNCTION__);
-       return IERR_OK;
+       return e;
 }
 
-static ION_READER_CONTEXT_CHANGE_NOTIFIER EMPTY_READER_CHANGE_NOTIFIER = {
-       on_context_change,
-       NULL
-};
-
 static inline void php_ion_reader_ctor(php_ion_reader *obj)
 {
        iERR err;
        php_ion_reader_options *opt = php_ion_obj(reader_options, obj->opt);
 
-       if (opt) {
-               opt->opt.context_change_notifier.context = obj;
+       if (opt && opt->opt.context_change_notifier.context) {
+               php_ion_reader_options_ccn_ctx *ctx = opt->opt.context_change_notifier.context;
+               ctx->obj = &obj->std;
+               opt->opt.context_change_notifier.notify = on_context_change;
        }
        if (obj->type == STREAM_READER) {
                PTR_CHECK(obj->stream.ptr);
@@ -1073,9 +1101,6 @@ static inline void php_ion_reader_ctor(php_ion_reader *obj)
                                (BYTE *) obj->buffer->val, obj->buffer->len,
                                opt ? &opt->opt : NULL);
        }
-       if (opt) {
-               opt->opt.context_change_notifier.context = NULL;
-       }
 
        ION_CHECK(err);
        OBJ_CHECK(obj);
@@ -1108,6 +1133,13 @@ typedef struct php_ion_writer_options {
 
 php_ion_decl(writer_options, Writer_Options);
 
+static inline zend_object *php_ion_writer_options_new(void)
+{
+       zend_object *obj = create_ion_Writer_Options(NULL);
+       zend_call_known_instance_method_with_0_params(obj->ce->constructor, obj, NULL);
+       return obj;
+}
+
 typedef struct php_ion_writer {
        ION_WRITER *writer;
        enum {
@@ -1326,6 +1358,8 @@ static inline void php_ion_serializer_php_ctor(php_ion_serializer_php *ser_obj)
        ser_obj->serializer.ids = global_ser->ids;
        ser_obj->serializer.tmp = global_ser->tmp;
 
+       zend_update_property_bool(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("multiSequence"),
+                       ser_obj->serializer.multi_seq);
        zend_update_property_bool(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callMagicSerialize"),
                        ser_obj->serializer.call_magic);
        if (ser_obj->serializer.call_custom) {
@@ -1336,7 +1370,7 @@ static inline void php_ion_serializer_php_ctor(php_ion_serializer_php *ser_obj)
                zend_update_property_null(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callCustomSerialize"));
        }
        if (!ser_obj->opt) {
-               ser_obj->opt = create_ion_Writer_Options(NULL);
+               ser_obj->opt = php_ion_writer_options_new();
        } else {
                GC_ADDREF(ser_obj->opt);
        }
@@ -1706,7 +1740,7 @@ void php_ion_serialize(php_ion_serializer *ser, zval *zv, zval *return_value)
        writer->type = BUFFER_WRITER;
 
        if (ser->options) {
-               zo_opt = writer->opt = create_ion_Writer_Options(NULL);
+               zo_opt = writer->opt = php_ion_writer_options_new();
                php_ion_obj(writer_options, writer->opt)->opt = *ser->options;
        }
 
@@ -1743,17 +1777,19 @@ static inline void php_ion_unserializer_php_ctor(php_ion_unserializer_php *ser_o
        ser_obj->unserializer.tmp = global_ser->tmp;
        ser_obj->unserializer.addref = global_ser->addref;
 
-       zend_update_property_bool(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callMagicSerialize"),
+       zend_update_property_bool(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("multiSequence"),
+                       ser_obj->unserializer.multi_seq);
+       zend_update_property_bool(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callMagicUnserialize"),
                        ser_obj->unserializer.call_magic);
        if (ser_obj->unserializer.call_custom) {
-               zend_update_property_str(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callCustomSerialize"),
+               zend_update_property_str(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callCustomUnserialize"),
                                ser_obj->unserializer.call_custom);
                ser_obj->unserializer.call_custom = zend_string_tolower(ser_obj->unserializer.call_custom);
        } else {
-               zend_update_property_null(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callCustomSerialize"));
+               zend_update_property_null(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callCustomUnserialize"));
        }
        if (!ser_obj->opt) {
-               ser_obj->opt = create_ion_Reader_Options(NULL);
+               ser_obj->opt = php_ion_reader_options_new();
        } else {
                GC_ADDREF(ser_obj->opt);
        }
@@ -2424,7 +2460,7 @@ void php_ion_unserialize(php_ion_unserializer *ser, zval *zdata, zval *return_va
        }
 
        if (ser->options) {
-               zo_opt = reader->opt = create_ion_Reader_Options(NULL);
+               zo_opt = reader->opt = php_ion_reader_options_new();
                php_ion_obj(reader_options, reader->opt)->opt = *ser->options;
        }
 
diff --git a/tests/Serializer/PHP.phpt b/tests/Serializer/PHP.phpt
new file mode 100644 (file)
index 0000000..6fad20b
--- /dev/null
@@ -0,0 +1,64 @@
+--TEST--
+ion\Serializer\PHP
+--EXTENSIONS--
+ion
+--FILE--
+TEST
+<?php
+$o1 = ion\serialize(["foo", ["p" => 1]]);
+$o2 = ion\serialize(["foo", ["p" => 1]], $s1 = new ion\Serializer\PHP);
+$o3 = ion\serialize(["foo", ["p" => 1]], $s2 = new ion\Serializer\PHP(new ion\Writer\Options));
+if ($o1 !== $o2) {
+       var_dump($o1, $o2);
+}
+var_dump($s1);
+if ($s1 != $s2) {
+       var_dump($s2);
+}
+?>
+DONE
+--EXPECTF--
+TEST
+object(ion\Serializer\PHP)#%d (4) {
+  ["writerOptions"]=>
+  object(ion\Writer\Options)#%d (15) {
+    ["catalog"]=>
+    NULL
+    ["decimalContext"]=>
+    NULL
+    ["outputBinary"]=>
+    bool(false)
+    ["compactFloats"]=>
+    bool(false)
+    ["escapeNonAscii"]=>
+    bool(false)
+    ["prettyPrint"]=>
+    bool(false)
+    ["indentTabs"]=>
+    bool(true)
+    ["indentSize"]=>
+    int(2)
+    ["smallContainersInline"]=>
+    bool(true)
+    ["suppressSystemValues"]=>
+    bool(false)
+    ["flushEveryValue"]=>
+    bool(false)
+    ["maxContainerDepth"]=>
+    int(10)
+    ["maxAnnotations"]=>
+    int(10)
+    ["tempBufferSize"]=>
+    int(16384)
+    ["allocationPageSize"]=>
+    int(65536)
+  }
+  ["multiSequence"]=>
+  bool(false)
+  ["callMagicSerialize"]=>
+  bool(true)
+  ["callCustomSerialize"]=>
+  NULL
+}
+DONE
+
index 2d51c48a731c4f810da0a9c46ee735abd91adefc..25df25cd192e5e1b841a89cb4a46bffbd0054f77 100644 (file)
@@ -31,7 +31,9 @@ $u = new ion\Unserializer\PHP(multiSequence: true);
 var_dump($u($buf));
 
 $u = new ion\Unserializer\PHP(multiSequence: true,
-       readerOptions: new ion\Reader\Options(catalog: $c));
+       readerOptions: new ion\Reader\Options(
+               catalog: $c,
+               onContextChange: fn(ion\Reader $r) => print("on_context_change\n")));
 var_dump($u($buf));
 
 ?>
diff --git a/tests/Unserializer/PHP.phpt b/tests/Unserializer/PHP.phpt
new file mode 100644 (file)
index 0000000..3783d1d
--- /dev/null
@@ -0,0 +1,59 @@
+--TEST--
+ion\Unserializer\PHP
+--EXTENSIONS--
+ion
+--FILE--
+TEST
+<?php
+$o1 = ion\unserialize("[foo,{p:1}]");
+$o2 = ion\unserialize("[foo,{p:1}]", $u1 = new ion\Unserializer\PHP);
+$o3 = ion\unserialize("[foo,{p:1}]", $u2 = new ion\Unserializer\PHP(new ion\Reader\Options));
+if ($o1 != $o2) {
+       var_dump($o1, $o2);
+}
+var_dump($u1);
+if ($u1 != $u2) {
+       var_dump($u2);
+}
+?>
+DONE
+--EXPECTF--
+TEST
+object(ion\Unserializer\PHP)#%d (4) {
+  ["readerOptions"]=>
+  object(ion\Reader\Options)#%d (13) {
+    ["catalog"]=>
+    NULL
+    ["decimalContext"]=>
+    NULL
+    ["onContextChange"]=>
+    NULL
+    ["returnSystemValues"]=>
+    bool(false)
+    ["newLine"]=>
+    int(10)
+    ["maxContainerDepth"]=>
+    int(10)
+    ["maxAnnotations"]=>
+    int(10)
+    ["maxAnnotationBuffered"]=>
+    int(512)
+    ["symbolThreshold"]=>
+    int(16384)
+    ["userValueThreshold"]=>
+    int(16384)
+    ["chunkThreshold"]=>
+    int(16384)
+    ["allocationPageSize"]=>
+    int(65536)
+    ["skipCharacterValidation"]=>
+    bool(false)
+  }
+  ["multiSequence"]=>
+  bool(false)
+  ["callMagicUnserialize"]=>
+  bool(true)
+  ["callCustomUnserialize"]=>
+  NULL
+}
+DONE
diff --git a/tests/serialize/custom.phpt b/tests/serialize/custom.phpt
new file mode 100644 (file)
index 0000000..7fdd6f0
--- /dev/null
@@ -0,0 +1,43 @@
+--TEST--
+ion\serialize/custom
+--EXTENSIONS--
+ion
+--FILE--
+TEST
+<?php
+class custom {
+       private $priv;
+       protected $prot;
+       public $pub;
+
+       function serialize() : array {
+               return [
+                       "priv" => 1,
+                       "prot" => 2,
+                       "pub" => 3
+               ];
+       }
+
+       function unserialize(array $data) : void {
+               foreach ($data as $k => $v) {
+                       $this->$k = $v;
+               }
+       }
+}
+$s = ion\serialize(new custom, new ion\Serializer\PHP(callCustomSerialize: "serialize"));
+echo $s,"\n";
+var_dump(ion\unserialize($s, new ion\Unserializer\PHP(callCustomUnserialize: "unserialize")));
+?>
+DONE
+--EXPECTF--
+TEST
+C::custom::{priv:1,prot:2,pub:3}
+object(custom)#%d (3) {
+  ["priv":"custom":private]=>
+  int(1)
+  ["prot":protected]=>
+  int(2)
+  ["pub"]=>
+  int(3)
+}
+DONE