From cba04b19e88dba2c13241b733bf7f595f1463082 Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Wed, 8 Feb 2017 10:27:54 -0800 Subject: Implement json encoding decoding for php. (#2682) --- php/ext/google/protobuf/def.c | 15 +++++ php/ext/google/protobuf/encode_decode.c | 111 ++++++++++++++++++++++++++++++-- php/ext/google/protobuf/message.c | 18 ++++++ php/ext/google/protobuf/protobuf.h | 5 ++ php/ext/google/protobuf/storage.c | 1 + 5 files changed, 144 insertions(+), 6 deletions(-) (limited to 'php/ext') diff --git a/php/ext/google/protobuf/def.c b/php/ext/google/protobuf/def.c index 0ce39ec3..32b158e2 100644 --- a/php/ext/google/protobuf/def.c +++ b/php/ext/google/protobuf/def.c @@ -398,10 +398,22 @@ static void descriptor_free_c(Descriptor *self TSRMLS_DC) { if (self->fill_method) { upb_pbdecodermethod_unref(self->fill_method, &self->fill_method); } + if (self->json_fill_method) { + upb_json_parsermethod_unref(self->json_fill_method, + &self->json_fill_method); + } if (self->pb_serialize_handlers) { upb_handlers_unref(self->pb_serialize_handlers, &self->pb_serialize_handlers); } + if (self->json_serialize_handlers) { + upb_handlers_unref(self->json_serialize_handlers, + &self->json_serialize_handlers); + } + if (self->json_serialize_handlers_preserve) { + upb_handlers_unref(self->json_serialize_handlers_preserve, + &self->json_serialize_handlers_preserve); + } } static void descriptor_init_c_instance(Descriptor *desc TSRMLS_DC) { @@ -411,7 +423,10 @@ static void descriptor_init_c_instance(Descriptor *desc TSRMLS_DC) { desc->klass = NULL; desc->fill_handlers = NULL; desc->fill_method = NULL; + desc->json_fill_method = NULL; desc->pb_serialize_handlers = NULL; + desc->json_serialize_handlers = NULL; + desc->json_serialize_handlers_preserve = NULL; } // ----------------------------------------------------------------------------- diff --git a/php/ext/google/protobuf/encode_decode.c b/php/ext/google/protobuf/encode_decode.c index 32a0fbea..57fc81d3 100644 --- a/php/ext/google/protobuf/encode_decode.c +++ b/php/ext/google/protobuf/encode_decode.c @@ -103,6 +103,7 @@ static void stackenv_uninit(stackenv* se); // Callback invoked by upb if any error occurs during parsing or serialization. static bool env_error_func(void* ud, const upb_status* status) { + char err_msg[100] = ""; stackenv* se = ud; // Free the env -- zend_error will longjmp up the stack past the // encode/decode function so it would not otherwise have been freed. @@ -110,7 +111,9 @@ static bool env_error_func(void* ud, const upb_status* status) { // TODO(teboring): have a way to verify that this is actually a parse error, // instead of just throwing "parse error" unconditionally. - zend_error(E_ERROR, se->php_error_template, upb_status_errmsg(status)); + sprintf(err_msg, se->php_error_template, upb_status_errmsg(status)); + TSRMLS_FETCH(); + zend_throw_exception(NULL, err_msg, 0 TSRMLS_CC); // Never reached. return false; } @@ -866,6 +869,14 @@ static const upb_pbdecodermethod *msgdef_decodermethod(Descriptor* desc) { return desc->fill_method; } +static const upb_json_parsermethod *msgdef_jsonparsermethod(Descriptor* desc) { + if (desc->json_fill_method == NULL) { + desc->json_fill_method = + upb_json_parsermethod_new(desc->msgdef, &desc->json_fill_method); + } + return desc->json_fill_method; +} + // ----------------------------------------------------------------------------- // Serializing. // ----------------------------------------------------------------------------- @@ -883,8 +894,8 @@ static void putsubmsg(zval* submsg, const upb_fielddef* f, upb_sink* sink, static void putarray(zval* array, const upb_fielddef* f, upb_sink* sink, int depth TSRMLS_DC); -static void putmap(zval* map, const upb_fielddef* f, upb_sink* sink, int depth - TSRMLS_DC); +static void putmap(zval* map, const upb_fielddef* f, upb_sink* sink, + int depth TSRMLS_DC); static upb_selector_t getsel(const upb_fielddef* f, upb_handlertype_t type) { upb_selector_t ret; @@ -961,10 +972,13 @@ static void putmap(zval* map, const upb_fielddef* f, upb_sink* sink, const upb_fielddef* key_field; const upb_fielddef* value_field; MapIter it; - int len; + int len, size; - if (map == NULL) return; - self = UNBOX(Map, map); + assert(map != NULL); + Map* intern = + (Map*)zend_object_store_get_object(map TSRMLS_CC); + size = upb_strtable_count(&intern->table); + if (size == 0) return; upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink); @@ -1197,6 +1211,25 @@ static const upb_handlers* msgdef_pb_serialize_handlers(Descriptor* desc) { return desc->pb_serialize_handlers; } +static const upb_handlers* msgdef_json_serialize_handlers( + Descriptor* desc, bool preserve_proto_fieldnames) { + if (preserve_proto_fieldnames) { + if (desc->json_serialize_handlers == NULL) { + desc->json_serialize_handlers = + upb_json_printer_newhandlers( + desc->msgdef, true, &desc->json_serialize_handlers); + } + return desc->json_serialize_handlers; + } else { + if (desc->json_serialize_handlers_preserve == NULL) { + desc->json_serialize_handlers_preserve = + upb_json_printer_newhandlers( + desc->msgdef, false, &desc->json_serialize_handlers_preserve); + } + return desc->json_serialize_handlers_preserve; + } +} + // ----------------------------------------------------------------------------- // PHP encode/decode methods // ----------------------------------------------------------------------------- @@ -1255,3 +1288,69 @@ PHP_METHOD(Message, decode) { stackenv_uninit(&se); } } + +PHP_METHOD(Message, jsonEncode) { + zval* php_descriptor = get_ce_obj(Z_OBJCE_P(getThis())); + Descriptor* desc = + (Descriptor*)zend_object_store_get_object(php_descriptor TSRMLS_CC); + + zend_bool preserve_proto_fieldnames = false; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", + &preserve_proto_fieldnames) == FAILURE) { + return; + } + + stringsink sink; + stringsink_init(&sink); + + { + const upb_handlers* serialize_handlers = + msgdef_json_serialize_handlers(desc, preserve_proto_fieldnames); + upb_json_printer* printer; + stackenv se; + + stackenv_init(&se, "Error occurred during encoding: %s"); + printer = upb_json_printer_create(&se.env, serialize_handlers, &sink.sink); + + putmsg(getThis(), desc, upb_json_printer_input(printer), 0 TSRMLS_CC); + + RETVAL_STRINGL(sink.ptr, sink.len, 1); + + stackenv_uninit(&se); + stringsink_uninit(&sink); + } +} + +PHP_METHOD(Message, jsonDecode) { + zval* php_descriptor = get_ce_obj(Z_OBJCE_P(getThis())); + Descriptor* desc = + (Descriptor*)zend_object_store_get_object(php_descriptor TSRMLS_CC); + MessageHeader* msg = zend_object_store_get_object(getThis() TSRMLS_CC); + + char *data = NULL; + int data_len; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &data_len) == + FAILURE) { + return; + } + + // TODO(teboring): Check and respect string encoding. If not UTF-8, we need to + // convert, because string handlers pass data directly to message string + // fields. + + // TODO(teboring): Clear message. + + { + const upb_json_parsermethod* method = msgdef_jsonparsermethod(desc); + stackenv se; + upb_sink sink; + upb_json_parser* parser; + stackenv_init(&se, "Error occurred during parsing: %s"); + + upb_sink_reset(&sink, get_fill_handlers(desc), msg); + parser = upb_json_parser_create(&se.env, method, &sink); + upb_bufsrc_putbuf(data, data_len, upb_json_parser_input(parser)); + + stackenv_uninit(&se); + } +} diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c index f5a9499d..e8b8ae81 100644 --- a/php/ext/google/protobuf/message.c +++ b/php/ext/google/protobuf/message.c @@ -30,6 +30,7 @@ #include #include +#include #include "protobuf.h" @@ -39,6 +40,8 @@ zend_object_handlers* message_handlers; static zend_function_entry message_methods[] = { PHP_ME(Message, encode, NULL, ZEND_ACC_PUBLIC) PHP_ME(Message, decode, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Message, jsonEncode, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Message, jsonDecode, NULL, ZEND_ACC_PUBLIC) PHP_ME(Message, readOneof, NULL, ZEND_ACC_PROTECTED) PHP_ME(Message, writeOneof, NULL, ZEND_ACC_PROTECTED) PHP_ME(Message, whichOneof, NULL, ZEND_ACC_PROTECTED) @@ -54,6 +57,8 @@ static zval* message_get_property(zval* object, zval* member, int type, const zend_literal* key TSRMLS_DC); static zval** message_get_property_ptr_ptr(zval* object, zval* member, int type, const zend_literal* key TSRMLS_DC); +static HashTable* message_get_properties(zval* object TSRMLS_DC); +static HashTable* message_get_gc(zval* object, zval*** table, int* n TSRMLS_DC); static zend_object_value message_create(zend_class_entry* ce TSRMLS_DC); static void message_free(void* object TSRMLS_DC); @@ -74,6 +79,8 @@ void message_init(TSRMLS_D) { message_handlers->write_property = message_set_property; message_handlers->read_property = message_get_property; message_handlers->get_property_ptr_ptr = message_get_property_ptr_ptr; + message_handlers->get_properties = message_get_properties; + message_handlers->get_gc = message_get_gc; } static void message_set_property(zval* object, zval* member, zval* value, @@ -144,6 +151,17 @@ static zval** message_get_property_ptr_ptr(zval* object, zval* member, int type, return NULL; } +static HashTable* message_get_properties(zval* object TSRMLS_DC) { + return NULL; +} + +static HashTable* message_get_gc(zval* object, zval*** table, int* n TSRMLS_DC) { + zend_object* zobj = Z_OBJ_P(object); + *table = zobj->properties_table; + *n = zobj->ce->default_properties_count; + return NULL; +} + // ----------------------------------------------------------------------------- // C Message Utilities // ----------------------------------------------------------------------------- diff --git a/php/ext/google/protobuf/protobuf.h b/php/ext/google/protobuf/protobuf.h index df452d6f..678f2682 100644 --- a/php/ext/google/protobuf/protobuf.h +++ b/php/ext/google/protobuf/protobuf.h @@ -122,7 +122,10 @@ struct Descriptor { zend_class_entry* klass; // begins as NULL const upb_handlers* fill_handlers; const upb_pbdecodermethod* fill_method; + const upb_json_parsermethod* json_fill_method; const upb_handlers* pb_serialize_handlers; + const upb_handlers* json_serialize_handlers; + const upb_handlers* json_serialize_handlers_preserve; }; extern zend_class_entry* descriptor_type; @@ -261,6 +264,8 @@ const upb_pbdecodermethod *new_fillmsg_decodermethod(Descriptor *desc, PHP_METHOD(Message, encode); PHP_METHOD(Message, decode); +PHP_METHOD(Message, jsonEncode); +PHP_METHOD(Message, jsonDecode); // ----------------------------------------------------------------------------- // Type check / conversion. diff --git a/php/ext/google/protobuf/storage.c b/php/ext/google/protobuf/storage.c index 42bcce59..ba6ac59e 100644 --- a/php/ext/google/protobuf/storage.c +++ b/php/ext/google/protobuf/storage.c @@ -482,6 +482,7 @@ void layout_init(MessageLayout* layout, void* storage, repeated_field_create_with_type(repeated_field_type, field, property_ptr TSRMLS_CC); DEREF(memory, zval**) = property_ptr; + property_ptr = NULL; } else { native_slot_init(upb_fielddef_type(field), memory, property_ptr); } -- cgit v1.2.3