improve object un/serialization
[awesomized/ext-ion] / ion_private.h
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: ion |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2021, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #include "ionc/ion.h"
14
15 #include "php.h"
16 #include "ext/date/php_date.h"
17
18 typedef struct php_ion_serializer {
19 ION_WRITER *writer;
20 ION_WRITER_OPTIONS *options;
21 smart_str *buffer;
22
23 zend_string *call_custom;
24 zend_bool call_magic;
25
26 uint32_t level;
27 HashTable *ids;
28 HashTable *tmp;
29 } php_ion_serializer;
30
31 typedef struct php_ion_annotaions {
32 uint8_t backref:1;
33 uint8_t makeref:1;
34 uint8_t object_prop:1;
35 uint8_t object_type;
36 zend_string *object_class;
37 zend_string *property_class;
38 } php_ion_annotations;
39
40 typedef struct php_ion_unserializer {
41 ION_READER *reader;
42 ION_READER_OPTIONS *options;
43 ION_TYPE type;
44
45 zend_string *call_custom;
46 zend_bool call_magic;
47
48 uint32_t level;
49 HashTable *ids;
50 HashTable *tmp;
51 HashTable *addref;
52
53 php_ion_annotations annotations;
54 } php_ion_unserializer;
55
56 ZEND_BEGIN_MODULE_GLOBALS(ion)
57
58 php_ion_serializer serializer;
59 php_ion_unserializer unserializer;
60
61 struct {
62 HashTable serializer[2];
63 HashTable unserializer[3];
64 } _ht;
65
66 ZEND_END_MODULE_GLOBALS(ion);
67
68 #ifdef ZTS
69 # define php_ion_globals (*((zend_ion_globals *) (*((void ***) tsrm_get_ls_cache()))[TSRM_UNSHUFFLE_RSRC_ID(ion_globals_id)]))
70 #else
71 # define php_ion_globals ion_globals
72 #endif
73
74 ZEND_DECLARE_MODULE_GLOBALS(ion);
75
76 static inline void php_ion_globals_serializer_init(void)
77 {
78 php_ion_serializer *s = &php_ion_globals.serializer;
79 HashTable *h = php_ion_globals._ht.serializer;
80
81 zend_hash_init(s->tmp = &h[0], 0, NULL, ZVAL_PTR_DTOR, 0);
82 zend_hash_init(s->ids = &h[1], 0, NULL, NULL, 0);
83 }
84
85 static inline uint32_t php_ion_globals_serializer_step(void)
86 {
87 php_ion_serializer *s = &php_ion_globals.serializer;
88 uint32_t level;
89
90 if (!(level = s->level++)) {
91 zend_hash_clean(s->ids);
92 zend_hash_clean(s->tmp);
93 }
94 return level;
95 }
96
97 static inline uint32_t php_ion_globals_serializer_exit(void)
98 {
99 php_ion_serializer *s = &php_ion_globals.serializer;
100
101 ZEND_ASSERT(s->level);
102 if (!--s->level) {
103 zend_hash_clean(s->ids);
104 zend_hash_clean(s->tmp);
105 }
106 return s->level;
107 }
108
109 static inline void php_ion_globals_serializer_dtor(void)
110 {
111 php_ion_serializer *s = &php_ion_globals.serializer;
112
113 zend_hash_destroy(s->tmp);
114 zend_hash_destroy(s->ids);
115 }
116
117 void ZVAL_ADDREF(zval *zv)
118 {
119 if (Z_ISREF_P(zv)) {
120 Z_TRY_ADDREF_P(Z_REFVAL_P(zv));
121 } else {
122 Z_TRY_ADDREF_P(zv);
123 }
124 }
125 static inline void php_ion_globals_unserializer_init(void)
126 {
127 php_ion_unserializer *s = &php_ion_globals.unserializer;
128 HashTable *h = php_ion_globals._ht.unserializer;
129
130 zend_hash_init(s->tmp = &h[0], 0, NULL, ZVAL_PTR_DTOR, 0);
131 zend_hash_init(s->ids = &h[1], 0, NULL, NULL, 0);
132 zend_hash_init(s->addref = &h[2], 0, NULL, ZVAL_ADDREF, 0);
133 }
134
135 static inline void php_ion_globals_unserializer_step(void)
136 {
137 php_ion_unserializer *s = &php_ion_globals.unserializer;
138
139 if (!s->level++) {
140 zend_hash_clean(s->addref);
141 zend_hash_clean(s->ids);
142 zend_hash_clean(s->tmp);
143 }
144 }
145
146 static inline void php_ion_globals_unserializer_exit(void)
147 {
148 php_ion_unserializer *s = &php_ion_globals.unserializer;
149
150 ZEND_ASSERT(s->level);
151 if (!--s->level) {
152 zend_hash_clean(s->addref);
153 zend_hash_clean(s->ids);
154 zend_hash_clean(s->tmp);
155 }
156 }
157
158 static inline void php_ion_globals_unserializer_dtor(void)
159 {
160 php_ion_unserializer *s = &php_ion_globals.unserializer;
161
162 zend_hash_destroy(s->addref);
163 zend_hash_destroy(s->ids);
164 zend_hash_destroy(s->tmp);
165 }
166
167 static zend_class_entry
168 *ce_Annotation,
169 *ce_Catalog,
170 *ce_Collection,
171 *ce_Decimal,
172 *ce_Decimal_Context,
173 *ce_Reader,
174 *ce_Reader_Options,
175 *ce_Reader_Reader,
176 *ce_Reader_Buffer,
177 *ce_Reader_Stream,
178 *ce_Reader_Buffer_Reader,
179 *ce_Reader_Stream_Reader,
180 *ce_Serializer,
181 *ce_Serializer_PHP,
182 *ce_Symbol,
183 *ce_Symbol_ImportLocation,
184 *ce_Symbol_System,
185 *ce_Symbol_System_SID,
186 *ce_Symbol_Table,
187 *ce_Timestamp,
188 *ce_Timestamp_Precision,
189 *ce_Type,
190 *ce_Unserializer,
191 *ce_Unserializer_PHP,
192 *ce_Writer,
193 *ce_Writer_Options,
194 *ce_Writer_Buffer,
195 *ce_Writer_Buffer_Writer,
196 *ce_Writer_Stream,
197 *ce_Writer_Stream_Writer,
198 *ce_Writer_Writer
199 ;
200
201 #define php_ion_decl(type, cname, ...) \
202 static zend_object_handlers oh_ ## cname; \
203 static inline zend_object *create_ion_ ## cname(zend_class_entry *ce) \
204 { \
205 if (!ce) ce = ce_ ## cname; \
206 php_ion_ ## type *o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce)); \
207 zend_object_std_init(&o->std, ce); \
208 object_properties_init(&o->std, ce); \
209 o->std.handlers = &oh_ ## cname; \
210 return &o->std; \
211 } \
212 static inline void free_ion_ ## cname(zend_object *std) \
213 { \
214 php_ion_ ## type *obj = php_ion_obj(type, std); \
215 __VA_ARGS__; \
216 zend_object_std_dtor(std); \
217 (void) obj; \
218 }
219 #define php_ion_register(type, cname, ...) do { \
220 ce_ ## cname = register_class_ion_ ## cname(__VA_ARGS__); \
221 ce_ ## cname ->create_object = create_ion_ ## cname; \
222 memcpy(&oh_ ## cname, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); \
223 oh_ ## cname .offset = offsetof(php_ion_ ## type, std); \
224 oh_ ## cname .free_obj = free_ion_ ## cname; \
225 } while (0)
226
227 #define php_ion_obj(type, obj) \
228 ((php_ion_ ## type *) (obj ? ((char *)(obj) - XtOffsetOf(php_ion_ ## type, std)) : NULL))
229
230 #define ION_CHECK(err, ...) do { \
231 iERR __err = err; \
232 if (__err) { \
233 zend_throw_exception_ex(spl_ce_RuntimeException, __err, "%s: %s", ion_error_to_str(__err), #err); \
234 __VA_ARGS__; \
235 return; \
236 } \
237 } while (0)
238
239 #define ION_CATCH(...) do { \
240 if (EG(exception)) { \
241 __VA_ARGS__; \
242 return; \
243 } \
244 } while (0)
245
246 #define PTR_CHECK(ptr, ...) do { \
247 if (!(ptr)) { \
248 zend_throw_error(NULL, "Uninitialized object"); \
249 __VA_ARGS__; \
250 return; \
251 } \
252 } while (0)
253
254 #define OBJ_CHECK(obj) do { \
255 PTR_CHECK(obj); \
256 PTR_CHECK(*((void **)obj)); \
257 } while (0)
258
259 static inline ION_STRING *ion_string_from_cstr(ION_STRING *is, const char *s, size_t l)
260 {
261 is->length = l;
262 is->value = (BYTE *) s;
263 return is;
264 }
265
266 static inline ION_STRING *ion_string_from_zend(ION_STRING *is, const zend_string *zs)
267 {
268 is->length = zs ? zs->len : 0;
269 is->value = (BYTE *) (zs ? zs->val : NULL);
270 return is;
271 }
272
273 static inline zend_string *zend_string_from_ion(const ION_STRING *s)
274 {
275 return zend_string_init((const char *) s->value, s->length, 0);
276 }
277
278 static inline void update_property_obj(zend_object *obj, const char *n, size_t l, zend_object *p)
279 {
280 zval zobj;
281 ZVAL_OBJ(&zobj, p);
282 zend_update_property(obj->ce, obj, n, l, &zobj);
283 }
284
285 typedef struct php_ion_type {
286 ION_TYPE typ;
287 zend_object std;
288 } php_ion_type;
289
290 php_ion_decl(type, Type);
291
292 #define RETURN_IONTYPE(typ) do { \
293 zend_object *__zo = php_ion_type_fetch(typ); \
294 if (UNEXPECTED(!__zo)) { \
295 RETURN_THROWS(); \
296 } \
297 RETURN_OBJ_COPY(__zo); \
298 } while(0)
299
300 static inline zend_object *php_ion_type_fetch(ION_TYPE typ)
301 {
302 zend_long index = ION_TYPE_INT(typ);
303 zval *ztype = zend_hash_index_find(ce_Type->backed_enum_table, index);
304
305 if (UNEXPECTED(!ztype || Z_TYPE_P(ztype) != IS_STRING)) {
306 zend_value_error(ZEND_LONG_FMT " is not a valid backing value for enum \"%s\"", index, ZSTR_VAL(ce_Type->name));
307 return NULL;
308 }
309 return zend_enum_get_case(ce_Type, Z_STR_P(ztype));
310 }
311
312 typedef struct php_ion_symbol_iloc {
313 ION_SYMBOL_IMPORT_LOCATION loc;
314 zend_string *name;
315 zend_object std;
316 } php_ion_symbol_iloc;
317
318 static inline void php_ion_symbol_iloc_ctor(php_ion_symbol_iloc *obj)
319 {
320 zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("location"), obj->loc.location);
321 zend_update_property_str(obj->std.ce, &obj->std, ZEND_STRL("name"), obj->name);
322 ion_string_from_zend(&obj->loc.name, obj->name);
323 }
324
325 static inline void php_ion_symbol_iloc_dtor(php_ion_symbol_iloc *obj)
326 {
327 zend_string_release(obj->name);
328 }
329
330 php_ion_decl(symbol_iloc, Symbol_ImportLocation, php_ion_symbol_iloc_dtor(obj));
331
332 typedef struct php_ion_symbol {
333 ION_SYMBOL sym;
334 zend_string *value;
335 zend_object *iloc, std;
336 } php_ion_symbol;
337
338 static inline void php_ion_symbol_ctor(php_ion_symbol *obj)
339 {
340 zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("sid"),
341 obj->sym.sid);
342 if (obj->value) {
343 zend_update_property_str(obj->std.ce, &obj->std, ZEND_STRL("value"), obj->value);
344 } else{
345 zend_update_property_null(obj->std.ce, &obj->std, ZEND_STRL("value"));
346 }
347 ion_string_from_zend(&obj->sym.value, obj->value);
348 if (obj->iloc) {
349 update_property_obj(&obj->std, ZEND_STRL("importLocation"), obj->iloc);
350 obj->sym.import_location = php_ion_obj(symbol_iloc, obj->iloc)->loc;
351 } else {
352 zend_update_property_null(obj->std.ce, &obj->std, ZEND_STRL("importLocation"));
353 }
354 }
355
356 static inline void php_ion_symbol_dtor(php_ion_symbol *obj)
357 {
358 if (obj->value) {
359 zend_string_release(obj->value);
360 }
361 }
362
363 static inline void php_ion_symbol_zval(ION_SYMBOL *sym_ptr, zval *return_value)
364 {
365 object_init_ex(return_value, ce_Symbol);
366 php_ion_symbol *sym = php_ion_obj(symbol, Z_OBJ_P(return_value));
367
368 sym->sym.sid = sym_ptr->sid;
369 sym->value = zend_string_from_ion(&sym_ptr->value);
370 if (!ION_SYMBOL_IMPORT_LOCATION_IS_NULL(sym_ptr)) {
371 zval ziloc;
372 object_init_ex(&ziloc, ce_Symbol_ImportLocation);
373 sym->iloc = Z_OBJ(ziloc);
374
375 php_ion_symbol_iloc *iloc = php_ion_obj(symbol_iloc, sym->iloc);
376 iloc->loc.location = sym_ptr->import_location.location;
377 iloc->name = zend_string_from_ion(&sym_ptr->import_location.name);
378
379 php_ion_symbol_iloc_ctor(iloc);
380 }
381
382 php_ion_symbol_ctor(sym);
383 }
384
385 php_ion_decl(symbol, Symbol, php_ion_symbol_dtor(obj));
386
387 typedef struct php_ion_symbol_table {
388 zend_object std;
389 } php_ion_symbol_table;
390
391 php_ion_decl(symbol_table, Symbol_Table);
392
393 typedef struct php_ion_decimal_ctx {
394 decContext ctx;
395 zend_object std;
396 } php_ion_decimal_ctx;
397
398 static inline void php_ion_decimal_ctx_ctor(php_ion_decimal_ctx *obj) {
399 zval tmp, *zbits = zend_read_property(obj->std.ce, &obj->std, ZEND_STRL("bits"), 1, &tmp);
400
401 int bits = 128;
402 if (zbits != &EG(uninitialized_zval)) {
403 bits = Z_LVAL_P(zbits);
404 } else {
405 zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("bits"), bits);
406 }
407 switch (bits) {
408 case 32:
409 case 64:
410 case 128:
411 decContextDefault(&obj->ctx, bits);
412 break;
413 default:
414 zend_throw_exception_ex(spl_ce_InvalidArgumentException, IERR_INVALID_ARG,
415 "Decimal context only allows 32, 64 or 128 bits");
416 }
417 }
418
419 php_ion_decl(decimal_ctx, Decimal_Context);
420
421 typedef struct php_ion_decimal {
422 ION_DECIMAL dec;
423 zend_object *ctx, std;
424 } php_ion_decimal;
425
426 static inline zend_string *php_ion_decimal_to_string(ION_DECIMAL *dec)
427 {
428 zend_string *zstr = zend_string_alloc(ION_DECIMAL_STRLEN(dec), 0);
429 (void) ion_decimal_to_string(dec, zstr->val);
430 return zend_string_truncate(zstr, strlen(zstr->val), 0);
431 }
432
433 static inline void php_ion_decimal_to_int(ION_DECIMAL *dec, decContext *ctx, zend_long *l)
434 {
435 ION_INT *ii = NULL;
436 ION_CHECK(ion_int_alloc(NULL, &ii));
437 ION_CHECK(ion_decimal_to_ion_int(dec, ctx, ii), ion_int_free(ii));
438 int64_t i64;
439 ION_CHECK(ion_int_to_int64(ii, &i64), ion_int_free(ii));
440 *l = i64;
441 ion_int_free(ii);
442 }
443
444 static inline void php_ion_decimal_ctor(php_ion_decimal *obj)
445 {
446 if (!obj->ctx) {
447 zval zdc;
448 object_init_ex(&zdc, ce_Decimal_Context);
449 obj->ctx = Z_OBJ(zdc);
450 php_ion_decimal_ctx_ctor(php_ion_obj(decimal_ctx, obj->ctx));
451 }
452 update_property_obj(&obj->std, ZEND_STRL("context"), obj->ctx);
453
454 if (ion_decimal_is_integer(&obj->dec)) {
455 zend_long l;
456 php_ion_decimal_to_int(&obj->dec, &php_ion_obj(decimal_ctx, obj->ctx)->ctx, &l);
457 zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("number"), l);
458 } else {
459 zend_string *zstr = php_ion_decimal_to_string(&obj->dec);
460 zend_update_property_str(obj->std.ce, &obj->std, ZEND_STRL("number"), zstr);
461 zend_string_release(zstr);
462 }
463 }
464
465 static inline void php_ion_decimal_dtor(php_ion_decimal *obj)
466 {
467 ion_decimal_free(&obj->dec);
468 }
469
470 php_ion_decl(decimal, Decimal, php_ion_decimal_dtor(obj));
471
472 typedef php_date_obj php_ion_timestamp;
473
474 static inline zend_long php_usec_from_ion(const decQuad *frac, decContext *ctx)
475 {
476 decQuad microsecs, result;
477 decQuadMultiply(&result, decQuadFromInt32(&microsecs, 1000000), frac, ctx);
478 return (zend_long) decQuadToUInt32(&result, ctx, DEC_ROUND_HALF_EVEN);
479 }
480
481 static inline decQuad *ion_ts_frac_from_usec(decQuad *frac, zend_long usec, decContext *ctx)
482 {
483 decQuad microsecs, us;
484 return decQuadDivide(frac, decQuadFromInt32(&us, usec), decQuadFromInt32(&microsecs, 1000000), ctx);
485 }
486
487 static inline zend_string *php_dt_format_from_precision(uint8_t precision)
488 {
489 switch (precision) {
490 case ION_TS_FRAC:
491 return zend_string_init(ZEND_STRL("c"), 0);
492 case ION_TS_SEC:
493 return zend_string_init(ZEND_STRL("Y-m-d\\TH:i:sP"), 0);
494 case ION_TS_MIN:
495 return zend_string_init(ZEND_STRL("Y-m-d\\TH:iP"), 0);
496 case ION_TS_DAY:
497 return zend_string_init(ZEND_STRL("Y-m-d\\T"), 0);
498 case ION_TS_MONTH:
499 return zend_string_init(ZEND_STRL("Y-m\\T"), 0);
500 case ION_TS_YEAR:
501 return zend_string_init(ZEND_STRL("Y\\T"), 0);
502 default:
503 return zend_string_init(ZEND_STRL("c"), 0);
504 }
505 }
506
507 static inline timelib_time* php_time_from_ion(const ION_TIMESTAMP *ts, decContext *ctx, zend_string **fmt)
508 {
509 timelib_time *time = timelib_time_ctor();
510
511 switch (ts->precision) {
512 case ION_TS_FRAC:
513 time->us = php_usec_from_ion(&ts->fraction, ctx);
514 /* fallthrough */
515 case ION_TS_SEC:
516 time->s = ts->seconds;
517 /* fallthrough */
518 case ION_TS_MIN:
519 time->i = ts->minutes;
520 time->h = ts->hours;
521 /* fallthrough */
522 case ION_TS_DAY:
523 time->d = ts->day;
524 /* fallthrough */
525 case ION_TS_MONTH:
526 time->m = ts->month;
527 /* fallthrough */
528 case ION_TS_YEAR:
529 time->y = ts->year;
530 /* fallthrough */
531 default:
532 time->z = ts->tz_offset * 60;
533 }
534
535 if (fmt) {
536 *fmt = php_dt_format_from_precision(ts->precision);
537 }
538 return time;
539 }
540
541 static inline ION_TIMESTAMP *ion_timestamp_from_php(ION_TIMESTAMP *buf, php_ion_timestamp *ts, decContext *ctx)
542 {
543 memset(buf, 0, sizeof(*buf));
544
545 zval tmp;
546 uint8_t precision = Z_LVAL_P(zend_read_property(ts->std.ce, &ts->std, ZEND_STRL("precision"), 0, &tmp));
547
548 if (!precision || precision > ION_TS_FRAC) {
549 zend_throw_exception_ex(spl_ce_InvalidArgumentException, IERR_INVALID_ARG,
550 "Invalid precision (%u) of ion\\Timestamp", (unsigned) precision);
551 } else switch ((buf->precision = precision)) {
552 case ION_TS_FRAC:
553 ion_ts_frac_from_usec(&buf->fraction, ts->time->us, ctx);
554 /* fallthrough */
555 case ION_TS_SEC:
556 buf->seconds = ts->time->s;
557 /* fallthrough */
558 case ION_TS_MIN:
559 buf->minutes = ts->time->i;
560 /* fallthrough */
561 case ION_TS_DAY:
562 buf->hours = ts->time->h;
563 buf->day = ts->time->d;
564 /* fallthrough */
565 case ION_TS_MONTH:
566 buf->month = ts->time->m;
567 /* fallthrough */
568 case ION_TS_YEAR:
569 buf->year = ts->time->y;
570 /* fallthrough */
571 default:
572 buf->tz_offset = ts->time->z / 60;
573 }
574
575 return buf;
576 }
577
578 static inline void php_ion_timestamp_ctor(php_ion_timestamp *obj, zend_long precision, zend_string *fmt, zend_string *dt, zval *tz)
579 {
580 if (!obj->time) {
581 php_date_initialize(obj, dt ? dt->val : "", dt ? dt->len : 0, fmt ? fmt->val : NULL, tz, PHP_DATE_INIT_CTOR);
582 }
583 zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("precision"), precision);
584
585 fmt = php_dt_format_from_precision(precision);
586 zend_update_property_str(obj->std.ce, &obj->std, ZEND_STRL("format"), fmt);
587 zend_string_release(fmt);
588 }
589
590 static inline void php_ion_timestamp_dtor(php_ion_timestamp *obj)
591 {
592 if (obj->time) {
593 timelib_time_dtor(obj->time);
594 }
595 }
596
597 php_ion_decl(timestamp, Timestamp, php_ion_timestamp_dtor(obj));
598
599 typedef struct php_ion_catalog {
600 ION_CATALOG *cat;
601 zend_object std;
602 } php_ion_catalog;
603
604 php_ion_decl(catalog, Catalog);
605
606 typedef struct php_ion_reader_options {
607 ION_READER_OPTIONS opt;
608 zend_object *cat, *dec_ctx, *cb, std;
609 } php_ion_reader_options;
610
611 php_ion_decl(reader_options, Reader_Options);
612
613 typedef struct php_ion_reader {
614 ION_READER *reader;
615 ION_TYPE state;
616 enum {
617 BUFFER_READER,
618 STREAM_READER,
619 } type;
620 union {
621 zend_string *buffer;
622 struct {
623 php_stream *ptr;
624 ION_STRING buf;
625 } stream;
626 };
627 zend_object *opt, std;
628 } php_ion_reader;
629
630 static inline iERR php_ion_reader_stream_handler(struct _ion_user_stream *user)
631 {
632 php_ion_reader *reader = (php_ion_reader *) user->handler_state;
633 size_t remaining = 0, spare = reader->stream.buf.length;
634
635 if (user->curr && user->limit && (remaining = user->limit - user->curr)) {
636 memmove(reader->stream.buf.value, user->curr, remaining);
637 user->limit -= remaining;
638 spare -= remaining;
639 } else {
640 user->curr = user->limit = reader->stream.buf.value;
641 }
642
643 ssize_t read = php_stream_read(reader->stream.ptr, (char *) user->limit, spare);
644 if (EXPECTED(read > 0)) {
645 user->limit += read;
646 return IERR_OK;
647 }
648
649 if (EXPECTED(read == 0)) {
650 return IERR_EOF;
651 }
652
653 return IERR_READ_ERROR;
654 }
655
656 static inline iERR on_context_change(void *context, ION_COLLECTION *imports)
657 {
658 if (context) {
659 php_ion_reader *obj = php_ion_obj(reader, context);
660 (void) obj;
661 }
662 return IERR_OK;
663 }
664
665 static ION_READER_CONTEXT_CHANGE_NOTIFIER EMPTY_READER_CHANGE_NOTIFIER = {
666 on_context_change,
667 NULL
668 };
669
670 static inline void php_ion_reader_ctor(php_ion_reader *obj)
671 {
672 iERR err;
673 php_ion_reader_options *opt = php_ion_obj(reader_options, obj->opt);
674
675 if (opt) {
676 opt->opt.context_change_notifier.context = obj;
677 }
678 if (obj->type == STREAM_READER) {
679 PTR_CHECK(obj->stream.ptr);
680 GC_ADDREF(obj->stream.ptr->res);
681
682 php_ion_reader_options *opt = php_ion_obj(reader_options, obj->opt);
683 obj->stream.buf.length = opt ? opt->opt.allocation_page_size : 0x1000;
684 obj->stream.buf.value = emalloc(obj->stream.buf.length);
685 err = ion_reader_open_stream(&obj->reader, obj, php_ion_reader_stream_handler, opt ? &opt->opt : NULL);
686
687 } else {
688 err = ion_reader_open_buffer(&obj->reader,
689 (BYTE *) obj->buffer->val, obj->buffer->len,
690 opt ? &opt->opt : NULL);
691 }
692 if (opt) {
693 opt->opt.context_change_notifier.context = NULL;
694 }
695
696 ION_CHECK(err);
697 OBJ_CHECK(obj);
698 }
699 static inline void php_ion_reader_dtor(php_ion_reader *obj)
700 {
701 if (obj->reader) {
702 ion_reader_close(obj->reader);
703 }
704 if (obj->type == STREAM_READER) {
705 if (obj->stream.buf.value) {
706 efree(obj->stream.buf.value);
707 }
708 if (obj->stream.ptr) {
709 zend_list_delete(obj->stream.ptr->res);
710 }
711 } else {
712 if (obj->buffer) {
713 zend_string_release(obj->buffer);
714 }
715 }
716 }
717
718 php_ion_decl(reader, Reader_Reader, php_ion_reader_dtor(obj));
719
720 typedef struct php_ion_writer_options {
721 ION_WRITER_OPTIONS opt;
722 zend_object *cat, *dec_ctx, *col, std;
723 } php_ion_writer_options;
724
725 php_ion_decl(writer_options, Writer_Options);
726
727 typedef struct php_ion_writer {
728 ION_WRITER *writer;
729 enum {
730 BUFFER_WRITER,
731 STREAM_WRITER,
732 } type;
733 union {
734 struct {
735 zval val;
736 smart_str str;
737 } buffer;
738 struct {
739 ION_STRING buf;
740 php_stream *ptr;
741 } stream;
742 };
743 zend_object *opt, std;
744
745 } php_ion_writer;
746
747 static inline iERR php_ion_writer_stream_handler(struct _ion_user_stream *user)
748 {
749 php_ion_writer *writer = (php_ion_writer *) user->handler_state;
750
751 if (EXPECTED(user->limit && user->curr)) {
752 ptrdiff_t len = user->curr - writer->stream.buf.value;
753 if (len != php_stream_write(writer->stream.ptr, (char *) writer->stream.buf.value, len)) {
754 return IERR_WRITE_ERROR;
755 }
756 }
757 user->curr = writer->stream.buf.value;
758 user->limit = writer->stream.buf.value + writer->stream.buf.length;
759 return IERR_OK;
760 }
761
762 #define REF_STR() do { \
763 ZVAL_NEW_STR(ref, obj->buffer.str.s); \
764 GC_ADDREF(obj->buffer.str.s); \
765 } while (0)
766
767 #define NEW_REF_STR() do {\
768 if (Z_STR_P(ref) != obj->buffer.str.s) { \
769 zval_ptr_dtor(ref); \
770 REF_STR(); \
771 } \
772 } while(0)
773
774 static inline void php_ion_writer_stream_init(php_ion_writer *obj, php_ion_writer_options *opt)
775 {
776 PTR_CHECK(obj->stream.ptr);
777 GC_ADDREF(obj->stream.ptr->res);
778
779 obj->stream.buf.length = opt ? opt->opt.allocation_page_size : 0x1000;
780 obj->stream.buf.value = emalloc(obj->stream.buf.length);
781 }
782 static inline void php_ion_writer_buffer_init(php_ion_writer *obj)
783 {
784 zval *ref = &obj->buffer.val;
785 ZVAL_DEREF(ref);
786
787 smart_str_alloc(&obj->buffer.str, 0, 0);
788 smart_str_0(&obj->buffer.str);
789 REF_STR();
790 }
791
792 static inline void php_ion_writer_buffer_grow(php_ion_writer *obj)
793 {
794 zval *ref = &obj->buffer.val;
795 ZVAL_DEREF(ref);
796
797 switch (GC_REFCOUNT(obj->buffer.str.s)) {
798 case 2:
799 // nothing to do
800 break;
801 case 1:
802 // we've been separated
803 GC_ADDREF(obj->buffer.str.s);
804 break;
805 default:
806 // we have to separate
807 fprintf(stderr, "SEPARATE\n");
808 obj->buffer.str.s = zend_string_dup(obj->buffer.str.s, 0);
809 break;
810 }
811
812 zend_string *old = obj->buffer.str.s;
813 GC_DELREF(old);
814 smart_str_erealloc(&obj->buffer.str, obj->buffer.str.a << 1);
815 if (old == obj->buffer.str.s) {
816 GC_ADDREF(old);
817 } else if(old == Z_STR_P(ref)) {
818 ZVAL_NULL(ref);
819 }
820
821 NEW_REF_STR();
822 }
823
824 static inline iERR php_ion_writer_buffer_handler(struct _ion_user_stream *user)
825 {
826 php_ion_writer *writer = (php_ion_writer *) user->handler_state;
827
828 if (user->curr) {
829 writer->buffer.str.s->len += user->curr - (BYTE *) &writer->buffer.str.s->val[writer->buffer.str.s->len];
830 smart_str_0(&writer->buffer.str);
831 if (user->limit == user->curr) {
832 php_ion_writer_buffer_grow(writer);
833 }
834 }
835 user->curr = (BYTE *) &writer->buffer.str.s->val[writer->buffer.str.s->len];
836 user->limit = user->curr + writer->buffer.str.a - writer->buffer.str.s->len;
837
838 return IERR_OK;
839 }
840
841 static inline void php_ion_writer_ctor(php_ion_writer *obj)
842 {
843 if (obj->opt) {
844 update_property_obj(&obj->std, ZEND_STRL("options"), obj->opt);
845 }
846
847 php_ion_writer_options *opt = php_ion_obj(writer_options, obj->opt);
848 ION_STREAM_HANDLER h;
849 if (obj->type == STREAM_WRITER) {
850 h = php_ion_writer_stream_handler;
851 php_ion_writer_stream_init(obj, opt);
852 } else {
853 h = php_ion_writer_buffer_handler;
854 php_ion_writer_buffer_init(obj);
855 }
856
857 ION_CHECK(ion_writer_open_stream(&obj->writer, h, obj, opt ? &opt->opt : NULL));
858 OBJ_CHECK(obj);
859 }
860
861 static inline void php_ion_writer_dtor(php_ion_writer *obj)
862 {
863 if (obj->writer) {
864 ion_writer_close(obj->writer);
865 }
866 if (obj->type == STREAM_WRITER) {
867 if (obj->stream.buf.value) {
868 efree(obj->stream.buf.value);
869 }
870 if (obj->stream.ptr) {
871 zend_list_delete(obj->stream.ptr->res);
872 }
873 } else {
874 if (obj->buffer.str.s) {
875 smart_str_0(&obj->buffer.str);
876 zend_string_release(obj->buffer.str.s);
877 }
878 zval_ptr_dtor(&obj->buffer.val);
879 }
880 }
881
882 php_ion_decl(writer, Writer_Writer, php_ion_writer_dtor(obj));
883
884 typedef struct php_ion_serializer_php {
885 php_ion_serializer serializer;
886 zend_object *opt, std;
887 } php_ion_serializer_php;
888
889 static inline void php_ion_serializer_php_ctor(php_ion_serializer_php *ser_obj)
890 {
891 php_ion_serializer *global_ser = &php_ion_globals.serializer;
892 ser_obj->serializer.ids = global_ser->ids;
893 ser_obj->serializer.tmp = global_ser->tmp;
894
895 zend_update_property_bool(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callMagicSerialize"),
896 ser_obj->serializer.call_magic);
897 if (ser_obj->serializer.call_custom) {
898 zend_update_property_str(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callCustomSerialize"),
899 ser_obj->serializer.call_custom);
900 ser_obj->serializer.call_custom = zend_string_tolower(ser_obj->serializer.call_custom);
901 } else {
902 zend_update_property_null(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callCustomSerialize"));
903 }
904 if (ser_obj->opt) {
905 php_ion_writer_options *o_woptions = php_ion_obj(writer_options, ser_obj->opt);
906 ser_obj->serializer.options = &o_woptions->opt;
907 update_property_obj(&ser_obj->std, ZEND_STRL("writerOptions"), ser_obj->opt);
908 } else {
909 zend_update_property_null(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("writerOptions"));
910 }
911 }
912
913 static inline void php_ion_serializer_php_dtor(php_ion_serializer_php *obj)
914 {
915 if (obj->serializer.call_custom) {
916 zend_string_release(obj->serializer.call_custom);
917 }
918 }
919
920 static inline void php_ion_serialize_zval(php_ion_serializer *, zval *);
921
922 static inline void php_ion_serialize_struct(php_ion_serializer *ser, zend_array *arr, bool props)
923 {
924 ION_CHECK(ion_writer_start_container(ser->writer, tid_STRUCT));
925
926 zval *v;
927 zend_ulong h;
928 zend_string *k = NULL;
929 if (arr) ZEND_HASH_FOREACH_KEY_VAL_IND(arr, h, k, v)
930 ION_STRING is;
931 if (k) {
932 size_t prop_len;
933 const char *class_name, *prop_name;
934 if (props && (SUCCESS == zend_unmangle_property_name_ex(k, &class_name, &prop_name, &prop_len)) && class_name) {
935 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_cstr(&is, ZEND_STRL("p"))));
936 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_cstr(&is, class_name, prop_name - class_name - 1)));
937 } else {
938 prop_name = k->val;
939 prop_len = k->len;
940 }
941 ION_CHECK(ion_writer_write_field_name(ser->writer, ion_string_from_cstr(&is, prop_name, prop_len)));
942 } else {
943 char buf[MAX_LENGTH_OF_LONG + 1], *end = buf + sizeof(buf) - 1;
944 char *ptr = zend_print_long_to_buf(end, (zend_long) h);
945 ION_CHECK(ion_writer_write_field_name(ser->writer, ion_string_from_cstr(&is, ptr, end - ptr)));
946 }
947
948 php_ion_serialize_zval(ser, v);
949 ION_CATCH();
950 ZEND_HASH_FOREACH_END();
951
952 ION_CHECK(ion_writer_finish_container(ser->writer));
953 }
954
955 static inline void php_ion_serialize_list(php_ion_serializer *ser, zend_array *arr)
956 {
957 ION_CHECK(ion_writer_start_container(ser->writer, tid_LIST));
958
959 zval *v;
960 ZEND_HASH_FOREACH_VAL_IND(arr, v)
961 php_ion_serialize_zval(ser, v);
962 ION_CATCH();
963 ZEND_HASH_FOREACH_END();
964
965 ION_CHECK(ion_writer_finish_container(ser->writer));
966 }
967
968 static inline void php_ion_serialize_array(php_ion_serializer *ser, zend_array *arr)
969 {
970 if (zend_array_is_list(arr)) {
971 php_ion_serialize_list(ser, arr);
972 } else {
973 php_ion_serialize_struct(ser, arr, false);
974 }
975 }
976
977 static inline void php_ion_serialize_object_iface(php_ion_serializer *ser, zend_object *zobject)
978 {
979 uint8_t *buf;
980 size_t len;
981 zval tmp;
982
983 ZVAL_OBJ(&tmp, zobject);
984 if (SUCCESS == zobject->ce->serialize(&tmp, &buf, &len, NULL)) {
985 ION_STRING is;
986 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_cstr(&is, ZEND_STRL("S"))));
987 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_zend(&is, zobject->ce->name)));
988 ION_CHECK(ion_writer_write_string(ser->writer, ion_string_from_cstr(&is, (char *) buf, len)));
989 efree(buf);
990 } else if (!EG(exception)){
991 zend_throw_exception_ex(spl_ce_UnexpectedValueException, IERR_INTERNAL_ERROR,
992 "Failed to serialize class %s", zobject->ce->name->val);
993 }
994 }
995
996 static inline void php_ion_serialize_object_magic(php_ion_serializer *ser, zend_object *zobject, zend_function *fn)
997 {
998 zval rv;
999
1000 ZVAL_NULL(&rv);
1001 zend_call_known_instance_method_with_0_params(fn ? fn : zobject->ce->__serialize, zobject, &rv);
1002 ION_CATCH();
1003
1004 if (IS_ARRAY == Z_TYPE(rv)) {
1005 ION_STRING is;
1006 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_cstr(&is, fn ? "C" : "O", 1)));
1007 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_zend(&is, zobject->ce->name)));
1008 php_ion_serialize_zval(ser, &rv);
1009 zval_ptr_dtor(&rv);
1010 } else {
1011 zend_throw_exception_ex(spl_ce_UnexpectedValueException, IERR_INTERNAL_ERROR,
1012 "%s serializer %s::%s did not return an array",
1013 fn ? "Custom" : "Magic", zobject->ce->name->val,
1014 fn ? fn->common.function_name->val : "__serialize");
1015 }
1016 }
1017
1018 static inline zend_string *fq_enum_case(zend_object *zobject)
1019 {
1020 zval *cn = zend_enum_fetch_case_name(zobject);
1021 zend_string *en = zend_string_alloc(zobject->ce->name->len + Z_STRLEN_P(cn) + strlen("::"), 0);
1022 memcpy(en->val, zobject->ce->name->val, zobject->ce->name->len);
1023 en->val[zobject->ce->name->len] = ':';
1024 en->val[zobject->ce->name->len + 1] = ':';
1025 memcpy(&en->val[zobject->ce->name->len + 2], Z_STRVAL_P(cn), Z_STRLEN_P(cn));
1026 en->val[en->len] = 0;
1027 return en;
1028 }
1029
1030 static inline void php_ion_serialize_object_enum(php_ion_serializer *ser, zend_object *zobject)
1031 {
1032 ION_STRING is;
1033 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_cstr(&is, ZEND_STRL("E"))));
1034
1035 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_zend(&is, zobject->ce->name)));
1036 zval *z_cname = zend_enum_fetch_case_name(zobject);
1037 ION_CHECK(ion_writer_write_symbol(ser->writer, ion_string_from_zend(&is, Z_STR_P(z_cname))));
1038 }
1039
1040 static inline void php_ion_serialize_object_std(php_ion_serializer *ser, zend_object *zobject)
1041 {
1042 ION_STRING is;
1043
1044 if (zobject->ce != zend_standard_class_def) {
1045 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_cstr(&is, ZEND_STRL("c"))));
1046 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_zend(&is, zobject->ce->name)));
1047 } else {
1048 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_cstr(&is, ZEND_STRL("o"))));
1049 }
1050
1051 zval zobj;
1052 ZVAL_OBJ(&zobj, zobject);
1053 HashTable *props = zend_get_properties_for(&zobj, ZEND_PROP_PURPOSE_SERIALIZE);
1054 if (props) {
1055 php_ion_serialize_struct(ser, props, true);
1056 zend_release_properties(props);
1057 } else {
1058 zend_throw_exception_ex(spl_ce_UnexpectedValueException, IERR_INTERNAL_ERROR,
1059 "Could not get properties for serialization of class %s",
1060 zobject->ce->name->val);
1061 }
1062 }
1063
1064 static inline bool can_call_magic_serialize(php_ion_serializer *ser, zend_class_entry *ce)
1065 {
1066 return ce->__serialize && ser->call_magic;
1067 }
1068
1069 static inline bool can_call_iface_serialize(php_ion_serializer *, zend_class_entry *ce)
1070 {
1071 return !!ce->serialize;
1072 }
1073
1074 static inline bool can_call_custom_serialize(php_ion_serializer *ser, zend_object *zobject, zend_function **fn)
1075 {
1076 if (ser->call_custom) {
1077 return !!(*fn = zend_hash_find_ptr(&zobject->ce->function_table, ser->call_custom));
1078 }
1079 return false;
1080 }
1081
1082 static inline void php_ion_serialize_object(php_ion_serializer *ser, zend_object *zobject)
1083 {
1084 zend_function *fn;
1085 zend_class_entry *ce = zobject->ce;
1086 ZEND_ASSERT(ce);
1087
1088 if (ce->ce_flags & ZEND_ACC_NOT_SERIALIZABLE) {
1089 zend_throw_exception_ex(spl_ce_InvalidArgumentException, IERR_INVALID_ARG,
1090 "Serializing %s is not allowed", ce->name->val);
1091 return;
1092 }
1093
1094 if (can_call_magic_serialize(ser, ce)) {
1095 php_ion_serialize_object_magic(ser, zobject, NULL);
1096 } else if (can_call_iface_serialize(ser, ce)) {
1097 php_ion_serialize_object_iface(ser, zobject);
1098 } else if (can_call_custom_serialize(ser, zobject, &fn)) {
1099 php_ion_serialize_object_magic(ser, zobject, fn);
1100 } else if (zobject->ce->ce_flags & ZEND_ACC_ENUM) {
1101 php_ion_serialize_object_enum(ser, zobject);
1102 } else if (ce == ce_Symbol) {
1103 ION_CHECK(ion_writer_write_ion_symbol(ser->writer, &php_ion_obj(symbol, zobject)->sym));
1104 } else if (ce == ce_Decimal) {
1105 ION_CHECK(ion_writer_write_ion_decimal(ser->writer, &php_ion_obj(decimal, zobject)->dec));
1106 } else if (ce == ce_Timestamp) {
1107 ION_TIMESTAMP its;
1108 php_ion_timestamp *pts = php_ion_obj(timestamp, zobject);
1109 decContext *ctx = ser->options ? ser->options->decimal_context : NULL;
1110 ION_CHECK(ion_writer_write_timestamp(ser->writer, ion_timestamp_from_php(&its, pts, ctx)));
1111 } else {
1112 php_ion_serialize_object_std(ser, zobject);
1113 }
1114 }
1115
1116 static inline void php_ion_serialize_refcounted(php_ion_serializer *ser, zval *zv)
1117 {
1118 zend_ulong idx = (zend_ulong) (uintptr_t) Z_COUNTED_P(zv);
1119
1120 ION_STRING is;
1121 if (zend_hash_index_exists(ser->ids, idx)) {
1122 zval *num = zend_hash_index_find(ser->ids, idx);
1123
1124 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_cstr(&is, ZEND_STRL("r"))));
1125 ION_CHECK(ion_writer_write_int64(ser->writer, Z_LVAL_P(num)));
1126 } else {
1127 zval num;
1128
1129 ZVAL_LONG(&num, zend_hash_num_elements(ser->ids));
1130 zend_hash_index_add(ser->ids, idx, &num);
1131
1132 Z_TRY_ADDREF_P(zv);
1133 zend_hash_next_index_insert(ser->tmp, zv);
1134
1135 switch (Z_TYPE_P(zv)) {
1136 case IS_STRING:
1137 ION_CHECK(ion_writer_write_string(ser->writer, ion_string_from_zend(&is, Z_STR_P(zv))));
1138 break;
1139
1140 case IS_ARRAY:
1141 php_ion_serialize_array(ser, Z_ARRVAL_P(zv));
1142 break;
1143
1144 case IS_OBJECT:
1145 php_ion_serialize_object(ser, Z_OBJ_P(zv));
1146 break;
1147
1148 case IS_REFERENCE:
1149 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_cstr(&is, ZEND_STRL("R"))));
1150 php_ion_serialize_zval(ser, Z_REFVAL_P(zv));
1151 break;
1152 }
1153 }
1154 }
1155
1156 static inline void php_ion_serialize_zval(php_ion_serializer *ser, zval *zv)
1157 {
1158 OBJ_CHECK(ser);
1159
1160 switch (Z_TYPE_P(zv)) {
1161 case IS_NULL:
1162 ION_CHECK(ion_writer_write_null(ser->writer));
1163 break;
1164 case IS_TRUE:
1165 ION_CHECK(ion_writer_write_bool(ser->writer, TRUE));
1166 break;
1167 case IS_FALSE:
1168 ION_CHECK(ion_writer_write_bool(ser->writer, FALSE));
1169 break;
1170 case IS_LONG:
1171 ION_CHECK(ion_writer_write_int64(ser->writer, Z_LVAL_P(zv)));
1172 break;
1173 case IS_DOUBLE:
1174 ION_CHECK(ion_writer_write_double(ser->writer, Z_DVAL_P(zv)));
1175 break;
1176 case IS_STRING:
1177 case IS_ARRAY:
1178 case IS_OBJECT:
1179 case IS_REFERENCE:
1180 php_ion_serialize_refcounted(ser, zv);
1181 break;
1182 default:
1183 zend_throw_exception_ex(spl_ce_InvalidArgumentException, IERR_INVALID_ARG,
1184 "Failed to serialize value of type %s", zend_zval_type_name(zv));
1185 }
1186 }
1187
1188 php_ion_decl(serializer_php, Serializer_PHP, php_ion_serializer_php_dtor(obj));
1189
1190 void php_ion_serialize(php_ion_serializer *ser, zval *zv, zval *return_value)
1191 {
1192 zend_object *zo_opt = NULL, *zo_ser = NULL;
1193
1194 if (!ser) {
1195 zo_ser = create_ion_Serializer_PHP(NULL);
1196 php_ion_serializer_php *o_ser = php_ion_obj(serializer_php, zo_ser);
1197 PTR_CHECK(o_ser);
1198 o_ser->serializer.call_magic = true;
1199 php_ion_serializer_php_ctor(o_ser);
1200 ION_CATCH();
1201 ser = &o_ser->serializer;
1202 }
1203
1204 zend_object *zo_writer = create_ion_Writer_Writer(ce_Writer_Buffer_Writer);
1205 php_ion_writer *writer = php_ion_obj(writer, zo_writer);
1206 writer->type = BUFFER_WRITER;
1207
1208 if (ser->options) {
1209 zo_opt = writer->opt = create_ion_Writer_Options(NULL);
1210 php_ion_obj(writer_options, writer->opt)->opt = *ser->options;
1211 }
1212
1213 php_ion_writer_ctor(writer);
1214 ser->writer = writer->writer;
1215 ser->buffer = &writer->buffer.str;
1216
1217 /* start off with a global PHP annotation instead of repeating it all over the place */
1218 if (0 == php_ion_globals_serializer_step()) {
1219 ION_STRING is;
1220 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_cstr(&is, ZEND_STRL("PHP"))),
1221 if (zo_ser) OBJ_RELEASE(zo_ser));
1222 }
1223 php_ion_serialize_zval(ser, zv);
1224 php_ion_globals_serializer_exit();
1225
1226 /* make sure to flush when done, else str.s might not contain everything until the writer is closed */
1227 ion_writer_flush(ser->writer, NULL);
1228 RETVAL_STR_COPY(ser->buffer->s);
1229
1230 OBJ_RELEASE(zo_writer);
1231 if (zo_opt) {
1232 OBJ_RELEASE(zo_opt);
1233 }
1234 if (zo_ser) {
1235 OBJ_RELEASE(zo_ser);
1236 }
1237 }
1238
1239 typedef struct php_ion_unserializer_php {
1240 php_ion_unserializer unserializer;
1241 zend_object *opt, std;
1242 } php_ion_unserializer_php;
1243
1244 static inline void php_ion_unserializer_php_ctor(php_ion_unserializer_php *ser_obj)
1245 {
1246 php_ion_unserializer *global_ser = &php_ion_globals.unserializer;
1247 ser_obj->unserializer.ids = global_ser->ids;
1248 ser_obj->unserializer.tmp = global_ser->tmp;
1249 ser_obj->unserializer.addref = global_ser->addref;
1250
1251 zend_update_property_bool(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callMagicSerialize"),
1252 ser_obj->unserializer.call_magic);
1253 if (ser_obj->unserializer.call_custom) {
1254 zend_update_property_str(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callCustomSerialize"),
1255 ser_obj->unserializer.call_custom);
1256 ser_obj->unserializer.call_custom = zend_string_tolower(ser_obj->unserializer.call_custom);
1257 } else {
1258 zend_update_property_null(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callCustomSerialize"));
1259 }
1260 if (ser_obj->opt) {
1261 php_ion_reader_options *o_roptions = php_ion_obj(reader_options, ser_obj->opt);
1262 ser_obj->unserializer.options = &o_roptions->opt;
1263 update_property_obj(&ser_obj->std, ZEND_STRL("readerOptions"), ser_obj->opt);
1264 } else {
1265 zend_update_property_null(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("readerOptions"));
1266 }
1267 }
1268
1269 static inline void php_ion_unserializer_php_dtor(php_ion_unserializer_php *obj)
1270 {
1271 if (obj->unserializer.call_custom) {
1272 zend_string_release(obj->unserializer.call_custom);
1273 }
1274 }
1275
1276 static inline void php_ion_unserialize_zval(php_ion_unserializer *ser, zval *return_value, ION_TYPE *typ);
1277
1278 static inline bool can_call_magic_unserialize(php_ion_unserializer *ser, zend_class_entry *ce)
1279 {
1280 return (ce->__unserialize && ser->call_magic);
1281 }
1282
1283 static inline bool can_call_iface_unserialize(php_ion_unserializer *ser, zend_class_entry *ce)
1284 {
1285 return !!ce->unserialize;
1286 }
1287
1288 static inline bool can_call_custom_unserialize(php_ion_unserializer *ser, zend_object *zobject, zend_function **fn)
1289 {
1290 if (ser->call_custom) {
1291 return !!(*fn = zend_hash_find_ptr(&zobject->ce->function_table, ser->call_custom));
1292 }
1293 return false;
1294 }
1295
1296 static inline zval *php_ion_unserialize_class(php_ion_unserializer *ser, zval *return_value)
1297 {
1298 zend_class_entry *ce = zend_lookup_class(ser->annotations.object_class);
1299
1300 if (ce) {
1301 object_init_ex(return_value, ce);
1302 return zend_hash_next_index_insert(ser->ids, return_value);
1303 }
1304
1305 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_IMPORT_NOT_FOUND,
1306 "Could not find class %s", ser->annotations.object_class->val);
1307 return NULL;
1308 }
1309
1310 static inline void php_ion_unserialize_object_enum(php_ion_unserializer *ser, zval *return_value)
1311 {
1312 zend_string *zs_case = zval_get_string(return_value);
1313 ION_CATCH();
1314
1315 zend_class_entry *ce = zend_lookup_class(ser->annotations.object_class);
1316 if (!ce || !(ce->ce_flags & ZEND_ACC_ENUM)) {
1317 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN,
1318 "Not a valid enum: %s", ser->annotations.object_class->val);
1319 return;
1320 }
1321 if (!zend_hash_exists(CE_CONSTANTS_TABLE(ce), zs_case)) {
1322 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN,
1323 "Not a valid enum case: %s::%s", ser->annotations.object_class->val, zs_case->val);
1324 return;
1325 }
1326 RETVAL_OBJ_COPY(zend_enum_get_case(ce, zs_case));
1327 zend_hash_next_index_insert(ser->ids, return_value);
1328 zend_string_release(zs_case);
1329 }
1330
1331 static inline void php_ion_unserialize_object_iface(php_ion_unserializer *ser, zval *return_value)
1332 {
1333 zend_string *s = zval_get_string(return_value);
1334 ION_CATCH();
1335
1336 zval *backref = php_ion_unserialize_class(ser, return_value);
1337 ION_CATCH(zend_string_release(s));
1338
1339 zend_class_entry *ce = Z_OBJCE_P(return_value);
1340 if (can_call_iface_unserialize(ser, ce)) {
1341 if (SUCCESS == ce->unserialize(backref, ce, (BYTE *) s->val, s->len, NULL)) {
1342 // remove all this Serializable crap in PHP-9
1343 zval_ptr_dtor(return_value);
1344 ZVAL_COPY_VALUE(return_value, backref);
1345 } else if (!EG(exception)) {
1346 zend_throw_exception_ex(spl_ce_UnexpectedValueException, IERR_INTERNAL_ERROR,
1347 "Failed to unserialize class %s", ce->name->val);
1348 }
1349 } else {
1350 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN,
1351 "Class %s does not implement Serializable", ser->annotations.object_class->val);
1352 }
1353 zend_string_release(s);
1354 }
1355
1356 static void php_ion_unserialize_props(php_ion_unserializer *ser, zval *return_value)
1357 {
1358 zend_hash_next_index_insert(ser->ids, return_value);
1359
1360 ION_CHECK(ion_reader_step_in(ser->reader));
1361
1362 while (true) {
1363 ION_TYPE typ;
1364 ION_CHECK(ion_reader_next(ser->reader, &typ));
1365
1366 if (typ == tid_EOF) {
1367 break;
1368 }
1369
1370 ION_STRING is;
1371 ION_CHECK(ion_reader_get_field_name(ser->reader, &is));
1372 zend_string *key = zend_string_from_ion(&is);
1373
1374 zval zvalue;
1375 php_ion_unserialize_zval(ser, &zvalue, &typ);
1376 ION_CATCH(zend_string_release(key));
1377
1378 zend_class_entry *ce = Z_OBJCE_P(return_value);
1379 if (ser->annotations.object_prop && ser->annotations.property_class->val[0] != '*') {
1380 ce = zend_lookup_class(ser->annotations.property_class);
1381 }
1382 zend_update_property_ex(ce, Z_OBJ_P(return_value), key, &zvalue);
1383 zval_ptr_dtor(&zvalue);
1384 zend_string_release(key);
1385 }
1386
1387 ION_CHECK(ion_reader_step_out(ser->reader));
1388 }
1389
1390 static inline void php_ion_unserialize_hash(php_ion_unserializer *ser, zval *return_value)
1391 {
1392 zend_hash_next_index_insert(ser->ids, return_value);
1393
1394 ION_CHECK(ion_reader_step_in(ser->reader));
1395
1396 while (true) {
1397 ION_TYPE typ;
1398 ION_CHECK(ion_reader_next(ser->reader, &typ));
1399
1400 ION_STRING name;
1401 ION_CHECK(ion_reader_get_field_name(ser->reader, &name));
1402 zend_string *key = zend_string_from_ion(&name);
1403
1404 zval zvalue;
1405 php_ion_unserialize_zval(ser, &zvalue, &typ);
1406 ION_CATCH(zend_string_release(key));
1407
1408 if (typ == tid_EOF) {
1409 zend_string_release(key);
1410 break;
1411 }
1412
1413 zend_symtable_update(HASH_OF(return_value), key, &zvalue);
1414
1415 zend_string_release(key);
1416 }
1417
1418 ION_CHECK(ion_reader_step_out(ser->reader));
1419 }
1420
1421 static inline void verify_unserializer(php_ion_unserializer *ser, zend_object *zobject, zend_function **fn)
1422 {
1423 switch (ser->annotations.object_type) {
1424 case 'c':
1425 *fn = NULL;
1426 break;
1427
1428 case 'C':
1429 if (!can_call_custom_unserialize(ser, zobject, fn)) {
1430 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN,
1431 "Could not find custom serializer method of %s", ser->annotations.object_class->val);
1432 }
1433 break;
1434
1435 case 'O':
1436 if (!can_call_magic_unserialize(ser, zobject->ce)) {
1437 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN,
1438 "Could not find method %s::__unserialize()", ser->annotations.object_class->val);
1439 }
1440 *fn = zobject->ce->__unserialize;
1441 break;
1442
1443 default:
1444 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN,
1445 "Invalid object type %c", ser->annotations.object_type);
1446 }
1447 }
1448
1449 static inline void php_ion_unserialize_object(php_ion_unserializer *ser, zval *return_value)
1450 {
1451 // backup possible backref to array returned by magic/custom __serialize()
1452 zval *input = zend_hash_next_index_insert(ser->tmp, return_value);
1453
1454 php_ion_unserialize_class(ser, return_value);
1455 ION_CATCH();
1456
1457 zend_function *fn = NULL;
1458 zend_object *zobject = Z_OBJ_P(return_value);
1459 verify_unserializer(ser, zobject, &fn);
1460 ION_CATCH();
1461
1462 // plain object
1463 if (!fn) {
1464 php_ion_unserialize_props(ser, return_value);
1465 return;
1466 }
1467
1468 // magic object
1469 if (Z_TYPE_P(input) != IS_ARRAY) {
1470 zval_ptr_dtor(input);
1471 array_init(input);
1472 zend_hash_real_init_mixed(Z_ARRVAL_P(input));
1473 php_ion_unserialize_hash(ser, input);
1474 ION_CATCH();
1475 }
1476 zval rv;
1477 ZVAL_NULL(&rv);
1478 zend_call_method_with_1_params(zobject, zobject->ce, &fn, "", &rv, input);
1479 zval_ptr_dtor(&rv);
1480 }
1481
1482 static inline void php_ion_unserialize_struct(php_ion_unserializer *ser, zval *return_value)
1483 {
1484 if (ser->annotations.object_class) {
1485 switch (ser->annotations.object_type) {
1486 case 'S':
1487 php_ion_unserialize_object_iface(ser, return_value);
1488 break;
1489 case 'E':
1490 php_ion_unserialize_object_enum(ser, return_value);
1491 break;
1492 default:
1493 php_ion_unserialize_object(ser, return_value);
1494 }
1495 } else if (!ser->annotations.object_type) {
1496 array_init(return_value);
1497 php_ion_unserialize_hash(ser, return_value);
1498 } else if (ser->annotations.object_type == 'o') {
1499 object_init(return_value);
1500 php_ion_unserialize_hash(ser, return_value);
1501 } else {
1502 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN,
1503 "Invalid object annotation %c::", ser->annotations.object_type);
1504 }
1505 }
1506
1507 static inline void php_ion_unserialize_list(php_ion_unserializer *ser, zval *return_value)
1508 {
1509 ION_CHECK(ion_reader_step_in(ser->reader));
1510 array_init(return_value);
1511 zend_hash_next_index_insert(ser->ids, return_value);
1512
1513 while (true) {
1514 ION_TYPE typ;
1515 ION_CHECK(ion_reader_next(ser->reader, &typ));
1516
1517 zval next;
1518 php_ion_unserialize_zval(ser, &next, &typ);
1519 ION_CATCH();
1520
1521 if (typ == tid_EOF) {
1522 break;
1523 }
1524
1525 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &next);
1526 }
1527
1528 ION_CHECK(ion_reader_step_out(ser->reader));
1529 }
1530
1531 static inline void php_ion_reader_read_lob(ION_READER *reader, zval *return_value)
1532 {
1533 zend_string *zstr = zend_string_alloc(0x1000, 0);
1534 again:
1535 SIZE read = 0;
1536 iERR err = ion_reader_read_lob_bytes(reader, (BYTE *) zstr->val, zstr->len, &read);
1537 if (err == IERR_BUFFER_TOO_SMALL) {
1538 zstr = zend_string_extend(zstr, zstr->len << 2, 0);
1539 goto again;
1540 }
1541 ION_CHECK(err, zend_string_release(zstr));
1542 if (zstr->len > read) {
1543 zstr = zend_string_truncate(zstr, read, 0);
1544 }
1545 RETURN_STR(zstr);
1546 }
1547
1548 static inline void php_ion_reader_read_timestamp(ION_READER *reader, ION_READER_OPTIONS *opt, zval *return_value)
1549 {
1550 ION_TIMESTAMP ts;
1551 ION_CHECK(ion_reader_read_timestamp(reader, &ts));
1552
1553 object_init_ex(return_value, ce_Timestamp);
1554 php_ion_timestamp *ts_obj = php_ion_obj(timestamp, Z_OBJ_P(return_value));
1555
1556 zend_string *fmt = NULL;
1557 decContext *ctx = opt ? opt->decimal_context : NULL;
1558 ts_obj->time = php_time_from_ion(&ts, ctx, &fmt);
1559 php_ion_timestamp_ctor(ts_obj, ts.precision, fmt, NULL, NULL);
1560 zend_string_release(fmt);
1561
1562 OBJ_CHECK(ts_obj);
1563 }
1564
1565 static inline void php_ion_reader_read_int(ION_READER *reader, zval *return_value)
1566 {
1567 ION_INT *num = NULL;
1568 ION_CHECK(ion_int_alloc(reader, &num));
1569 ION_CHECK(ion_reader_read_ion_int(reader, num));
1570
1571 // TODO: SIZEOF_ZEND_LONG == 4
1572 int64_t i64;
1573 iERR err = ion_int_to_int64(num, &i64);
1574 switch (err) {
1575 case IERR_OK:
1576 RETVAL_LONG(i64);
1577 goto done;
1578
1579 case IERR_NUMERIC_OVERFLOW:
1580 SIZE max, len;
1581 ION_CHECK(ion_int_char_length(num, &max));
1582 zend_string *zs = zend_string_alloc(max-1, 0);
1583
1584 err = ion_int_to_char(num, (BYTE *) zs->val, max, &len);
1585 ZEND_ASSERT(len == zs->len);
1586 RETVAL_STR(zs);
1587 /* fall through */
1588
1589 default:
1590 done:
1591 ion_int_free(num);
1592 ION_CHECK(err);
1593 }
1594 }
1595
1596 static inline void php_ion_unserialize_backref(php_ion_unserializer *ser, zval *return_value)
1597 {
1598 zval *backref = zend_hash_index_find(ser->ids, Z_LVAL_P(return_value));
1599
1600 if (backref) {
1601 ZVAL_COPY_VALUE(return_value, backref);
1602 zend_hash_next_index_insert(ser->addref, return_value);
1603 } else {
1604 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INTERNAL_ERROR,
1605 "Could not find back reference %ld", Z_LVAL_P(return_value));
1606 }
1607 }
1608
1609 static inline void php_ion_unserialize_annotations(php_ion_unserializer *ser)
1610 {
1611 memset(&ser->annotations, 0, sizeof(ser->annotations));
1612
1613 int32_t ann_cnt;
1614 ION_CHECK(ion_reader_get_annotation_count(ser->reader, &ann_cnt));
1615 for (int32_t i = 0; i < ann_cnt; ++i) {
1616 ION_STRING ann_str;
1617 ION_CHECK(ion_reader_get_an_annotation(ser->reader, i, &ann_str));
1618
1619 switch (*ann_str.value) {
1620 case 'R':
1621 if (ser->annotations.makeref) {
1622 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_SYNTAX,
1623 "Invalid multiple reference annotations");
1624 return;
1625 }
1626 ser->annotations.makeref = true;
1627 break;
1628
1629 case 'r':
1630 if (ser->annotations.backref) {
1631 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_SYNTAX,
1632 "Invalid multiple back reference annotations");
1633 return;
1634 }
1635 ser->annotations.backref = true;
1636 break;
1637
1638 case 'p':
1639 if (ser->annotations.object_prop) {
1640 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_SYNTAX,
1641 "Invalid multiple object property annotations");
1642 return;
1643 }
1644 ser->annotations.object_prop = true;
1645
1646 ION_STRING prop_class;
1647 ION_CHECK(ion_reader_get_an_annotation(ser->reader, ++i, &prop_class));
1648 ser->annotations.property_class = zend_string_from_ion(&prop_class);
1649
1650 zval zptmp;
1651 ZVAL_STR(&zptmp, ser->annotations.property_class);
1652 zend_hash_next_index_insert(ser->tmp, &zptmp);
1653 break;
1654
1655 case 'E':
1656 case 'S':
1657 case 'O':
1658 case 'C':
1659 case 'o':
1660 case 'c':
1661 if (ser->annotations.object_type) {
1662 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_SYNTAX,
1663 "Invalid multiple object type annotations: %c::%c",
1664 ser->annotations.object_type, *ann_str.value);
1665 return;
1666 }
1667 if ('o' != (ser->annotations.object_type = *ann_str.value)) {
1668 ION_STRING class_name;
1669 ION_CHECK(ion_reader_get_an_annotation(ser->reader, ++i, &class_name));
1670 ser->annotations.object_class = zend_string_from_ion(&class_name);
1671
1672 zval zctmp;
1673 ZVAL_STR(&zctmp, ser->annotations.object_class);
1674 zend_hash_next_index_insert(ser->tmp, &zctmp);
1675 }
1676 break;
1677 }
1678
1679 // sanity checks
1680 if (ser->annotations.object_type && ser->annotations.object_type != 'o' && !ser->annotations.object_class) {
1681 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_SYNTAX,
1682 "Invalid object annotation without class name: %c::", ser->annotations.object_type);
1683 return;
1684 }
1685 if (ser->annotations.object_type == 'o' && ser->annotations.object_class) {
1686 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_SYNTAX,
1687 "Invalid object annotation with class name: o::%s", ser->annotations.object_class->val);
1688 return;
1689 }
1690 }
1691 }
1692
1693 static inline void php_ion_unserialize_zval(php_ion_unserializer *ser, zval *return_value, ION_TYPE *typ)
1694 {
1695 if (typ) {
1696 memcpy(&ser->type, typ, sizeof(ser->type));
1697 } else {
1698 typ = &ser->type;
1699 ION_CHECK(ion_reader_next(ser->reader, typ));
1700 }
1701
1702 php_ion_unserialize_annotations(ser);
1703 ION_CATCH();
1704
1705 if (ser->annotations.makeref) {
1706 ZVAL_MAKE_REF(return_value);
1707 zend_hash_next_index_insert(ser->ids, return_value);
1708 ZVAL_DEREF(return_value);
1709 }
1710
1711 BOOL bval;
1712 ION_CHECK(ion_reader_is_null(ser->reader, &bval));
1713 if (bval) {
1714 goto read_null;
1715 }
1716
1717 switch (ION_TYPE_INT(*typ)) {
1718 case tid_NULL_INT:
1719 read_null: ;
1720 ION_CHECK(ion_reader_read_null(ser->reader, typ));
1721 RETURN_NULL();
1722
1723 case tid_BOOL_INT:
1724 ION_CHECK(ion_reader_read_bool(ser->reader, &bval));
1725 RETURN_BOOL(bval);
1726
1727 case tid_INT_INT:
1728 php_ion_reader_read_int(ser->reader, return_value);
1729 if (ser->annotations.backref) {
1730 ION_CATCH();
1731 php_ion_unserialize_backref(ser, return_value);
1732 }
1733 if (ser->annotations.object_type) {
1734 if (!ser->annotations.backref) {
1735 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_SYNTAX,
1736 "Invalid object type annotation: %c::" ZEND_LONG_FMT,
1737 ser->annotations.object_type, Z_LVAL_P(return_value));
1738 return;
1739 }
1740 goto unserialize_struct;
1741 }
1742 return;
1743
1744 case tid_FLOAT_INT:
1745 double d;
1746 ION_CHECK(ion_reader_read_double(ser->reader, &d));
1747 RETURN_DOUBLE(d);
1748
1749 case tid_DECIMAL_INT:
1750 object_init_ex(return_value, ce_Decimal);
1751 php_ion_decimal *dec = php_ion_obj(decimal, Z_OBJ_P(return_value));
1752 ION_CHECK(ion_reader_read_ion_decimal(ser->reader, &dec->dec));
1753 php_ion_decimal_ctor(dec);
1754 zend_hash_next_index_insert(ser->ids, return_value);
1755 return;
1756
1757 case tid_TIMESTAMP_INT:
1758 php_ion_reader_read_timestamp(ser->reader, ser->options, return_value);
1759 zend_hash_next_index_insert(ser->ids, return_value);
1760 return;
1761
1762 case tid_SYMBOL_INT:
1763 ION_SYMBOL sym;
1764 ION_CHECK(ion_reader_read_ion_symbol(ser->reader, &sym));
1765 php_ion_symbol_zval(&sym, return_value);
1766 if (ser->annotations.object_type) {
1767 zend_hash_next_index_insert(ser->tmp, return_value);
1768 goto unserialize_struct;
1769 }
1770 zend_hash_next_index_insert(ser->ids, return_value);
1771 return;
1772
1773 case tid_STRING_INT:
1774 ION_STRING str;
1775 ION_CHECK(ion_reader_read_string(ser->reader, &str));
1776 RETVAL_STRINGL((char *) str.value, str.length);
1777 if (ser->annotations.object_type) {
1778 zend_hash_next_index_insert(ser->tmp, return_value);
1779 goto unserialize_struct;
1780 }
1781 zend_hash_next_index_insert(ser->ids, return_value);
1782 return;
1783
1784 case tid_CLOB_INT:
1785 case tid_BLOB_INT:
1786 php_ion_reader_read_lob(ser->reader, return_value);
1787 if (ser->annotations.object_type) {
1788 zend_hash_next_index_insert(ser->tmp, return_value);
1789 goto unserialize_struct;
1790 }
1791 zend_hash_next_index_insert(ser->ids, return_value);
1792 return;
1793
1794 case tid_LIST_INT:
1795 case tid_SEXP_INT: // FIXME
1796 php_ion_unserialize_list(ser, return_value);
1797 if (!ser->annotations.object_type) {
1798 return;
1799 }
1800 /* fall through */
1801
1802 case tid_STRUCT_INT:
1803 unserialize_struct: ;
1804 php_ion_unserialize_struct(ser, return_value);
1805 return;
1806
1807 case tid_none_INT:
1808 ZEND_ASSERT(0);
1809 break;
1810
1811 case tid_DATAGRAM_INT:
1812 ZEND_ASSERT(!"datagram");
1813 case tid_EOF_INT:
1814 return;
1815 }
1816 }
1817
1818 php_ion_decl(unserializer_php, Unserializer_PHP, php_ion_unserializer_php_dtor(obj));
1819
1820 void php_ion_unserialize(php_ion_unserializer *ser, zval *zdata, zval *return_value)
1821 {
1822 zend_object *zo_opt = NULL, *zo_ser = NULL;
1823
1824 if (!ser) {
1825 zo_ser = create_ion_Unserializer_PHP(NULL);
1826 php_ion_unserializer_php *o_ser = php_ion_obj(unserializer_php, zo_ser);
1827 PTR_CHECK(o_ser);
1828 o_ser->unserializer.call_magic = true;
1829 php_ion_unserializer_php_ctor(o_ser);
1830 ION_CATCH();
1831 ser = &o_ser->unserializer;
1832 }
1833
1834 zend_object *zo_reader;
1835 php_ion_reader *reader;
1836 ZVAL_DEREF(zdata);
1837 switch (Z_TYPE_P(zdata)) {
1838 case IS_STRING:
1839 zo_reader = create_ion_Reader_Reader(ce_Reader_Buffer_Reader);
1840 reader = php_ion_obj(reader, zo_reader);
1841 reader->type = BUFFER_READER;
1842 reader->buffer = zend_string_copy(Z_STR_P(zdata));
1843 break;
1844
1845 case IS_RESOURCE:
1846 zo_reader = create_ion_Reader_Reader(ce_Reader_Stream_Reader);
1847 reader = php_ion_obj(reader, zo_reader);
1848 reader->type = STREAM_READER;
1849 php_stream_from_zval_no_verify(reader->stream.ptr, zdata);
1850 break;
1851
1852 default:
1853 ZEND_ASSERT(!IS_STRING && !IS_RESOURCE);
1854 }
1855
1856 if (ser->options) {
1857 zo_opt = reader->opt = create_ion_Reader_Options(NULL);
1858 php_ion_obj(reader_options, reader->opt)->opt = *ser->options;
1859 }
1860
1861 php_ion_reader_ctor(reader);
1862 ser->reader = reader->reader;
1863
1864 php_ion_globals_unserializer_step();
1865 php_ion_unserialize_zval(ser, return_value, NULL);
1866 php_ion_globals_unserializer_exit();
1867
1868 OBJ_RELEASE(zo_reader);
1869 if (zo_opt) {
1870 OBJ_RELEASE(zo_opt);
1871 }
1872 if (zo_ser) {
1873 OBJ_RELEASE(zo_ser);
1874 }
1875 }