+}
+
+static void php_ion_serialize_object_lob(php_ion_serializer *ser, zend_object *zobject)
+{
+ zval tmp_type, *type = zend_read_property_ex(NULL, zobject, ZSTR_KNOWN(ZEND_STR_TYPE), 0, &tmp_type);
+ zval tmp_value, *value = zend_read_property_ex(NULL, zobject, ZSTR_KNOWN(ZEND_STR_VALUE), 0, &tmp_value);
+
+ switch (Z_LVAL_P(zend_enum_fetch_case_value(Z_OBJ_P(type)))) {
+ case tid_BLOB_INT:
+ if (can_serialize_fast(ser)) {
+ ION_CHECK(ion_writer_write_blob(php_ion_obj(writer, ser->wri)->writer, (BYTE *) Z_STRVAL_P(value), Z_STRLEN_P(value)));
+ } else {
+ zend_call_method_with_1_params(ser->wri, NULL, NULL, "writeBLob", NULL, value);
+ }
+ break;
+ case tid_CLOB_INT:
+ if (can_serialize_fast(ser)) {
+ ION_CHECK(ion_writer_write_clob(php_ion_obj(writer, ser->wri)->writer, (BYTE *) Z_STRVAL_P(value), Z_STRLEN_P(value)));
+ } else {
+ zend_call_method_with_1_params(ser->wri, NULL, NULL, "writeCLob", NULL, value);
+ }
+ break;
+ default:
+ zend_throw_exception_ex(ce_Exception, IERR_INVALID_ARG,
+ "Unsupported LOB type: ion\\Type::%s", Z_STRVAL_P(zend_enum_fetch_case_name(Z_OBJ_P(type))));
+ break;
+ }
+}
+
+static bool can_call_magic_serialize(php_ion_serializer *ser, zend_class_entry *ce)
+{
+ return ce->__serialize && ser->call_magic;
+}
+
+static bool can_call_iface_serialize(php_ion_serializer *ser, zend_class_entry *ce)
+{
+ (void) ser;
+ return !!ce->serialize; // NOLINT
+}
+
+static bool can_call_custom_serialize(php_ion_serializer *ser, zend_object *zobject, zend_function **fn)
+{
+ if (ser->call_custom) {
+ return !!(*fn = zend_hash_find_ptr(&zobject->ce->function_table, ser->call_custom)); // NOLINT
+ }
+ return false;
+}
+
+static bool is_special_class(const zend_class_entry *ce, zend_class_entry **target) {
+#define IS_TARGET(_ce) \
+ if (instanceof_function(ce, _ce)) { \
+ *target = _ce; \
+ return true; \
+ }
+ IS_TARGET(ce_Symbol);
+ IS_TARGET(ce_Decimal);
+ IS_TARGET(ce_Timestamp);
+ IS_TARGET(ce_LOB);
+ return false;
+#undef IS_TARGET
+}
+
+static void php_ion_serialize_object(php_ion_serializer *ser, zend_object *zobject) {
+ zend_function *fn;
+ zend_class_entry *special_ce, *ce = zobject->ce;
+ ZEND_ASSERT(ce);
+
+ if (ce->ce_flags & ZEND_ACC_NOT_SERIALIZABLE) {
+ zend_throw_exception_ex(ce_Exception, IERR_INVALID_ARG,
+ "Serializing %s is not allowed", ce->name->val);
+ return;
+ }
+
+ if (can_call_magic_serialize(ser, ce)) {
+ php_ion_serialize_object_magic(ser, zobject, NULL);
+ } else if (can_call_iface_serialize(ser, ce)) {
+ php_ion_serialize_object_iface(ser, zobject);
+ } else if (can_call_custom_serialize(ser, zobject, &fn)) {
+ php_ion_serialize_object_magic(ser, zobject, fn);
+ } else if (zobject->ce->ce_flags & ZEND_ACC_ENUM) {
+ php_ion_serialize_object_enum(ser, zobject);
+ } else if (!is_special_class(ce, &special_ce)) {
+ php_ion_serialize_object_std(ser, zobject);
+ } else {
+ if (can_serialize_fast(ser) || special_ce == ce_LOB) {
+ if (special_ce == ce_Symbol) {
+ ION_CHECK(ion_writer_write_ion_symbol(php_ion_obj(writer, ser->wri)->writer, &php_ion_obj(symbol, zobject)->sym));
+ } else if (special_ce == ce_Decimal) {
+ ION_CHECK(ion_writer_write_ion_decimal(php_ion_obj(writer, ser->wri)->writer, &php_ion_obj(decimal, zobject)->dec));
+ } else if (special_ce == ce_Timestamp) {
+ ION_TIMESTAMP its;
+ php_ion_timestamp *pts = php_ion_obj(timestamp, zobject);
+ decContext *ctx = php_ion_obj(writer, ser->wri)->options.decimal_context;
+ ION_CHECK(ion_writer_write_timestamp(php_ion_obj(writer, ser->wri)->writer, ion_timestamp_from_php(&its, pts, ctx)));
+ } else {
+ assert(special_ce == ce_LOB);
+ php_ion_serialize_object_lob(ser, zobject);
+ }
+ } else {
+ zval z_param;
+ const char *method = NULL;
+
+ ZVAL_OBJ(&z_param, zobject);
+ if (special_ce == ce_Symbol) {
+ method = "writeSymbol";
+ } else if (special_ce == ce_Decimal) {
+ method = "writeDecimal";
+ } else if (special_ce == ce_Timestamp) {
+ method = "writeTimestamp";
+ } else {
+ assert(!!method);
+ }
+ zend_call_method(ser->wri, NULL, NULL, method, strlen(method), NULL, 1, &z_param, NULL);
+ }
+ }
+}
+
+static bool php_ion_serialize_system_value(php_ion_serializer *ser, zval *zv)
+{
+ if (1 == php_ion_globals.serializer.level) {
+ if (Z_TYPE_P(zv) == IS_OBJECT) {
+ if (Z_OBJCE_P(zv) == ce_Symbol_Table_Shared) {
+ php_ion_symbol_table *obj = php_ion_obj(symbol_table, Z_OBJ_P(zv));
+ ION_CHECK_RETURN(true, ion_symbol_table_unload(obj->tab, php_ion_obj(writer, ser->wri)->writer));
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static bool php_ion_serialize_backref(php_ion_serializer *ser, zval *zv)
+{
+ if (Z_TYPE_P(zv) == IS_STRING && Z_STR_P(zv) == zend_empty_string) {
+ return false;
+ }
+ if (Z_TYPE_P(zv) == IS_ARRAY && Z_ARR_P(zv) == &zend_empty_array) {
+ return false;
+ }
+
+ zend_ulong idx = (zend_ulong) (uintptr_t) Z_COUNTED_P(zv);
+ zval *ref = zend_hash_index_find(ser->ids, idx);
+ if (!ref) {
+ zval num;
+
+ ZVAL_LONG(&num, zend_hash_num_elements(ser->ids));
+ zend_hash_index_add(ser->ids, idx, &num);
+
+ Z_TRY_ADDREF_P(zv);
+ zend_hash_next_index_insert(ser->tmp, zv);
+
+ return false;
+ }