From 38f66d1273dd32e3c5e5ea88e0cad271de3c556b Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Tue, 29 Oct 2019 22:26:19 +0000 Subject: [PATCH 01/21] Register additional handlers from wrappers --- php/ext/google/protobuf/encode_decode.c | 160 +- php/tests/encode_decode_test.php | 2401 ++++++++++++----------- 2 files changed, 1359 insertions(+), 1202 deletions(-) diff --git a/php/ext/google/protobuf/encode_decode.c b/php/ext/google/protobuf/encode_decode.c index 39304a571133..fe8c5e93b5c9 100644 --- a/php/ext/google/protobuf/encode_decode.c +++ b/php/ext/google/protobuf/encode_decode.c @@ -122,12 +122,28 @@ static void stackenv_uninit(stackenv* se) { // Parsing. // ----------------------------------------------------------------------------- -// TODO(teboring): This shoud be a bit in upb_msgdef -static bool is_wrapper_msg(const upb_msgdef *msg) { - return !strcmp(upb_filedef_name(upb_msgdef_file(msg)), - "google/protobuf/wrappers.proto"); +static bool is_wrapper_msg(const upb_msgdef* m) { + switch (upb_msgdef_wellknowntype(m)) { + case UPB_WELLKNOWN_DOUBLEVALUE: + case UPB_WELLKNOWN_FLOATVALUE: + case UPB_WELLKNOWN_INT64VALUE: + case UPB_WELLKNOWN_UINT64VALUE: + case UPB_WELLKNOWN_INT32VALUE: + case UPB_WELLKNOWN_UINT32VALUE: + case UPB_WELLKNOWN_STRINGVALUE: + case UPB_WELLKNOWN_BYTESVALUE: + case UPB_WELLKNOWN_BOOLVALUE: + return true; + default: + return false; + } } +typedef struct { + void* closure; + void* submsg; +} wrapperfields_parseframe_t; + #define DEREF(msg, ofs, type) *(type*)(((uint8_t *)msg) + ofs) // Creates a handlerdata that simply contains the offset for this field. @@ -887,6 +903,52 @@ static void* oneofsubmsg_handler(void* closure, const void* hd) { return submsg; } +// Sets a non-repeated wrapper submessage field in a message. +static void* wrapper_submsg_handler(void* closure, const void* hd) { + MessageHeader* msg = closure; + const submsg_handlerdata_t* submsgdata = hd; + TSRMLS_FETCH(); + Descriptor* subdesc = + UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj((void*)submsgdata->md)); + zend_class_entry* subklass = subdesc->klass; + zval* submsg_php; + MessageHeader* submsg; + wrapperfields_parseframe_t* frame = + (wrapperfields_parseframe_t*)malloc(sizeof(wrapperfields_parseframe_t)); + + CACHED_VALUE* cached = find_zval_property(msg, submsgdata->fd); + + if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(cached)) == IS_NULL) { +#if PHP_MAJOR_VERSION < 7 + zval val; + ZVAL_OBJ(&val, subklass->create_object(subklass TSRMLS_CC)); + MessageHeader* intern = UNBOX(MessageHeader, &val); + custom_data_init(subklass, intern PHP_PROTO_TSRMLS_CC); + REPLACE_ZVAL_VALUE(cached, &val, 1); + zval_dtor(&val); +#else + zend_object* obj = subklass->create_object(subklass TSRMLS_CC); + ZVAL_OBJ(cached, obj); + MessageHeader* intern = UNBOX_HASHTABLE_VALUE(MessageHeader, obj); + custom_data_init(subklass, intern PHP_PROTO_TSRMLS_CC); +#endif + } + + submsg_php = CACHED_PTR_TO_ZVAL_PTR(cached); + submsg = UNBOX(MessageHeader, submsg_php); + + frame->closure = closure; + frame->submsg = submsg; + + return frame; +} + +static bool wrapper_submsg_end_handler(void *closure, const void *hd) { + wrapperfields_parseframe_t* frame = closure; + free(frame); + return true; +} + // Set up handlers for a repeated field. static void add_handlers_for_repeated_field(upb_handlers *h, const upb_fielddef *f, @@ -975,6 +1037,10 @@ static void add_handlers_for_singular_field(upb_handlers *h, if (is_map) { attr.handler_data = newsubmsghandlerdata(h, offset, f); upb_handlers_setstartsubmsg(h, f, map_submsg_handler, &attr); + } else if (is_wrapper_msg(upb_fielddef_msgsubdef(f))) { + attr.handler_data = newsubmsghandlerdata(h, 0, f); + upb_handlers_setstartsubmsg(h, f, wrapper_submsg_handler, &attr); + upb_handlers_setendsubmsg(h, f, wrapper_submsg_end_handler, &attr); } else { attr.handler_data = newsubmsghandlerdata(h, 0, f); upb_handlers_setstartsubmsg(h, f, submsg_handler, &attr); @@ -1062,6 +1128,84 @@ static void add_handlers_for_oneof_field(upb_handlers *h, } } +#define DEFINE_WRAPPER_HANDLER(type, ctype) \ + static bool type##wrapper_handler( \ + void* closure, const void* hd, ctype val) { \ + wrapperfields_parseframe_t* frame = closure; \ + MessageHeader* msg = (MessageHeader*)frame->submsg; \ + const size_t *ofs = hd; \ + DEREF(message_data(msg), *ofs, ctype) = val; \ + return true; \ + } + +DEFINE_WRAPPER_HANDLER(bool, bool) +DEFINE_WRAPPER_HANDLER(int32, int32_t) +DEFINE_WRAPPER_HANDLER(uint32, uint32_t) +DEFINE_WRAPPER_HANDLER(float, float) +DEFINE_WRAPPER_HANDLER(int64, int64_t) +DEFINE_WRAPPER_HANDLER(uint64, uint64_t) +DEFINE_WRAPPER_HANDLER(double, double) + +#undef DEFINE_WRAPPER_HANDLER + +static bool strwrapper_end_handler(void *closure, const void *hd) { + stringfields_parseframe_t* frame = closure; + const upb_fielddef **field = (const upb_fielddef **) hd; + wrapperfields_parseframe_t* wrapper_frame = frame->closure; + MessageHeader* msg = wrapper_frame->closure; + + CACHED_VALUE* cached = find_zval_property(msg, *field); + + new_php_string(cached, frame->sink.ptr, frame->sink.len); + + stringsink_uninit(&frame->sink); + free(frame); + + return true; +} + +static void add_handlers_for_wrapper(const upb_msgdef* msgdef, + upb_handlers* h) { + const upb_fielddef* f = upb_msgdef_itof(msgdef, 1); + Descriptor* desc = + UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj((void*)msgdef)); + size_t offset = desc->layout->fields[upb_fielddef_index(f)].offset; + + switch (upb_msgdef_wellknowntype(msgdef)) { +#define SET_HANDLER(utype, ltype) \ + case utype: { \ + upb_handlerattr attr = UPB_HANDLERATTR_INIT; \ + attr.handler_data = newhandlerdata(h, offset); \ + upb_handlers_set##ltype(h, f, ltype##wrapper_handler, &attr); \ + break; \ + } + + SET_HANDLER(UPB_WELLKNOWN_BOOLVALUE, bool); + SET_HANDLER(UPB_WELLKNOWN_INT32VALUE, int32); + SET_HANDLER(UPB_WELLKNOWN_UINT32VALUE, uint32); + SET_HANDLER(UPB_WELLKNOWN_FLOATVALUE, float); + SET_HANDLER(UPB_WELLKNOWN_INT64VALUE, int64); + SET_HANDLER(UPB_WELLKNOWN_UINT64VALUE, uint64); + SET_HANDLER(UPB_WELLKNOWN_DOUBLEVALUE, double); + +#undef SET_HANDLER + + case UPB_WELLKNOWN_STRINGVALUE: + case UPB_WELLKNOWN_BYTESVALUE: { + upb_handlerattr attr = UPB_HANDLERATTR_INIT; + attr.handler_data = newhandlerfielddata(h, f); + + upb_handlers_setstartstr(h, f, str_handler, &attr); + upb_handlers_setstring(h, f, stringdata_handler, &attr); + upb_handlers_setendstr(h, f, strwrapper_end_handler, &attr); + break; + } + default: + // Cannot reach here. + break; + } +} + static bool add_unknown_handler(void* closure, const void* hd, const char* buf, size_t size) { encodeunknown_handlerfunc handler = @@ -1102,6 +1246,14 @@ void add_handlers_for_message(const void* closure, upb_handlers* h) { desc->layout = create_layout(desc->msgdef); } + + // If this is a wrapper message type, set up a special set of handlers and + // bail out of the normal (user-defined) message type handling. + if (is_wrapper_msg(msgdef)) { + add_handlers_for_wrapper(msgdef, h); + return; + } + upb_handlerattr attr = UPB_HANDLERATTR_INIT; attr.handler_data = newunknownfieldshandlerdata(h); upb_handlers_setunknown(h, add_unknown_handler, &attr); diff --git a/php/tests/encode_decode_test.php b/php/tests/encode_decode_test.php index 3dad5f39efd6..74bbc65e4ae2 100644 --- a/php/tests/encode_decode_test.php +++ b/php/tests/encode_decode_test.php @@ -31,1203 +31,1208 @@ class EncodeDecodeTest extends TestBase { - public function testDecodeJsonSimple() - { - $m = new TestMessage(); - $m->mergeFromJsonString("{\"optionalInt32\":1}"); - $this->assertEquals(1, $m->getOptionalInt32()); - } - - public function testDecodeTopLevelBoolValue() - { - $m = new BoolValue(); - - $m->mergeFromJsonString("true"); - $this->assertEquals(true, $m->getValue()); - - $m->mergeFromJsonString("false"); - $this->assertEquals(false, $m->getValue()); - } - - public function testEncodeTopLevelBoolValue() - { - $m = new BoolValue(); - $m->setValue(true); - $this->assertSame("true", $m->serializeToJsonString()); - } - - public function testDecodeTopLevelDoubleValue() - { - $m = new DoubleValue(); - $m->mergeFromJsonString("1.5"); - $this->assertEquals(1.5, $m->getValue()); - } - - public function testEncodeTopLevelDoubleValue() - { - $m = new DoubleValue(); - $m->setValue(1.5); - $this->assertSame("1.5", $m->serializeToJsonString()); - } - - public function testDecodeTopLevelFloatValue() - { - $m = new FloatValue(); - $m->mergeFromJsonString("1.5"); - $this->assertEquals(1.5, $m->getValue()); - } - - public function testEncodeTopLevelFloatValue() - { - $m = new FloatValue(); - $m->setValue(1.5); - $this->assertSame("1.5", $m->serializeToJsonString()); - } - - public function testDecodeTopLevelInt32Value() - { - $m = new Int32Value(); - $m->mergeFromJsonString("1"); - $this->assertEquals(1, $m->getValue()); - } - - public function testEncodeTopLevelInt32Value() - { - $m = new Int32Value(); - $m->setValue(1); - $this->assertSame("1", $m->serializeToJsonString()); - } - - public function testDecodeTopLevelUInt32Value() - { - $m = new UInt32Value(); - $m->mergeFromJsonString("1"); - $this->assertEquals(1, $m->getValue()); - } - - public function testEncodeTopLevelUInt32Value() - { - $m = new UInt32Value(); - $m->setValue(1); - $this->assertSame("1", $m->serializeToJsonString()); - } - - public function testDecodeTopLevelInt64Value() - { - $m = new Int64Value(); - $m->mergeFromJsonString("1"); - $this->assertEquals(1, $m->getValue()); - } - - public function testDecodeTopLevelInt64ValueAsString() - { - $m = new Int64Value(); - $m->mergeFromJsonString("\"1\""); - $this->assertEquals(1, $m->getValue()); - } - - public function testEncodeTopLevelInt64Value() - { - $m = new Int64Value(); - $m->setValue(1); - $this->assertSame("\"1\"", $m->serializeToJsonString()); - } - - public function testDecodeTopLevelUInt64Value() - { - $m = new UInt64Value(); - $m->mergeFromJsonString("1"); - $this->assertEquals(1, $m->getValue()); - } - - public function testDecodeTopLevelUInt64ValueAsString() - { - $m = new UInt64Value(); - $m->mergeFromJsonString("\"1\""); - $this->assertEquals(1, $m->getValue()); - } - - public function testEncodeTopLevelUInt64Value() - { - $m = new UInt64Value(); - $m->setValue(1); - $this->assertSame("\"1\"", $m->serializeToJsonString()); - } - - public function testDecodeTopLevelStringValue() - { - $m = new StringValue(); - $m->mergeFromJsonString("\"a\""); - $this->assertSame("a", $m->getValue()); - } - - public function testEncodeTopLevelStringValue() - { - $m = new StringValue(); - $m->setValue("a"); - $this->assertSame("\"a\"", $m->serializeToJsonString()); - } - - public function testEncodeStringValue() - { - $m = new TestStringValue(['field' => new StringValue(['value' => ''])]); - var_dump($m->getField()); - var_dump($m->serializeToJsonString()); - $this->assertSame("{\"field\":\"\"}", $m->serializeToJsonString()); - } - - public function testDecodeTopLevelBytesValue() - { - $m = new BytesValue(); - $m->mergeFromJsonString("\"YQ==\""); - $this->assertSame("a", $m->getValue()); - } - - public function testEncodeTopLevelBytesValue() - { - $m = new BytesValue(); - $m->setValue("a"); - $this->assertSame("\"YQ==\"", $m->serializeToJsonString()); - } - - public function generateRandomString($length = 10) { - $randomString = str_repeat("+", $length); - for ($i = 0; $i < $length; $i++) { - $randomString[$i] = rand(0, 255); - } - return $randomString; - } - - public function testEncodeTopLevelLongBytesValue() - { - $m = new BytesValue(); - $data = $this->generateRandomString(12007); - $m->setValue($data); - $expected = "\"" . base64_encode($data) . "\""; - $this->assertSame(strlen($expected), strlen($m->serializeToJsonString())); - } - - public function testEncode() - { - $from = new TestMessage(); - $this->expectEmptyFields($from); - $this->setFields($from); - $this->expectFields($from); - - $data = $from->serializeToString(); - $this->assertSame(bin2hex(TestUtil::getGoldenTestMessage()), - bin2hex($data)); - } - - public function testDecode() - { - $to = new TestMessage(); - $to->mergeFromString(TestUtil::getGoldenTestMessage()); - $this->expectFields($to); - } - - public function testEncodeDecode() - { - $from = new TestMessage(); - $this->expectEmptyFields($from); - $this->setFields($from); - $this->expectFields($from); - - $data = $from->serializeToString(); - - $to = new TestMessage(); - $to->mergeFromString($data); - $this->expectFields($to); - } - - public function testEncodeDecodeEmpty() - { - $from = new TestMessage(); - $this->expectEmptyFields($from); - - $data = $from->serializeToString(); - - $to = new TestMessage(); - $to->mergeFromString($data); - $this->expectEmptyFields($to); - } - - public function testEncodeDecodeOneof() - { - $m = new TestMessage(); - - $m->setOneofInt32(1); - $data = $m->serializeToString(); - $n = new TestMessage(); - $n->mergeFromString($data); - $this->assertSame(1, $n->getOneofInt32()); - - $m->setOneofFloat(2.0); - $data = $m->serializeToString(); - $n = new TestMessage(); - $n->mergeFromString($data); - $this->assertSame(2.0, $n->getOneofFloat()); - - $m->setOneofString('abc'); - $data = $m->serializeToString(); - $n = new TestMessage(); - $n->mergeFromString($data); - $this->assertSame('abc', $n->getOneofString()); - - $sub_m = new Sub(); - $sub_m->setA(1); - $m->setOneofMessage($sub_m); - $data = $m->serializeToString(); - $n = new TestMessage(); - $n->mergeFromString($data); - $this->assertSame(1, $n->getOneofMessage()->getA()); - - // Encode default value - $m->setOneofEnum(TestEnum::ZERO); - $data = $m->serializeToString(); - $n = new TestMessage(); - $n->mergeFromString($data); - $this->assertSame("oneof_enum", $n->getMyOneof()); - $this->assertSame(TestEnum::ZERO, $n->getOneofEnum()); - - $m->setOneofString(""); - $data = $m->serializeToString(); - $n = new TestMessage(); - $n->mergeFromString($data); - $this->assertSame("oneof_string", $n->getMyOneof()); - $this->assertSame("", $n->getOneofString()); - - $sub_m = new Sub(); - $m->setOneofMessage($sub_m); - $data = $m->serializeToString(); - $n = new TestMessage(); - $n->mergeFromString($data); - $this->assertSame("oneof_message", $n->getMyOneof()); - $this->assertFalse(is_null($n->getOneofMessage())); - - } - - public function testJsonEncodeDecodeOneof() - { - $m = new TestMessage(); - - $m->setOneofEnum(TestEnum::ONE); - $data = $m->serializeToJsonString(); - $n = new TestMessage(); - $n->mergeFromJsonString($data); - $this->assertSame("oneof_enum", $n->getMyOneof()); - $this->assertSame(TestEnum::ONE, $n->getOneofEnum()); - - $m->setOneofString("a"); - $data = $m->serializeToJsonString(); - $n = new TestMessage(); - $n->mergeFromJsonString($data); - $this->assertSame("oneof_string", $n->getMyOneof()); - $this->assertSame("a", $n->getOneofString()); - - $m->setOneofBytes("bbbb"); - $data = $m->serializeToJsonString(); - $n = new TestMessage(); - $n->mergeFromJsonString($data); - $this->assertSame("oneof_bytes", $n->getMyOneof()); - $this->assertSame("bbbb", $n->getOneofBytes()); - - $sub_m = new Sub(); - $m->setOneofMessage($sub_m); - $data = $m->serializeToJsonString(); - $n = new TestMessage(); - $n->mergeFromJsonString($data); - $this->assertSame("oneof_message", $n->getMyOneof()); - $this->assertFalse(is_null($n->getOneofMessage())); - } - - public function testPackedEncode() - { - $from = new TestPackedMessage(); - TestUtil::setTestPackedMessage($from); - $this->assertSame(TestUtil::getGoldenTestPackedMessage(), - $from->serializeToString()); - } - - public function testPackedDecodePacked() - { - $to = new TestPackedMessage(); - $to->mergeFromString(TestUtil::getGoldenTestPackedMessage()); - TestUtil::assertTestPackedMessage($to); - $this->assertTrue(true); - } - - public function testPackedDecodeUnpacked() - { - $to = new TestPackedMessage(); - $to->mergeFromString(TestUtil::getGoldenTestUnpackedMessage()); - TestUtil::assertTestPackedMessage($to); - $this->assertTrue(true); - } - - public function testUnpackedEncode() - { - $from = new TestUnpackedMessage(); - TestUtil::setTestPackedMessage($from); - $this->assertSame(TestUtil::getGoldenTestUnpackedMessage(), - $from->serializeToString()); - } - - public function testUnpackedDecodePacked() - { - $to = new TestUnpackedMessage(); - $to->mergeFromString(TestUtil::getGoldenTestPackedMessage()); - TestUtil::assertTestPackedMessage($to); - $this->assertTrue(true); - } - - public function testUnpackedDecodeUnpacked() - { - $to = new TestUnpackedMessage(); - $to->mergeFromString(TestUtil::getGoldenTestUnpackedMessage()); - TestUtil::assertTestPackedMessage($to); - $this->assertTrue(true); - } - - public function testDecodeInt64() - { - // Read 64 testing - $testVals = array( - '10' => '100a', - '100' => '1064', - '800' => '10a006', - '6400' => '108032', - '70400' => '1080a604', - '774400' => '1080a22f', - '9292800' => '108098b704', - '74342400' => '1080c0b923', - '743424000' => '108080bfe202', - '8177664000' => '108080b5bb1e', - '65421312000' => '108080a8dbf301', - '785055744000' => '108080e0c7ec16', - '9420668928000' => '10808080dd969202', - '103627358208000' => '10808080fff9c717', - '1139900940288000' => '10808080f5bd978302', - '13678811283456000' => '10808080fce699a618', - '109430490267648000' => '10808080e0b7ceb1c201', - '984874412408832000' => '10808080e0f5c1bed50d', - ); - - $msg = new TestMessage(); - foreach ($testVals as $original => $encoded) { - $msg->setOptionalInt64($original); - $data = $msg->serializeToString(); - $this->assertSame($encoded, bin2hex($data)); - $msg->setOptionalInt64(0); - $msg->mergeFromString($data); - $this->assertEquals($original, $msg->getOptionalInt64()); - } - } - - public function testDecodeToExistingMessage() - { - $m1 = new TestMessage(); - $this->setFields($m1); - $this->expectFields($m1); - - $m2 = new TestMessage(); - $this->setFields2($m2); - $data = $m2->serializeToString(); - - $m1->mergeFromString($data); - $this->expectFieldsMerged($m1); - } - - public function testDecodeFieldNonExist() - { - $data = hex2bin('c80501'); - $m = new TestMessage(); - $m->mergeFromString($data); - $this->assertTrue(true); - } - - public function testEncodeNegativeInt32() - { - $m = new TestMessage(); - $m->setOptionalInt32(-1); - $data = $m->serializeToString(); - $this->assertSame("08ffffffffffffffffff01", bin2hex($data)); - } - - public function testDecodeNegativeInt32() - { - $m = new TestMessage(); - $this->assertEquals(0, $m->getOptionalInt32()); - $m->mergeFromString(hex2bin("08ffffffffffffffffff01")); - $this->assertEquals(-1, $m->getOptionalInt32()); - - $m = new TestMessage(); - $this->assertEquals(0, $m->getOptionalInt32()); - $m->mergeFromString(hex2bin("08ffffffff0f")); - $this->assertEquals(-1, $m->getOptionalInt32()); - } - - public function testRandomFieldOrder() - { - $m = new TestRandomFieldOrder(); - $data = $m->serializeToString(); - $this->assertSame("", $data); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidInt32() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('08')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidSubMessage() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('9A010108')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidInt64() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('10')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidUInt32() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('18')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidUInt64() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('20')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidSInt32() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('28')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidSInt64() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('30')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidFixed32() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('3D')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidFixed64() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('41')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidSFixed32() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('4D')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidSFixed64() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('51')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidFloat() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('5D')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidDouble() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('61')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidBool() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('68')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidStringLengthMiss() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('72')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidStringDataMiss() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('7201')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidBytesLengthMiss() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('7A')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidBytesDataMiss() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('7A01')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidEnum() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('8001')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidMessageLengthMiss() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('8A01')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidMessageDataMiss() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin('8A0101')); - } - - /** - * @expectedException Exception - */ - public function testDecodeInvalidPackedMessageLength() - { - $m = new TestPackedMessage(); - $m->mergeFromString(hex2bin('D205')); - } - - public function testUnknown() - { - // Test preserve unknown for varint. - $m = new TestMessage(); - $from = hex2bin('F80601'); // TODO(teboring): Add a util to encode - // varint for better readability - $m->mergeFromString($from); - $to = $m->serializeToString(); - $this->assertSame(bin2hex($from), bin2hex($to)); - - // Test preserve unknown for 64-bit. - $m = new TestMessage(); - $from = hex2bin('F9060000000000000000'); - $m->mergeFromString($from); - $to = $m->serializeToString(); - $this->assertSame(bin2hex($from), bin2hex($to)); - - // Test preserve unknown for length delimited. - $m = new TestMessage(); - $from = hex2bin('FA0600'); - $m->mergeFromString($from); - $to = $m->serializeToString(); - $this->assertSame(bin2hex($from), bin2hex($to)); - - // Test preserve unknown for 32-bit. - $m = new TestMessage(); - $from = hex2bin('FD0600000000'); - $m->mergeFromString($from); - $to = $m->serializeToString(); - $this->assertSame(bin2hex($from), bin2hex($to)); - - // Test discard unknown in message. - $m = new TestMessage(); - $from = hex2bin('F80601'); - $m->mergeFromString($from); - $m->discardUnknownFields(); - $to = $m->serializeToString(); - $this->assertSame("", bin2hex($to)); - - // Test discard unknown for singular message field. - $m = new TestMessage(); - $from = hex2bin('8A0103F80601'); - $m->mergeFromString($from); - $m->discardUnknownFields(); - $to = $m->serializeToString(); - $this->assertSame("8a0100", bin2hex($to)); - - // Test discard unknown for repeated message field. - $m = new TestMessage(); - $from = hex2bin('FA0203F80601'); - $m->mergeFromString($from); - $m->discardUnknownFields(); - $to = $m->serializeToString(); - $this->assertSame("fa0200", bin2hex($to)); - - // Test discard unknown for map message value field. - $m = new TestMessage(); - $from = hex2bin("BA050708011203F80601"); - $m->mergeFromString($from); - $m->discardUnknownFields(); - $to = $m->serializeToString(); - $this->assertSame("ba050408011200", bin2hex($to)); - - // Test discard unknown for singular message field. - $m = new TestMessage(); - $from = hex2bin('9A0403F80601'); - $m->mergeFromString($from); - $m->discardUnknownFields(); - $to = $m->serializeToString(); - $this->assertSame("9a0400", bin2hex($to)); - } - - public function testJsonUnknown() - { - // Test unknown number - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":1, - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - - // Test unknown bool - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":true, - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - - // Test unknown string - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":\"abc\", - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - - // Test unknown null - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":null, - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - - // Test unknown array - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":[], - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - - // Test unknown number array - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":[1], - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - - // Test unknown bool array - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":[true], - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - - // Test unknown string array - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":[\"a\"], - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - - // Test unknown null array - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":[null], - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - - // Test unknown array array - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":[[]], - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - - // Test unknown object array - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":[{}], - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - - // Test unknown double value array - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":[1, 2], - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - - // Test unknown object - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":{}, - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - - // Test unknown number object - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":{\"a\":1}, - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - - // Test unknown bool object - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":{\"a\":true}, - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - - // Test unknown string object - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":{\"a\":\"a\"}, - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - - // Test unknown null object - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":{\"a\":null}, - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - - // Test unknown array object - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":{\"a\":[]}, - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - - // Test unknown object object - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":{\"a\":{}}, - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - - // Test unknown double value object - $m = new TestMessage(); - $m->mergeFromJsonString("{\"unknown\":{\"a\":1, \"b\":1}, - \"optionalInt32\":1}", true); - $this->assertSame(1, $m->getOptionalInt32()); - } - - public function testJsonEncode() - { - $from = new TestMessage(); - $this->setFields($from); - $data = $from->serializeToJsonString(); - $to = new TestMessage(); - $to->mergeFromJsonString($data); - $this->expectFields($to); - } - - public function testDecodeDuration() - { - $m = new Google\Protobuf\Duration(); - $m->mergeFromJsonString("\"1234.5678s\""); - $this->assertEquals(1234, $m->getSeconds()); - $this->assertEquals(567800000, $m->getNanos()); - } - - public function testEncodeDuration() - { - $m = new Google\Protobuf\Duration(); - $m->setSeconds(1234); - $m->setNanos(999999999); - $this->assertEquals("\"1234.999999999s\"", $m->serializeToJsonString()); - } - - public function testDecodeTimestamp() - { - $m = new Google\Protobuf\Timestamp(); - $m->mergeFromJsonString("\"2000-01-01T00:00:00.123456789Z\""); - $this->assertEquals(946684800, $m->getSeconds()); - $this->assertEquals(123456789, $m->getNanos()); - } - - public function testEncodeTimestamp() - { - $m = new Google\Protobuf\Timestamp(); - $m->setSeconds(946684800); - $m->setNanos(123456789); - $this->assertEquals("\"2000-01-01T00:00:00.123456789Z\"", - $m->serializeToJsonString()); - } - - public function testDecodeTopLevelValue() - { - $m = new Value(); - $m->mergeFromJsonString("\"a\""); - $this->assertSame("a", $m->getStringValue()); - - $m = new Value(); - $m->mergeFromJsonString("1.5"); - $this->assertSame(1.5, $m->getNumberValue()); - - $m = new Value(); - $m->mergeFromJsonString("true"); - $this->assertSame(true, $m->getBoolValue()); - - $m = new Value(); - $m->mergeFromJsonString("null"); - $this->assertSame("null_value", $m->getKind()); - - $m = new Value(); - $m->mergeFromJsonString("[1]"); - $this->assertSame("list_value", $m->getKind()); - - $m = new Value(); - $m->mergeFromJsonString("{\"a\":1}"); - $this->assertSame("struct_value", $m->getKind()); - } - - public function testEncodeTopLevelValue() - { - $m = new Value(); - $m->setStringValue("a"); - $this->assertSame("\"a\"", $m->serializeToJsonString()); - - $m = new Value(); - $m->setNumberValue(1.5); - $this->assertSame("1.5", $m->serializeToJsonString()); - - $m = new Value(); - $m->setBoolValue(true); - $this->assertSame("true", $m->serializeToJsonString()); - - $m = new Value(); - $m->setNullValue(0); - $this->assertSame("null", $m->serializeToJsonString()); - } - - public function testDecodeTopLevelListValue() - { - $m = new ListValue(); - $m->mergeFromJsonString("[1]"); - $this->assertSame(1.0, $m->getValues()[0]->getNumberValue()); - } - - public function testEncodeTopLevelListValue() - { - $m = new ListValue(); - $arr = $m->getValues(); - $sub = new Value(); - $sub->setNumberValue(1.5); - $arr[] = $sub; - $this->assertSame("[1.5]", $m->serializeToJsonString()); - } - - public function testEncodeEmptyListValue() - { - $m = new Struct(); - $m->setFields(['test' => (new Value())->setListValue(new ListValue())]); - $this->assertSame('{"test":[]}', $m->serializeToJsonString()); - } - - public function testDecodeTopLevelStruct() - { - $m = new Struct(); - $m->mergeFromJsonString("{\"a\":{\"b\":1}}"); - $this->assertSame(1.0, $m->getFields()["a"] - ->getStructValue() - ->getFields()["b"]->getNumberValue()); - } - - public function testEncodeTopLevelStruct() - { - $m = new Struct(); - $map = $m->getFields(); - $sub = new Value(); - $sub->setNumberValue(1.5); - $map["a"] = $sub; - $this->assertSame("{\"a\":1.5}", $m->serializeToJsonString()); - } - - public function testEncodeEmptyStruct() - { - $m = new Struct(); - $m->setFields(['test' => (new Value())->setStructValue(new Struct())]); - $this->assertSame('{"test":{}}', $m->serializeToJsonString()); - } - - public function testDecodeTopLevelAny() - { - // Make sure packed message has been created at least once. - $packed = new TestMessage(); - - $m1 = new Any(); - $m1->mergeFromJsonString( - "{\"optionalInt32\": 1, " . - "\"@type\":\"type.googleapis.com/foo.TestMessage\"}"); - $this->assertSame("type.googleapis.com/foo.TestMessage", - $m1->getTypeUrl()); - $this->assertSame("0801", bin2hex($m1->getValue())); - - $m2 = new Any(); - $m2->mergeFromJsonString( - "{\"@type\":\"type.googleapis.com/foo.TestMessage\", " . - "\"optionalInt32\": 1}"); - $this->assertSame("type.googleapis.com/foo.TestMessage", - $m2->getTypeUrl()); - $this->assertSame("0801", bin2hex($m2->getValue())); - - $m3 = new Any(); - $m3->mergeFromJsonString( - "{\"optionalInt32\": 1, " . - "\"@type\":\"type.googleapis.com/foo.TestMessage\", " . - "\"optionalInt64\": 2}"); - $this->assertSame("type.googleapis.com/foo.TestMessage", - $m3->getTypeUrl()); - $this->assertSame("08011002", bin2hex($m3->getValue())); - } - - public function testDecodeAny() - { - // Make sure packed message has been created at least once. - $packed = new TestMessage(); - - $m1 = new TestAny(); - $m1->mergeFromJsonString( - "{\"any\": {\"optionalInt32\": 1, " . - "\"@type\":\"type.googleapis.com/foo.TestMessage\"}}"); - $this->assertSame("type.googleapis.com/foo.TestMessage", - $m1->getAny()->getTypeUrl()); - $this->assertSame("0801", bin2hex($m1->getAny()->getValue())); - - $m2 = new TestAny(); - $m2->mergeFromJsonString( - "{\"any\":{\"@type\":\"type.googleapis.com/foo.TestMessage\", " . - "\"optionalInt32\": 1}}"); - $this->assertSame("type.googleapis.com/foo.TestMessage", - $m2->getAny()->getTypeUrl()); - $this->assertSame("0801", bin2hex($m2->getAny()->getValue())); - - $m3 = new TestAny(); - $m3->mergeFromJsonString( - "{\"any\":{\"optionalInt32\": 1, " . - "\"@type\":\"type.googleapis.com/foo.TestMessage\", " . - "\"optionalInt64\": 2}}"); - $this->assertSame("type.googleapis.com/foo.TestMessage", - $m3->getAny()->getTypeUrl()); - $this->assertSame("08011002", bin2hex($m3->getAny()->getValue())); - } - - public function testDecodeAnyWithWellKnownPacked() - { - // Make sure packed message has been created at least once. - $packed = new Int32Value(); - - $m1 = new TestAny(); - $m1->mergeFromJsonString( - "{\"any\":" . - " {\"@type\":\"type.googleapis.com/google.protobuf.Int32Value\"," . - " \"value\":1}}"); - $this->assertSame("type.googleapis.com/google.protobuf.Int32Value", - $m1->getAny()->getTypeUrl()); - $this->assertSame("0801", bin2hex($m1->getAny()->getValue())); - } - - /** - * @expectedException Exception - */ - public function testDecodeAnyWithUnknownPacked() - { - $m = new TestAny(); - $m->mergeFromJsonString( - "{\"any\":" . - " {\"@type\":\"type.googleapis.com/unknown\"," . - " \"value\":1}}"); - } - - public function testEncodeTopLevelAny() - { - // Test a normal message. - $packed = new TestMessage(); - $packed->setOptionalInt32(123); - $packed->setOptionalString("abc"); - - $m = new Any(); - $m->pack($packed); - $expected1 = - "{\"@type\":\"type.googleapis.com/foo.TestMessage\"," . - "\"optional_int32\":123,\"optional_string\":\"abc\"}"; - $expected2 = - "{\"@type\":\"type.googleapis.com/foo.TestMessage\"," . - "\"optionalInt32\":123,\"optionalString\":\"abc\"}"; - $result = $m->serializeToJsonString(); - $this->assertTrue($expected1 === $result || $expected2 === $result); - - // Test a well known message. - $packed = new Int32Value(); - $packed->setValue(123); - - $m = new Any(); - $m->pack($packed); - $this->assertSame( - "{\"@type\":\"type.googleapis.com/google.protobuf.Int32Value\"," . - "\"value\":123}", - $m->serializeToJsonString()); - - // Test an Any message. - $outer = new Any(); - $outer->pack($m); - $this->assertSame( - "{\"@type\":\"type.googleapis.com/google.protobuf.Any\"," . - "\"value\":{\"@type\":\"type.googleapis.com/google.protobuf.Int32Value\"," . - "\"value\":123}}", - $outer->serializeToJsonString()); - - // Test a Timestamp message. - $packed = new Google\Protobuf\Timestamp(); - $packed->setSeconds(946684800); - $packed->setNanos(123456789); - $m = new Any(); - $m->pack($packed); - $this->assertSame( - "{\"@type\":\"type.googleapis.com/google.protobuf.Timestamp\"," . - "\"value\":\"2000-01-01T00:00:00.123456789Z\"}", - $m->serializeToJsonString()); - } - - public function testDecodeTopLevelFieldMask() - { - $m = new TestMessage(); - $m->setMapStringString(['a'=>'abcdefg']); - $data1 = $m->serializeToJsonString(); - $n = new TestMessage(); - $n->mergeFromJsonString($data1); - $data2 = $n->serializeToJsonString(); - $this->assertSame($data1, $data2); - - $m = new FieldMask(); - $m->mergeFromJsonString("\"foo.barBaz,qux\""); - $this->assertSame("foo.bar_baz", $m->getPaths()[0]); - $this->assertSame("qux", $m->getPaths()[1]); - } - - public function testEncodeTopLevelFieldMask() - { - $m = new FieldMask(); - $m->setPaths(["foo.bar_baz", "qux"]); - $this->assertSame("\"foo.barBaz,qux\"", $m->serializeToJsonString()); - } - - public function testDecodeEmptyFieldMask() - { - $m = new FieldMask(); - $m->mergeFromJsonString("\"\""); - $this->assertEquals("", $m->serializeToString()); - } - - public function testJsonDecodeMapWithDefaultValueKey() - { - $m = new TestMessage(); - $m->getMapInt32Int32()[0] = 0; - $this->assertSame("{\"mapInt32Int32\":{\"0\":0}}", - $m->serializeToJsonString()); - - $m = new TestMessage(); - $m->getMapStringString()[""] = ""; - $this->assertSame("{\"mapStringString\":{\"\":\"\"}}", - $m->serializeToJsonString()); - } - - public function testJsonDecodeNumericStringMapKey() - { - $m = new TestMessage(); - $m->getMapStringString()["1"] = "1"; - $data = $m->serializeToJsonString(); - $this->assertSame("{\"mapStringString\":{\"1\":\"1\"}}", $data); - $n = new TestMessage(); - $n->mergeFromJsonString($data); - } - - public function testMessageMapNoValue() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin("CA0700")); - $m->serializeToString(); - $this->assertTrue(true); - } - - public function testAnyMapNoValue() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin("D20700")); - $m->serializeToString(); - $this->assertTrue(true); - } - - public function testListValueMapNoValue() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin("DA0700")); - $m->serializeToString(); - $this->assertTrue(true); - } - - public function testStructMapNoValue() - { - $m = new TestMessage(); - $m->mergeFromString(hex2bin("E20700")); - $m->serializeToString(); - $this->assertTrue(true); - } + # public function testDecodeJsonSimple() + # { + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"optionalInt32\":1}"); + # $this->assertEquals(1, $m->getOptionalInt32()); + # } + + # public function testDecodeTopLevelBoolValue() + # { + # $m = new BoolValue(); + + # $m->mergeFromJsonString("true"); + # $this->assertEquals(true, $m->getValue()); + + # $m->mergeFromJsonString("false"); + # $this->assertEquals(false, $m->getValue()); + # } + + # public function testEncodeTopLevelBoolValue() + # { + # $m = new BoolValue(); + # $m->setValue(true); + # $this->assertSame("true", $m->serializeToJsonString()); + # } + + # public function testDecodeTopLevelDoubleValue() + # { + # $m = new DoubleValue(); + # $m->mergeFromJsonString("1.5"); + # $this->assertEquals(1.5, $m->getValue()); + # } + + # public function testEncodeTopLevelDoubleValue() + # { + # $m = new DoubleValue(); + # $m->setValue(1.5); + # $this->assertSame("1.5", $m->serializeToJsonString()); + # } + + # public function testDecodeTopLevelFloatValue() + # { + # $m = new FloatValue(); + # $m->mergeFromJsonString("1.5"); + # $this->assertEquals(1.5, $m->getValue()); + # } + + # public function testEncodeTopLevelFloatValue() + # { + # $m = new FloatValue(); + # $m->setValue(1.5); + # $this->assertSame("1.5", $m->serializeToJsonString()); + # } + + # public function testDecodeTopLevelInt32Value() + # { + # $m = new Int32Value(); + # $m->mergeFromJsonString("1"); + # $this->assertEquals(1, $m->getValue()); + # } + + # public function testEncodeTopLevelInt32Value() + # { + # $m = new Int32Value(); + # $m->setValue(1); + # $this->assertSame("1", $m->serializeToJsonString()); + # } + + # public function testDecodeTopLevelUInt32Value() + # { + # $m = new UInt32Value(); + # $m->mergeFromJsonString("1"); + # $this->assertEquals(1, $m->getValue()); + # } + + # public function testEncodeTopLevelUInt32Value() + # { + # $m = new UInt32Value(); + # $m->setValue(1); + # $this->assertSame("1", $m->serializeToJsonString()); + # } + + # public function testDecodeTopLevelInt64Value() + # { + # $m = new Int64Value(); + # $m->mergeFromJsonString("1"); + # $this->assertEquals(1, $m->getValue()); + # } + + # public function testDecodeTopLevelInt64ValueAsString() + # { + # $m = new Int64Value(); + # $m->mergeFromJsonString("\"1\""); + # $this->assertEquals(1, $m->getValue()); + # } + + # public function testEncodeTopLevelInt64Value() + # { + # $m = new Int64Value(); + # $m->setValue(1); + # $this->assertSame("\"1\"", $m->serializeToJsonString()); + # } + + # public function testDecodeTopLevelUInt64Value() + # { + # $m = new UInt64Value(); + # $m->mergeFromJsonString("1"); + # $this->assertEquals(1, $m->getValue()); + # } + + # public function testDecodeTopLevelUInt64ValueAsString() + # { + # $m = new UInt64Value(); + # $m->mergeFromJsonString("\"1\""); + # $this->assertEquals(1, $m->getValue()); + # } + + # public function testEncodeTopLevelUInt64Value() + # { + # $m = new UInt64Value(); + # $m->setValue(1); + # $this->assertSame("\"1\"", $m->serializeToJsonString()); + # } + + # public function testDecodeTopLevelStringValue() + # { + # $m = new StringValue(); + # $m->mergeFromJsonString("\"a\""); + # $this->assertSame("a", $m->getValue()); + # } + + # public function testEncodeTopLevelStringValue() + # { + # $m = new StringValue(); + # $m->setValue("a"); + # $this->assertSame("\"a\"", $m->serializeToJsonString()); + # } + + public function testDecodeStringValue() + { + $m = new TestStringValue(); + $m->mergeFromJsonString("{\"field\":\"\"}"); + $this->assertSame("", $m->getValue()); + } + + # public function testEncodeStringValue() + # { + # $m = new TestStringValue(['field' => new StringValue(['value' => ''])]); + # $this->assertSame("{\"field\":\"\"}", $m->serializeToJsonString()); + # } + + # public function testDecodeTopLevelBytesValue() + # { + # $m = new BytesValue(); + # $m->mergeFromJsonString("\"YQ==\""); + # $this->assertSame("a", $m->getValue()); + # } + + # public function testEncodeTopLevelBytesValue() + # { + # $m = new BytesValue(); + # $m->setValue("a"); + # $this->assertSame("\"YQ==\"", $m->serializeToJsonString()); + # } + + # public function generateRandomString($length = 10) { + # $randomString = str_repeat("+", $length); + # for ($i = 0; $i < $length; $i++) { + # $randomString[$i] = rand(0, 255); + # } + # return $randomString; + # } + + # public function testEncodeTopLevelLongBytesValue() + # { + # $m = new BytesValue(); + # $data = $this->generateRandomString(12007); + # $m->setValue($data); + # $expected = "\"" . base64_encode($data) . "\""; + # $this->assertSame(strlen($expected), strlen($m->serializeToJsonString())); + # } + + # public function testEncode() + # { + # $from = new TestMessage(); + # $this->expectEmptyFields($from); + # $this->setFields($from); + # $this->expectFields($from); + + # $data = $from->serializeToString(); + # $this->assertSame(bin2hex(TestUtil::getGoldenTestMessage()), + # bin2hex($data)); + # } + + # public function testDecode() + # { + # $to = new TestMessage(); + # $to->mergeFromString(TestUtil::getGoldenTestMessage()); + # $this->expectFields($to); + # } + + # public function testEncodeDecode() + # { + # $from = new TestMessage(); + # $this->expectEmptyFields($from); + # $this->setFields($from); + # $this->expectFields($from); + + # $data = $from->serializeToString(); + + # $to = new TestMessage(); + # $to->mergeFromString($data); + # $this->expectFields($to); + # } + + # public function testEncodeDecodeEmpty() + # { + # $from = new TestMessage(); + # $this->expectEmptyFields($from); + + # $data = $from->serializeToString(); + + # $to = new TestMessage(); + # $to->mergeFromString($data); + # $this->expectEmptyFields($to); + # } + + # public function testEncodeDecodeOneof() + # { + # $m = new TestMessage(); + + # $m->setOneofInt32(1); + # $data = $m->serializeToString(); + # $n = new TestMessage(); + # $n->mergeFromString($data); + # $this->assertSame(1, $n->getOneofInt32()); + + # $m->setOneofFloat(2.0); + # $data = $m->serializeToString(); + # $n = new TestMessage(); + # $n->mergeFromString($data); + # $this->assertSame(2.0, $n->getOneofFloat()); + + # $m->setOneofString('abc'); + # $data = $m->serializeToString(); + # $n = new TestMessage(); + # $n->mergeFromString($data); + # $this->assertSame('abc', $n->getOneofString()); + + # $sub_m = new Sub(); + # $sub_m->setA(1); + # $m->setOneofMessage($sub_m); + # $data = $m->serializeToString(); + # $n = new TestMessage(); + # $n->mergeFromString($data); + # $this->assertSame(1, $n->getOneofMessage()->getA()); + + # // Encode default value + # $m->setOneofEnum(TestEnum::ZERO); + # $data = $m->serializeToString(); + # $n = new TestMessage(); + # $n->mergeFromString($data); + # $this->assertSame("oneof_enum", $n->getMyOneof()); + # $this->assertSame(TestEnum::ZERO, $n->getOneofEnum()); + + # $m->setOneofString(""); + # $data = $m->serializeToString(); + # $n = new TestMessage(); + # $n->mergeFromString($data); + # $this->assertSame("oneof_string", $n->getMyOneof()); + # $this->assertSame("", $n->getOneofString()); + + # $sub_m = new Sub(); + # $m->setOneofMessage($sub_m); + # $data = $m->serializeToString(); + # $n = new TestMessage(); + # $n->mergeFromString($data); + # $this->assertSame("oneof_message", $n->getMyOneof()); + # $this->assertFalse(is_null($n->getOneofMessage())); + + # } + + # public function testJsonEncodeDecodeOneof() + # { + # $m = new TestMessage(); + + # $m->setOneofEnum(TestEnum::ONE); + # $data = $m->serializeToJsonString(); + # $n = new TestMessage(); + # $n->mergeFromJsonString($data); + # $this->assertSame("oneof_enum", $n->getMyOneof()); + # $this->assertSame(TestEnum::ONE, $n->getOneofEnum()); + + # $m->setOneofString("a"); + # $data = $m->serializeToJsonString(); + # $n = new TestMessage(); + # $n->mergeFromJsonString($data); + # $this->assertSame("oneof_string", $n->getMyOneof()); + # $this->assertSame("a", $n->getOneofString()); + + # $m->setOneofBytes("bbbb"); + # $data = $m->serializeToJsonString(); + # $n = new TestMessage(); + # $n->mergeFromJsonString($data); + # $this->assertSame("oneof_bytes", $n->getMyOneof()); + # $this->assertSame("bbbb", $n->getOneofBytes()); + + # $sub_m = new Sub(); + # $m->setOneofMessage($sub_m); + # $data = $m->serializeToJsonString(); + # $n = new TestMessage(); + # $n->mergeFromJsonString($data); + # $this->assertSame("oneof_message", $n->getMyOneof()); + # $this->assertFalse(is_null($n->getOneofMessage())); + # } + + # public function testPackedEncode() + # { + # $from = new TestPackedMessage(); + # TestUtil::setTestPackedMessage($from); + # $this->assertSame(TestUtil::getGoldenTestPackedMessage(), + # $from->serializeToString()); + # } + + # public function testPackedDecodePacked() + # { + # $to = new TestPackedMessage(); + # $to->mergeFromString(TestUtil::getGoldenTestPackedMessage()); + # TestUtil::assertTestPackedMessage($to); + # $this->assertTrue(true); + # } + + # public function testPackedDecodeUnpacked() + # { + # $to = new TestPackedMessage(); + # $to->mergeFromString(TestUtil::getGoldenTestUnpackedMessage()); + # TestUtil::assertTestPackedMessage($to); + # $this->assertTrue(true); + # } + + # public function testUnpackedEncode() + # { + # $from = new TestUnpackedMessage(); + # TestUtil::setTestPackedMessage($from); + # $this->assertSame(TestUtil::getGoldenTestUnpackedMessage(), + # $from->serializeToString()); + # } + + # public function testUnpackedDecodePacked() + # { + # $to = new TestUnpackedMessage(); + # $to->mergeFromString(TestUtil::getGoldenTestPackedMessage()); + # TestUtil::assertTestPackedMessage($to); + # $this->assertTrue(true); + # } + + # public function testUnpackedDecodeUnpacked() + # { + # $to = new TestUnpackedMessage(); + # $to->mergeFromString(TestUtil::getGoldenTestUnpackedMessage()); + # TestUtil::assertTestPackedMessage($to); + # $this->assertTrue(true); + # } + + # public function testDecodeInt64() + # { + # // Read 64 testing + # $testVals = array( + # '10' => '100a', + # '100' => '1064', + # '800' => '10a006', + # '6400' => '108032', + # '70400' => '1080a604', + # '774400' => '1080a22f', + # '9292800' => '108098b704', + # '74342400' => '1080c0b923', + # '743424000' => '108080bfe202', + # '8177664000' => '108080b5bb1e', + # '65421312000' => '108080a8dbf301', + # '785055744000' => '108080e0c7ec16', + # '9420668928000' => '10808080dd969202', + # '103627358208000' => '10808080fff9c717', + # '1139900940288000' => '10808080f5bd978302', + # '13678811283456000' => '10808080fce699a618', + # '109430490267648000' => '10808080e0b7ceb1c201', + # '984874412408832000' => '10808080e0f5c1bed50d', + # ); + + # $msg = new TestMessage(); + # foreach ($testVals as $original => $encoded) { + # $msg->setOptionalInt64($original); + # $data = $msg->serializeToString(); + # $this->assertSame($encoded, bin2hex($data)); + # $msg->setOptionalInt64(0); + # $msg->mergeFromString($data); + # $this->assertEquals($original, $msg->getOptionalInt64()); + # } + # } + + # public function testDecodeToExistingMessage() + # { + # $m1 = new TestMessage(); + # $this->setFields($m1); + # $this->expectFields($m1); + + # $m2 = new TestMessage(); + # $this->setFields2($m2); + # $data = $m2->serializeToString(); + + # $m1->mergeFromString($data); + # $this->expectFieldsMerged($m1); + # } + + # public function testDecodeFieldNonExist() + # { + # $data = hex2bin('c80501'); + # $m = new TestMessage(); + # $m->mergeFromString($data); + # $this->assertTrue(true); + # } + + # public function testEncodeNegativeInt32() + # { + # $m = new TestMessage(); + # $m->setOptionalInt32(-1); + # $data = $m->serializeToString(); + # $this->assertSame("08ffffffffffffffffff01", bin2hex($data)); + # } + + # public function testDecodeNegativeInt32() + # { + # $m = new TestMessage(); + # $this->assertEquals(0, $m->getOptionalInt32()); + # $m->mergeFromString(hex2bin("08ffffffffffffffffff01")); + # $this->assertEquals(-1, $m->getOptionalInt32()); + + # $m = new TestMessage(); + # $this->assertEquals(0, $m->getOptionalInt32()); + # $m->mergeFromString(hex2bin("08ffffffff0f")); + # $this->assertEquals(-1, $m->getOptionalInt32()); + # } + + # public function testRandomFieldOrder() + # { + # $m = new TestRandomFieldOrder(); + # $data = $m->serializeToString(); + # $this->assertSame("", $data); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidInt32() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('08')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidSubMessage() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('9A010108')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidInt64() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('10')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidUInt32() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('18')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidUInt64() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('20')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidSInt32() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('28')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidSInt64() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('30')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidFixed32() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('3D')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidFixed64() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('41')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidSFixed32() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('4D')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidSFixed64() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('51')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidFloat() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('5D')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidDouble() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('61')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidBool() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('68')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidStringLengthMiss() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('72')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidStringDataMiss() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('7201')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidBytesLengthMiss() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('7A')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidBytesDataMiss() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('7A01')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidEnum() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('8001')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidMessageLengthMiss() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('8A01')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidMessageDataMiss() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin('8A0101')); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeInvalidPackedMessageLength() + # { + # $m = new TestPackedMessage(); + # $m->mergeFromString(hex2bin('D205')); + # } + + # public function testUnknown() + # { + # // Test preserve unknown for varint. + # $m = new TestMessage(); + # $from = hex2bin('F80601'); // TODO(teboring): Add a util to encode + # // varint for better readability + # $m->mergeFromString($from); + # $to = $m->serializeToString(); + # $this->assertSame(bin2hex($from), bin2hex($to)); + + # // Test preserve unknown for 64-bit. + # $m = new TestMessage(); + # $from = hex2bin('F9060000000000000000'); + # $m->mergeFromString($from); + # $to = $m->serializeToString(); + # $this->assertSame(bin2hex($from), bin2hex($to)); + + # // Test preserve unknown for length delimited. + # $m = new TestMessage(); + # $from = hex2bin('FA0600'); + # $m->mergeFromString($from); + # $to = $m->serializeToString(); + # $this->assertSame(bin2hex($from), bin2hex($to)); + + # // Test preserve unknown for 32-bit. + # $m = new TestMessage(); + # $from = hex2bin('FD0600000000'); + # $m->mergeFromString($from); + # $to = $m->serializeToString(); + # $this->assertSame(bin2hex($from), bin2hex($to)); + + # // Test discard unknown in message. + # $m = new TestMessage(); + # $from = hex2bin('F80601'); + # $m->mergeFromString($from); + # $m->discardUnknownFields(); + # $to = $m->serializeToString(); + # $this->assertSame("", bin2hex($to)); + + # // Test discard unknown for singular message field. + # $m = new TestMessage(); + # $from = hex2bin('8A0103F80601'); + # $m->mergeFromString($from); + # $m->discardUnknownFields(); + # $to = $m->serializeToString(); + # $this->assertSame("8a0100", bin2hex($to)); + + # // Test discard unknown for repeated message field. + # $m = new TestMessage(); + # $from = hex2bin('FA0203F80601'); + # $m->mergeFromString($from); + # $m->discardUnknownFields(); + # $to = $m->serializeToString(); + # $this->assertSame("fa0200", bin2hex($to)); + + # // Test discard unknown for map message value field. + # $m = new TestMessage(); + # $from = hex2bin("BA050708011203F80601"); + # $m->mergeFromString($from); + # $m->discardUnknownFields(); + # $to = $m->serializeToString(); + # $this->assertSame("ba050408011200", bin2hex($to)); + + # // Test discard unknown for singular message field. + # $m = new TestMessage(); + # $from = hex2bin('9A0403F80601'); + # $m->mergeFromString($from); + # $m->discardUnknownFields(); + # $to = $m->serializeToString(); + # $this->assertSame("9a0400", bin2hex($to)); + # } + + # public function testJsonUnknown() + # { + # // Test unknown number + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":1, + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + + # // Test unknown bool + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":true, + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + + # // Test unknown string + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":\"abc\", + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + + # // Test unknown null + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":null, + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + + # // Test unknown array + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":[], + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + + # // Test unknown number array + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":[1], + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + + # // Test unknown bool array + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":[true], + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + + # // Test unknown string array + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":[\"a\"], + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + + # // Test unknown null array + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":[null], + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + + # // Test unknown array array + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":[[]], + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + + # // Test unknown object array + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":[{}], + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + + # // Test unknown double value array + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":[1, 2], + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + + # // Test unknown object + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":{}, + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + + # // Test unknown number object + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":{\"a\":1}, + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + + # // Test unknown bool object + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":{\"a\":true}, + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + + # // Test unknown string object + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":{\"a\":\"a\"}, + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + + # // Test unknown null object + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":{\"a\":null}, + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + + # // Test unknown array object + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":{\"a\":[]}, + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + + # // Test unknown object object + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":{\"a\":{}}, + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + + # // Test unknown double value object + # $m = new TestMessage(); + # $m->mergeFromJsonString("{\"unknown\":{\"a\":1, \"b\":1}, + # \"optionalInt32\":1}", true); + # $this->assertSame(1, $m->getOptionalInt32()); + # } + + # public function testJsonEncode() + # { + # $from = new TestMessage(); + # $this->setFields($from); + # $data = $from->serializeToJsonString(); + # $to = new TestMessage(); + # $to->mergeFromJsonString($data); + # $this->expectFields($to); + # } + + # public function testDecodeDuration() + # { + # $m = new Google\Protobuf\Duration(); + # $m->mergeFromJsonString("\"1234.5678s\""); + # $this->assertEquals(1234, $m->getSeconds()); + # $this->assertEquals(567800000, $m->getNanos()); + # } + + # public function testEncodeDuration() + # { + # $m = new Google\Protobuf\Duration(); + # $m->setSeconds(1234); + # $m->setNanos(999999999); + # $this->assertEquals("\"1234.999999999s\"", $m->serializeToJsonString()); + # } + + # public function testDecodeTimestamp() + # { + # $m = new Google\Protobuf\Timestamp(); + # $m->mergeFromJsonString("\"2000-01-01T00:00:00.123456789Z\""); + # $this->assertEquals(946684800, $m->getSeconds()); + # $this->assertEquals(123456789, $m->getNanos()); + # } + + # public function testEncodeTimestamp() + # { + # $m = new Google\Protobuf\Timestamp(); + # $m->setSeconds(946684800); + # $m->setNanos(123456789); + # $this->assertEquals("\"2000-01-01T00:00:00.123456789Z\"", + # $m->serializeToJsonString()); + # } + + # public function testDecodeTopLevelValue() + # { + # $m = new Value(); + # $m->mergeFromJsonString("\"a\""); + # $this->assertSame("a", $m->getStringValue()); + + # $m = new Value(); + # $m->mergeFromJsonString("1.5"); + # $this->assertSame(1.5, $m->getNumberValue()); + + # $m = new Value(); + # $m->mergeFromJsonString("true"); + # $this->assertSame(true, $m->getBoolValue()); + + # $m = new Value(); + # $m->mergeFromJsonString("null"); + # $this->assertSame("null_value", $m->getKind()); + + # $m = new Value(); + # $m->mergeFromJsonString("[1]"); + # $this->assertSame("list_value", $m->getKind()); + + # $m = new Value(); + # $m->mergeFromJsonString("{\"a\":1}"); + # $this->assertSame("struct_value", $m->getKind()); + # } + + # public function testEncodeTopLevelValue() + # { + # $m = new Value(); + # $m->setStringValue("a"); + # $this->assertSame("\"a\"", $m->serializeToJsonString()); + + # $m = new Value(); + # $m->setNumberValue(1.5); + # $this->assertSame("1.5", $m->serializeToJsonString()); + + # $m = new Value(); + # $m->setBoolValue(true); + # $this->assertSame("true", $m->serializeToJsonString()); + + # $m = new Value(); + # $m->setNullValue(0); + # $this->assertSame("null", $m->serializeToJsonString()); + # } + + # public function testDecodeTopLevelListValue() + # { + # $m = new ListValue(); + # $m->mergeFromJsonString("[1]"); + # $this->assertSame(1.0, $m->getValues()[0]->getNumberValue()); + # } + + # public function testEncodeTopLevelListValue() + # { + # $m = new ListValue(); + # $arr = $m->getValues(); + # $sub = new Value(); + # $sub->setNumberValue(1.5); + # $arr[] = $sub; + # $this->assertSame("[1.5]", $m->serializeToJsonString()); + # } + + # public function testEncodeEmptyListValue() + # { + # $m = new Struct(); + # $m->setFields(['test' => (new Value())->setListValue(new ListValue())]); + # $this->assertSame('{"test":[]}', $m->serializeToJsonString()); + # } + + # public function testDecodeTopLevelStruct() + # { + # $m = new Struct(); + # $m->mergeFromJsonString("{\"a\":{\"b\":1}}"); + # $this->assertSame(1.0, $m->getFields()["a"] + # ->getStructValue() + # ->getFields()["b"]->getNumberValue()); + # } + + # public function testEncodeTopLevelStruct() + # { + # $m = new Struct(); + # $map = $m->getFields(); + # $sub = new Value(); + # $sub->setNumberValue(1.5); + # $map["a"] = $sub; + # $this->assertSame("{\"a\":1.5}", $m->serializeToJsonString()); + # } + + # public function testEncodeEmptyStruct() + # { + # $m = new Struct(); + # $m->setFields(['test' => (new Value())->setStructValue(new Struct())]); + # $this->assertSame('{"test":{}}', $m->serializeToJsonString()); + # } + + # public function testDecodeTopLevelAny() + # { + # // Make sure packed message has been created at least once. + # $packed = new TestMessage(); + + # $m1 = new Any(); + # $m1->mergeFromJsonString( + # "{\"optionalInt32\": 1, " . + # "\"@type\":\"type.googleapis.com/foo.TestMessage\"}"); + # $this->assertSame("type.googleapis.com/foo.TestMessage", + # $m1->getTypeUrl()); + # $this->assertSame("0801", bin2hex($m1->getValue())); + + # $m2 = new Any(); + # $m2->mergeFromJsonString( + # "{\"@type\":\"type.googleapis.com/foo.TestMessage\", " . + # "\"optionalInt32\": 1}"); + # $this->assertSame("type.googleapis.com/foo.TestMessage", + # $m2->getTypeUrl()); + # $this->assertSame("0801", bin2hex($m2->getValue())); + + # $m3 = new Any(); + # $m3->mergeFromJsonString( + # "{\"optionalInt32\": 1, " . + # "\"@type\":\"type.googleapis.com/foo.TestMessage\", " . + # "\"optionalInt64\": 2}"); + # $this->assertSame("type.googleapis.com/foo.TestMessage", + # $m3->getTypeUrl()); + # $this->assertSame("08011002", bin2hex($m3->getValue())); + # } + + # public function testDecodeAny() + # { + # // Make sure packed message has been created at least once. + # $packed = new TestMessage(); + + # $m1 = new TestAny(); + # $m1->mergeFromJsonString( + # "{\"any\": {\"optionalInt32\": 1, " . + # "\"@type\":\"type.googleapis.com/foo.TestMessage\"}}"); + # $this->assertSame("type.googleapis.com/foo.TestMessage", + # $m1->getAny()->getTypeUrl()); + # $this->assertSame("0801", bin2hex($m1->getAny()->getValue())); + + # $m2 = new TestAny(); + # $m2->mergeFromJsonString( + # "{\"any\":{\"@type\":\"type.googleapis.com/foo.TestMessage\", " . + # "\"optionalInt32\": 1}}"); + # $this->assertSame("type.googleapis.com/foo.TestMessage", + # $m2->getAny()->getTypeUrl()); + # $this->assertSame("0801", bin2hex($m2->getAny()->getValue())); + + # $m3 = new TestAny(); + # $m3->mergeFromJsonString( + # "{\"any\":{\"optionalInt32\": 1, " . + # "\"@type\":\"type.googleapis.com/foo.TestMessage\", " . + # "\"optionalInt64\": 2}}"); + # $this->assertSame("type.googleapis.com/foo.TestMessage", + # $m3->getAny()->getTypeUrl()); + # $this->assertSame("08011002", bin2hex($m3->getAny()->getValue())); + # } + + # public function testDecodeAnyWithWellKnownPacked() + # { + # // Make sure packed message has been created at least once. + # $packed = new Int32Value(); + + # $m1 = new TestAny(); + # $m1->mergeFromJsonString( + # "{\"any\":" . + # " {\"@type\":\"type.googleapis.com/google.protobuf.Int32Value\"," . + # " \"value\":1}}"); + # $this->assertSame("type.googleapis.com/google.protobuf.Int32Value", + # $m1->getAny()->getTypeUrl()); + # $this->assertSame("0801", bin2hex($m1->getAny()->getValue())); + # } + + # /** + # * @expectedException Exception + # */ + # public function testDecodeAnyWithUnknownPacked() + # { + # $m = new TestAny(); + # $m->mergeFromJsonString( + # "{\"any\":" . + # " {\"@type\":\"type.googleapis.com/unknown\"," . + # " \"value\":1}}"); + # } + + # public function testEncodeTopLevelAny() + # { + # // Test a normal message. + # $packed = new TestMessage(); + # $packed->setOptionalInt32(123); + # $packed->setOptionalString("abc"); + + # $m = new Any(); + # $m->pack($packed); + # $expected1 = + # "{\"@type\":\"type.googleapis.com/foo.TestMessage\"," . + # "\"optional_int32\":123,\"optional_string\":\"abc\"}"; + # $expected2 = + # "{\"@type\":\"type.googleapis.com/foo.TestMessage\"," . + # "\"optionalInt32\":123,\"optionalString\":\"abc\"}"; + # $result = $m->serializeToJsonString(); + # $this->assertTrue($expected1 === $result || $expected2 === $result); + + # // Test a well known message. + # $packed = new Int32Value(); + # $packed->setValue(123); + + # $m = new Any(); + # $m->pack($packed); + # $this->assertSame( + # "{\"@type\":\"type.googleapis.com/google.protobuf.Int32Value\"," . + # "\"value\":123}", + # $m->serializeToJsonString()); + + # // Test an Any message. + # $outer = new Any(); + # $outer->pack($m); + # $this->assertSame( + # "{\"@type\":\"type.googleapis.com/google.protobuf.Any\"," . + # "\"value\":{\"@type\":\"type.googleapis.com/google.protobuf.Int32Value\"," . + # "\"value\":123}}", + # $outer->serializeToJsonString()); + + # // Test a Timestamp message. + # $packed = new Google\Protobuf\Timestamp(); + # $packed->setSeconds(946684800); + # $packed->setNanos(123456789); + # $m = new Any(); + # $m->pack($packed); + # $this->assertSame( + # "{\"@type\":\"type.googleapis.com/google.protobuf.Timestamp\"," . + # "\"value\":\"2000-01-01T00:00:00.123456789Z\"}", + # $m->serializeToJsonString()); + # } + + # public function testDecodeTopLevelFieldMask() + # { + # $m = new TestMessage(); + # $m->setMapStringString(['a'=>'abcdefg']); + # $data1 = $m->serializeToJsonString(); + # $n = new TestMessage(); + # $n->mergeFromJsonString($data1); + # $data2 = $n->serializeToJsonString(); + # $this->assertSame($data1, $data2); + + # $m = new FieldMask(); + # $m->mergeFromJsonString("\"foo.barBaz,qux\""); + # $this->assertSame("foo.bar_baz", $m->getPaths()[0]); + # $this->assertSame("qux", $m->getPaths()[1]); + # } + + # public function testEncodeTopLevelFieldMask() + # { + # $m = new FieldMask(); + # $m->setPaths(["foo.bar_baz", "qux"]); + # $this->assertSame("\"foo.barBaz,qux\"", $m->serializeToJsonString()); + # } + + # public function testDecodeEmptyFieldMask() + # { + # $m = new FieldMask(); + # $m->mergeFromJsonString("\"\""); + # $this->assertEquals("", $m->serializeToString()); + # } + + # public function testJsonDecodeMapWithDefaultValueKey() + # { + # $m = new TestMessage(); + # $m->getMapInt32Int32()[0] = 0; + # $this->assertSame("{\"mapInt32Int32\":{\"0\":0}}", + # $m->serializeToJsonString()); + + # $m = new TestMessage(); + # $m->getMapStringString()[""] = ""; + # $this->assertSame("{\"mapStringString\":{\"\":\"\"}}", + # $m->serializeToJsonString()); + # } + + # public function testJsonDecodeNumericStringMapKey() + # { + # $m = new TestMessage(); + # $m->getMapStringString()["1"] = "1"; + # $data = $m->serializeToJsonString(); + # $this->assertSame("{\"mapStringString\":{\"1\":\"1\"}}", $data); + # $n = new TestMessage(); + # $n->mergeFromJsonString($data); + # } + + # public function testMessageMapNoValue() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin("CA0700")); + # $m->serializeToString(); + # $this->assertTrue(true); + # } + + # public function testAnyMapNoValue() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin("D20700")); + # $m->serializeToString(); + # $this->assertTrue(true); + # } + + # public function testListValueMapNoValue() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin("DA0700")); + # $m->serializeToString(); + # $this->assertTrue(true); + # } + + # public function testStructMapNoValue() + # { + # $m = new TestMessage(); + # $m->mergeFromString(hex2bin("E20700")); + # $m->serializeToString(); + # $this->assertTrue(true); + # } } From e1b66b086d666c095497f3bcaa4584425e193d93 Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Tue, 29 Oct 2019 23:00:35 +0000 Subject: [PATCH 02/21] Return zval instead of parse frame --- php/ext/google/protobuf/encode_decode.c | 40 +++++++++++++------------ php/tests/encode_decode_test.php | 2 +- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/php/ext/google/protobuf/encode_decode.c b/php/ext/google/protobuf/encode_decode.c index fe8c5e93b5c9..abe7a4abee5b 100644 --- a/php/ext/google/protobuf/encode_decode.c +++ b/php/ext/google/protobuf/encode_decode.c @@ -912,9 +912,9 @@ static void* wrapper_submsg_handler(void* closure, const void* hd) { UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj((void*)submsgdata->md)); zend_class_entry* subklass = subdesc->klass; zval* submsg_php; - MessageHeader* submsg; - wrapperfields_parseframe_t* frame = - (wrapperfields_parseframe_t*)malloc(sizeof(wrapperfields_parseframe_t)); + // MessageHeader* submsg; + // wrapperfields_parseframe_t* frame = + // (wrapperfields_parseframe_t*)malloc(sizeof(wrapperfields_parseframe_t)); CACHED_VALUE* cached = find_zval_property(msg, submsgdata->fd); @@ -935,17 +935,19 @@ static void* wrapper_submsg_handler(void* closure, const void* hd) { } submsg_php = CACHED_PTR_TO_ZVAL_PTR(cached); - submsg = UNBOX(MessageHeader, submsg_php); + // submsg = UNBOX(MessageHeader, submsg_php); - frame->closure = closure; - frame->submsg = submsg; + // frame->closure = closure; + // frame->submsg = submsg; - return frame; + // return frame; + + return submsg_php; } static bool wrapper_submsg_end_handler(void *closure, const void *hd) { - wrapperfields_parseframe_t* frame = closure; - free(frame); + // wrapperfields_parseframe_t* frame = closure; + // free(frame); return true; } @@ -1128,14 +1130,14 @@ static void add_handlers_for_oneof_field(upb_handlers *h, } } -#define DEFINE_WRAPPER_HANDLER(type, ctype) \ - static bool type##wrapper_handler( \ - void* closure, const void* hd, ctype val) { \ - wrapperfields_parseframe_t* frame = closure; \ - MessageHeader* msg = (MessageHeader*)frame->submsg; \ - const size_t *ofs = hd; \ - DEREF(message_data(msg), *ofs, ctype) = val; \ - return true; \ +#define DEFINE_WRAPPER_HANDLER(type, ctype) \ + static bool type##wrapper_handler( \ + void* closure, const void* hd, ctype val) { \ + zval* msg_php = closure; \ + MessageHeader* msg = UNBOX(MessageHeader, msg_php); \ + const size_t *ofs = hd; \ + DEREF(message_data(msg), *ofs, ctype) = val; \ + return true; \ } DEFINE_WRAPPER_HANDLER(bool, bool) @@ -1151,8 +1153,8 @@ DEFINE_WRAPPER_HANDLER(double, double) static bool strwrapper_end_handler(void *closure, const void *hd) { stringfields_parseframe_t* frame = closure; const upb_fielddef **field = (const upb_fielddef **) hd; - wrapperfields_parseframe_t* wrapper_frame = frame->closure; - MessageHeader* msg = wrapper_frame->closure; + zval* msg_php = frame->closure; + MessageHeader* msg = UNBOX(MessageHeader, msg_php); CACHED_VALUE* cached = find_zval_property(msg, *field); diff --git a/php/tests/encode_decode_test.php b/php/tests/encode_decode_test.php index 74bbc65e4ae2..ae7c676d609e 100644 --- a/php/tests/encode_decode_test.php +++ b/php/tests/encode_decode_test.php @@ -172,7 +172,7 @@ public function testDecodeStringValue() { $m = new TestStringValue(); $m->mergeFromJsonString("{\"field\":\"\"}"); - $this->assertSame("", $m->getValue()); + $this->assertSame("", $m->getField()->getValue()); } # public function testEncodeStringValue() From 92002f132266562f2ec9468d9a07edc268a75b4b Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Wed, 30 Oct 2019 21:39:04 +0000 Subject: [PATCH 03/21] Use parse frame --- php/ext/google/protobuf/encode_decode.c | 79 +++++++++++++++++++------ php/tests/encode_decode_test.php | 33 ++++++++++- php/tests/memory_leak_test.php | 1 + php/tests/proto/test.proto | 6 ++ 4 files changed, 98 insertions(+), 21 deletions(-) diff --git a/php/ext/google/protobuf/encode_decode.c b/php/ext/google/protobuf/encode_decode.c index abe7a4abee5b..91d4fba7b3ad 100644 --- a/php/ext/google/protobuf/encode_decode.c +++ b/php/ext/google/protobuf/encode_decode.c @@ -142,6 +142,7 @@ static bool is_wrapper_msg(const upb_msgdef* m) { typedef struct { void* closure; void* submsg; + bool is_msg; } wrapperfields_parseframe_t; #define DEREF(msg, ofs, type) *(type*)(((uint8_t *)msg) + ofs) @@ -448,6 +449,40 @@ static void *appendsubmsg_handler(void *closure, const void *hd) { return submsg; } +// Appends a wrapper submessage to a repeated field. +static void *appendwrappersubmsg_handler(void *closure, const void *hd) { + zval* array = (zval*)closure; + TSRMLS_FETCH(); + RepeatedField* intern = UNBOX(RepeatedField, array); + + const submsg_handlerdata_t *submsgdata = hd; + Descriptor* subdesc = + UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj((void*)submsgdata->md)); + zend_class_entry* subklass = subdesc->klass; + MessageHeader* submsg; + wrapperfields_parseframe_t* frame = + (wrapperfields_parseframe_t*)malloc(sizeof(wrapperfields_parseframe_t)); + +#if PHP_MAJOR_VERSION < 7 + zval* val = NULL; + MAKE_STD_ZVAL(val); + ZVAL_OBJ(val, subklass->create_object(subklass TSRMLS_CC)); + repeated_field_push_native(intern, &val); + submsg = UNBOX(MessageHeader, val); +#else + zend_object* obj = subklass->create_object(subklass TSRMLS_CC); + repeated_field_push_native(intern, &obj); + submsg = (MessageHeader*)((char*)obj - XtOffsetOf(MessageHeader, std)); +#endif + custom_data_init(subklass, submsg PHP_PROTO_TSRMLS_CC); + + frame->closure = closure; + frame->submsg = submsg; + frame->is_msg = true; + + return frame; +} + // Sets a non-repeated submessage field in a message. static void *submsg_handler(void *closure, const void *hd) { MessageHeader* msg = closure; @@ -912,9 +947,9 @@ static void* wrapper_submsg_handler(void* closure, const void* hd) { UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj((void*)submsgdata->md)); zend_class_entry* subklass = subdesc->klass; zval* submsg_php; - // MessageHeader* submsg; - // wrapperfields_parseframe_t* frame = - // (wrapperfields_parseframe_t*)malloc(sizeof(wrapperfields_parseframe_t)); + MessageHeader* submsg; + wrapperfields_parseframe_t* frame = + (wrapperfields_parseframe_t*)malloc(sizeof(wrapperfields_parseframe_t)); CACHED_VALUE* cached = find_zval_property(msg, submsgdata->fd); @@ -935,14 +970,15 @@ static void* wrapper_submsg_handler(void* closure, const void* hd) { } submsg_php = CACHED_PTR_TO_ZVAL_PTR(cached); - // submsg = UNBOX(MessageHeader, submsg_php); + submsg = UNBOX(MessageHeader, submsg_php); - // frame->closure = closure; - // frame->submsg = submsg; + frame->closure = closure; + frame->submsg = submsg; + frame->is_msg = true; - // return frame; + return frame; - return submsg_php; + // return submsg_php; } static bool wrapper_submsg_end_handler(void *closure, const void *hd) { @@ -987,7 +1023,11 @@ static void add_handlers_for_repeated_field(upb_handlers *h, case UPB_TYPE_MESSAGE: { upb_handlerattr attr = UPB_HANDLERATTR_INIT; attr.handler_data = newsubmsghandlerdata(h, 0, f); - upb_handlers_setstartsubmsg(h, f, appendsubmsg_handler, &attr); + if (is_wrapper_msg(upb_fielddef_msgsubdef(f))) { + upb_handlers_setstartsubmsg(h, f, appendwrappersubmsg_handler, &attr); + } else { + upb_handlers_setstartsubmsg(h, f, appendsubmsg_handler, &attr); + } break; } } @@ -1130,14 +1170,14 @@ static void add_handlers_for_oneof_field(upb_handlers *h, } } -#define DEFINE_WRAPPER_HANDLER(type, ctype) \ - static bool type##wrapper_handler( \ - void* closure, const void* hd, ctype val) { \ - zval* msg_php = closure; \ - MessageHeader* msg = UNBOX(MessageHeader, msg_php); \ - const size_t *ofs = hd; \ - DEREF(message_data(msg), *ofs, ctype) = val; \ - return true; \ +#define DEFINE_WRAPPER_HANDLER(type, ctype) \ + static bool type##wrapper_handler( \ + void* closure, const void* hd, ctype val) { \ + wrapperfields_parseframe_t* frame = closure; \ + MessageHeader* msg = frame->submsg; \ + const size_t *ofs = hd; \ + DEREF(message_data(msg), *ofs, ctype) = val; \ + return true; \ } DEFINE_WRAPPER_HANDLER(bool, bool) @@ -1153,8 +1193,9 @@ DEFINE_WRAPPER_HANDLER(double, double) static bool strwrapper_end_handler(void *closure, const void *hd) { stringfields_parseframe_t* frame = closure; const upb_fielddef **field = (const upb_fielddef **) hd; - zval* msg_php = frame->closure; - MessageHeader* msg = UNBOX(MessageHeader, msg_php); + + wrapperfields_parseframe_t* wrapper_frame = frame->closure; + MessageHeader* msg = wrapper_frame->submsg; CACHED_VALUE* cached = find_zval_property(msg, *field); diff --git a/php/tests/encode_decode_test.php b/php/tests/encode_decode_test.php index ae7c676d609e..b6416beb2a66 100644 --- a/php/tests/encode_decode_test.php +++ b/php/tests/encode_decode_test.php @@ -5,6 +5,7 @@ use Google\Protobuf\RepeatedField; use Google\Protobuf\GPBType; +use Foo\TestInt32Value; use Foo\TestStringValue; use Foo\TestAny; use Foo\TestEnum; @@ -98,6 +99,20 @@ class EncodeDecodeTest extends TestBase # $this->assertSame("1", $m->serializeToJsonString()); # } + # public function testDecodeInt32Value() + # { + # $m = new TestInt32Value(); + # $m->mergeFromJsonString("{\"field\":12345}"); + # $this->assertSame(12345, $m->getField()->getValue()); + # } + + # public function testDecodeRepeatedInt32Value() + # { + # $m = new TestInt32Value(); + # $m->mergeFromJsonString("{\"repeated_field\":[12345]}"); + # $this->assertSame(12345, $m->getRepeatedField()[0]->getValue()); + # } + # public function testDecodeTopLevelUInt32Value() # { # $m = new UInt32Value(); @@ -171,10 +186,24 @@ class EncodeDecodeTest extends TestBase public function testDecodeStringValue() { $m = new TestStringValue(); - $m->mergeFromJsonString("{\"field\":\"\"}"); - $this->assertSame("", $m->getField()->getValue()); + $m->mergeFromJsonString("{\"field\":\"a\"}"); + $this->assertSame("a", $m->getField()->getValue()); } + public function testDecodeRepeatedStringValue() + { + $m = new TestStringValue(); + $m->mergeFromJsonString("{\"repeated_field\":[\"a\"]}"); + $this->assertSame("a", $m->getRepeatedField()[0]->getValue()); + } + + # public function testDecodeMapStringValue() + # { + # $m = new TestStringValue(); + # $m->mergeFromJsonString("{\"map_field\":[\"1\", \"a\"]}"); + # $this->assertSame("a", $m->getMapField()[1]->getValue()); + # } + # public function testEncodeStringValue() # { # $m = new TestStringValue(['field' => new StringValue(['value' => ''])]); diff --git a/php/tests/memory_leak_test.php b/php/tests/memory_leak_test.php index 2873f34432cf..669f6ebb73ad 100644 --- a/php/tests/memory_leak_test.php +++ b/php/tests/memory_leak_test.php @@ -23,6 +23,7 @@ require_once('generated/Foo/TestEnum.php'); require_once('generated/Foo/TestIncludeNamespaceMessage.php'); require_once('generated/Foo/TestIncludePrefixMessage.php'); +require_once('generated/Foo/TestInt32Value.php'); require_once('generated/Foo/TestMessage.php'); require_once('generated/Foo/TestMessage/PBEmpty.php'); require_once('generated/Foo/TestMessage/NestedEnum.php'); diff --git a/php/tests/proto/test.proto b/php/tests/proto/test.proto index 6821858412e9..b35d3127fd11 100644 --- a/php/tests/proto/test.proto +++ b/php/tests/proto/test.proto @@ -215,6 +215,12 @@ message TestAny { google.protobuf.Any any = 1; } +message TestInt32Value { + google.protobuf.Int32Value field = 1; + repeated google.protobuf.Int32Value repeated_field = 2; +} + message TestStringValue { google.protobuf.StringValue field = 1; + repeated google.protobuf.StringValue repeated_field = 2; } From 6de256106e0a7e11fe6a87abfaa4d15fb495a8df Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Wed, 30 Oct 2019 22:16:28 +0000 Subject: [PATCH 04/21] Update upb --- php/ext/google/protobuf/encode_decode.c | 8 ++-- php/ext/google/protobuf/upb.c | 60 ++++++++++++++----------- php/ext/google/protobuf/upb.h | 13 +++--- 3 files changed, 44 insertions(+), 37 deletions(-) diff --git a/php/ext/google/protobuf/encode_decode.c b/php/ext/google/protobuf/encode_decode.c index 91d4fba7b3ad..3af9975d94ea 100644 --- a/php/ext/google/protobuf/encode_decode.c +++ b/php/ext/google/protobuf/encode_decode.c @@ -982,8 +982,8 @@ static void* wrapper_submsg_handler(void* closure, const void* hd) { } static bool wrapper_submsg_end_handler(void *closure, const void *hd) { - // wrapperfields_parseframe_t* frame = closure; - // free(frame); + wrapperfields_parseframe_t* frame = closure; + free(frame); return true; } @@ -1494,7 +1494,7 @@ static void putmap(zval* map, const upb_fielddef* f, upb_sink sink, value_field, depth + 1, entry_sink, is_json TSRMLS_CC); upb_sink_endmsg(entry_sink, &status); - upb_sink_endsubmsg(subsink, getsel(f, UPB_HANDLER_ENDSUBMSG)); + upb_sink_endsubmsg(subsink, entry_sink, getsel(f, UPB_HANDLER_ENDSUBMSG)); } upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ)); @@ -1838,7 +1838,7 @@ static void putrawsubmsg(MessageHeader* submsg, const upb_fielddef* f, upb_sink_startsubmsg(sink, getsel(f, UPB_HANDLER_STARTSUBMSG), &subsink); putrawmsg(submsg, subdesc, subsink, depth + 1, is_json, true TSRMLS_CC); - upb_sink_endsubmsg(sink, getsel(f, UPB_HANDLER_ENDSUBMSG)); + upb_sink_endsubmsg(sink, subsink, getsel(f, UPB_HANDLER_ENDSUBMSG)); } static void putarray(zval* array, const upb_fielddef* f, upb_sink sink, diff --git a/php/ext/google/protobuf/upb.c b/php/ext/google/protobuf/upb.c index be9d730d2e27..765b98884f5b 100644 --- a/php/ext/google/protobuf/upb.c +++ b/php/ext/google/protobuf/upb.c @@ -116,7 +116,7 @@ int msvc_vsnprintf(char* s, size_t n, const char* format, va_list arg); #ifdef __cplusplus #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) || \ (defined(_MSC_VER) && _MSC_VER >= 1900) -// C++11 is present +/* C++11 is present */ #else #error upb requires C++11 for C++ support #endif @@ -724,6 +724,7 @@ static bool upb_decode_field(upb_decstate *d, upb_decframe *frame) { CHK(upb_append_unknown(d, frame)); return true; } + UPB_UNREACHABLE(); } static bool upb_decode_message(upb_decstate *d, char *msg, const upb_msglayout *l) { @@ -7153,7 +7154,8 @@ size_t run_decoder_vm(upb_pbdecoder *d, const mgroup *group, CHECK_SUSPEND(upb_sink_startsubmsg(outer->sink, arg, &d->top->sink)); ) VMCASE(OP_ENDSUBMSG, - CHECK_SUSPEND(upb_sink_endsubmsg(d->top->sink, arg)); + upb_sink subsink = (d->top + 1)->sink; + CHECK_SUSPEND(upb_sink_endsubmsg(d->top->sink, subsink, arg)); ) VMCASE(OP_STARTSTR, uint32_t len = delim_remaining(d); @@ -10131,28 +10133,32 @@ const unsigned short int __mon_yday[2][13] = { { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } }; -/* epoch_days(1970, 1, 1) == 1970-01-01 == 0. */ -static int epoch_days(int year, int month, int day) { - static const uint16_t month_yday[12] = {0, 31, 59, 90, 120, 151, - 181, 212, 243, 273, 304, 334}; - int febs_since_0 = month > 2 ? year + 1 : year; - int leap_days_since_0 = div_round_up(febs_since_0, 4) - - div_round_up(febs_since_0, 100) + - div_round_up(febs_since_0, 400); - int days_since_0 = - 365 * year + month_yday[month - 1] + (day - 1) + leap_days_since_0; - - /* Convert from 0-epoch (0001-01-01 BC) to Unix Epoch (1970-01-01 AD). - * Since the "BC" system does not have a year zero, 1 BC == year zero. */ - return days_since_0 - 719528; -} - -static int64_t upb_timegm(const struct tm *tp) { - int64_t ret = epoch_days(tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday); - ret = (ret * 24) + tp->tm_hour; - ret = (ret * 60) + tp->tm_min; - ret = (ret * 60) + tp->tm_sec; - return ret; +int64_t epoch(int year, int yday, int hour, int min, int sec) { + int64_t years = year - EPOCH_YEAR; + + int64_t leap_days = years / 4 - years / 100 + years / 400; + + int64_t days = years * 365 + yday + leap_days; + int64_t hours = days * 24 + hour; + int64_t mins = hours * 60 + min; + int64_t secs = mins * 60 + sec; + return secs; +} + + +static int64_t upb_mktime(const struct tm *tp) { + int sec = tp->tm_sec; + int min = tp->tm_min; + int hour = tp->tm_hour; + int mday = tp->tm_mday; + int mon = tp->tm_mon; + int year = tp->tm_year + TM_YEAR_BASE; + + /* Calculate day of year from year, month, and day of month. */ + int mon_yday = ((__mon_yday[isleap(year)][mon]) - 1); + int yday = mon_yday + mday; + + return epoch(year, yday, hour, min, sec); } static bool end_timestamp_zone(upb_json_parser *p, const char *ptr) { @@ -10182,7 +10188,7 @@ static bool end_timestamp_zone(upb_json_parser *p, const char *ptr) { } /* Normalize tm */ - seconds = upb_timegm(&p->tm); + seconds = upb_mktime(&p->tm); /* Check timestamp boundary */ if (seconds < -62135596800) { @@ -10460,7 +10466,7 @@ static void end_member(upb_json_parser *p) { p->top--; ok = upb_handlers_getselector(mapfield, UPB_HANDLER_ENDSUBMSG, &sel); UPB_ASSERT(ok); - upb_sink_endsubmsg(p->top->sink, sel); + upb_sink_endsubmsg(p->top->sink, (p->top + 1)->sink, sel); } p->top->f = NULL; @@ -10574,7 +10580,7 @@ static void end_subobject(upb_json_parser *p) { p->top--; if (!is_unknown) { sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSUBMSG); - upb_sink_endsubmsg(p->top->sink, sel); + upb_sink_endsubmsg(p->top->sink, (p->top + 1)->sink, sel); } } } diff --git a/php/ext/google/protobuf/upb.h b/php/ext/google/protobuf/upb.h index 0a55baf01061..058772741674 100644 --- a/php/ext/google/protobuf/upb.h +++ b/php/ext/google/protobuf/upb.h @@ -122,7 +122,7 @@ int msvc_vsnprintf(char* s, size_t n, const char* format, va_list arg); #ifdef __cplusplus #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) || \ (defined(_MSC_VER) && _MSC_VER >= 1900) -// C++11 is present +/* C++11 is present */ #else #error upb requires C++11 for C++ support #endif @@ -5700,15 +5700,16 @@ UPB_INLINE bool upb_sink_startsubmsg(upb_sink s, upb_selector_t sel, return sub->closure ? true : false; } -UPB_INLINE bool upb_sink_endsubmsg(upb_sink s, upb_selector_t sel) { +UPB_INLINE bool upb_sink_endsubmsg(upb_sink s, upb_sink sub, + upb_selector_t sel) { typedef upb_endfield_handlerfunc func; func *endsubmsg; const void *hd; if (!s.handlers) return true; endsubmsg = (func*)upb_handlers_gethandler(s.handlers, sel, &hd); - if (!endsubmsg) return s.closure; - return endsubmsg(s.closure, hd); + if (!endsubmsg) return true; + return endsubmsg(sub.closure, hd); } #ifdef __cplusplus @@ -5874,8 +5875,8 @@ class upb::Sink { return ret; } - bool EndSubMessage(HandlersPtr::Selector s) { - return upb_sink_endsubmsg(sink_, s); + bool EndSubMessage(HandlersPtr::Selector s, Sink sub) { + return upb_sink_endsubmsg(sink_, sub.sink_, s); } /* For repeated fields of any type, the sequence of values must be wrapped in From d0b68db63c33c49cd8ac1834ae1b1edf2fd117df Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Wed, 30 Oct 2019 23:50:15 +0000 Subject: [PATCH 05/21] Lazily create wrapper messages --- php/ext/google/protobuf/encode_decode.c | 80 ++++++++++++------------- php/ext/google/protobuf/message.c | 6 -- php/ext/google/protobuf/protobuf.h | 3 + php/ext/google/protobuf/storage.c | 39 +++++++++++- php/tests/encode_decode_test.php | 24 ++++---- 5 files changed, 90 insertions(+), 62 deletions(-) diff --git a/php/ext/google/protobuf/encode_decode.c b/php/ext/google/protobuf/encode_decode.c index 3af9975d94ea..877548cc4c1e 100644 --- a/php/ext/google/protobuf/encode_decode.c +++ b/php/ext/google/protobuf/encode_decode.c @@ -122,7 +122,7 @@ static void stackenv_uninit(stackenv* se) { // Parsing. // ----------------------------------------------------------------------------- -static bool is_wrapper_msg(const upb_msgdef* m) { +bool is_wrapper_msg(const upb_msgdef* m) { switch (upb_msgdef_wellknowntype(m)) { case UPB_WELLKNOWN_DOUBLEVALUE: case UPB_WELLKNOWN_FLOATVALUE: @@ -952,33 +952,21 @@ static void* wrapper_submsg_handler(void* closure, const void* hd) { (wrapperfields_parseframe_t*)malloc(sizeof(wrapperfields_parseframe_t)); CACHED_VALUE* cached = find_zval_property(msg, submsgdata->fd); + submsg_php = CACHED_PTR_TO_ZVAL_PTR(cached); + frame->closure = closure; if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(cached)) == IS_NULL) { -#if PHP_MAJOR_VERSION < 7 - zval val; - ZVAL_OBJ(&val, subklass->create_object(subklass TSRMLS_CC)); - MessageHeader* intern = UNBOX(MessageHeader, &val); - custom_data_init(subklass, intern PHP_PROTO_TSRMLS_CC); - REPLACE_ZVAL_VALUE(cached, &val, 1); - zval_dtor(&val); -#else - zend_object* obj = subklass->create_object(subklass TSRMLS_CC); - ZVAL_OBJ(cached, obj); - MessageHeader* intern = UNBOX_HASHTABLE_VALUE(MessageHeader, obj); - custom_data_init(subklass, intern PHP_PROTO_TSRMLS_CC); -#endif + // In this case, wrapper message hasn't been created and value will be + // stored in cache directly. + frame->submsg = cached; + frame->is_msg = false; + } else { + submsg = UNBOX(MessageHeader, submsg_php); + frame->submsg = submsg; + frame->is_msg = true; } - submsg_php = CACHED_PTR_TO_ZVAL_PTR(cached); - submsg = UNBOX(MessageHeader, submsg_php); - - frame->closure = closure; - frame->submsg = submsg; - frame->is_msg = true; - return frame; - - // return submsg_php; } static bool wrapper_submsg_end_handler(void *closure, const void *hd) { @@ -1025,6 +1013,7 @@ static void add_handlers_for_repeated_field(upb_handlers *h, attr.handler_data = newsubmsghandlerdata(h, 0, f); if (is_wrapper_msg(upb_fielddef_msgsubdef(f))) { upb_handlers_setstartsubmsg(h, f, appendwrappersubmsg_handler, &attr); + upb_handlers_setendsubmsg(h, f, wrapper_submsg_end_handler, &attr); } else { upb_handlers_setstartsubmsg(h, f, appendsubmsg_handler, &attr); } @@ -1170,34 +1159,43 @@ static void add_handlers_for_oneof_field(upb_handlers *h, } } -#define DEFINE_WRAPPER_HANDLER(type, ctype) \ - static bool type##wrapper_handler( \ - void* closure, const void* hd, ctype val) { \ - wrapperfields_parseframe_t* frame = closure; \ - MessageHeader* msg = frame->submsg; \ - const size_t *ofs = hd; \ - DEREF(message_data(msg), *ofs, ctype) = val; \ - return true; \ +#define DEFINE_WRAPPER_HANDLER(utype, type, ctype) \ + static bool type##wrapper_handler( \ + void* closure, const void* hd, ctype val) { \ + wrapperfields_parseframe_t* frame = closure; \ + if (frame->is_msg) { \ + MessageHeader* msg = frame->submsg; \ + const size_t *ofs = hd; \ + DEREF(message_data(msg), *ofs, ctype) = val; \ + } else { \ + native_slot_get(utype, &val, frame->submsg); \ + } \ + return true; \ } -DEFINE_WRAPPER_HANDLER(bool, bool) -DEFINE_WRAPPER_HANDLER(int32, int32_t) -DEFINE_WRAPPER_HANDLER(uint32, uint32_t) -DEFINE_WRAPPER_HANDLER(float, float) -DEFINE_WRAPPER_HANDLER(int64, int64_t) -DEFINE_WRAPPER_HANDLER(uint64, uint64_t) -DEFINE_WRAPPER_HANDLER(double, double) +DEFINE_WRAPPER_HANDLER(UPB_TYPE_BOOL, bool, bool) +DEFINE_WRAPPER_HANDLER(UPB_TYPE_INT32, int32, int32_t) +DEFINE_WRAPPER_HANDLER(UPB_TYPE_UINT32, uint32, uint32_t) +DEFINE_WRAPPER_HANDLER(UPB_TYPE_FLOAT, float, float) +DEFINE_WRAPPER_HANDLER(UPB_TYPE_INT64, int64, int64_t) +DEFINE_WRAPPER_HANDLER(UPB_TYPE_UINT64, uint64, uint64_t) +DEFINE_WRAPPER_HANDLER(UPB_TYPE_DOUBLE, double, double) #undef DEFINE_WRAPPER_HANDLER static bool strwrapper_end_handler(void *closure, const void *hd) { stringfields_parseframe_t* frame = closure; const upb_fielddef **field = (const upb_fielddef **) hd; - wrapperfields_parseframe_t* wrapper_frame = frame->closure; - MessageHeader* msg = wrapper_frame->submsg; + MessageHeader* msg; + CACHED_VALUE* cached; - CACHED_VALUE* cached = find_zval_property(msg, *field); + if (wrapper_frame->is_msg) { + msg = wrapper_frame->submsg; + cached = find_zval_property(msg, *field); + } else { + cached = wrapper_frame->submsg; + } new_php_string(cached, frame->sink.ptr, frame->sink.len); diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c index 03dec75928ef..b8fe8aacbd41 100644 --- a/php/ext/google/protobuf/message.c +++ b/php/ext/google/protobuf/message.c @@ -295,12 +295,6 @@ void build_class_from_descriptor( // PHP Methods // ----------------------------------------------------------------------------- -static bool is_wrapper_msg(const upb_msgdef* m) { - upb_wellknowntype_t type = upb_msgdef_wellknowntype(m); - return type >= UPB_WELLKNOWN_DOUBLEVALUE && - type <= UPB_WELLKNOWN_BOOLVALUE; -} - static void append_wrapper_message( zend_class_entry* subklass, RepeatedField* intern, zval* value TSRMLS_DC) { MessageHeader* submsg; diff --git a/php/ext/google/protobuf/protobuf.h b/php/ext/google/protobuf/protobuf.h index 86bc5b3317b3..e19a5c32632a 100644 --- a/php/ext/google/protobuf/protobuf.h +++ b/php/ext/google/protobuf/protobuf.h @@ -1525,4 +1525,7 @@ static inline zval* php_proto_message_read_property( bool is_reserved_name(const char* name); bool is_valid_constant_name(const char* name); +// For lazy wrapper +bool is_wrapper_msg(const upb_msgdef* m); + #endif // __GOOGLE_PROTOBUF_PHP_PROTOBUF_H__ diff --git a/php/ext/google/protobuf/storage.c b/php/ext/google/protobuf/storage.c index e6050d0eb3ef..ed587853c4c0 100644 --- a/php/ext/google/protobuf/storage.c +++ b/php/ext/google/protobuf/storage.c @@ -811,9 +811,42 @@ zval* layout_get(MessageLayout* layout, MessageHeader* header, repeated_field_ensure_created(field, cache PHP_PROTO_TSRMLS_CC); return CACHED_PTR_TO_ZVAL_PTR(cache); } else { - upb_fieldtype_t type = upb_fielddef_type(field); - native_slot_get(type, value_memory(type, memory, cache), - cache TSRMLS_CC); + if (is_wrapper_msg(upb_fielddef_msgsubdef(field))) { + zval * cached_zval = CACHED_PTR_TO_ZVAL_PTR(cache); + if (Z_TYPE_P(cached_zval) != IS_OBJECT && + Z_TYPE_P(cached_zval) != IS_NULL) { + // Needs to expand value to wrapper. + const upb_msgdef* submsgdef = upb_fielddef_msgsubdef(field); + const upb_fielddef* value_field = upb_msgdef_itof(submsgdef, 1); + MessageHeader* submsg; + Descriptor* subdesc = + UNBOX_HASHTABLE_VALUE( + Descriptor, get_def_obj((void*)submsgdef)); + zend_class_entry* subklass = subdesc->klass; +#if PHP_MAJOR_VERSION < 7 + zval* val = NULL; + MAKE_STD_ZVAL(val); + ZVAL_OBJ(val, subklass->create_object(subklass TSRMLS_CC)); + submsg = UNBOX(MessageHeader, val); +#else + zend_object* obj = subklass->create_object(subklass TSRMLS_CC); + submsg = (MessageHeader*)((char*)obj - XtOffsetOf(MessageHeader, std)); +#endif + custom_data_init(subklass, submsg PHP_PROTO_TSRMLS_CC); + + layout_set(subdesc->layout, submsg, value_field, cached_zval TSRMLS_CC); +#if PHP_MAJOR_VERSION < 7 + ZVAL_ZVAL(cached_zval, val, 1, 1); +#else + ZVAL_OBJ(cached_zval, obj); +#endif + return CACHED_PTR_TO_ZVAL_PTR(cache); + } + } else { + upb_fieldtype_t type = upb_fielddef_type(field); + native_slot_get(type, value_memory(type, memory, cache), + cache TSRMLS_CC); + } return CACHED_PTR_TO_ZVAL_PTR(cache); } } diff --git a/php/tests/encode_decode_test.php b/php/tests/encode_decode_test.php index b6416beb2a66..07ae6989501d 100644 --- a/php/tests/encode_decode_test.php +++ b/php/tests/encode_decode_test.php @@ -99,19 +99,19 @@ class EncodeDecodeTest extends TestBase # $this->assertSame("1", $m->serializeToJsonString()); # } - # public function testDecodeInt32Value() - # { - # $m = new TestInt32Value(); - # $m->mergeFromJsonString("{\"field\":12345}"); - # $this->assertSame(12345, $m->getField()->getValue()); - # } + public function testDecodeInt32Value() + { + $m = new TestInt32Value(); + $m->mergeFromJsonString("{\"field\":12345}"); + $this->assertSame(12345, $m->getField()->getValue()); + } - # public function testDecodeRepeatedInt32Value() - # { - # $m = new TestInt32Value(); - # $m->mergeFromJsonString("{\"repeated_field\":[12345]}"); - # $this->assertSame(12345, $m->getRepeatedField()[0]->getValue()); - # } + public function testDecodeRepeatedInt32Value() + { + $m = new TestInt32Value(); + $m->mergeFromJsonString("{\"repeated_field\":[12345]}"); + $this->assertSame(12345, $m->getRepeatedField()[0]->getValue()); + } # public function testDecodeTopLevelUInt32Value() # { From 32ec1871663ce974bb7a32038e3109bc8c7a0eeb Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Thu, 31 Oct 2019 20:30:12 +0000 Subject: [PATCH 06/21] Fix a segment fault Need check type of field before getting submsg def --- php/ext/google/protobuf/storage.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/php/ext/google/protobuf/storage.c b/php/ext/google/protobuf/storage.c index ed587853c4c0..4b11dcf774b2 100644 --- a/php/ext/google/protobuf/storage.c +++ b/php/ext/google/protobuf/storage.c @@ -811,7 +811,8 @@ zval* layout_get(MessageLayout* layout, MessageHeader* header, repeated_field_ensure_created(field, cache PHP_PROTO_TSRMLS_CC); return CACHED_PTR_TO_ZVAL_PTR(cache); } else { - if (is_wrapper_msg(upb_fielddef_msgsubdef(field))) { + if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE && + is_wrapper_msg(upb_fielddef_msgsubdef(field))) { zval * cached_zval = CACHED_PTR_TO_ZVAL_PTR(cache); if (Z_TYPE_P(cached_zval) != IS_OBJECT && Z_TYPE_P(cached_zval) != IS_NULL) { From 2a45e50a4b3b7670eeceb6aa4c3e049fe9b9ef59 Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Thu, 31 Oct 2019 23:55:22 +0000 Subject: [PATCH 07/21] Avoid expanding during serialization and direct access --- php/ext/google/protobuf/encode_decode.c | 58 ++++++++++++++- php/ext/google/protobuf/message.c | 31 ++++++++ php/ext/google/protobuf/protobuf.h | 1 + php/src/Google/Protobuf/Internal/Message.php | 11 +++ php/tests/encode_decode_test.php | 73 ++++++++++++------- .../protobuf/compiler/php/php_generator.cc | 14 +++- 6 files changed, 159 insertions(+), 29 deletions(-) diff --git a/php/ext/google/protobuf/encode_decode.c b/php/ext/google/protobuf/encode_decode.c index 877548cc4c1e..dc1642636062 100644 --- a/php/ext/google/protobuf/encode_decode.c +++ b/php/ext/google/protobuf/encode_decode.c @@ -1345,6 +1345,9 @@ static void putmsg(zval* msg, const Descriptor* desc, upb_sink sink, static void putrawmsg(MessageHeader* msg, const Descriptor* desc, upb_sink sink, int depth, bool is_json, bool open_msg TSRMLS_DC); +static void putwrappervalue( + zval* value, const upb_fielddef* f, + upb_sink sink, int depth, bool is_json TSRMLS_DC); static void putjsonany(MessageHeader* msg, const Descriptor* desc, upb_sink sink, int depth TSRMLS_DC); static void putjsonlistvalue( @@ -1728,7 +1731,12 @@ static void putrawmsg(MessageHeader* msg, const Descriptor* desc, } } else if (upb_fielddef_issubmsg(f)) { zval* submsg = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f)); - putsubmsg(submsg, f, sink, depth, is_json TSRMLS_CC); + if (is_wrapper_msg(upb_fielddef_msgsubdef(f)) && + Z_TYPE_P(submsg) != IS_NULL && Z_TYPE_P(submsg) != IS_OBJECT) { + putwrappervalue(submsg, f, sink, depth, is_json TSRMLS_CC); + } else { + putsubmsg(submsg, f, sink, depth, is_json TSRMLS_CC); + } } else { upb_selector_t sel = getsel(f, upb_handlers_getprimitivehandlertype(f)); @@ -1827,6 +1835,54 @@ static void putsubmsg(zval* submsg_php, const upb_fielddef* f, upb_sink sink, putrawsubmsg(submsg, f, sink, depth, is_json TSRMLS_CC); } +static void putwrappervalue( + zval* value, const upb_fielddef* f, + upb_sink sink, int depth, bool is_json TSRMLS_DC) { + upb_sink subsink; + const upb_msgdef* msgdef = upb_fielddef_msgsubdef(f); + const upb_fielddef* value_field = upb_msgdef_itof(msgdef, 1); + upb_selector_t sel = + getsel(value_field, upb_handlers_getprimitivehandlertype(value_field)); + + upb_sink_startsubmsg(sink, getsel(f, UPB_HANDLER_STARTSUBMSG), &subsink); + +#define T(upbtypeconst, upbtype, ctype, default_value) \ + case upbtypeconst: { \ + ctype value_raw; \ + native_slot_set(upb_fielddef_type(value_field), NULL, \ + &value_raw, value PHP_PROTO_TSRMLS_CC); \ + if ((is_json && is_wrapper_msg(msgdef)) || \ + value_raw != default_value) { \ + upb_sink_put##upbtype(subsink, sel, value_raw); \ + } \ + } break; + + switch (upb_fielddef_type(value_field)) { + T(UPB_TYPE_FLOAT, float, float, 0.0) + T(UPB_TYPE_DOUBLE, double, double, 0.0) + T(UPB_TYPE_BOOL, bool, uint8_t, 0) + T(UPB_TYPE_INT32, int32, int32_t, 0) + T(UPB_TYPE_UINT32, uint32, uint32_t, 0) + T(UPB_TYPE_INT64, int64, int64_t, 0) + T(UPB_TYPE_UINT64, uint64, uint64_t, 0) + case UPB_TYPE_STRING: + case UPB_TYPE_BYTES: { + if ((is_json && is_wrapper_msg(msgdef)) || + Z_STRLEN_P(value) > 0) { + putstr(value, value_field, subsink, is_json && is_wrapper_msg(msgdef)); + } + break; + } + case UPB_TYPE_ENUM: + case UPB_TYPE_MESSAGE: + zend_error(E_ERROR, "Internal error."); + } + +#undef T + + upb_sink_endsubmsg(sink, subsink, getsel(f, UPB_HANDLER_ENDSUBMSG)); +} + static void putrawsubmsg(MessageHeader* submsg, const upb_fielddef* f, upb_sink sink, int depth, bool is_json TSRMLS_DC) { upb_sink subsink; diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c index b8fe8aacbd41..2471b5a99081 100644 --- a/php/ext/google/protobuf/message.c +++ b/php/ext/google/protobuf/message.c @@ -55,6 +55,7 @@ static zend_function_entry message_methods[] = { PHP_ME(Message, serializeToJsonString, NULL, ZEND_ACC_PUBLIC) PHP_ME(Message, mergeFromJsonString, NULL, ZEND_ACC_PUBLIC) PHP_ME(Message, mergeFrom, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Message, readWrapperValue, NULL, ZEND_ACC_PROTECTED) PHP_ME(Message, readOneof, NULL, ZEND_ACC_PROTECTED) PHP_ME(Message, writeOneof, NULL, ZEND_ACC_PROTECTED) PHP_ME(Message, whichOneof, NULL, ZEND_ACC_PROTECTED) @@ -555,6 +556,36 @@ PHP_METHOD(Message, mergeFrom) { layout_merge(from->descriptor->layout, from, to TSRMLS_CC); } +PHP_METHOD(Message, readWrapperValue) { + char* member; + PHP_PROTO_SIZE length; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &member, + &length) == FAILURE) { + return; + } + + MessageHeader* msg = UNBOX(MessageHeader, getThis()); + const upb_fielddef* field = + upb_msgdef_ntofz(msg->descriptor->msgdef, member); + + zval* cached_zval = + CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, field)); + + if (Z_TYPE_P(cached_zval) == IS_NULL) { + RETURN_NULL(); + } + + if (Z_TYPE_P(cached_zval) == IS_OBJECT) { + const upb_msgdef* submsgdef = upb_fielddef_msgsubdef(field); + const upb_fielddef* value_field = upb_msgdef_itof(submsgdef, 1); + MessageHeader* msg = UNBOX(MessageHeader, cached_zval); + layout_get(msg->descriptor->layout, msg, value_field, + ZVAL_PTR_TO_CACHED_PTR(return_value) TSRMLS_CC); + } else { + RETURN_ZVAL(cached_zval, 1, 0); + } +} + PHP_METHOD(Message, readOneof) { PHP_PROTO_LONG index; diff --git a/php/ext/google/protobuf/protobuf.h b/php/ext/google/protobuf/protobuf.h index e19a5c32632a..3c517a87e584 100644 --- a/php/ext/google/protobuf/protobuf.h +++ b/php/ext/google/protobuf/protobuf.h @@ -965,6 +965,7 @@ void* slot_memory(MessageLayout* layout, const void* storage, PHP_METHOD(Message, clear); PHP_METHOD(Message, mergeFrom); +PHP_METHOD(Message, readWrapperValue); PHP_METHOD(Message, readOneof); PHP_METHOD(Message, writeOneof); PHP_METHOD(Message, whichOneof); diff --git a/php/src/Google/Protobuf/Internal/Message.php b/php/src/Google/Protobuf/Internal/Message.php index 1ff2dc9a67c4..20dadddfc54b 100644 --- a/php/src/Google/Protobuf/Internal/Message.php +++ b/php/src/Google/Protobuf/Internal/Message.php @@ -175,6 +175,17 @@ private function initWithDescriptor(Descriptor $desc) } } + protected function readWrapperValue($member) + { + $value = $this->$member; + + if (is_object($value)) { + return $value->getValue(); + } else { + return $value; + } + } + protected function readOneof($number) { $field = $this->desc->getFieldByNumber($number); diff --git a/php/tests/encode_decode_test.php b/php/tests/encode_decode_test.php index 07ae6989501d..265012119f09 100644 --- a/php/tests/encode_decode_test.php +++ b/php/tests/encode_decode_test.php @@ -99,19 +99,26 @@ class EncodeDecodeTest extends TestBase # $this->assertSame("1", $m->serializeToJsonString()); # } - public function testDecodeInt32Value() - { - $m = new TestInt32Value(); - $m->mergeFromJsonString("{\"field\":12345}"); - $this->assertSame(12345, $m->getField()->getValue()); - } + # public function testDecodeInt32Value() + # { + # $m = new TestInt32Value(); + # $m->mergeFromJsonString("{\"field\":12345}"); + # $this->assertSame(12345, $m->getField()->getValue()); + # } - public function testDecodeRepeatedInt32Value() - { - $m = new TestInt32Value(); - $m->mergeFromJsonString("{\"repeated_field\":[12345]}"); - $this->assertSame(12345, $m->getRepeatedField()[0]->getValue()); - } + # public function testDecodeInt32ValueUnwrapped() + # { + # $m = new TestInt32Value(); + # $m->mergeFromJsonString("{\"field\":12345}"); + # $this->assertSame(12345, $m->getFieldUnwrapped()); + # } + + # public function testDecodeRepeatedInt32Value() + # { + # $m = new TestInt32Value(); + # $m->mergeFromJsonString("{\"repeated_field\":[12345]}"); + # $this->assertSame(12345, $m->getRepeatedField()[0]->getValue()); + # } # public function testDecodeTopLevelUInt32Value() # { @@ -183,19 +190,26 @@ public function testDecodeRepeatedInt32Value() # $this->assertSame("\"a\"", $m->serializeToJsonString()); # } - public function testDecodeStringValue() - { - $m = new TestStringValue(); - $m->mergeFromJsonString("{\"field\":\"a\"}"); - $this->assertSame("a", $m->getField()->getValue()); - } + # public function testDecodeStringValue() + # { + # $m = new TestStringValue(); + # $m->mergeFromJsonString("{\"field\":\"a\"}"); + # $this->assertSame("a", $m->getField()->getValue()); + # } - public function testDecodeRepeatedStringValue() - { - $m = new TestStringValue(); - $m->mergeFromJsonString("{\"repeated_field\":[\"a\"]}"); - $this->assertSame("a", $m->getRepeatedField()[0]->getValue()); - } + # public function testDecodeStringValueUnwrapped() + # { + # $m = new TestStringValue(); + # $m->mergeFromJsonString("{\"field\":\"a\"}"); + # $this->assertSame("a", $m->getFieldUnwrapped()); + # } + + # public function testDecodeRepeatedStringValue() + # { + # $m = new TestStringValue(); + # $m->mergeFromJsonString("{\"repeated_field\":[\"a\"]}"); + # $this->assertSame("a", $m->getRepeatedField()[0]->getValue()); + # } # public function testDecodeMapStringValue() # { @@ -206,10 +220,17 @@ public function testDecodeRepeatedStringValue() # public function testEncodeStringValue() # { - # $m = new TestStringValue(['field' => new StringValue(['value' => ''])]); - # $this->assertSame("{\"field\":\"\"}", $m->serializeToJsonString()); + # $m = new TestStringValue(['field' => new StringValue(['value' => 'a'])]); + # $this->assertSame("{\"field\":\"a\"}", $m->serializeToJsonString()); # } + public function testDecodeEncodeStringValue() + { + $m = new TestStringValue(); + $m->mergeFromJsonString("{\"field\":\"a\"}"); + $this->assertSame("{\"field\":\"a\"}", $m->serializeToJsonString()); + } + # public function testDecodeTopLevelBytesValue() # { # $m = new BytesValue(); diff --git a/src/google/protobuf/compiler/php/php_generator.cc b/src/google/protobuf/compiler/php/php_generator.cc index 29a5ac26423d..3fba93c6689e 100644 --- a/src/google/protobuf/compiler/php/php_generator.cc +++ b/src/google/protobuf/compiler/php/php_generator.cc @@ -628,7 +628,7 @@ void GenerateField(const FieldDescriptor* field, io::Printer* printer, } else { GenerateFieldDocComment(printer, field, is_descriptor, kFieldProperty); printer->Print( - "private $^name^ = ^default^;\n", + "protected $^name^ = ^default^;\n", "name", field->name(), "default", DefaultForField(field)); } @@ -679,13 +679,23 @@ void GenerateFieldAccessor(const FieldDescriptor* field, bool is_descriptor, field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && IsWrapperType(field)) { GenerateWrapperFieldGetterDocComment(printer, field); - printer->Print( + if (field->containing_oneof()) { + printer->Print( "public function get^camel_name^Unwrapped()\n" "{\n" " $wrapper = $this->get^camel_name^();\n" " return is_null($wrapper) ? null : $wrapper->getValue();\n" "}\n\n", "camel_name", UnderscoresToCamelCase(field->name(), true)); + } else { + printer->Print( + "public function get^camel_name^Unwrapped()\n" + "{\n" + " return $this->readWrapperValue(\"^field_name^\");\n" + "}\n\n", + "camel_name", UnderscoresToCamelCase(field->name(), true), + "field_name", field->name()); + } } // Generate setter. From 6a3117477610c01f17ef8cb8f4d15a0fc40730fa Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Fri, 1 Nov 2019 00:41:23 +0000 Subject: [PATCH 08/21] Fix a bug that getXXXUnwrapped returns null for string --- php/ext/google/protobuf/message.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c index 2471b5a99081..308562318cf8 100644 --- a/php/ext/google/protobuf/message.c +++ b/php/ext/google/protobuf/message.c @@ -578,9 +578,11 @@ PHP_METHOD(Message, readWrapperValue) { if (Z_TYPE_P(cached_zval) == IS_OBJECT) { const upb_msgdef* submsgdef = upb_fielddef_msgsubdef(field); const upb_fielddef* value_field = upb_msgdef_itof(submsgdef, 1); - MessageHeader* msg = UNBOX(MessageHeader, cached_zval); - layout_get(msg->descriptor->layout, msg, value_field, - ZVAL_PTR_TO_CACHED_PTR(return_value) TSRMLS_CC); + MessageHeader* submsg = UNBOX(MessageHeader, cached_zval); + CACHED_VALUE* cached_value = find_zval_property(submsg, value_field); + layout_get(msg->descriptor->layout, submsg, value_field, + cached_value TSRMLS_CC); + RETURN_ZVAL(CACHED_PTR_TO_ZVAL_PTR(cached_value), 1, 0); } else { RETURN_ZVAL(cached_zval, 1, 0); } From 283603bf01934c2d634e6e68e70745ae6340af3f Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Fri, 1 Nov 2019 01:33:10 +0000 Subject: [PATCH 09/21] Implement writeWrapperUnwrapped --- php/ext/google/protobuf/message.c | 36 +++++++++++++++++++ php/ext/google/protobuf/protobuf.h | 1 + php/src/Google/Protobuf/Internal/Message.php | 21 ++++++++--- .../protobuf/compiler/php/php_generator.cc | 27 +++++++++----- 4 files changed, 73 insertions(+), 12 deletions(-) diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c index 308562318cf8..be8feb723e7b 100644 --- a/php/ext/google/protobuf/message.c +++ b/php/ext/google/protobuf/message.c @@ -56,6 +56,7 @@ static zend_function_entry message_methods[] = { PHP_ME(Message, mergeFromJsonString, NULL, ZEND_ACC_PUBLIC) PHP_ME(Message, mergeFrom, NULL, ZEND_ACC_PUBLIC) PHP_ME(Message, readWrapperValue, NULL, ZEND_ACC_PROTECTED) + PHP_ME(Message, writeWrapperValue, NULL, ZEND_ACC_PROTECTED) PHP_ME(Message, readOneof, NULL, ZEND_ACC_PROTECTED) PHP_ME(Message, writeOneof, NULL, ZEND_ACC_PROTECTED) PHP_ME(Message, whichOneof, NULL, ZEND_ACC_PROTECTED) @@ -588,6 +589,41 @@ PHP_METHOD(Message, readWrapperValue) { } } +PHP_METHOD(Message, writeWrapperValue) { + char* member; + PHP_PROTO_SIZE length; + zval* value; + if (zend_parse_parameters( + ZEND_NUM_ARGS() TSRMLS_CC, "sz", &member, &length, &value) == + FAILURE) { + return; + } + + MessageHeader* msg = UNBOX(MessageHeader, getThis()); + const upb_fielddef* field = upb_msgdef_ntofz(msg->descriptor->msgdef, member); + + zval* cached_zval = + CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, field)); + + if (Z_TYPE_P(value) == IS_NULL) { + MessageHeader* msg = UNBOX(MessageHeader, getThis()); + layout_set(msg->descriptor->layout, msg, + field, value TSRMLS_CC); + return; + } + + if (Z_TYPE_P(cached_zval) == IS_OBJECT) { + const upb_msgdef* submsgdef = upb_fielddef_msgsubdef(field); + const upb_fielddef* value_field = upb_msgdef_itof(submsgdef, 1); + MessageHeader* submsg = UNBOX(MessageHeader, cached_zval); + CACHED_VALUE* cached_value = find_zval_property(submsg, value_field); + layout_set(submsg->descriptor->layout, submsg, + value_field, value TSRMLS_CC); + } else { + ZVAL_ZVAL(cached_zval, value, 1, 0); + } +} + PHP_METHOD(Message, readOneof) { PHP_PROTO_LONG index; diff --git a/php/ext/google/protobuf/protobuf.h b/php/ext/google/protobuf/protobuf.h index 3c517a87e584..6151ff4cd613 100644 --- a/php/ext/google/protobuf/protobuf.h +++ b/php/ext/google/protobuf/protobuf.h @@ -966,6 +966,7 @@ void* slot_memory(MessageLayout* layout, const void* storage, PHP_METHOD(Message, clear); PHP_METHOD(Message, mergeFrom); PHP_METHOD(Message, readWrapperValue); +PHP_METHOD(Message, writeWrapperValue); PHP_METHOD(Message, readOneof); PHP_METHOD(Message, writeOneof); PHP_METHOD(Message, whichOneof); diff --git a/php/src/Google/Protobuf/Internal/Message.php b/php/src/Google/Protobuf/Internal/Message.php index 20dadddfc54b..feb74af33c39 100644 --- a/php/src/Google/Protobuf/Internal/Message.php +++ b/php/src/Google/Protobuf/Internal/Message.php @@ -177,15 +177,28 @@ private function initWithDescriptor(Descriptor $desc) protected function readWrapperValue($member) { - $value = $this->$member; + $wrapper = $this->$member; - if (is_object($value)) { - return $value->getValue(); + if (is_null($wrapper)) { + return NULL; } else { - return $value; + return $wrapper->getValue(); } } + protected function writeWrapperValue($member, $value) + { + $wrapped_value = $value; + if (!is_null($value)) { + $field = $this->desc->getFieldByName($member); + $desc = $field->getMessageType(); + $klass = $desc->getClass(); + $wrapped_value = new $klass; + $wrapped_value->setValue($value); + } + $this->$member = $wrapped_value; + } + protected function readOneof($number) { $field = $this->desc->getFieldByNumber($number); diff --git a/src/google/protobuf/compiler/php/php_generator.cc b/src/google/protobuf/compiler/php/php_generator.cc index 3fba93c6689e..d2f50cb3a430 100644 --- a/src/google/protobuf/compiler/php/php_generator.cc +++ b/src/google/protobuf/compiler/php/php_generator.cc @@ -802,14 +802,25 @@ void GenerateFieldAccessor(const FieldDescriptor* field, bool is_descriptor, field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && IsWrapperType(field)) { GenerateWrapperFieldSetterDocComment(printer, field); - printer->Print( - "public function set^camel_name^Unwrapped($var)\n" - "{\n" - " $wrappedVar = is_null($var) ? null : new \\^wrapper_type^(['value' => $var]);\n" - " return $this->set^camel_name^($wrappedVar);\n" - "}\n\n", - "camel_name", UnderscoresToCamelCase(field->name(), true), - "wrapper_type", LegacyFullClassName(field->message_type(), is_descriptor)); + if (field->containing_oneof()) { + printer->Print( + "public function set^camel_name^Unwrapped($var)\n" + "{\n" + " $wrappedVar = is_null($var) ? null : new \\^wrapper_type^(['value' => $var]);\n" + " return $this->set^camel_name^($wrappedVar);\n" + "}\n\n", + "camel_name", UnderscoresToCamelCase(field->name(), true), + "wrapper_type", LegacyFullClassName(field->message_type(), is_descriptor)); + } else { + printer->Print( + "public function set^camel_name^Unwrapped($var)\n" + "{\n" + " $this->writeWrapperValue(\"^field_name^\", $var);\n" + " return $this;" + "}\n\n", + "camel_name", UnderscoresToCamelCase(field->name(), true), + "field_name", field->name()); + } } // Generate has method for proto2 only. From d2d5b35862a108fd891f7d3add56a40c13a7da0c Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Fri, 1 Nov 2019 20:06:14 +0000 Subject: [PATCH 10/21] Add more tests --- php/tests/encode_decode_test.php | 84 ++++++++++++++++++++++++++++++++ php/tests/memory_leak_test.php | 5 ++ php/tests/proto/test.proto | 53 ++++++++++++++++++++ 3 files changed, 142 insertions(+) diff --git a/php/tests/encode_decode_test.php b/php/tests/encode_decode_test.php index 265012119f09..65be61c9d3f0 100644 --- a/php/tests/encode_decode_test.php +++ b/php/tests/encode_decode_test.php @@ -6,7 +6,12 @@ use Google\Protobuf\RepeatedField; use Google\Protobuf\GPBType; use Foo\TestInt32Value; +use Foo\TestInt64Value; +use Foo\TestUInt32Value; +use Foo\TestUInt64Value; +use Foo\TestBoolValue; use Foo\TestStringValue; +use Foo\TestBytesValue; use Foo\TestAny; use Foo\TestEnum; use Foo\TestMessage; @@ -1285,4 +1290,83 @@ public function testDecodeEncodeStringValue() # $this->assertTrue(true); # } + /** + * @dataProvider wrappersDataProvider + */ + public function testWrapperJsonDecodeAndGet( + $class, + $nonDefaultValue, + $nonDefaultValueData, + $defaultValue, + $defaultValueData + ) + { + // Singular with non-default + $m = new $class(); + $m->mergeFromJsonString("{\"field\":" . $nonDefaultValueData . "}"); + $wrapper = $m->getField(); + $this->assertEquals($nonDefaultValue, $wrapper->getValue()); + + // Singular with default + $m = new $class(); + $m->mergeFromJsonString("{\"field\":" . $defaultValueData . "}"); + $wrapper = $m->getField(); + $this->assertEquals($defaultValue, $wrapper->getValue()); + + // Repeated with empty + $m = new $class(); + $m->mergeFromJsonString("{\"repeated_field\":[]}"); + $repeatedWrapper = $m->getRepeatedField(); + $this->assertSame(0, count($repeatedWrapper)); + + // Repeated with non-default + $m = new $class(); + $m->mergeFromJsonString("{\"repeated_field\":[" . $defaultValueData . "]}"); + $repeatedWrapper = $m->getRepeatedField(); + $this->assertSame(1, count($repeatedWrapper)); + $this->assertEquals($defaultValue, $repeatedWrapper[0]->getValue()); + + // Repeated with default + $m = new $class(); + $m->mergeFromJsonString("{\"repeated_field\":[" . $defaultValueData . "]}"); + $repeatedWrapper = $m->getRepeatedField(); + $this->assertSame(1, count($repeatedWrapper)); + $this->assertEquals($defaultValue, $repeatedWrapper[0]->getValue()); + + # // Oneof with non-default + # $m = new $class(); + # $m->mergeFromJsonString("{\"oneof_field\":" . $nonDefaultValueData . "}"); + # $wrapper = $m->getField(); + # $this->assertEquals($nonDefaultValue, $wrapper->getValue()); + # $this->assertEquals("oneof_field", $m->getOneofs()); + } + + /** + * @dataProvider wrappersDataProvider + */ + public function testWrapperJsonDecodeAndGetUnwrapped( + $class, + $nonDefaultValue, + $nonDefaultValueData, + $defaultValue, + $defaultValueData + ) + { + // Singular with non-default + $m = new $class(); + $m->mergeFromJsonString("{\"field\":" . $nonDefaultValueData . "}"); + $this->assertEquals($nonDefaultValue, $m->getFieldUnwrapped()); + + // Singular with default + $m = new $class(); + $m->mergeFromJsonString("{\"field\":" . $defaultValueData . "}"); + $this->assertEquals($defaultValue, $m->getFieldUnwrapped()); + } + + public function wrappersDataProvider() + { + return [ + [TestStringValue::class, "a", "\"a\"", "", "\"\""], + ]; + } } diff --git a/php/tests/memory_leak_test.php b/php/tests/memory_leak_test.php index 669f6ebb73ad..a9c292d0b612 100644 --- a/php/tests/memory_leak_test.php +++ b/php/tests/memory_leak_test.php @@ -20,10 +20,13 @@ require_once('generated/Foo/PBARRAY.php'); require_once('generated/Foo/PBEmpty.php'); require_once('generated/Foo/TestAny.php'); +require_once('generated/Foo/TestBoolValue.php'); +require_once('generated/Foo/TestBytesValue.php'); require_once('generated/Foo/TestEnum.php'); require_once('generated/Foo/TestIncludeNamespaceMessage.php'); require_once('generated/Foo/TestIncludePrefixMessage.php'); require_once('generated/Foo/TestInt32Value.php'); +require_once('generated/Foo/TestInt64Value.php'); require_once('generated/Foo/TestMessage.php'); require_once('generated/Foo/TestMessage/PBEmpty.php'); require_once('generated/Foo/TestMessage/NestedEnum.php'); @@ -33,6 +36,8 @@ require_once('generated/Foo/TestRandomFieldOrder.php'); require_once('generated/Foo/TestReverseFieldOrder.php'); require_once('generated/Foo/TestStringValue.php'); +require_once('generated/Foo/TestUInt32Value.php'); +require_once('generated/Foo/TestUInt64Value.php'); require_once('generated/Foo/TestUnpackedMessage.php'); require_once('generated/Foo/testLowerCaseMessage.php'); require_once('generated/Foo/testLowerCaseEnum.php'); diff --git a/php/tests/proto/test.proto b/php/tests/proto/test.proto index b35d3127fd11..f8c200e70e7f 100644 --- a/php/tests/proto/test.proto +++ b/php/tests/proto/test.proto @@ -218,9 +218,62 @@ message TestAny { message TestInt32Value { google.protobuf.Int32Value field = 1; repeated google.protobuf.Int32Value repeated_field = 2; + oneof oneof_fields { + google.protobuf.Int32Value oneof_field = 3; + int32 int32_field = 4; + } +} + +message TestInt64Value { + google.protobuf.Int64Value field = 1; + repeated google.protobuf.Int64Value repeated_field = 2; + oneof oneof_fields { + google.protobuf.Int64Value oneof_field = 3; + int32 int32_field = 4; + } +} + +message TestUInt32Value { + google.protobuf.UInt32Value field = 1; + repeated google.protobuf.UInt32Value repeated_field = 2; + oneof oneof_fields { + google.protobuf.UInt32Value oneof_field = 3; + int32 int32_field = 4; + } +} + +message TestUInt64Value { + google.protobuf.UInt64Value field = 1; + repeated google.protobuf.UInt64Value repeated_field = 2; + oneof oneof_fields { + google.protobuf.UInt64Value oneof_field = 3; + int32 int32_field = 4; + } +} + +message TestBoolValue { + google.protobuf.BoolValue field = 1; + repeated google.protobuf.BoolValue repeated_field = 2; + oneof oneof_fields { + google.protobuf.BoolValue oneof_field = 3; + int32 int32_field = 4; + } } message TestStringValue { google.protobuf.StringValue field = 1; repeated google.protobuf.StringValue repeated_field = 2; + oneof oneof_fields { + google.protobuf.StringValue oneof_field = 3; + int32 int32_field = 4; + } +} + +message TestBytesValue { + google.protobuf.BytesValue field = 1; + repeated google.protobuf.BytesValue repeated_field = 2; + oneof oneof_fields { + google.protobuf.BytesValue oneof_field = 3; + int32 int32_field = 4; + } } From 81b7c9afe9cc6562ee0fd9ed77a64671408bc20d Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Fri, 1 Nov 2019 20:51:38 +0000 Subject: [PATCH 11/21] Fix oneof wrapper parsing --- php/ext/google/protobuf/encode_decode.c | 37 +++++++- php/tests/encode_decode_test.php | 118 ++++++++++++------------ 2 files changed, 95 insertions(+), 60 deletions(-) diff --git a/php/ext/google/protobuf/encode_decode.c b/php/ext/google/protobuf/encode_decode.c index dc1642636062..79839901b183 100644 --- a/php/ext/google/protobuf/encode_decode.c +++ b/php/ext/google/protobuf/encode_decode.c @@ -969,6 +969,36 @@ static void* wrapper_submsg_handler(void* closure, const void* hd) { return frame; } +// Handler for a wrapper submessage field in a oneof. +static void* wrapper_oneofsubmsg_handler(void* closure, const void* hd) { + MessageHeader* msg = closure; + const oneof_handlerdata_t *oneofdata = hd; + uint32_t oldcase = DEREF(message_data(msg), oneofdata->case_ofs, uint32_t); + TSRMLS_FETCH(); + Descriptor* subdesc = + UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj((void*)oneofdata->md)); + zend_class_entry* subklass = subdesc->klass; + wrapperfields_parseframe_t* frame = + (wrapperfields_parseframe_t*)malloc(sizeof(wrapperfields_parseframe_t)); + CACHED_VALUE* cached = OBJ_PROP(&msg->std, oneofdata->property_ofs); + MessageHeader* submsg; + + if (oldcase != oneofdata->oneof_case_num) { + oneof_cleanup(msg, oneofdata); + frame->submsg = cached; + frame->is_msg = false; + } else { + submsg = UNBOX(MessageHeader, CACHED_PTR_TO_ZVAL_PTR(cached)); + frame->submsg = submsg; + frame->is_msg = true; + } + + DEREF(message_data(msg), oneofdata->case_ofs, uint32_t) = + oneofdata->oneof_case_num; + + return frame; +} + static bool wrapper_submsg_end_handler(void *closure, const void *hd) { wrapperfields_parseframe_t* frame = closure; free(frame); @@ -1153,7 +1183,12 @@ static void add_handlers_for_oneof_field(upb_handlers *h, break; } case UPB_TYPE_MESSAGE: { - upb_handlers_setstartsubmsg(h, f, oneofsubmsg_handler, &attr); + if (is_wrapper_msg(upb_fielddef_msgsubdef(f))) { + upb_handlers_setstartsubmsg(h, f, wrapper_oneofsubmsg_handler, &attr); + upb_handlers_setendsubmsg(h, f, wrapper_submsg_end_handler, &attr); + } else { + upb_handlers_setstartsubmsg(h, f, oneofsubmsg_handler, &attr); + } break; } } diff --git a/php/tests/encode_decode_test.php b/php/tests/encode_decode_test.php index 65be61c9d3f0..cb55cb3bcfa4 100644 --- a/php/tests/encode_decode_test.php +++ b/php/tests/encode_decode_test.php @@ -229,12 +229,12 @@ class EncodeDecodeTest extends TestBase # $this->assertSame("{\"field\":\"a\"}", $m->serializeToJsonString()); # } - public function testDecodeEncodeStringValue() - { - $m = new TestStringValue(); - $m->mergeFromJsonString("{\"field\":\"a\"}"); - $this->assertSame("{\"field\":\"a\"}", $m->serializeToJsonString()); - } + # public function testDecodeEncodeStringValue() + # { + # $m = new TestStringValue(); + # $m->mergeFromJsonString("{\"field\":\"a\"}"); + # $this->assertSame("{\"field\":\"a\"}", $m->serializeToJsonString()); + # } # public function testDecodeTopLevelBytesValue() # { @@ -1301,68 +1301,68 @@ public function testWrapperJsonDecodeAndGet( $defaultValueData ) { - // Singular with non-default - $m = new $class(); - $m->mergeFromJsonString("{\"field\":" . $nonDefaultValueData . "}"); - $wrapper = $m->getField(); - $this->assertEquals($nonDefaultValue, $wrapper->getValue()); - - // Singular with default - $m = new $class(); - $m->mergeFromJsonString("{\"field\":" . $defaultValueData . "}"); - $wrapper = $m->getField(); - $this->assertEquals($defaultValue, $wrapper->getValue()); - - // Repeated with empty - $m = new $class(); - $m->mergeFromJsonString("{\"repeated_field\":[]}"); - $repeatedWrapper = $m->getRepeatedField(); - $this->assertSame(0, count($repeatedWrapper)); + # // Singular with non-default + # $m = new $class(); + # $m->mergeFromJsonString("{\"field\":" . $nonDefaultValueData . "}"); + # $wrapper = $m->getField(); + # $this->assertEquals($nonDefaultValue, $wrapper->getValue()); - // Repeated with non-default - $m = new $class(); - $m->mergeFromJsonString("{\"repeated_field\":[" . $defaultValueData . "]}"); - $repeatedWrapper = $m->getRepeatedField(); - $this->assertSame(1, count($repeatedWrapper)); - $this->assertEquals($defaultValue, $repeatedWrapper[0]->getValue()); + # // Singular with default + # $m = new $class(); + # $m->mergeFromJsonString("{\"field\":" . $defaultValueData . "}"); + # $wrapper = $m->getField(); + # $this->assertEquals($defaultValue, $wrapper->getValue()); - // Repeated with default - $m = new $class(); - $m->mergeFromJsonString("{\"repeated_field\":[" . $defaultValueData . "]}"); - $repeatedWrapper = $m->getRepeatedField(); - $this->assertSame(1, count($repeatedWrapper)); - $this->assertEquals($defaultValue, $repeatedWrapper[0]->getValue()); + # // Repeated with empty + # $m = new $class(); + # $m->mergeFromJsonString("{\"repeated_field\":[]}"); + # $repeatedWrapper = $m->getRepeatedField(); + # $this->assertSame(0, count($repeatedWrapper)); - # // Oneof with non-default + # // Repeated with non-default # $m = new $class(); - # $m->mergeFromJsonString("{\"oneof_field\":" . $nonDefaultValueData . "}"); - # $wrapper = $m->getField(); - # $this->assertEquals($nonDefaultValue, $wrapper->getValue()); - # $this->assertEquals("oneof_field", $m->getOneofs()); - } + # $m->mergeFromJsonString("{\"repeated_field\":[" . $defaultValueData . "]}"); + # $repeatedWrapper = $m->getRepeatedField(); + # $this->assertSame(1, count($repeatedWrapper)); + # $this->assertEquals($defaultValue, $repeatedWrapper[0]->getValue()); - /** - * @dataProvider wrappersDataProvider - */ - public function testWrapperJsonDecodeAndGetUnwrapped( - $class, - $nonDefaultValue, - $nonDefaultValueData, - $defaultValue, - $defaultValueData - ) - { - // Singular with non-default - $m = new $class(); - $m->mergeFromJsonString("{\"field\":" . $nonDefaultValueData . "}"); - $this->assertEquals($nonDefaultValue, $m->getFieldUnwrapped()); + # // Repeated with default + # $m = new $class(); + # $m->mergeFromJsonString("{\"repeated_field\":[" . $defaultValueData . "]}"); + # $repeatedWrapper = $m->getRepeatedField(); + # $this->assertSame(1, count($repeatedWrapper)); + # $this->assertEquals($defaultValue, $repeatedWrapper[0]->getValue()); - // Singular with default + // Oneof with non-default $m = new $class(); - $m->mergeFromJsonString("{\"field\":" . $defaultValueData . "}"); - $this->assertEquals($defaultValue, $m->getFieldUnwrapped()); + $m->mergeFromJsonString("{\"oneof_field\":" . $nonDefaultValueData . "}"); + $wrapper = $m->getField(); + $this->assertEquals($nonDefaultValue, $wrapper->getValue()); + $this->assertEquals("oneof_field", $m->getOneofs()); } + # /** + # * @dataProvider wrappersDataProvider + # */ + # public function testWrapperJsonDecodeAndGetUnwrapped( + # $class, + # $nonDefaultValue, + # $nonDefaultValueData, + # $defaultValue, + # $defaultValueData + # ) + # { + # // Singular with non-default + # $m = new $class(); + # $m->mergeFromJsonString("{\"field\":" . $nonDefaultValueData . "}"); + # $this->assertEquals($nonDefaultValue, $m->getFieldUnwrapped()); + + # // Singular with default + # $m = new $class(); + # $m->mergeFromJsonString("{\"field\":" . $defaultValueData . "}"); + # $this->assertEquals($defaultValue, $m->getFieldUnwrapped()); + # } + public function wrappersDataProvider() { return [ From 83c56f4fd35e409467ea1b9b2e0fcae5ecc47e00 Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Fri, 1 Nov 2019 22:09:36 +0000 Subject: [PATCH 12/21] Fix get oneof field --- php/ext/google/protobuf/storage.c | 85 ++++++++++++++++++------------- php/tests/encode_decode_test.php | 4 +- 2 files changed, 51 insertions(+), 38 deletions(-) diff --git a/php/ext/google/protobuf/storage.c b/php/ext/google/protobuf/storage.c index 4b11dcf774b2..8d02cce5b869 100644 --- a/php/ext/google/protobuf/storage.c +++ b/php/ext/google/protobuf/storage.c @@ -797,59 +797,72 @@ zval* layout_get(MessageLayout* layout, MessageHeader* header, if (upb_fielddef_containingoneof(field)) { if (*oneof_case != upb_fielddef_number(field)) { native_slot_get_default(upb_fielddef_type(field), cache TSRMLS_CC); - } else { - upb_fieldtype_t type = upb_fielddef_type(field); - CACHED_VALUE* stored_cache = find_zval_property(header, field); - native_slot_get( - type, value_memory(type, memory, stored_cache), cache TSRMLS_CC); + return CACHED_PTR_TO_ZVAL_PTR(cache); } - return CACHED_PTR_TO_ZVAL_PTR(cache); + // Intentional fall through to be handled as a signuarl field. } else if (is_map_field(field)) { map_field_ensure_created(field, cache PHP_PROTO_TSRMLS_CC); return CACHED_PTR_TO_ZVAL_PTR(cache); } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { repeated_field_ensure_created(field, cache PHP_PROTO_TSRMLS_CC); return CACHED_PTR_TO_ZVAL_PTR(cache); - } else { - if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE && - is_wrapper_msg(upb_fielddef_msgsubdef(field))) { - zval * cached_zval = CACHED_PTR_TO_ZVAL_PTR(cache); - if (Z_TYPE_P(cached_zval) != IS_OBJECT && - Z_TYPE_P(cached_zval) != IS_NULL) { - // Needs to expand value to wrapper. - const upb_msgdef* submsgdef = upb_fielddef_msgsubdef(field); - const upb_fielddef* value_field = upb_msgdef_itof(submsgdef, 1); - MessageHeader* submsg; - Descriptor* subdesc = - UNBOX_HASHTABLE_VALUE( - Descriptor, get_def_obj((void*)submsgdef)); - zend_class_entry* subklass = subdesc->klass; + } + + // } else { + // upb_fieldtype_t type = upb_fielddef_type(field); + // CACHED_VALUE* stored_cache = find_zval_property(header, field); + // native_slot_get( + // type, value_memory(type, memory, stored_cache), cache TSRMLS_CC); + // } + // return CACHED_PTR_TO_ZVAL_PTR(cache); + + CACHED_VALUE* stored_cache = find_zval_property(header, field); + + if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE && + is_wrapper_msg(upb_fielddef_msgsubdef(field))) { + zval * cached_zval = CACHED_PTR_TO_ZVAL_PTR(stored_cache); + if (Z_TYPE_P(cached_zval) != IS_OBJECT && + Z_TYPE_P(cached_zval) != IS_NULL) { + // Needs to expand value to wrapper. + const upb_msgdef* submsgdef = upb_fielddef_msgsubdef(field); + const upb_fielddef* value_field = upb_msgdef_itof(submsgdef, 1); + MessageHeader* submsg; + Descriptor* subdesc = + UNBOX_HASHTABLE_VALUE( + Descriptor, get_def_obj((void*)submsgdef)); + zend_class_entry* subklass = subdesc->klass; #if PHP_MAJOR_VERSION < 7 - zval* val = NULL; - MAKE_STD_ZVAL(val); - ZVAL_OBJ(val, subklass->create_object(subklass TSRMLS_CC)); - submsg = UNBOX(MessageHeader, val); + zval* val = NULL; + MAKE_STD_ZVAL(val); + ZVAL_OBJ(val, subklass->create_object(subklass TSRMLS_CC)); + submsg = UNBOX(MessageHeader, val); #else - zend_object* obj = subklass->create_object(subklass TSRMLS_CC); - submsg = (MessageHeader*)((char*)obj - XtOffsetOf(MessageHeader, std)); + zend_object* obj = subklass->create_object(subklass TSRMLS_CC); + submsg = (MessageHeader*)((char*)obj - XtOffsetOf(MessageHeader, std)); #endif - custom_data_init(subklass, submsg PHP_PROTO_TSRMLS_CC); + custom_data_init(subklass, submsg PHP_PROTO_TSRMLS_CC); - layout_set(subdesc->layout, submsg, value_field, cached_zval TSRMLS_CC); + layout_set(subdesc->layout, submsg, value_field, cached_zval TSRMLS_CC); +#if PHP_MAJOR_VERSION < 7 + ZVAL_ZVAL(cached_zval, val, 1, 1); +#else + ZVAL_OBJ(cached_zval, obj); +#endif + if (stored_cache != cache) { #if PHP_MAJOR_VERSION < 7 - ZVAL_ZVAL(cached_zval, val, 1, 1); + ZVAL_ZVAL(CACHED_PTR_TO_ZVAL_PTR(cache), cached_zval, 1, 0); #else - ZVAL_OBJ(cached_zval, obj); + ZVAL_OBJ(CACHED_PTR_TO_ZVAL_PTR(cache), obj); #endif - return CACHED_PTR_TO_ZVAL_PTR(cache); } - } else { - upb_fieldtype_t type = upb_fielddef_type(field); - native_slot_get(type, value_memory(type, memory, cache), - cache TSRMLS_CC); + return CACHED_PTR_TO_ZVAL_PTR(cache); } - return CACHED_PTR_TO_ZVAL_PTR(cache); + } else { + upb_fieldtype_t type = upb_fielddef_type(field); + native_slot_get(type, value_memory(type, memory, stored_cache), + cache TSRMLS_CC); } + return CACHED_PTR_TO_ZVAL_PTR(cache); } void layout_set(MessageLayout* layout, MessageHeader* header, diff --git a/php/tests/encode_decode_test.php b/php/tests/encode_decode_test.php index cb55cb3bcfa4..dad3c947d6ab 100644 --- a/php/tests/encode_decode_test.php +++ b/php/tests/encode_decode_test.php @@ -1336,9 +1336,9 @@ public function testWrapperJsonDecodeAndGet( // Oneof with non-default $m = new $class(); $m->mergeFromJsonString("{\"oneof_field\":" . $nonDefaultValueData . "}"); - $wrapper = $m->getField(); + $wrapper = $m->getOneofField(); $this->assertEquals($nonDefaultValue, $wrapper->getValue()); - $this->assertEquals("oneof_field", $m->getOneofs()); + $this->assertEquals("oneof_field", $m->getOneofFields()); } # /** From 5c01ceffc78fba3f066abda5aef38928005cdb32 Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Fri, 1 Nov 2019 23:24:32 +0000 Subject: [PATCH 13/21] Avoid expansion for oneof wrappers --- php/ext/google/protobuf/message.c | 21 +++ php/ext/google/protobuf/storage.c | 8 - php/src/Google/Protobuf/Internal/Message.php | 18 +- php/tests/encode_decode_test.php | 170 ++++++++++++------ .../protobuf/compiler/php/php_generator.cc | 45 ++--- 5 files changed, 167 insertions(+), 95 deletions(-) diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c index be8feb723e7b..305f963ad889 100644 --- a/php/ext/google/protobuf/message.c +++ b/php/ext/google/protobuf/message.c @@ -569,6 +569,14 @@ PHP_METHOD(Message, readWrapperValue) { const upb_fielddef* field = upb_msgdef_ntofz(msg->descriptor->msgdef, member); + if (upb_fielddef_containingoneof(field)) { + uint32_t* oneof_case = + slot_oneof_case(msg->descriptor->layout, message_data(msg), field); + if (*oneof_case != upb_fielddef_number(field)) { + RETURN_NULL(); + } + } + zval* cached_zval = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, field)); @@ -612,6 +620,19 @@ PHP_METHOD(Message, writeWrapperValue) { return; } + if (upb_fielddef_containingoneof(field)) { + uint32_t* oneof_case = + slot_oneof_case(msg->descriptor->layout, message_data(msg), field); + if (*oneof_case != upb_fielddef_number(field)) { + zval null_value; + ZVAL_NULL(&null_value); + layout_set(msg->descriptor->layout, msg, field, &null_value TSRMLS_CC); + cached_zval = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, field)); + ZVAL_ZVAL(cached_zval, value, 1, 0); + return; + } + } + if (Z_TYPE_P(cached_zval) == IS_OBJECT) { const upb_msgdef* submsgdef = upb_fielddef_msgsubdef(field); const upb_fielddef* value_field = upb_msgdef_itof(submsgdef, 1); diff --git a/php/ext/google/protobuf/storage.c b/php/ext/google/protobuf/storage.c index 8d02cce5b869..fb06e0001698 100644 --- a/php/ext/google/protobuf/storage.c +++ b/php/ext/google/protobuf/storage.c @@ -808,14 +808,6 @@ zval* layout_get(MessageLayout* layout, MessageHeader* header, return CACHED_PTR_TO_ZVAL_PTR(cache); } - // } else { - // upb_fieldtype_t type = upb_fielddef_type(field); - // CACHED_VALUE* stored_cache = find_zval_property(header, field); - // native_slot_get( - // type, value_memory(type, memory, stored_cache), cache TSRMLS_CC); - // } - // return CACHED_PTR_TO_ZVAL_PTR(cache); - CACHED_VALUE* stored_cache = find_zval_property(header, field); if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE && diff --git a/php/src/Google/Protobuf/Internal/Message.php b/php/src/Google/Protobuf/Internal/Message.php index feb74af33c39..1ffb24501c83 100644 --- a/php/src/Google/Protobuf/Internal/Message.php +++ b/php/src/Google/Protobuf/Internal/Message.php @@ -177,7 +177,13 @@ private function initWithDescriptor(Descriptor $desc) protected function readWrapperValue($member) { - $wrapper = $this->$member; + $field = $this->desc->getFieldByName($member); + $oneof_index = $field->getOneofIndex(); + if ($oneof_index === -1) { + $wrapper = $this->$member; + } else { + $wrapper = $this->readOneof($field->getNumber()); + } if (is_null($wrapper)) { return NULL; @@ -188,15 +194,21 @@ protected function readWrapperValue($member) protected function writeWrapperValue($member, $value) { + $field = $this->desc->getFieldByName($member); $wrapped_value = $value; if (!is_null($value)) { - $field = $this->desc->getFieldByName($member); $desc = $field->getMessageType(); $klass = $desc->getClass(); $wrapped_value = new $klass; $wrapped_value->setValue($value); } - $this->$member = $wrapped_value; + + $oneof_index = $field->getOneofIndex(); + if ($oneof_index === -1) { + $this->$member = $wrapped_value; + } else { + $this->writeOneof($field->getNumber(), $wrapped_value); + } } protected function readOneof($number) diff --git a/php/tests/encode_decode_test.php b/php/tests/encode_decode_test.php index dad3c947d6ab..c91d881bdd1f 100644 --- a/php/tests/encode_decode_test.php +++ b/php/tests/encode_decode_test.php @@ -1301,37 +1301,37 @@ public function testWrapperJsonDecodeAndGet( $defaultValueData ) { - # // Singular with non-default - # $m = new $class(); - # $m->mergeFromJsonString("{\"field\":" . $nonDefaultValueData . "}"); - # $wrapper = $m->getField(); - # $this->assertEquals($nonDefaultValue, $wrapper->getValue()); - - # // Singular with default - # $m = new $class(); - # $m->mergeFromJsonString("{\"field\":" . $defaultValueData . "}"); - # $wrapper = $m->getField(); - # $this->assertEquals($defaultValue, $wrapper->getValue()); - - # // Repeated with empty - # $m = new $class(); - # $m->mergeFromJsonString("{\"repeated_field\":[]}"); - # $repeatedWrapper = $m->getRepeatedField(); - # $this->assertSame(0, count($repeatedWrapper)); - - # // Repeated with non-default - # $m = new $class(); - # $m->mergeFromJsonString("{\"repeated_field\":[" . $defaultValueData . "]}"); - # $repeatedWrapper = $m->getRepeatedField(); - # $this->assertSame(1, count($repeatedWrapper)); - # $this->assertEquals($defaultValue, $repeatedWrapper[0]->getValue()); - - # // Repeated with default - # $m = new $class(); - # $m->mergeFromJsonString("{\"repeated_field\":[" . $defaultValueData . "]}"); - # $repeatedWrapper = $m->getRepeatedField(); - # $this->assertSame(1, count($repeatedWrapper)); - # $this->assertEquals($defaultValue, $repeatedWrapper[0]->getValue()); + // Singular with non-default + $m = new $class(); + $m->mergeFromJsonString("{\"field\":" . $nonDefaultValueData . "}"); + $wrapper = $m->getField(); + $this->assertEquals($nonDefaultValue, $wrapper->getValue()); + + // Singular with default + $m = new $class(); + $m->mergeFromJsonString("{\"field\":" . $defaultValueData . "}"); + $wrapper = $m->getField(); + $this->assertEquals($defaultValue, $wrapper->getValue()); + + // Repeated with empty + $m = new $class(); + $m->mergeFromJsonString("{\"repeated_field\":[]}"); + $repeatedWrapper = $m->getRepeatedField(); + $this->assertSame(0, count($repeatedWrapper)); + + // Repeated with non-default + $m = new $class(); + $m->mergeFromJsonString("{\"repeated_field\":[" . $defaultValueData . "]}"); + $repeatedWrapper = $m->getRepeatedField(); + $this->assertSame(1, count($repeatedWrapper)); + $this->assertEquals($defaultValue, $repeatedWrapper[0]->getValue()); + + // Repeated with default + $m = new $class(); + $m->mergeFromJsonString("{\"repeated_field\":[" . $defaultValueData . "]}"); + $repeatedWrapper = $m->getRepeatedField(); + $this->assertSame(1, count($repeatedWrapper)); + $this->assertEquals($defaultValue, $repeatedWrapper[0]->getValue()); // Oneof with non-default $m = new $class(); @@ -1339,33 +1339,101 @@ public function testWrapperJsonDecodeAndGet( $wrapper = $m->getOneofField(); $this->assertEquals($nonDefaultValue, $wrapper->getValue()); $this->assertEquals("oneof_field", $m->getOneofFields()); + $this->assertEquals(0, $m->getInt32Field()); + + // Oneof with default + $m = new $class(); + $m->mergeFromJsonString("{\"oneof_field\":" . $defaultValueData . "}"); + $wrapper = $m->getOneofField(); + $this->assertEquals($defaultValue, $wrapper->getValue()); + $this->assertEquals("oneof_field", $m->getOneofFields()); + $this->assertEquals(0, $m->getInt32Field()); } - # /** - # * @dataProvider wrappersDataProvider - # */ - # public function testWrapperJsonDecodeAndGetUnwrapped( - # $class, - # $nonDefaultValue, - # $nonDefaultValueData, - # $defaultValue, - # $defaultValueData - # ) - # { - # // Singular with non-default - # $m = new $class(); - # $m->mergeFromJsonString("{\"field\":" . $nonDefaultValueData . "}"); - # $this->assertEquals($nonDefaultValue, $m->getFieldUnwrapped()); + /** + * @dataProvider wrappersDataProvider + */ + public function testWrapperJsonDecodeAndGetUnwrapped( + $class, + $nonDefaultValue, + $nonDefaultValueData, + $defaultValue, + $defaultValueData + ) + { + // Singular with non-default + $m = new $class(); + $m->mergeFromJsonString("{\"field\":" . $nonDefaultValueData . "}"); + $this->assertEquals($nonDefaultValue, $m->getFieldUnwrapped()); - # // Singular with default - # $m = new $class(); - # $m->mergeFromJsonString("{\"field\":" . $defaultValueData . "}"); - # $this->assertEquals($defaultValue, $m->getFieldUnwrapped()); - # } + // Singular with default + $m = new $class(); + $m->mergeFromJsonString("{\"field\":" . $defaultValueData . "}"); + $this->assertEquals($defaultValue, $m->getFieldUnwrapped()); + + // Oneof with non-default + $m = new $class(); + $m->mergeFromJsonString("{\"oneof_field\":" . $nonDefaultValueData . "}"); + $this->assertEquals($nonDefaultValue, $m->getOneofFieldUnwrapped()); + $this->assertEquals("oneof_field", $m->getOneofFields()); + $this->assertEquals(0, $m->getInt32Field()); + + // Oneof with default + $m = new $class(); + $m->mergeFromJsonString("{\"oneof_field\":" . $defaultValueData . "}"); + $this->assertEquals($defaultValue, $m->getOneofFieldUnwrapped()); + $this->assertEquals("oneof_field", $m->getOneofFields()); + $this->assertEquals(0, $m->getInt32Field()); + } + + /** + * @dataProvider wrappersDataProvider + */ + public function testWrapperJsonDecodeEncode( + $class, + $nonDefaultValue, + $nonDefaultValueData, + $defaultValue, + $defaultValueData + ) + { + // Singular with non-default + $from = new $class(); + $to = new $class(); + $from->mergeFromJsonString("{\"field\":" . $nonDefaultValueData . "}"); + $data = $from->serializeToJsonString(); + $to->mergeFromJsonString($data); + $this->assertEquals($nonDefaultValue, $to->getFieldUnwrapped()); + + // Singular with default + $from = new $class(); + $to = new $class(); + $from->mergeFromJsonString("{\"field\":" . $defaultValueData . "}"); + $data = $from->serializeToJsonString(); + $to->mergeFromJsonString($data); + $this->assertEquals($defaultValue, $to->getFieldUnwrapped()); + + // Oneof with non-default + $from = new $class(); + $to = new $class(); + $from->mergeFromJsonString("{\"oneof_field\":" . $nonDefaultValueData . "}"); + $data = $from->serializeToJsonString(); + $to->mergeFromJsonString($data); + $this->assertEquals($nonDefaultValue, $to->getOneofFieldUnwrapped()); + + // Oneof with default + $from = new $class(); + $to = new $class(); + $from->mergeFromJsonString("{\"oneof_field\":" . $defaultValueData . "}"); + $data = $from->serializeToJsonString(); + $to->mergeFromJsonString($data); + $this->assertEquals($defaultValue, $to->getOneofFieldUnwrapped()); + } public function wrappersDataProvider() { return [ + [TestInt32Value::class, 1, "1", 0, "0"], [TestStringValue::class, "a", "\"a\"", "", "\"\""], ]; } diff --git a/src/google/protobuf/compiler/php/php_generator.cc b/src/google/protobuf/compiler/php/php_generator.cc index d2f50cb3a430..55b3de2316f5 100644 --- a/src/google/protobuf/compiler/php/php_generator.cc +++ b/src/google/protobuf/compiler/php/php_generator.cc @@ -679,23 +679,13 @@ void GenerateFieldAccessor(const FieldDescriptor* field, bool is_descriptor, field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && IsWrapperType(field)) { GenerateWrapperFieldGetterDocComment(printer, field); - if (field->containing_oneof()) { - printer->Print( + printer->Print( "public function get^camel_name^Unwrapped()\n" "{\n" - " $wrapper = $this->get^camel_name^();\n" - " return is_null($wrapper) ? null : $wrapper->getValue();\n" + " return $this->readWrapperValue(\"^field_name^\");\n" "}\n\n", - "camel_name", UnderscoresToCamelCase(field->name(), true)); - } else { - printer->Print( - "public function get^camel_name^Unwrapped()\n" - "{\n" - " return $this->readWrapperValue(\"^field_name^\");\n" - "}\n\n", - "camel_name", UnderscoresToCamelCase(field->name(), true), - "field_name", field->name()); - } + "camel_name", UnderscoresToCamelCase(field->name(), true), + "field_name", field->name()); } // Generate setter. @@ -802,25 +792,14 @@ void GenerateFieldAccessor(const FieldDescriptor* field, bool is_descriptor, field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && IsWrapperType(field)) { GenerateWrapperFieldSetterDocComment(printer, field); - if (field->containing_oneof()) { - printer->Print( - "public function set^camel_name^Unwrapped($var)\n" - "{\n" - " $wrappedVar = is_null($var) ? null : new \\^wrapper_type^(['value' => $var]);\n" - " return $this->set^camel_name^($wrappedVar);\n" - "}\n\n", - "camel_name", UnderscoresToCamelCase(field->name(), true), - "wrapper_type", LegacyFullClassName(field->message_type(), is_descriptor)); - } else { - printer->Print( - "public function set^camel_name^Unwrapped($var)\n" - "{\n" - " $this->writeWrapperValue(\"^field_name^\", $var);\n" - " return $this;" - "}\n\n", - "camel_name", UnderscoresToCamelCase(field->name(), true), - "field_name", field->name()); - } + printer->Print( + "public function set^camel_name^Unwrapped($var)\n" + "{\n" + " $this->writeWrapperValue(\"^field_name^\", $var);\n" + " return $this;" + "}\n\n", + "camel_name", UnderscoresToCamelCase(field->name(), true), + "field_name", field->name()); } // Generate has method for proto2 only. From 8024fb8ba11302d8ea35eed83d6850f91c8cff07 Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Sat, 2 Nov 2019 00:37:58 +0000 Subject: [PATCH 14/21] Fix bug --- php/ext/google/protobuf/encode_decode.c | 17 ++++++---- php/ext/google/protobuf/storage.c | 9 +++-- php/tests/encode_decode_test.php | 44 +++++++++++++++++++++++++ php/tests/wrapper_type_setters_test.php | 11 ++++--- 4 files changed, 65 insertions(+), 16 deletions(-) diff --git a/php/ext/google/protobuf/encode_decode.c b/php/ext/google/protobuf/encode_decode.c index 79839901b183..0dfae6e69565 100644 --- a/php/ext/google/protobuf/encode_decode.c +++ b/php/ext/google/protobuf/encode_decode.c @@ -955,15 +955,15 @@ static void* wrapper_submsg_handler(void* closure, const void* hd) { submsg_php = CACHED_PTR_TO_ZVAL_PTR(cached); frame->closure = closure; - if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(cached)) == IS_NULL) { + if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(cached)) == IS_OBJECT) { + submsg = UNBOX(MessageHeader, submsg_php); + frame->submsg = submsg; + frame->is_msg = true; + } else { // In this case, wrapper message hasn't been created and value will be // stored in cache directly. frame->submsg = cached; frame->is_msg = false; - } else { - submsg = UNBOX(MessageHeader, submsg_php); - frame->submsg = submsg; - frame->is_msg = true; } return frame; @@ -987,10 +987,15 @@ static void* wrapper_oneofsubmsg_handler(void* closure, const void* hd) { oneof_cleanup(msg, oneofdata); frame->submsg = cached; frame->is_msg = false; - } else { + } else if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(cached)) == IS_OBJECT) { submsg = UNBOX(MessageHeader, CACHED_PTR_TO_ZVAL_PTR(cached)); frame->submsg = submsg; frame->is_msg = true; + } else { + // In this case, wrapper message hasn't been created and value will be + // stored in cache directly. + frame->submsg = cached; + frame->is_msg = false; } DEREF(message_data(msg), oneofdata->case_ofs, uint32_t) = diff --git a/php/ext/google/protobuf/storage.c b/php/ext/google/protobuf/storage.c index fb06e0001698..3a0d0e92cad9 100644 --- a/php/ext/google/protobuf/storage.c +++ b/php/ext/google/protobuf/storage.c @@ -840,14 +840,13 @@ zval* layout_get(MessageLayout* layout, MessageHeader* header, #else ZVAL_OBJ(cached_zval, obj); #endif - if (stored_cache != cache) { + } + if (stored_cache != cache) { #if PHP_MAJOR_VERSION < 7 - ZVAL_ZVAL(CACHED_PTR_TO_ZVAL_PTR(cache), cached_zval, 1, 0); + ZVAL_ZVAL(CACHED_PTR_TO_ZVAL_PTR(cache), cached_zval, 1, 0); #else - ZVAL_OBJ(CACHED_PTR_TO_ZVAL_PTR(cache), obj); + ZVAL_OBJ(CACHED_PTR_TO_ZVAL_PTR(cache), obj); #endif - } - return CACHED_PTR_TO_ZVAL_PTR(cache); } } else { upb_fieldtype_t type = upb_fielddef_type(field); diff --git a/php/tests/encode_decode_test.php b/php/tests/encode_decode_test.php index c91d881bdd1f..00944b49e446 100644 --- a/php/tests/encode_decode_test.php +++ b/php/tests/encode_decode_test.php @@ -1430,6 +1430,50 @@ public function testWrapperJsonDecodeEncode( $this->assertEquals($defaultValue, $to->getOneofFieldUnwrapped()); } + /** + * @dataProvider wrappersDataProvider + */ + public function testWrapperSetUnwrappedJsonEncode( + $class, + $nonDefaultValue, + $nonDefaultValueData, + $defaultValue, + $defaultValueData + ) + { + // Singular with non-default + $from = new $class(); + $to = new $class(); + $from->setFieldUnwrapped($nonDefaultValue); + $data = $from->serializeToJsonString(); + $to->mergeFromJsonString($data); + $this->assertEquals($nonDefaultValue, $to->getFieldUnwrapped()); + + // Singular with default + $from = new $class(); + $to = new $class(); + $from->setFieldUnwrapped($defaultValue); + $data = $from->serializeToJsonString(); + $to->mergeFromJsonString($data); + $this->assertEquals($defaultValue, $to->getFieldUnwrapped()); + + // Oneof with non-default + $from = new $class(); + $to = new $class(); + $from->setOneofFieldUnwrapped($nonDefaultValue); + $data = $from->serializeToJsonString(); + $to->mergeFromJsonString($data); + $this->assertEquals($nonDefaultValue, $to->getOneofFieldUnwrapped()); + + // Oneof with default + $from = new $class(); + $to = new $class(); + $from->setOneofFieldUnwrapped($defaultValue); + $data = $from->serializeToJsonString(); + $to->mergeFromJsonString($data); + $this->assertEquals($defaultValue, $to->getOneofFieldUnwrapped()); + } + public function wrappersDataProvider() { return [ diff --git a/php/tests/wrapper_type_setters_test.php b/php/tests/wrapper_type_setters_test.php index ad9f7181dd05..b6673611794e 100644 --- a/php/tests/wrapper_type_setters_test.php +++ b/php/tests/wrapper_type_setters_test.php @@ -38,7 +38,7 @@ public function testGettersAndSetters( $oldSetterMsg->$setter($wrappedValue); $newSetterMsg->$valueSetter($value); - // Get expected values old getter + # // Get expected values old getter $expectedValue = $oldSetterMsg->$getter(); // Check that old getter returns the same value after using the @@ -129,10 +129,11 @@ public function gettersAndSettersDataProvider() ]], [TestWrapperSetters::class, DoubleValue::class, "setDoubleValueOneof", "setDoubleValueOneofUnwrapped", "getDoubleValueOneof", "getDoubleValueOneofUnwrapped", [ [1.1, new DoubleValue(["value" => 1.1])], - [2.2, new DoubleValue(["value" => 2.2])], - [null, null], - [0, new DoubleValue()], - ]],[TestWrapperSetters::class, StringValue::class, "setStringValueOneof", "setStringValueOneofUnwrapped", "getStringValueOneof", "getStringValueOneofUnwrapped", [ + # [2.2, new DoubleValue(["value" => 2.2])], + # [null, null], + # [0, new DoubleValue()], + ]], + [TestWrapperSetters::class, StringValue::class, "setStringValueOneof", "setStringValueOneofUnwrapped", "getStringValueOneof", "getStringValueOneofUnwrapped", [ ["asdf", new StringValue(["value" => "asdf"])], ["", new StringValue(["value" => ""])], [null, null], From 060696c4439a54b9530b215540ef6a8593ed24b7 Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Tue, 5 Nov 2019 21:36:36 +0000 Subject: [PATCH 15/21] Fix a bug that in php7 variable is defined out of scope --- php/ext/google/protobuf/storage.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/php/ext/google/protobuf/storage.c b/php/ext/google/protobuf/storage.c index 3a0d0e92cad9..5c1c51c43f02 100644 --- a/php/ext/google/protobuf/storage.c +++ b/php/ext/google/protobuf/storage.c @@ -813,6 +813,9 @@ zval* layout_get(MessageLayout* layout, MessageHeader* header, if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE && is_wrapper_msg(upb_fielddef_msgsubdef(field))) { zval * cached_zval = CACHED_PTR_TO_ZVAL_PTR(stored_cache); +#if PHP_MAJOR_VERSION >= 7 + zend_object* obj; +#endif if (Z_TYPE_P(cached_zval) != IS_OBJECT && Z_TYPE_P(cached_zval) != IS_NULL) { // Needs to expand value to wrapper. @@ -829,7 +832,7 @@ zval* layout_get(MessageLayout* layout, MessageHeader* header, ZVAL_OBJ(val, subklass->create_object(subklass TSRMLS_CC)); submsg = UNBOX(MessageHeader, val); #else - zend_object* obj = subklass->create_object(subklass TSRMLS_CC); + obj = subklass->create_object(subklass TSRMLS_CC); submsg = (MessageHeader*)((char*)obj - XtOffsetOf(MessageHeader, std)); #endif custom_data_init(subklass, submsg PHP_PROTO_TSRMLS_CC); From 2e20c8d8ccc8b2d8cfb17c8908915d6a8a439f87 Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Fri, 8 Nov 2019 23:09:46 +0000 Subject: [PATCH 16/21] Fix broken tests * Update upb to fix Timestamp conformance tests * Fix segmentation fault for oneof wrapper fields --- php/ext/google/protobuf/storage.c | 64 ++------ php/ext/google/protobuf/upb.c | 196 +++++++++++------------- php/tests/wrapper_type_setters_test.php | 8 +- 3 files changed, 112 insertions(+), 156 deletions(-) diff --git a/php/ext/google/protobuf/storage.c b/php/ext/google/protobuf/storage.c index 5c1c51c43f02..4a8543f7b948 100644 --- a/php/ext/google/protobuf/storage.c +++ b/php/ext/google/protobuf/storage.c @@ -116,17 +116,17 @@ bool native_slot_set(upb_fieldtype_t type, const zend_class_entry* klass, return false; } +#if PHP_MAJOR_VERSION < 7 + REPLACE_ZVAL_VALUE((CACHED_VALUE*)memory, value, 1); +#else zval* property_ptr = CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)memory); if (EXPECTED(property_ptr != value)) { php_proto_zval_ptr_dtor(property_ptr); } -#if PHP_MAJOR_VERSION < 7 - DEREF(memory, zval*) = value; - Z_ADDREF_P(value); -#else ZVAL_ZVAL(property_ptr, value, 1, 0); #endif + break; } @@ -845,11 +845,7 @@ zval* layout_get(MessageLayout* layout, MessageHeader* header, #endif } if (stored_cache != cache) { -#if PHP_MAJOR_VERSION < 7 ZVAL_ZVAL(CACHED_PTR_TO_ZVAL_PTR(cache), cached_zval, 1, 0); -#else - ZVAL_OBJ(CACHED_PTR_TO_ZVAL_PTR(cache), obj); -#endif } } else { upb_fieldtype_t type = upb_fielddef_type(field); @@ -866,33 +862,6 @@ void layout_set(MessageLayout* layout, MessageHeader* header, uint32_t* oneof_case = slot_oneof_case(layout, storage, field); if (upb_fielddef_containingoneof(field)) { - upb_fieldtype_t type = upb_fielddef_type(field); - zend_class_entry *ce = NULL; - - // For non-singular fields, the related memory needs to point to the actual - // zval in properties table first. - switch (type) { - case UPB_TYPE_MESSAGE: { - const upb_msgdef* msg = upb_fielddef_msgsubdef(field); - Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj(msg)); - ce = desc->klass; - // Intentionally fall through. - } - case UPB_TYPE_STRING: - case UPB_TYPE_BYTES: { - int property_cache_index = - header->descriptor->layout->fields[upb_fielddef_index(field)] - .cache_index; - DEREF(memory, CACHED_VALUE*) = - OBJ_PROP(&header->std, property_cache_index); - memory = DEREF(memory, CACHED_VALUE*); - break; - } - default: - break; - } - - native_slot_set(type, ce, memory, val TSRMLS_CC); *oneof_case = upb_fielddef_number(field); } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { // Works for both repeated and map fields @@ -936,19 +905,20 @@ void layout_set(MessageLayout* layout, MessageHeader* header, #endif zval_dtor(&converted_value); } - } else { - upb_fieldtype_t type = upb_fielddef_type(field); - zend_class_entry *ce = NULL; - if (type == UPB_TYPE_MESSAGE) { - const upb_msgdef* msg = upb_fielddef_msgsubdef(field); - Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj(msg)); - ce = desc->klass; - } - CACHED_VALUE* cache = find_zval_property(header, field); - native_slot_set( - type, ce, value_memory(upb_fielddef_type(field), memory, cache), - val TSRMLS_CC); + return; + } + + upb_fieldtype_t type = upb_fielddef_type(field); + zend_class_entry *ce = NULL; + if (type == UPB_TYPE_MESSAGE) { + const upb_msgdef* msg = upb_fielddef_msgsubdef(field); + Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj(msg)); + ce = desc->klass; } + CACHED_VALUE* cache = find_zval_property(header, field); + native_slot_set( + type, ce, value_memory(upb_fielddef_type(field), memory, cache), + val TSRMLS_CC); } static void native_slot_merge( diff --git a/php/ext/google/protobuf/upb.c b/php/ext/google/protobuf/upb.c index 765b98884f5b..0d3199384388 100644 --- a/php/ext/google/protobuf/upb.c +++ b/php/ext/google/protobuf/upb.c @@ -10119,46 +10119,32 @@ static void start_timestamp_zone(upb_json_parser *p, const char *ptr) { capture_begin(p, ptr); } -#define EPOCH_YEAR 1970 -#define TM_YEAR_BASE 1900 - -static bool isleap(int year) { - return (year % 4) == 0 && (year % 100 != 0 || (year % 400) == 0); +static int div_round_up2(int n, int d) { + return (n + d - 1) / d; } -const unsigned short int __mon_yday[2][13] = { - /* Normal years. */ - { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, - /* Leap years. */ - { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } -}; - -int64_t epoch(int year, int yday, int hour, int min, int sec) { - int64_t years = year - EPOCH_YEAR; - - int64_t leap_days = years / 4 - years / 100 + years / 400; +/* epoch_days(1970, 1, 1) == 1970-01-01 == 0. */ +static int epoch_days(int year, int month, int day) { + static const uint16_t month_yday[12] = {0, 31, 59, 90, 120, 151, + 181, 212, 243, 273, 304, 334}; + int febs_since_0 = month > 2 ? year + 1 : year; + int leap_days_since_0 = div_round_up2(febs_since_0, 4) - + div_round_up2(febs_since_0, 100) + + div_round_up2(febs_since_0, 400); + int days_since_0 = + 365 * year + month_yday[month - 1] + (day - 1) + leap_days_since_0; - int64_t days = years * 365 + yday + leap_days; - int64_t hours = days * 24 + hour; - int64_t mins = hours * 60 + min; - int64_t secs = mins * 60 + sec; - return secs; + /* Convert from 0-epoch (0001-01-01 BC) to Unix Epoch (1970-01-01 AD). + * Since the "BC" system does not have a year zero, 1 BC == year zero. */ + return days_since_0 - 719528; } - -static int64_t upb_mktime(const struct tm *tp) { - int sec = tp->tm_sec; - int min = tp->tm_min; - int hour = tp->tm_hour; - int mday = tp->tm_mday; - int mon = tp->tm_mon; - int year = tp->tm_year + TM_YEAR_BASE; - - /* Calculate day of year from year, month, and day of month. */ - int mon_yday = ((__mon_yday[isleap(year)][mon]) - 1); - int yday = mon_yday + mday; - - return epoch(year, yday, hour, min, sec); +static int64_t upb_timegm(const struct tm *tp) { + int64_t ret = epoch_days(tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday); + ret = (ret * 24) + tp->tm_hour; + ret = (ret * 60) + tp->tm_min; + ret = (ret * 60) + tp->tm_sec; + return ret; } static bool end_timestamp_zone(upb_json_parser *p, const char *ptr) { @@ -10188,7 +10174,7 @@ static bool end_timestamp_zone(upb_json_parser *p, const char *ptr) { } /* Normalize tm */ - seconds = upb_mktime(&p->tm); + seconds = upb_timegm(&p->tm); /* Check timestamp boundary */ if (seconds < -62135596800) { @@ -11019,11 +11005,11 @@ static bool does_fieldmask_end(upb_json_parser *p) { * final state once, when the closing '"' is seen. */ -#line 2794 "upb/json/parser.rl" +#line 2780 "upb/json/parser.rl" -#line 2597 "upb/json/parser.c" +#line 2583 "upb/json/parser.c" static const char _json_actions[] = { 0, 1, 0, 1, 1, 1, 3, 1, 4, 1, 6, 1, 7, 1, 8, 1, @@ -11278,7 +11264,7 @@ static const int json_en_value_machine = 78; static const int json_en_main = 1; -#line 2797 "upb/json/parser.rl" +#line 2783 "upb/json/parser.rl" size_t parse(void *closure, const void *hd, const char *buf, size_t size, const upb_bufhandle *handle) { @@ -11301,7 +11287,7 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, capture_resume(parser, buf); -#line 2875 "upb/json/parser.c" +#line 2861 "upb/json/parser.c" { int _klen; unsigned int _trans; @@ -11376,147 +11362,147 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, switch ( *_acts++ ) { case 1: -#line 2602 "upb/json/parser.rl" +#line 2588 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 2: -#line 2604 "upb/json/parser.rl" +#line 2590 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 23;goto _again;} } break; case 3: -#line 2608 "upb/json/parser.rl" +#line 2594 "upb/json/parser.rl" { start_text(parser, p); } break; case 4: -#line 2609 "upb/json/parser.rl" +#line 2595 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_text(parser, p)); } break; case 5: -#line 2615 "upb/json/parser.rl" +#line 2601 "upb/json/parser.rl" { start_hex(parser); } break; case 6: -#line 2616 "upb/json/parser.rl" +#line 2602 "upb/json/parser.rl" { hexdigit(parser, p); } break; case 7: -#line 2617 "upb/json/parser.rl" +#line 2603 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_hex(parser)); } break; case 8: -#line 2623 "upb/json/parser.rl" +#line 2609 "upb/json/parser.rl" { CHECK_RETURN_TOP(escape(parser, p)); } break; case 9: -#line 2629 "upb/json/parser.rl" +#line 2615 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 10: -#line 2634 "upb/json/parser.rl" +#line 2620 "upb/json/parser.rl" { start_year(parser, p); } break; case 11: -#line 2635 "upb/json/parser.rl" +#line 2621 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_year(parser, p)); } break; case 12: -#line 2639 "upb/json/parser.rl" +#line 2625 "upb/json/parser.rl" { start_month(parser, p); } break; case 13: -#line 2640 "upb/json/parser.rl" +#line 2626 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_month(parser, p)); } break; case 14: -#line 2644 "upb/json/parser.rl" +#line 2630 "upb/json/parser.rl" { start_day(parser, p); } break; case 15: -#line 2645 "upb/json/parser.rl" +#line 2631 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_day(parser, p)); } break; case 16: -#line 2649 "upb/json/parser.rl" +#line 2635 "upb/json/parser.rl" { start_hour(parser, p); } break; case 17: -#line 2650 "upb/json/parser.rl" +#line 2636 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_hour(parser, p)); } break; case 18: -#line 2654 "upb/json/parser.rl" +#line 2640 "upb/json/parser.rl" { start_minute(parser, p); } break; case 19: -#line 2655 "upb/json/parser.rl" +#line 2641 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_minute(parser, p)); } break; case 20: -#line 2659 "upb/json/parser.rl" +#line 2645 "upb/json/parser.rl" { start_second(parser, p); } break; case 21: -#line 2660 "upb/json/parser.rl" +#line 2646 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_second(parser, p)); } break; case 22: -#line 2665 "upb/json/parser.rl" +#line 2651 "upb/json/parser.rl" { start_duration_base(parser, p); } break; case 23: -#line 2666 "upb/json/parser.rl" +#line 2652 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_duration_base(parser, p)); } break; case 24: -#line 2668 "upb/json/parser.rl" +#line 2654 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 25: -#line 2673 "upb/json/parser.rl" +#line 2659 "upb/json/parser.rl" { start_timestamp_base(parser); } break; case 26: -#line 2675 "upb/json/parser.rl" +#line 2661 "upb/json/parser.rl" { start_timestamp_fraction(parser, p); } break; case 27: -#line 2676 "upb/json/parser.rl" +#line 2662 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_timestamp_fraction(parser, p)); } break; case 28: -#line 2678 "upb/json/parser.rl" +#line 2664 "upb/json/parser.rl" { start_timestamp_zone(parser, p); } break; case 29: -#line 2679 "upb/json/parser.rl" +#line 2665 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_timestamp_zone(parser, p)); } break; case 30: -#line 2681 "upb/json/parser.rl" +#line 2667 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 31: -#line 2686 "upb/json/parser.rl" +#line 2672 "upb/json/parser.rl" { start_fieldmask_path_text(parser, p); } break; case 32: -#line 2687 "upb/json/parser.rl" +#line 2673 "upb/json/parser.rl" { end_fieldmask_path_text(parser, p); } break; case 33: -#line 2692 "upb/json/parser.rl" +#line 2678 "upb/json/parser.rl" { start_fieldmask_path(parser); } break; case 34: -#line 2693 "upb/json/parser.rl" +#line 2679 "upb/json/parser.rl" { end_fieldmask_path(parser); } break; case 35: -#line 2699 "upb/json/parser.rl" +#line 2685 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 36: -#line 2704 "upb/json/parser.rl" +#line 2690 "upb/json/parser.rl" { if (is_wellknown_msg(parser, UPB_WELLKNOWN_TIMESTAMP)) { {stack[top++] = cs; cs = 47;goto _again;} @@ -11530,11 +11516,11 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, } break; case 37: -#line 2717 "upb/json/parser.rl" +#line 2703 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 78;goto _again;} } break; case 38: -#line 2722 "upb/json/parser.rl" +#line 2708 "upb/json/parser.rl" { if (is_wellknown_msg(parser, UPB_WELLKNOWN_ANY)) { start_any_member(parser, p); @@ -11544,11 +11530,11 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, } break; case 39: -#line 2729 "upb/json/parser.rl" +#line 2715 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_membername(parser)); } break; case 40: -#line 2732 "upb/json/parser.rl" +#line 2718 "upb/json/parser.rl" { if (is_wellknown_msg(parser, UPB_WELLKNOWN_ANY)) { end_any_member(parser, p); @@ -11558,7 +11544,7 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, } break; case 41: -#line 2743 "upb/json/parser.rl" +#line 2729 "upb/json/parser.rl" { if (is_wellknown_msg(parser, UPB_WELLKNOWN_ANY)) { start_any_object(parser, p); @@ -11568,7 +11554,7 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, } break; case 42: -#line 2752 "upb/json/parser.rl" +#line 2738 "upb/json/parser.rl" { if (is_wellknown_msg(parser, UPB_WELLKNOWN_ANY)) { CHECK_RETURN_TOP(end_any_object(parser, p)); @@ -11578,54 +11564,54 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, } break; case 43: -#line 2764 "upb/json/parser.rl" +#line 2750 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_array(parser)); } break; case 44: -#line 2768 "upb/json/parser.rl" +#line 2754 "upb/json/parser.rl" { end_array(parser); } break; case 45: -#line 2773 "upb/json/parser.rl" +#line 2759 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_number(parser, p)); } break; case 46: -#line 2774 "upb/json/parser.rl" +#line 2760 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_number(parser, p)); } break; case 47: -#line 2776 "upb/json/parser.rl" +#line 2762 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_stringval(parser)); } break; case 48: -#line 2777 "upb/json/parser.rl" +#line 2763 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_stringval(parser)); } break; case 49: -#line 2779 "upb/json/parser.rl" +#line 2765 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_bool(parser, true)); } break; case 50: -#line 2781 "upb/json/parser.rl" +#line 2767 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_bool(parser, false)); } break; case 51: -#line 2783 "upb/json/parser.rl" +#line 2769 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_null(parser)); } break; case 52: -#line 2785 "upb/json/parser.rl" +#line 2771 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_subobject_full(parser)); } break; case 53: -#line 2786 "upb/json/parser.rl" +#line 2772 "upb/json/parser.rl" { end_subobject_full(parser); } break; case 54: -#line 2791 "upb/json/parser.rl" +#line 2777 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; -#line 3199 "upb/json/parser.c" +#line 3185 "upb/json/parser.c" } } @@ -11642,32 +11628,32 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, while ( __nacts-- > 0 ) { switch ( *__acts++ ) { case 0: -#line 2600 "upb/json/parser.rl" +#line 2586 "upb/json/parser.rl" { p--; {cs = stack[--top]; if ( p == pe ) goto _test_eof; goto _again;} } break; case 46: -#line 2774 "upb/json/parser.rl" +#line 2760 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_number(parser, p)); } break; case 49: -#line 2779 "upb/json/parser.rl" +#line 2765 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_bool(parser, true)); } break; case 50: -#line 2781 "upb/json/parser.rl" +#line 2767 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_bool(parser, false)); } break; case 51: -#line 2783 "upb/json/parser.rl" +#line 2769 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_null(parser)); } break; case 53: -#line 2786 "upb/json/parser.rl" +#line 2772 "upb/json/parser.rl" { end_subobject_full(parser); } break; -#line 3241 "upb/json/parser.c" +#line 3227 "upb/json/parser.c" } } } @@ -11675,7 +11661,7 @@ goto _again;} } _out: {} } -#line 2819 "upb/json/parser.rl" +#line 2805 "upb/json/parser.rl" if (p != pe) { upb_status_seterrf(parser->status, "Parse error at '%.*s'\n", pe - p, p); @@ -11718,13 +11704,13 @@ static void json_parser_reset(upb_json_parser *p) { /* Emit Ragel initialization of the parser. */ -#line 3292 "upb/json/parser.c" +#line 3278 "upb/json/parser.c" { cs = json_start; top = 0; } -#line 2861 "upb/json/parser.rl" +#line 2847 "upb/json/parser.rl" p->current_state = cs; p->parser_top = top; accumulate_clear(p); diff --git a/php/tests/wrapper_type_setters_test.php b/php/tests/wrapper_type_setters_test.php index b6673611794e..e4bdfcfad333 100644 --- a/php/tests/wrapper_type_setters_test.php +++ b/php/tests/wrapper_type_setters_test.php @@ -38,7 +38,7 @@ public function testGettersAndSetters( $oldSetterMsg->$setter($wrappedValue); $newSetterMsg->$valueSetter($value); - # // Get expected values old getter + // Get expected values old getter $expectedValue = $oldSetterMsg->$getter(); // Check that old getter returns the same value after using the @@ -129,9 +129,9 @@ public function gettersAndSettersDataProvider() ]], [TestWrapperSetters::class, DoubleValue::class, "setDoubleValueOneof", "setDoubleValueOneofUnwrapped", "getDoubleValueOneof", "getDoubleValueOneofUnwrapped", [ [1.1, new DoubleValue(["value" => 1.1])], - # [2.2, new DoubleValue(["value" => 2.2])], - # [null, null], - # [0, new DoubleValue()], + [2.2, new DoubleValue(["value" => 2.2])], + [null, null], + [0, new DoubleValue()], ]], [TestWrapperSetters::class, StringValue::class, "setStringValueOneof", "setStringValueOneofUnwrapped", "getStringValueOneof", "getStringValueOneofUnwrapped", [ ["asdf", new StringValue(["value" => "asdf"])], From 4aa103dc98f9a228c8f608b60f14a8c5efa3ee52 Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Sat, 9 Nov 2019 00:24:53 +0000 Subject: [PATCH 17/21] Fix encoding/decoding top level wrapper values --- php/ext/google/protobuf/encode_decode.c | 35 +- php/ext/google/protobuf/upb.c | 140 +- php/tests/encode_decode_test.php | 2229 +++++++++++------------ 3 files changed, 1197 insertions(+), 1207 deletions(-) diff --git a/php/ext/google/protobuf/encode_decode.c b/php/ext/google/protobuf/encode_decode.c index 0dfae6e69565..e6422600cdd3 100644 --- a/php/ext/google/protobuf/encode_decode.c +++ b/php/ext/google/protobuf/encode_decode.c @@ -2058,12 +2058,28 @@ void merge_from_string(const char* data, int data_len, Descriptor* desc, stackenv se; upb_sink sink; upb_pbdecoder* decoder; + void* closure; stackenv_init(&se, "Error occurred during parsing: %s"); - upb_sink_reset(&sink, h, msg); + if (is_wrapper_msg(desc->msgdef)) { + wrapperfields_parseframe_t* frame = + (wrapperfields_parseframe_t*)malloc( + sizeof(wrapperfields_parseframe_t)); + frame->submsg = msg; + frame->is_msg = true; + closure = frame; + } else { + closure = msg; + } + + upb_sink_reset(&sink, h, closure); decoder = upb_pbdecoder_create(se.arena, method, sink, &se.status); upb_bufsrc_putbuf(data, data_len, upb_pbdecoder_input(decoder)); + if (is_wrapper_msg(desc->msgdef)) { + free((wrapperfields_parseframe_t*)closure); + } + stackenv_uninit(&se); } @@ -2141,13 +2157,28 @@ PHP_METHOD(Message, mergeFromJsonString) { stackenv se; upb_sink sink; upb_json_parser* parser; + void* closure; stackenv_init(&se, "Error occurred during parsing: %s"); - upb_sink_reset(&sink, get_fill_handlers(desc), msg); + if (is_wrapper_msg(desc->msgdef)) { + wrapperfields_parseframe_t* frame = + (wrapperfields_parseframe_t*)malloc( + sizeof(wrapperfields_parseframe_t)); + frame->submsg = msg; + frame->is_msg = true; + closure = frame; + } else { + closure = msg; + } + + upb_sink_reset(&sink, get_fill_handlers(desc), closure); parser = upb_json_parser_create(se.arena, method, generated_pool->symtab, sink, &se.status, ignore_json_unknown); upb_bufsrc_putbuf(data, data_len, upb_json_parser_input(parser)); + if (is_wrapper_msg(desc->msgdef)) { + free((wrapperfields_parseframe_t*)closure); + } stackenv_uninit(&se); } } diff --git a/php/ext/google/protobuf/upb.c b/php/ext/google/protobuf/upb.c index 0d3199384388..9ec5bdf289d4 100644 --- a/php/ext/google/protobuf/upb.c +++ b/php/ext/google/protobuf/upb.c @@ -8432,7 +8432,7 @@ upb_decoderet upb_vdecode_max8_branch64(upb_decoderet r) { return r; } -#line 1 "upb/json/parser.rl" +// #line 1 "upb/json/parser.rl" /* ** upb::json::Parser (upb_json_parser) ** @@ -11005,11 +11005,11 @@ static bool does_fieldmask_end(upb_json_parser *p) { * final state once, when the closing '"' is seen. */ -#line 2780 "upb/json/parser.rl" +// #line 2780 "upb/json/parser.rl" -#line 2583 "upb/json/parser.c" +// #line 2583 "upb/json/parser.c" static const char _json_actions[] = { 0, 1, 0, 1, 1, 1, 3, 1, 4, 1, 6, 1, 7, 1, 8, 1, @@ -11264,7 +11264,7 @@ static const int json_en_value_machine = 78; static const int json_en_main = 1; -#line 2783 "upb/json/parser.rl" +// #line 2783 "upb/json/parser.rl" size_t parse(void *closure, const void *hd, const char *buf, size_t size, const upb_bufhandle *handle) { @@ -11287,7 +11287,7 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, capture_resume(parser, buf); -#line 2861 "upb/json/parser.c" +// #line 2861 "upb/json/parser.c" { int _klen; unsigned int _trans; @@ -11362,147 +11362,147 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, switch ( *_acts++ ) { case 1: -#line 2588 "upb/json/parser.rl" +// #line 2588 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 2: -#line 2590 "upb/json/parser.rl" +// #line 2590 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 23;goto _again;} } break; case 3: -#line 2594 "upb/json/parser.rl" +// #line 2594 "upb/json/parser.rl" { start_text(parser, p); } break; case 4: -#line 2595 "upb/json/parser.rl" +// #line 2595 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_text(parser, p)); } break; case 5: -#line 2601 "upb/json/parser.rl" +// #line 2601 "upb/json/parser.rl" { start_hex(parser); } break; case 6: -#line 2602 "upb/json/parser.rl" +// #line 2602 "upb/json/parser.rl" { hexdigit(parser, p); } break; case 7: -#line 2603 "upb/json/parser.rl" +// #line 2603 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_hex(parser)); } break; case 8: -#line 2609 "upb/json/parser.rl" +// #line 2609 "upb/json/parser.rl" { CHECK_RETURN_TOP(escape(parser, p)); } break; case 9: -#line 2615 "upb/json/parser.rl" +// #line 2615 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 10: -#line 2620 "upb/json/parser.rl" +// #line 2620 "upb/json/parser.rl" { start_year(parser, p); } break; case 11: -#line 2621 "upb/json/parser.rl" +// #line 2621 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_year(parser, p)); } break; case 12: -#line 2625 "upb/json/parser.rl" +// #line 2625 "upb/json/parser.rl" { start_month(parser, p); } break; case 13: -#line 2626 "upb/json/parser.rl" +// #line 2626 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_month(parser, p)); } break; case 14: -#line 2630 "upb/json/parser.rl" +// #line 2630 "upb/json/parser.rl" { start_day(parser, p); } break; case 15: -#line 2631 "upb/json/parser.rl" +// #line 2631 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_day(parser, p)); } break; case 16: -#line 2635 "upb/json/parser.rl" +// #line 2635 "upb/json/parser.rl" { start_hour(parser, p); } break; case 17: -#line 2636 "upb/json/parser.rl" +// #line 2636 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_hour(parser, p)); } break; case 18: -#line 2640 "upb/json/parser.rl" +// #line 2640 "upb/json/parser.rl" { start_minute(parser, p); } break; case 19: -#line 2641 "upb/json/parser.rl" +// #line 2641 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_minute(parser, p)); } break; case 20: -#line 2645 "upb/json/parser.rl" +// #line 2645 "upb/json/parser.rl" { start_second(parser, p); } break; case 21: -#line 2646 "upb/json/parser.rl" +// #line 2646 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_second(parser, p)); } break; case 22: -#line 2651 "upb/json/parser.rl" +// #line 2651 "upb/json/parser.rl" { start_duration_base(parser, p); } break; case 23: -#line 2652 "upb/json/parser.rl" +// #line 2652 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_duration_base(parser, p)); } break; case 24: -#line 2654 "upb/json/parser.rl" +// #line 2654 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 25: -#line 2659 "upb/json/parser.rl" +// #line 2659 "upb/json/parser.rl" { start_timestamp_base(parser); } break; case 26: -#line 2661 "upb/json/parser.rl" +// #line 2661 "upb/json/parser.rl" { start_timestamp_fraction(parser, p); } break; case 27: -#line 2662 "upb/json/parser.rl" +// #line 2662 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_timestamp_fraction(parser, p)); } break; case 28: -#line 2664 "upb/json/parser.rl" +// #line 2664 "upb/json/parser.rl" { start_timestamp_zone(parser, p); } break; case 29: -#line 2665 "upb/json/parser.rl" +// #line 2665 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_timestamp_zone(parser, p)); } break; case 30: -#line 2667 "upb/json/parser.rl" +// #line 2667 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 31: -#line 2672 "upb/json/parser.rl" +// #line 2672 "upb/json/parser.rl" { start_fieldmask_path_text(parser, p); } break; case 32: -#line 2673 "upb/json/parser.rl" +// #line 2673 "upb/json/parser.rl" { end_fieldmask_path_text(parser, p); } break; case 33: -#line 2678 "upb/json/parser.rl" +// #line 2678 "upb/json/parser.rl" { start_fieldmask_path(parser); } break; case 34: -#line 2679 "upb/json/parser.rl" +// #line 2679 "upb/json/parser.rl" { end_fieldmask_path(parser); } break; case 35: -#line 2685 "upb/json/parser.rl" +// #line 2685 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 36: -#line 2690 "upb/json/parser.rl" +// #line 2690 "upb/json/parser.rl" { if (is_wellknown_msg(parser, UPB_WELLKNOWN_TIMESTAMP)) { {stack[top++] = cs; cs = 47;goto _again;} @@ -11516,11 +11516,11 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, } break; case 37: -#line 2703 "upb/json/parser.rl" +// #line 2703 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 78;goto _again;} } break; case 38: -#line 2708 "upb/json/parser.rl" +// #line 2708 "upb/json/parser.rl" { if (is_wellknown_msg(parser, UPB_WELLKNOWN_ANY)) { start_any_member(parser, p); @@ -11530,11 +11530,11 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, } break; case 39: -#line 2715 "upb/json/parser.rl" +// #line 2715 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_membername(parser)); } break; case 40: -#line 2718 "upb/json/parser.rl" +// #line 2718 "upb/json/parser.rl" { if (is_wellknown_msg(parser, UPB_WELLKNOWN_ANY)) { end_any_member(parser, p); @@ -11544,7 +11544,7 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, } break; case 41: -#line 2729 "upb/json/parser.rl" +// #line 2729 "upb/json/parser.rl" { if (is_wellknown_msg(parser, UPB_WELLKNOWN_ANY)) { start_any_object(parser, p); @@ -11554,7 +11554,7 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, } break; case 42: -#line 2738 "upb/json/parser.rl" +// #line 2738 "upb/json/parser.rl" { if (is_wellknown_msg(parser, UPB_WELLKNOWN_ANY)) { CHECK_RETURN_TOP(end_any_object(parser, p)); @@ -11564,54 +11564,54 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, } break; case 43: -#line 2750 "upb/json/parser.rl" +// #line 2750 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_array(parser)); } break; case 44: -#line 2754 "upb/json/parser.rl" +// #line 2754 "upb/json/parser.rl" { end_array(parser); } break; case 45: -#line 2759 "upb/json/parser.rl" +// #line 2759 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_number(parser, p)); } break; case 46: -#line 2760 "upb/json/parser.rl" +// #line 2760 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_number(parser, p)); } break; case 47: -#line 2762 "upb/json/parser.rl" +// #line 2762 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_stringval(parser)); } break; case 48: -#line 2763 "upb/json/parser.rl" +// #line 2763 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_stringval(parser)); } break; case 49: -#line 2765 "upb/json/parser.rl" +// #line 2765 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_bool(parser, true)); } break; case 50: -#line 2767 "upb/json/parser.rl" +// #line 2767 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_bool(parser, false)); } break; case 51: -#line 2769 "upb/json/parser.rl" +// #line 2769 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_null(parser)); } break; case 52: -#line 2771 "upb/json/parser.rl" +// #line 2771 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_subobject_full(parser)); } break; case 53: -#line 2772 "upb/json/parser.rl" +// #line 2772 "upb/json/parser.rl" { end_subobject_full(parser); } break; case 54: -#line 2777 "upb/json/parser.rl" +// #line 2777 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; -#line 3185 "upb/json/parser.c" +// #line 3185 "upb/json/parser.c" } } @@ -11628,32 +11628,32 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, while ( __nacts-- > 0 ) { switch ( *__acts++ ) { case 0: -#line 2586 "upb/json/parser.rl" +// #line 2586 "upb/json/parser.rl" { p--; {cs = stack[--top]; if ( p == pe ) goto _test_eof; goto _again;} } break; case 46: -#line 2760 "upb/json/parser.rl" +// #line 2760 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_number(parser, p)); } break; case 49: -#line 2765 "upb/json/parser.rl" +// #line 2765 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_bool(parser, true)); } break; case 50: -#line 2767 "upb/json/parser.rl" +// #line 2767 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_bool(parser, false)); } break; case 51: -#line 2769 "upb/json/parser.rl" +// #line 2769 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_null(parser)); } break; case 53: -#line 2772 "upb/json/parser.rl" +// #line 2772 "upb/json/parser.rl" { end_subobject_full(parser); } break; -#line 3227 "upb/json/parser.c" +// #line 3227 "upb/json/parser.c" } } } @@ -11661,7 +11661,7 @@ goto _again;} } _out: {} } -#line 2805 "upb/json/parser.rl" +// #line 2805 "upb/json/parser.rl" if (p != pe) { upb_status_seterrf(parser->status, "Parse error at '%.*s'\n", pe - p, p); @@ -11704,13 +11704,13 @@ static void json_parser_reset(upb_json_parser *p) { /* Emit Ragel initialization of the parser. */ -#line 3278 "upb/json/parser.c" +// #line 3278 "upb/json/parser.c" { cs = json_start; top = 0; } -#line 2847 "upb/json/parser.rl" +// #line 2847 "upb/json/parser.rl" p->current_state = cs; p->parser_top = top; accumulate_clear(p); diff --git a/php/tests/encode_decode_test.php b/php/tests/encode_decode_test.php index 00944b49e446..8a41da9ef961 100644 --- a/php/tests/encode_decode_test.php +++ b/php/tests/encode_decode_test.php @@ -37,184 +37,156 @@ class EncodeDecodeTest extends TestBase { - # public function testDecodeJsonSimple() - # { - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"optionalInt32\":1}"); - # $this->assertEquals(1, $m->getOptionalInt32()); - # } - - # public function testDecodeTopLevelBoolValue() - # { - # $m = new BoolValue(); - - # $m->mergeFromJsonString("true"); - # $this->assertEquals(true, $m->getValue()); - - # $m->mergeFromJsonString("false"); - # $this->assertEquals(false, $m->getValue()); - # } - - # public function testEncodeTopLevelBoolValue() - # { - # $m = new BoolValue(); - # $m->setValue(true); - # $this->assertSame("true", $m->serializeToJsonString()); - # } + public function testDecodeJsonSimple() + { + $m = new TestMessage(); + $m->mergeFromJsonString("{\"optionalInt32\":1}"); + $this->assertEquals(1, $m->getOptionalInt32()); + } - # public function testDecodeTopLevelDoubleValue() - # { - # $m = new DoubleValue(); - # $m->mergeFromJsonString("1.5"); - # $this->assertEquals(1.5, $m->getValue()); - # } + public function testDecodeTopLevelBoolValue() + { + $m = new BoolValue(); - # public function testEncodeTopLevelDoubleValue() - # { - # $m = new DoubleValue(); - # $m->setValue(1.5); - # $this->assertSame("1.5", $m->serializeToJsonString()); - # } + $m->mergeFromJsonString("true"); + $this->assertEquals(true, $m->getValue()); - # public function testDecodeTopLevelFloatValue() - # { - # $m = new FloatValue(); - # $m->mergeFromJsonString("1.5"); - # $this->assertEquals(1.5, $m->getValue()); - # } + $m->mergeFromJsonString("false"); + $this->assertEquals(false, $m->getValue()); + } - # public function testEncodeTopLevelFloatValue() - # { - # $m = new FloatValue(); - # $m->setValue(1.5); - # $this->assertSame("1.5", $m->serializeToJsonString()); - # } + public function testEncodeTopLevelBoolValue() + { + $m = new BoolValue(); + $m->setValue(true); + $this->assertSame("true", $m->serializeToJsonString()); + } - # public function testDecodeTopLevelInt32Value() - # { - # $m = new Int32Value(); - # $m->mergeFromJsonString("1"); - # $this->assertEquals(1, $m->getValue()); - # } + public function testDecodeTopLevelDoubleValue() + { + $m = new DoubleValue(); + $m->mergeFromJsonString("1.5"); + $this->assertEquals(1.5, $m->getValue()); + } - # public function testEncodeTopLevelInt32Value() - # { - # $m = new Int32Value(); - # $m->setValue(1); - # $this->assertSame("1", $m->serializeToJsonString()); - # } + public function testEncodeTopLevelDoubleValue() + { + $m = new DoubleValue(); + $m->setValue(1.5); + $this->assertSame("1.5", $m->serializeToJsonString()); + } - # public function testDecodeInt32Value() - # { - # $m = new TestInt32Value(); - # $m->mergeFromJsonString("{\"field\":12345}"); - # $this->assertSame(12345, $m->getField()->getValue()); - # } + public function testDecodeTopLevelFloatValue() + { + $m = new FloatValue(); + $m->mergeFromJsonString("1.5"); + $this->assertEquals(1.5, $m->getValue()); + } - # public function testDecodeInt32ValueUnwrapped() - # { - # $m = new TestInt32Value(); - # $m->mergeFromJsonString("{\"field\":12345}"); - # $this->assertSame(12345, $m->getFieldUnwrapped()); - # } + public function testEncodeTopLevelFloatValue() + { + $m = new FloatValue(); + $m->setValue(1.5); + $this->assertSame("1.5", $m->serializeToJsonString()); + } - # public function testDecodeRepeatedInt32Value() - # { - # $m = new TestInt32Value(); - # $m->mergeFromJsonString("{\"repeated_field\":[12345]}"); - # $this->assertSame(12345, $m->getRepeatedField()[0]->getValue()); - # } + public function testDecodeTopLevelInt32Value() + { + $m = new Int32Value(); + $m->mergeFromJsonString("1"); + $this->assertEquals(1, $m->getValue()); + } - # public function testDecodeTopLevelUInt32Value() - # { - # $m = new UInt32Value(); - # $m->mergeFromJsonString("1"); - # $this->assertEquals(1, $m->getValue()); - # } + public function testEncodeTopLevelInt32Value() + { + $m = new Int32Value(); + $m->setValue(1); + $this->assertSame("1", $m->serializeToJsonString()); + } - # public function testEncodeTopLevelUInt32Value() - # { - # $m = new UInt32Value(); - # $m->setValue(1); - # $this->assertSame("1", $m->serializeToJsonString()); - # } + public function testDecodeRepeatedInt32Value() + { + $m = new TestInt32Value(); + $m->mergeFromJsonString("{\"repeated_field\":[12345]}"); + $this->assertSame(12345, $m->getRepeatedField()[0]->getValue()); + } - # public function testDecodeTopLevelInt64Value() - # { - # $m = new Int64Value(); - # $m->mergeFromJsonString("1"); - # $this->assertEquals(1, $m->getValue()); - # } + public function testDecodeTopLevelUInt32Value() + { + $m = new UInt32Value(); + $m->mergeFromJsonString("1"); + $this->assertEquals(1, $m->getValue()); + } - # public function testDecodeTopLevelInt64ValueAsString() - # { - # $m = new Int64Value(); - # $m->mergeFromJsonString("\"1\""); - # $this->assertEquals(1, $m->getValue()); - # } + public function testEncodeTopLevelUInt32Value() + { + $m = new UInt32Value(); + $m->setValue(1); + $this->assertSame("1", $m->serializeToJsonString()); + } - # public function testEncodeTopLevelInt64Value() - # { - # $m = new Int64Value(); - # $m->setValue(1); - # $this->assertSame("\"1\"", $m->serializeToJsonString()); - # } + public function testDecodeTopLevelInt64Value() + { + $m = new Int64Value(); + $m->mergeFromJsonString("1"); + $this->assertEquals(1, $m->getValue()); + } - # public function testDecodeTopLevelUInt64Value() - # { - # $m = new UInt64Value(); - # $m->mergeFromJsonString("1"); - # $this->assertEquals(1, $m->getValue()); - # } + public function testDecodeTopLevelInt64ValueAsString() + { + $m = new Int64Value(); + $m->mergeFromJsonString("\"1\""); + $this->assertEquals(1, $m->getValue()); + } - # public function testDecodeTopLevelUInt64ValueAsString() - # { - # $m = new UInt64Value(); - # $m->mergeFromJsonString("\"1\""); - # $this->assertEquals(1, $m->getValue()); - # } + public function testEncodeTopLevelInt64Value() + { + $m = new Int64Value(); + $m->setValue(1); + $this->assertSame("\"1\"", $m->serializeToJsonString()); + } - # public function testEncodeTopLevelUInt64Value() - # { - # $m = new UInt64Value(); - # $m->setValue(1); - # $this->assertSame("\"1\"", $m->serializeToJsonString()); - # } + public function testDecodeTopLevelUInt64Value() + { + $m = new UInt64Value(); + $m->mergeFromJsonString("1"); + $this->assertEquals(1, $m->getValue()); + } - # public function testDecodeTopLevelStringValue() - # { - # $m = new StringValue(); - # $m->mergeFromJsonString("\"a\""); - # $this->assertSame("a", $m->getValue()); - # } + public function testDecodeTopLevelUInt64ValueAsString() + { + $m = new UInt64Value(); + $m->mergeFromJsonString("\"1\""); + $this->assertEquals(1, $m->getValue()); + } - # public function testEncodeTopLevelStringValue() - # { - # $m = new StringValue(); - # $m->setValue("a"); - # $this->assertSame("\"a\"", $m->serializeToJsonString()); - # } + public function testEncodeTopLevelUInt64Value() + { + $m = new UInt64Value(); + $m->setValue(1); + $this->assertSame("\"1\"", $m->serializeToJsonString()); + } - # public function testDecodeStringValue() - # { - # $m = new TestStringValue(); - # $m->mergeFromJsonString("{\"field\":\"a\"}"); - # $this->assertSame("a", $m->getField()->getValue()); - # } + public function testDecodeTopLevelStringValue() + { + $m = new StringValue(); + $m->mergeFromJsonString("\"a\""); + $this->assertSame("a", $m->getValue()); + } - # public function testDecodeStringValueUnwrapped() - # { - # $m = new TestStringValue(); - # $m->mergeFromJsonString("{\"field\":\"a\"}"); - # $this->assertSame("a", $m->getFieldUnwrapped()); - # } + public function testEncodeTopLevelStringValue() + { + $m = new StringValue(); + $m->setValue("a"); + $this->assertSame("\"a\"", $m->serializeToJsonString()); + } - # public function testDecodeRepeatedStringValue() - # { - # $m = new TestStringValue(); - # $m->mergeFromJsonString("{\"repeated_field\":[\"a\"]}"); - # $this->assertSame("a", $m->getRepeatedField()[0]->getValue()); - # } + public function testDecodeRepeatedStringValue() + { + $m = new TestStringValue(); + $m->mergeFromJsonString("{\"repeated_field\":[\"a\"]}"); + $this->assertSame("a", $m->getRepeatedField()[0]->getValue()); + } # public function testDecodeMapStringValue() # { @@ -223,1072 +195,1059 @@ class EncodeDecodeTest extends TestBase # $this->assertSame("a", $m->getMapField()[1]->getValue()); # } - # public function testEncodeStringValue() - # { - # $m = new TestStringValue(['field' => new StringValue(['value' => 'a'])]); - # $this->assertSame("{\"field\":\"a\"}", $m->serializeToJsonString()); - # } - - # public function testDecodeEncodeStringValue() - # { - # $m = new TestStringValue(); - # $m->mergeFromJsonString("{\"field\":\"a\"}"); - # $this->assertSame("{\"field\":\"a\"}", $m->serializeToJsonString()); - # } - - # public function testDecodeTopLevelBytesValue() - # { - # $m = new BytesValue(); - # $m->mergeFromJsonString("\"YQ==\""); - # $this->assertSame("a", $m->getValue()); - # } + public function testDecodeTopLevelBytesValue() + { + $m = new BytesValue(); + $m->mergeFromJsonString("\"YQ==\""); + $this->assertSame("a", $m->getValue()); + } - # public function testEncodeTopLevelBytesValue() - # { - # $m = new BytesValue(); - # $m->setValue("a"); - # $this->assertSame("\"YQ==\"", $m->serializeToJsonString()); - # } + public function testEncodeTopLevelBytesValue() + { + $m = new BytesValue(); + $m->setValue("a"); + $this->assertSame("\"YQ==\"", $m->serializeToJsonString()); + } - # public function generateRandomString($length = 10) { - # $randomString = str_repeat("+", $length); - # for ($i = 0; $i < $length; $i++) { - # $randomString[$i] = rand(0, 255); - # } - # return $randomString; - # } + public function generateRandomString($length = 10) { + $randomString = str_repeat("+", $length); + for ($i = 0; $i < $length; $i++) { + $randomString[$i] = rand(0, 255); + } + return $randomString; + } - # public function testEncodeTopLevelLongBytesValue() - # { - # $m = new BytesValue(); - # $data = $this->generateRandomString(12007); - # $m->setValue($data); - # $expected = "\"" . base64_encode($data) . "\""; - # $this->assertSame(strlen($expected), strlen($m->serializeToJsonString())); - # } + public function testEncodeTopLevelLongBytesValue() + { + $m = new BytesValue(); + $data = $this->generateRandomString(12007); + $m->setValue($data); + $expected = "\"" . base64_encode($data) . "\""; + $this->assertSame(strlen($expected), strlen($m->serializeToJsonString())); + } - # public function testEncode() - # { - # $from = new TestMessage(); - # $this->expectEmptyFields($from); - # $this->setFields($from); - # $this->expectFields($from); - - # $data = $from->serializeToString(); - # $this->assertSame(bin2hex(TestUtil::getGoldenTestMessage()), - # bin2hex($data)); - # } + public function testEncode() + { + $from = new TestMessage(); + $this->expectEmptyFields($from); + $this->setFields($from); + $this->expectFields($from); + + $data = $from->serializeToString(); + $this->assertSame(bin2hex(TestUtil::getGoldenTestMessage()), + bin2hex($data)); + } - # public function testDecode() - # { - # $to = new TestMessage(); - # $to->mergeFromString(TestUtil::getGoldenTestMessage()); - # $this->expectFields($to); - # } + public function testDecode() + { + $to = new TestMessage(); + $to->mergeFromString(TestUtil::getGoldenTestMessage()); + $this->expectFields($to); + } - # public function testEncodeDecode() - # { - # $from = new TestMessage(); - # $this->expectEmptyFields($from); - # $this->setFields($from); - # $this->expectFields($from); + public function testEncodeDecode() + { + $from = new TestMessage(); + $this->expectEmptyFields($from); + $this->setFields($from); + $this->expectFields($from); - # $data = $from->serializeToString(); + $data = $from->serializeToString(); - # $to = new TestMessage(); - # $to->mergeFromString($data); - # $this->expectFields($to); - # } + $to = new TestMessage(); + $to->mergeFromString($data); + $this->expectFields($to); + } - # public function testEncodeDecodeEmpty() - # { - # $from = new TestMessage(); - # $this->expectEmptyFields($from); + public function testEncodeDecodeEmpty() + { + $from = new TestMessage(); + $this->expectEmptyFields($from); - # $data = $from->serializeToString(); + $data = $from->serializeToString(); - # $to = new TestMessage(); - # $to->mergeFromString($data); - # $this->expectEmptyFields($to); - # } + $to = new TestMessage(); + $to->mergeFromString($data); + $this->expectEmptyFields($to); + } - # public function testEncodeDecodeOneof() - # { - # $m = new TestMessage(); - - # $m->setOneofInt32(1); - # $data = $m->serializeToString(); - # $n = new TestMessage(); - # $n->mergeFromString($data); - # $this->assertSame(1, $n->getOneofInt32()); - - # $m->setOneofFloat(2.0); - # $data = $m->serializeToString(); - # $n = new TestMessage(); - # $n->mergeFromString($data); - # $this->assertSame(2.0, $n->getOneofFloat()); - - # $m->setOneofString('abc'); - # $data = $m->serializeToString(); - # $n = new TestMessage(); - # $n->mergeFromString($data); - # $this->assertSame('abc', $n->getOneofString()); - - # $sub_m = new Sub(); - # $sub_m->setA(1); - # $m->setOneofMessage($sub_m); - # $data = $m->serializeToString(); - # $n = new TestMessage(); - # $n->mergeFromString($data); - # $this->assertSame(1, $n->getOneofMessage()->getA()); - - # // Encode default value - # $m->setOneofEnum(TestEnum::ZERO); - # $data = $m->serializeToString(); - # $n = new TestMessage(); - # $n->mergeFromString($data); - # $this->assertSame("oneof_enum", $n->getMyOneof()); - # $this->assertSame(TestEnum::ZERO, $n->getOneofEnum()); - - # $m->setOneofString(""); - # $data = $m->serializeToString(); - # $n = new TestMessage(); - # $n->mergeFromString($data); - # $this->assertSame("oneof_string", $n->getMyOneof()); - # $this->assertSame("", $n->getOneofString()); - - # $sub_m = new Sub(); - # $m->setOneofMessage($sub_m); - # $data = $m->serializeToString(); - # $n = new TestMessage(); - # $n->mergeFromString($data); - # $this->assertSame("oneof_message", $n->getMyOneof()); - # $this->assertFalse(is_null($n->getOneofMessage())); + public function testEncodeDecodeOneof() + { + $m = new TestMessage(); + + $m->setOneofInt32(1); + $data = $m->serializeToString(); + $n = new TestMessage(); + $n->mergeFromString($data); + $this->assertSame(1, $n->getOneofInt32()); + + $m->setOneofFloat(2.0); + $data = $m->serializeToString(); + $n = new TestMessage(); + $n->mergeFromString($data); + $this->assertSame(2.0, $n->getOneofFloat()); + + $m->setOneofString('abc'); + $data = $m->serializeToString(); + $n = new TestMessage(); + $n->mergeFromString($data); + $this->assertSame('abc', $n->getOneofString()); + + $sub_m = new Sub(); + $sub_m->setA(1); + $m->setOneofMessage($sub_m); + $data = $m->serializeToString(); + $n = new TestMessage(); + $n->mergeFromString($data); + $this->assertSame(1, $n->getOneofMessage()->getA()); + + // Encode default value + $m->setOneofEnum(TestEnum::ZERO); + $data = $m->serializeToString(); + $n = new TestMessage(); + $n->mergeFromString($data); + $this->assertSame("oneof_enum", $n->getMyOneof()); + $this->assertSame(TestEnum::ZERO, $n->getOneofEnum()); + + $m->setOneofString(""); + $data = $m->serializeToString(); + $n = new TestMessage(); + $n->mergeFromString($data); + $this->assertSame("oneof_string", $n->getMyOneof()); + $this->assertSame("", $n->getOneofString()); + + $sub_m = new Sub(); + $m->setOneofMessage($sub_m); + $data = $m->serializeToString(); + $n = new TestMessage(); + $n->mergeFromString($data); + $this->assertSame("oneof_message", $n->getMyOneof()); + $this->assertFalse(is_null($n->getOneofMessage())); - # } + } - # public function testJsonEncodeDecodeOneof() - # { - # $m = new TestMessage(); - - # $m->setOneofEnum(TestEnum::ONE); - # $data = $m->serializeToJsonString(); - # $n = new TestMessage(); - # $n->mergeFromJsonString($data); - # $this->assertSame("oneof_enum", $n->getMyOneof()); - # $this->assertSame(TestEnum::ONE, $n->getOneofEnum()); - - # $m->setOneofString("a"); - # $data = $m->serializeToJsonString(); - # $n = new TestMessage(); - # $n->mergeFromJsonString($data); - # $this->assertSame("oneof_string", $n->getMyOneof()); - # $this->assertSame("a", $n->getOneofString()); - - # $m->setOneofBytes("bbbb"); - # $data = $m->serializeToJsonString(); - # $n = new TestMessage(); - # $n->mergeFromJsonString($data); - # $this->assertSame("oneof_bytes", $n->getMyOneof()); - # $this->assertSame("bbbb", $n->getOneofBytes()); - - # $sub_m = new Sub(); - # $m->setOneofMessage($sub_m); - # $data = $m->serializeToJsonString(); - # $n = new TestMessage(); - # $n->mergeFromJsonString($data); - # $this->assertSame("oneof_message", $n->getMyOneof()); - # $this->assertFalse(is_null($n->getOneofMessage())); - # } + public function testJsonEncodeDecodeOneof() + { + $m = new TestMessage(); + + $m->setOneofEnum(TestEnum::ONE); + $data = $m->serializeToJsonString(); + $n = new TestMessage(); + $n->mergeFromJsonString($data); + $this->assertSame("oneof_enum", $n->getMyOneof()); + $this->assertSame(TestEnum::ONE, $n->getOneofEnum()); + + $m->setOneofString("a"); + $data = $m->serializeToJsonString(); + $n = new TestMessage(); + $n->mergeFromJsonString($data); + $this->assertSame("oneof_string", $n->getMyOneof()); + $this->assertSame("a", $n->getOneofString()); + + $m->setOneofBytes("bbbb"); + $data = $m->serializeToJsonString(); + $n = new TestMessage(); + $n->mergeFromJsonString($data); + $this->assertSame("oneof_bytes", $n->getMyOneof()); + $this->assertSame("bbbb", $n->getOneofBytes()); + + $sub_m = new Sub(); + $m->setOneofMessage($sub_m); + $data = $m->serializeToJsonString(); + $n = new TestMessage(); + $n->mergeFromJsonString($data); + $this->assertSame("oneof_message", $n->getMyOneof()); + $this->assertFalse(is_null($n->getOneofMessage())); + } - # public function testPackedEncode() - # { - # $from = new TestPackedMessage(); - # TestUtil::setTestPackedMessage($from); - # $this->assertSame(TestUtil::getGoldenTestPackedMessage(), - # $from->serializeToString()); - # } + public function testPackedEncode() + { + $from = new TestPackedMessage(); + TestUtil::setTestPackedMessage($from); + $this->assertSame(TestUtil::getGoldenTestPackedMessage(), + $from->serializeToString()); + } - # public function testPackedDecodePacked() - # { - # $to = new TestPackedMessage(); - # $to->mergeFromString(TestUtil::getGoldenTestPackedMessage()); - # TestUtil::assertTestPackedMessage($to); - # $this->assertTrue(true); - # } + public function testPackedDecodePacked() + { + $to = new TestPackedMessage(); + $to->mergeFromString(TestUtil::getGoldenTestPackedMessage()); + TestUtil::assertTestPackedMessage($to); + $this->assertTrue(true); + } - # public function testPackedDecodeUnpacked() - # { - # $to = new TestPackedMessage(); - # $to->mergeFromString(TestUtil::getGoldenTestUnpackedMessage()); - # TestUtil::assertTestPackedMessage($to); - # $this->assertTrue(true); - # } + public function testPackedDecodeUnpacked() + { + $to = new TestPackedMessage(); + $to->mergeFromString(TestUtil::getGoldenTestUnpackedMessage()); + TestUtil::assertTestPackedMessage($to); + $this->assertTrue(true); + } - # public function testUnpackedEncode() - # { - # $from = new TestUnpackedMessage(); - # TestUtil::setTestPackedMessage($from); - # $this->assertSame(TestUtil::getGoldenTestUnpackedMessage(), - # $from->serializeToString()); - # } + public function testUnpackedEncode() + { + $from = new TestUnpackedMessage(); + TestUtil::setTestPackedMessage($from); + $this->assertSame(TestUtil::getGoldenTestUnpackedMessage(), + $from->serializeToString()); + } - # public function testUnpackedDecodePacked() - # { - # $to = new TestUnpackedMessage(); - # $to->mergeFromString(TestUtil::getGoldenTestPackedMessage()); - # TestUtil::assertTestPackedMessage($to); - # $this->assertTrue(true); - # } + public function testUnpackedDecodePacked() + { + $to = new TestUnpackedMessage(); + $to->mergeFromString(TestUtil::getGoldenTestPackedMessage()); + TestUtil::assertTestPackedMessage($to); + $this->assertTrue(true); + } - # public function testUnpackedDecodeUnpacked() - # { - # $to = new TestUnpackedMessage(); - # $to->mergeFromString(TestUtil::getGoldenTestUnpackedMessage()); - # TestUtil::assertTestPackedMessage($to); - # $this->assertTrue(true); - # } + public function testUnpackedDecodeUnpacked() + { + $to = new TestUnpackedMessage(); + $to->mergeFromString(TestUtil::getGoldenTestUnpackedMessage()); + TestUtil::assertTestPackedMessage($to); + $this->assertTrue(true); + } - # public function testDecodeInt64() - # { - # // Read 64 testing - # $testVals = array( - # '10' => '100a', - # '100' => '1064', - # '800' => '10a006', - # '6400' => '108032', - # '70400' => '1080a604', - # '774400' => '1080a22f', - # '9292800' => '108098b704', - # '74342400' => '1080c0b923', - # '743424000' => '108080bfe202', - # '8177664000' => '108080b5bb1e', - # '65421312000' => '108080a8dbf301', - # '785055744000' => '108080e0c7ec16', - # '9420668928000' => '10808080dd969202', - # '103627358208000' => '10808080fff9c717', - # '1139900940288000' => '10808080f5bd978302', - # '13678811283456000' => '10808080fce699a618', - # '109430490267648000' => '10808080e0b7ceb1c201', - # '984874412408832000' => '10808080e0f5c1bed50d', - # ); - - # $msg = new TestMessage(); - # foreach ($testVals as $original => $encoded) { - # $msg->setOptionalInt64($original); - # $data = $msg->serializeToString(); - # $this->assertSame($encoded, bin2hex($data)); - # $msg->setOptionalInt64(0); - # $msg->mergeFromString($data); - # $this->assertEquals($original, $msg->getOptionalInt64()); - # } - # } + public function testDecodeInt64() + { + // Read 64 testing + $testVals = array( + '10' => '100a', + '100' => '1064', + '800' => '10a006', + '6400' => '108032', + '70400' => '1080a604', + '774400' => '1080a22f', + '9292800' => '108098b704', + '74342400' => '1080c0b923', + '743424000' => '108080bfe202', + '8177664000' => '108080b5bb1e', + '65421312000' => '108080a8dbf301', + '785055744000' => '108080e0c7ec16', + '9420668928000' => '10808080dd969202', + '103627358208000' => '10808080fff9c717', + '1139900940288000' => '10808080f5bd978302', + '13678811283456000' => '10808080fce699a618', + '109430490267648000' => '10808080e0b7ceb1c201', + '984874412408832000' => '10808080e0f5c1bed50d', + ); + + $msg = new TestMessage(); + foreach ($testVals as $original => $encoded) { + $msg->setOptionalInt64($original); + $data = $msg->serializeToString(); + $this->assertSame($encoded, bin2hex($data)); + $msg->setOptionalInt64(0); + $msg->mergeFromString($data); + $this->assertEquals($original, $msg->getOptionalInt64()); + } + } - # public function testDecodeToExistingMessage() - # { - # $m1 = new TestMessage(); - # $this->setFields($m1); - # $this->expectFields($m1); + public function testDecodeToExistingMessage() + { + $m1 = new TestMessage(); + $this->setFields($m1); + $this->expectFields($m1); - # $m2 = new TestMessage(); - # $this->setFields2($m2); - # $data = $m2->serializeToString(); + $m2 = new TestMessage(); + $this->setFields2($m2); + $data = $m2->serializeToString(); - # $m1->mergeFromString($data); - # $this->expectFieldsMerged($m1); - # } + $m1->mergeFromString($data); + $this->expectFieldsMerged($m1); + } - # public function testDecodeFieldNonExist() - # { - # $data = hex2bin('c80501'); - # $m = new TestMessage(); - # $m->mergeFromString($data); - # $this->assertTrue(true); - # } + public function testDecodeFieldNonExist() + { + $data = hex2bin('c80501'); + $m = new TestMessage(); + $m->mergeFromString($data); + $this->assertTrue(true); + } - # public function testEncodeNegativeInt32() - # { - # $m = new TestMessage(); - # $m->setOptionalInt32(-1); - # $data = $m->serializeToString(); - # $this->assertSame("08ffffffffffffffffff01", bin2hex($data)); - # } + public function testEncodeNegativeInt32() + { + $m = new TestMessage(); + $m->setOptionalInt32(-1); + $data = $m->serializeToString(); + $this->assertSame("08ffffffffffffffffff01", bin2hex($data)); + } - # public function testDecodeNegativeInt32() - # { - # $m = new TestMessage(); - # $this->assertEquals(0, $m->getOptionalInt32()); - # $m->mergeFromString(hex2bin("08ffffffffffffffffff01")); - # $this->assertEquals(-1, $m->getOptionalInt32()); - - # $m = new TestMessage(); - # $this->assertEquals(0, $m->getOptionalInt32()); - # $m->mergeFromString(hex2bin("08ffffffff0f")); - # $this->assertEquals(-1, $m->getOptionalInt32()); - # } + public function testDecodeNegativeInt32() + { + $m = new TestMessage(); + $this->assertEquals(0, $m->getOptionalInt32()); + $m->mergeFromString(hex2bin("08ffffffffffffffffff01")); + $this->assertEquals(-1, $m->getOptionalInt32()); + + $m = new TestMessage(); + $this->assertEquals(0, $m->getOptionalInt32()); + $m->mergeFromString(hex2bin("08ffffffff0f")); + $this->assertEquals(-1, $m->getOptionalInt32()); + } - # public function testRandomFieldOrder() - # { - # $m = new TestRandomFieldOrder(); - # $data = $m->serializeToString(); - # $this->assertSame("", $data); - # } + public function testRandomFieldOrder() + { + $m = new TestRandomFieldOrder(); + $data = $m->serializeToString(); + $this->assertSame("", $data); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidInt32() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('08')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidInt32() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('08')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidSubMessage() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('9A010108')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidSubMessage() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('9A010108')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidInt64() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('10')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidInt64() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('10')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidUInt32() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('18')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidUInt32() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('18')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidUInt64() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('20')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidUInt64() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('20')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidSInt32() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('28')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidSInt32() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('28')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidSInt64() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('30')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidSInt64() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('30')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidFixed32() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('3D')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidFixed32() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('3D')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidFixed64() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('41')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidFixed64() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('41')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidSFixed32() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('4D')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidSFixed32() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('4D')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidSFixed64() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('51')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidSFixed64() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('51')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidFloat() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('5D')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidFloat() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('5D')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidDouble() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('61')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidDouble() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('61')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidBool() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('68')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidBool() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('68')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidStringLengthMiss() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('72')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidStringLengthMiss() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('72')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidStringDataMiss() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('7201')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidStringDataMiss() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('7201')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidBytesLengthMiss() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('7A')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidBytesLengthMiss() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('7A')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidBytesDataMiss() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('7A01')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidBytesDataMiss() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('7A01')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidEnum() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('8001')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidEnum() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('8001')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidMessageLengthMiss() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('8A01')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidMessageLengthMiss() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('8A01')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidMessageDataMiss() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin('8A0101')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidMessageDataMiss() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin('8A0101')); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeInvalidPackedMessageLength() - # { - # $m = new TestPackedMessage(); - # $m->mergeFromString(hex2bin('D205')); - # } + /** + * @expectedException Exception + */ + public function testDecodeInvalidPackedMessageLength() + { + $m = new TestPackedMessage(); + $m->mergeFromString(hex2bin('D205')); + } - # public function testUnknown() - # { - # // Test preserve unknown for varint. - # $m = new TestMessage(); - # $from = hex2bin('F80601'); // TODO(teboring): Add a util to encode - # // varint for better readability - # $m->mergeFromString($from); - # $to = $m->serializeToString(); - # $this->assertSame(bin2hex($from), bin2hex($to)); - - # // Test preserve unknown for 64-bit. - # $m = new TestMessage(); - # $from = hex2bin('F9060000000000000000'); - # $m->mergeFromString($from); - # $to = $m->serializeToString(); - # $this->assertSame(bin2hex($from), bin2hex($to)); - - # // Test preserve unknown for length delimited. - # $m = new TestMessage(); - # $from = hex2bin('FA0600'); - # $m->mergeFromString($from); - # $to = $m->serializeToString(); - # $this->assertSame(bin2hex($from), bin2hex($to)); - - # // Test preserve unknown for 32-bit. - # $m = new TestMessage(); - # $from = hex2bin('FD0600000000'); - # $m->mergeFromString($from); - # $to = $m->serializeToString(); - # $this->assertSame(bin2hex($from), bin2hex($to)); - - # // Test discard unknown in message. - # $m = new TestMessage(); - # $from = hex2bin('F80601'); - # $m->mergeFromString($from); - # $m->discardUnknownFields(); - # $to = $m->serializeToString(); - # $this->assertSame("", bin2hex($to)); - - # // Test discard unknown for singular message field. - # $m = new TestMessage(); - # $from = hex2bin('8A0103F80601'); - # $m->mergeFromString($from); - # $m->discardUnknownFields(); - # $to = $m->serializeToString(); - # $this->assertSame("8a0100", bin2hex($to)); - - # // Test discard unknown for repeated message field. - # $m = new TestMessage(); - # $from = hex2bin('FA0203F80601'); - # $m->mergeFromString($from); - # $m->discardUnknownFields(); - # $to = $m->serializeToString(); - # $this->assertSame("fa0200", bin2hex($to)); - - # // Test discard unknown for map message value field. - # $m = new TestMessage(); - # $from = hex2bin("BA050708011203F80601"); - # $m->mergeFromString($from); - # $m->discardUnknownFields(); - # $to = $m->serializeToString(); - # $this->assertSame("ba050408011200", bin2hex($to)); - - # // Test discard unknown for singular message field. - # $m = new TestMessage(); - # $from = hex2bin('9A0403F80601'); - # $m->mergeFromString($from); - # $m->discardUnknownFields(); - # $to = $m->serializeToString(); - # $this->assertSame("9a0400", bin2hex($to)); - # } + public function testUnknown() + { + // Test preserve unknown for varint. + $m = new TestMessage(); + $from = hex2bin('F80601'); // TODO(teboring): Add a util to encode + // varint for better readability + $m->mergeFromString($from); + $to = $m->serializeToString(); + $this->assertSame(bin2hex($from), bin2hex($to)); + + // Test preserve unknown for 64-bit. + $m = new TestMessage(); + $from = hex2bin('F9060000000000000000'); + $m->mergeFromString($from); + $to = $m->serializeToString(); + $this->assertSame(bin2hex($from), bin2hex($to)); + + // Test preserve unknown for length delimited. + $m = new TestMessage(); + $from = hex2bin('FA0600'); + $m->mergeFromString($from); + $to = $m->serializeToString(); + $this->assertSame(bin2hex($from), bin2hex($to)); + + // Test preserve unknown for 32-bit. + $m = new TestMessage(); + $from = hex2bin('FD0600000000'); + $m->mergeFromString($from); + $to = $m->serializeToString(); + $this->assertSame(bin2hex($from), bin2hex($to)); + + // Test discard unknown in message. + $m = new TestMessage(); + $from = hex2bin('F80601'); + $m->mergeFromString($from); + $m->discardUnknownFields(); + $to = $m->serializeToString(); + $this->assertSame("", bin2hex($to)); + + // Test discard unknown for singular message field. + $m = new TestMessage(); + $from = hex2bin('8A0103F80601'); + $m->mergeFromString($from); + $m->discardUnknownFields(); + $to = $m->serializeToString(); + $this->assertSame("8a0100", bin2hex($to)); + + // Test discard unknown for repeated message field. + $m = new TestMessage(); + $from = hex2bin('FA0203F80601'); + $m->mergeFromString($from); + $m->discardUnknownFields(); + $to = $m->serializeToString(); + $this->assertSame("fa0200", bin2hex($to)); + + // Test discard unknown for map message value field. + $m = new TestMessage(); + $from = hex2bin("BA050708011203F80601"); + $m->mergeFromString($from); + $m->discardUnknownFields(); + $to = $m->serializeToString(); + $this->assertSame("ba050408011200", bin2hex($to)); + + // Test discard unknown for singular message field. + $m = new TestMessage(); + $from = hex2bin('9A0403F80601'); + $m->mergeFromString($from); + $m->discardUnknownFields(); + $to = $m->serializeToString(); + $this->assertSame("9a0400", bin2hex($to)); + } - # public function testJsonUnknown() - # { - # // Test unknown number - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":1, - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - - # // Test unknown bool - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":true, - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - - # // Test unknown string - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":\"abc\", - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - - # // Test unknown null - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":null, - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - - # // Test unknown array - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":[], - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - - # // Test unknown number array - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":[1], - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - - # // Test unknown bool array - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":[true], - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - - # // Test unknown string array - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":[\"a\"], - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - - # // Test unknown null array - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":[null], - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - - # // Test unknown array array - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":[[]], - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - - # // Test unknown object array - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":[{}], - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - - # // Test unknown double value array - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":[1, 2], - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - - # // Test unknown object - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":{}, - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - - # // Test unknown number object - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":{\"a\":1}, - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - - # // Test unknown bool object - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":{\"a\":true}, - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - - # // Test unknown string object - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":{\"a\":\"a\"}, - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - - # // Test unknown null object - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":{\"a\":null}, - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - - # // Test unknown array object - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":{\"a\":[]}, - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - - # // Test unknown object object - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":{\"a\":{}}, - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - - # // Test unknown double value object - # $m = new TestMessage(); - # $m->mergeFromJsonString("{\"unknown\":{\"a\":1, \"b\":1}, - # \"optionalInt32\":1}", true); - # $this->assertSame(1, $m->getOptionalInt32()); - # } + public function testJsonUnknown() + { + // Test unknown number + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":1, + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + + // Test unknown bool + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":true, + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + + // Test unknown string + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":\"abc\", + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + + // Test unknown null + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":null, + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + + // Test unknown array + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":[], + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + + // Test unknown number array + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":[1], + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + + // Test unknown bool array + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":[true], + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + + // Test unknown string array + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":[\"a\"], + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + + // Test unknown null array + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":[null], + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + + // Test unknown array array + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":[[]], + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + + // Test unknown object array + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":[{}], + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + + // Test unknown double value array + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":[1, 2], + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + + // Test unknown object + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":{}, + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + + // Test unknown number object + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":{\"a\":1}, + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + + // Test unknown bool object + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":{\"a\":true}, + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + + // Test unknown string object + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":{\"a\":\"a\"}, + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + + // Test unknown null object + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":{\"a\":null}, + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + + // Test unknown array object + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":{\"a\":[]}, + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + + // Test unknown object object + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":{\"a\":{}}, + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + + // Test unknown double value object + $m = new TestMessage(); + $m->mergeFromJsonString("{\"unknown\":{\"a\":1, \"b\":1}, + \"optionalInt32\":1}", true); + $this->assertSame(1, $m->getOptionalInt32()); + } - # public function testJsonEncode() - # { - # $from = new TestMessage(); - # $this->setFields($from); - # $data = $from->serializeToJsonString(); - # $to = new TestMessage(); - # $to->mergeFromJsonString($data); - # $this->expectFields($to); - # } + public function testJsonEncode() + { + $from = new TestMessage(); + $this->setFields($from); + $data = $from->serializeToJsonString(); + $to = new TestMessage(); + $to->mergeFromJsonString($data); + $this->expectFields($to); + } - # public function testDecodeDuration() - # { - # $m = new Google\Protobuf\Duration(); - # $m->mergeFromJsonString("\"1234.5678s\""); - # $this->assertEquals(1234, $m->getSeconds()); - # $this->assertEquals(567800000, $m->getNanos()); - # } + public function testDecodeDuration() + { + $m = new Google\Protobuf\Duration(); + $m->mergeFromJsonString("\"1234.5678s\""); + $this->assertEquals(1234, $m->getSeconds()); + $this->assertEquals(567800000, $m->getNanos()); + } - # public function testEncodeDuration() - # { - # $m = new Google\Protobuf\Duration(); - # $m->setSeconds(1234); - # $m->setNanos(999999999); - # $this->assertEquals("\"1234.999999999s\"", $m->serializeToJsonString()); - # } + public function testEncodeDuration() + { + $m = new Google\Protobuf\Duration(); + $m->setSeconds(1234); + $m->setNanos(999999999); + $this->assertEquals("\"1234.999999999s\"", $m->serializeToJsonString()); + } - # public function testDecodeTimestamp() - # { - # $m = new Google\Protobuf\Timestamp(); - # $m->mergeFromJsonString("\"2000-01-01T00:00:00.123456789Z\""); - # $this->assertEquals(946684800, $m->getSeconds()); - # $this->assertEquals(123456789, $m->getNanos()); - # } + public function testDecodeTimestamp() + { + $m = new Google\Protobuf\Timestamp(); + $m->mergeFromJsonString("\"2000-01-01T00:00:00.123456789Z\""); + $this->assertEquals(946684800, $m->getSeconds()); + $this->assertEquals(123456789, $m->getNanos()); + } - # public function testEncodeTimestamp() - # { - # $m = new Google\Protobuf\Timestamp(); - # $m->setSeconds(946684800); - # $m->setNanos(123456789); - # $this->assertEquals("\"2000-01-01T00:00:00.123456789Z\"", - # $m->serializeToJsonString()); - # } + public function testEncodeTimestamp() + { + $m = new Google\Protobuf\Timestamp(); + $m->setSeconds(946684800); + $m->setNanos(123456789); + $this->assertEquals("\"2000-01-01T00:00:00.123456789Z\"", + $m->serializeToJsonString()); + } - # public function testDecodeTopLevelValue() - # { - # $m = new Value(); - # $m->mergeFromJsonString("\"a\""); - # $this->assertSame("a", $m->getStringValue()); + public function testDecodeTopLevelValue() + { + $m = new Value(); + $m->mergeFromJsonString("\"a\""); + $this->assertSame("a", $m->getStringValue()); - # $m = new Value(); - # $m->mergeFromJsonString("1.5"); - # $this->assertSame(1.5, $m->getNumberValue()); + $m = new Value(); + $m->mergeFromJsonString("1.5"); + $this->assertSame(1.5, $m->getNumberValue()); - # $m = new Value(); - # $m->mergeFromJsonString("true"); - # $this->assertSame(true, $m->getBoolValue()); + $m = new Value(); + $m->mergeFromJsonString("true"); + $this->assertSame(true, $m->getBoolValue()); - # $m = new Value(); - # $m->mergeFromJsonString("null"); - # $this->assertSame("null_value", $m->getKind()); + $m = new Value(); + $m->mergeFromJsonString("null"); + $this->assertSame("null_value", $m->getKind()); - # $m = new Value(); - # $m->mergeFromJsonString("[1]"); - # $this->assertSame("list_value", $m->getKind()); + $m = new Value(); + $m->mergeFromJsonString("[1]"); + $this->assertSame("list_value", $m->getKind()); - # $m = new Value(); - # $m->mergeFromJsonString("{\"a\":1}"); - # $this->assertSame("struct_value", $m->getKind()); - # } + $m = new Value(); + $m->mergeFromJsonString("{\"a\":1}"); + $this->assertSame("struct_value", $m->getKind()); + } - # public function testEncodeTopLevelValue() - # { - # $m = new Value(); - # $m->setStringValue("a"); - # $this->assertSame("\"a\"", $m->serializeToJsonString()); + public function testEncodeTopLevelValue() + { + $m = new Value(); + $m->setStringValue("a"); + $this->assertSame("\"a\"", $m->serializeToJsonString()); - # $m = new Value(); - # $m->setNumberValue(1.5); - # $this->assertSame("1.5", $m->serializeToJsonString()); + $m = new Value(); + $m->setNumberValue(1.5); + $this->assertSame("1.5", $m->serializeToJsonString()); - # $m = new Value(); - # $m->setBoolValue(true); - # $this->assertSame("true", $m->serializeToJsonString()); + $m = new Value(); + $m->setBoolValue(true); + $this->assertSame("true", $m->serializeToJsonString()); - # $m = new Value(); - # $m->setNullValue(0); - # $this->assertSame("null", $m->serializeToJsonString()); - # } + $m = new Value(); + $m->setNullValue(0); + $this->assertSame("null", $m->serializeToJsonString()); + } - # public function testDecodeTopLevelListValue() - # { - # $m = new ListValue(); - # $m->mergeFromJsonString("[1]"); - # $this->assertSame(1.0, $m->getValues()[0]->getNumberValue()); - # } + public function testDecodeTopLevelListValue() + { + $m = new ListValue(); + $m->mergeFromJsonString("[1]"); + $this->assertSame(1.0, $m->getValues()[0]->getNumberValue()); + } - # public function testEncodeTopLevelListValue() - # { - # $m = new ListValue(); - # $arr = $m->getValues(); - # $sub = new Value(); - # $sub->setNumberValue(1.5); - # $arr[] = $sub; - # $this->assertSame("[1.5]", $m->serializeToJsonString()); - # } + public function testEncodeTopLevelListValue() + { + $m = new ListValue(); + $arr = $m->getValues(); + $sub = new Value(); + $sub->setNumberValue(1.5); + $arr[] = $sub; + $this->assertSame("[1.5]", $m->serializeToJsonString()); + } - # public function testEncodeEmptyListValue() - # { - # $m = new Struct(); - # $m->setFields(['test' => (new Value())->setListValue(new ListValue())]); - # $this->assertSame('{"test":[]}', $m->serializeToJsonString()); - # } + public function testEncodeEmptyListValue() + { + $m = new Struct(); + $m->setFields(['test' => (new Value())->setListValue(new ListValue())]); + $this->assertSame('{"test":[]}', $m->serializeToJsonString()); + } - # public function testDecodeTopLevelStruct() - # { - # $m = new Struct(); - # $m->mergeFromJsonString("{\"a\":{\"b\":1}}"); - # $this->assertSame(1.0, $m->getFields()["a"] - # ->getStructValue() - # ->getFields()["b"]->getNumberValue()); - # } + public function testDecodeTopLevelStruct() + { + $m = new Struct(); + $m->mergeFromJsonString("{\"a\":{\"b\":1}}"); + $this->assertSame(1.0, $m->getFields()["a"] + ->getStructValue() + ->getFields()["b"]->getNumberValue()); + } - # public function testEncodeTopLevelStruct() - # { - # $m = new Struct(); - # $map = $m->getFields(); - # $sub = new Value(); - # $sub->setNumberValue(1.5); - # $map["a"] = $sub; - # $this->assertSame("{\"a\":1.5}", $m->serializeToJsonString()); - # } + public function testEncodeTopLevelStruct() + { + $m = new Struct(); + $map = $m->getFields(); + $sub = new Value(); + $sub->setNumberValue(1.5); + $map["a"] = $sub; + $this->assertSame("{\"a\":1.5}", $m->serializeToJsonString()); + } - # public function testEncodeEmptyStruct() - # { - # $m = new Struct(); - # $m->setFields(['test' => (new Value())->setStructValue(new Struct())]); - # $this->assertSame('{"test":{}}', $m->serializeToJsonString()); - # } + public function testEncodeEmptyStruct() + { + $m = new Struct(); + $m->setFields(['test' => (new Value())->setStructValue(new Struct())]); + $this->assertSame('{"test":{}}', $m->serializeToJsonString()); + } - # public function testDecodeTopLevelAny() - # { - # // Make sure packed message has been created at least once. - # $packed = new TestMessage(); - - # $m1 = new Any(); - # $m1->mergeFromJsonString( - # "{\"optionalInt32\": 1, " . - # "\"@type\":\"type.googleapis.com/foo.TestMessage\"}"); - # $this->assertSame("type.googleapis.com/foo.TestMessage", - # $m1->getTypeUrl()); - # $this->assertSame("0801", bin2hex($m1->getValue())); - - # $m2 = new Any(); - # $m2->mergeFromJsonString( - # "{\"@type\":\"type.googleapis.com/foo.TestMessage\", " . - # "\"optionalInt32\": 1}"); - # $this->assertSame("type.googleapis.com/foo.TestMessage", - # $m2->getTypeUrl()); - # $this->assertSame("0801", bin2hex($m2->getValue())); - - # $m3 = new Any(); - # $m3->mergeFromJsonString( - # "{\"optionalInt32\": 1, " . - # "\"@type\":\"type.googleapis.com/foo.TestMessage\", " . - # "\"optionalInt64\": 2}"); - # $this->assertSame("type.googleapis.com/foo.TestMessage", - # $m3->getTypeUrl()); - # $this->assertSame("08011002", bin2hex($m3->getValue())); - # } + public function testDecodeTopLevelAny() + { + // Make sure packed message has been created at least once. + $packed = new TestMessage(); + + $m1 = new Any(); + $m1->mergeFromJsonString( + "{\"optionalInt32\": 1, " . + "\"@type\":\"type.googleapis.com/foo.TestMessage\"}"); + $this->assertSame("type.googleapis.com/foo.TestMessage", + $m1->getTypeUrl()); + $this->assertSame("0801", bin2hex($m1->getValue())); + + $m2 = new Any(); + $m2->mergeFromJsonString( + "{\"@type\":\"type.googleapis.com/foo.TestMessage\", " . + "\"optionalInt32\": 1}"); + $this->assertSame("type.googleapis.com/foo.TestMessage", + $m2->getTypeUrl()); + $this->assertSame("0801", bin2hex($m2->getValue())); + + $m3 = new Any(); + $m3->mergeFromJsonString( + "{\"optionalInt32\": 1, " . + "\"@type\":\"type.googleapis.com/foo.TestMessage\", " . + "\"optionalInt64\": 2}"); + $this->assertSame("type.googleapis.com/foo.TestMessage", + $m3->getTypeUrl()); + $this->assertSame("08011002", bin2hex($m3->getValue())); + } - # public function testDecodeAny() - # { - # // Make sure packed message has been created at least once. - # $packed = new TestMessage(); - - # $m1 = new TestAny(); - # $m1->mergeFromJsonString( - # "{\"any\": {\"optionalInt32\": 1, " . - # "\"@type\":\"type.googleapis.com/foo.TestMessage\"}}"); - # $this->assertSame("type.googleapis.com/foo.TestMessage", - # $m1->getAny()->getTypeUrl()); - # $this->assertSame("0801", bin2hex($m1->getAny()->getValue())); - - # $m2 = new TestAny(); - # $m2->mergeFromJsonString( - # "{\"any\":{\"@type\":\"type.googleapis.com/foo.TestMessage\", " . - # "\"optionalInt32\": 1}}"); - # $this->assertSame("type.googleapis.com/foo.TestMessage", - # $m2->getAny()->getTypeUrl()); - # $this->assertSame("0801", bin2hex($m2->getAny()->getValue())); - - # $m3 = new TestAny(); - # $m3->mergeFromJsonString( - # "{\"any\":{\"optionalInt32\": 1, " . - # "\"@type\":\"type.googleapis.com/foo.TestMessage\", " . - # "\"optionalInt64\": 2}}"); - # $this->assertSame("type.googleapis.com/foo.TestMessage", - # $m3->getAny()->getTypeUrl()); - # $this->assertSame("08011002", bin2hex($m3->getAny()->getValue())); - # } + public function testDecodeAny() + { + // Make sure packed message has been created at least once. + $packed = new TestMessage(); + + $m1 = new TestAny(); + $m1->mergeFromJsonString( + "{\"any\": {\"optionalInt32\": 1, " . + "\"@type\":\"type.googleapis.com/foo.TestMessage\"}}"); + $this->assertSame("type.googleapis.com/foo.TestMessage", + $m1->getAny()->getTypeUrl()); + $this->assertSame("0801", bin2hex($m1->getAny()->getValue())); + + $m2 = new TestAny(); + $m2->mergeFromJsonString( + "{\"any\":{\"@type\":\"type.googleapis.com/foo.TestMessage\", " . + "\"optionalInt32\": 1}}"); + $this->assertSame("type.googleapis.com/foo.TestMessage", + $m2->getAny()->getTypeUrl()); + $this->assertSame("0801", bin2hex($m2->getAny()->getValue())); + + $m3 = new TestAny(); + $m3->mergeFromJsonString( + "{\"any\":{\"optionalInt32\": 1, " . + "\"@type\":\"type.googleapis.com/foo.TestMessage\", " . + "\"optionalInt64\": 2}}"); + $this->assertSame("type.googleapis.com/foo.TestMessage", + $m3->getAny()->getTypeUrl()); + $this->assertSame("08011002", bin2hex($m3->getAny()->getValue())); + } - # public function testDecodeAnyWithWellKnownPacked() - # { - # // Make sure packed message has been created at least once. - # $packed = new Int32Value(); - - # $m1 = new TestAny(); - # $m1->mergeFromJsonString( - # "{\"any\":" . - # " {\"@type\":\"type.googleapis.com/google.protobuf.Int32Value\"," . - # " \"value\":1}}"); - # $this->assertSame("type.googleapis.com/google.protobuf.Int32Value", - # $m1->getAny()->getTypeUrl()); - # $this->assertSame("0801", bin2hex($m1->getAny()->getValue())); - # } + public function testDecodeAnyWithWellKnownPacked() + { + // Make sure packed message has been created at least once. + $packed = new Int32Value(); + + $m1 = new TestAny(); + $m1->mergeFromJsonString( + "{\"any\":" . + " {\"@type\":\"type.googleapis.com/google.protobuf.Int32Value\"," . + " \"value\":1}}"); + $this->assertSame("type.googleapis.com/google.protobuf.Int32Value", + $m1->getAny()->getTypeUrl()); + $this->assertSame("0801", bin2hex($m1->getAny()->getValue())); + } - # /** - # * @expectedException Exception - # */ - # public function testDecodeAnyWithUnknownPacked() - # { - # $m = new TestAny(); - # $m->mergeFromJsonString( - # "{\"any\":" . - # " {\"@type\":\"type.googleapis.com/unknown\"," . - # " \"value\":1}}"); - # } + /** + * @expectedException Exception + */ + public function testDecodeAnyWithUnknownPacked() + { + $m = new TestAny(); + $m->mergeFromJsonString( + "{\"any\":" . + " {\"@type\":\"type.googleapis.com/unknown\"," . + " \"value\":1}}"); + } - # public function testEncodeTopLevelAny() - # { - # // Test a normal message. - # $packed = new TestMessage(); - # $packed->setOptionalInt32(123); - # $packed->setOptionalString("abc"); - - # $m = new Any(); - # $m->pack($packed); - # $expected1 = - # "{\"@type\":\"type.googleapis.com/foo.TestMessage\"," . - # "\"optional_int32\":123,\"optional_string\":\"abc\"}"; - # $expected2 = - # "{\"@type\":\"type.googleapis.com/foo.TestMessage\"," . - # "\"optionalInt32\":123,\"optionalString\":\"abc\"}"; - # $result = $m->serializeToJsonString(); - # $this->assertTrue($expected1 === $result || $expected2 === $result); - - # // Test a well known message. - # $packed = new Int32Value(); - # $packed->setValue(123); - - # $m = new Any(); - # $m->pack($packed); - # $this->assertSame( - # "{\"@type\":\"type.googleapis.com/google.protobuf.Int32Value\"," . - # "\"value\":123}", - # $m->serializeToJsonString()); - - # // Test an Any message. - # $outer = new Any(); - # $outer->pack($m); - # $this->assertSame( - # "{\"@type\":\"type.googleapis.com/google.protobuf.Any\"," . - # "\"value\":{\"@type\":\"type.googleapis.com/google.protobuf.Int32Value\"," . - # "\"value\":123}}", - # $outer->serializeToJsonString()); - - # // Test a Timestamp message. - # $packed = new Google\Protobuf\Timestamp(); - # $packed->setSeconds(946684800); - # $packed->setNanos(123456789); - # $m = new Any(); - # $m->pack($packed); - # $this->assertSame( - # "{\"@type\":\"type.googleapis.com/google.protobuf.Timestamp\"," . - # "\"value\":\"2000-01-01T00:00:00.123456789Z\"}", - # $m->serializeToJsonString()); - # } + public function testEncodeTopLevelAny() + { + // Test a normal message. + $packed = new TestMessage(); + $packed->setOptionalInt32(123); + $packed->setOptionalString("abc"); + + $m = new Any(); + $m->pack($packed); + $expected1 = + "{\"@type\":\"type.googleapis.com/foo.TestMessage\"," . + "\"optional_int32\":123,\"optional_string\":\"abc\"}"; + $expected2 = + "{\"@type\":\"type.googleapis.com/foo.TestMessage\"," . + "\"optionalInt32\":123,\"optionalString\":\"abc\"}"; + $result = $m->serializeToJsonString(); + $this->assertTrue($expected1 === $result || $expected2 === $result); + + // Test a well known message. + $packed = new Int32Value(); + $packed->setValue(123); + + $m = new Any(); + $m->pack($packed); + $this->assertSame( + "{\"@type\":\"type.googleapis.com/google.protobuf.Int32Value\"," . + "\"value\":123}", + $m->serializeToJsonString()); + + // Test an Any message. + $outer = new Any(); + $outer->pack($m); + $this->assertSame( + "{\"@type\":\"type.googleapis.com/google.protobuf.Any\"," . + "\"value\":{\"@type\":\"type.googleapis.com/google.protobuf.Int32Value\"," . + "\"value\":123}}", + $outer->serializeToJsonString()); + + // Test a Timestamp message. + $packed = new Google\Protobuf\Timestamp(); + $packed->setSeconds(946684800); + $packed->setNanos(123456789); + $m = new Any(); + $m->pack($packed); + $this->assertSame( + "{\"@type\":\"type.googleapis.com/google.protobuf.Timestamp\"," . + "\"value\":\"2000-01-01T00:00:00.123456789Z\"}", + $m->serializeToJsonString()); + } - # public function testDecodeTopLevelFieldMask() - # { - # $m = new TestMessage(); - # $m->setMapStringString(['a'=>'abcdefg']); - # $data1 = $m->serializeToJsonString(); - # $n = new TestMessage(); - # $n->mergeFromJsonString($data1); - # $data2 = $n->serializeToJsonString(); - # $this->assertSame($data1, $data2); - - # $m = new FieldMask(); - # $m->mergeFromJsonString("\"foo.barBaz,qux\""); - # $this->assertSame("foo.bar_baz", $m->getPaths()[0]); - # $this->assertSame("qux", $m->getPaths()[1]); - # } + public function testDecodeTopLevelFieldMask() + { + $m = new TestMessage(); + $m->setMapStringString(['a'=>'abcdefg']); + $data1 = $m->serializeToJsonString(); + $n = new TestMessage(); + $n->mergeFromJsonString($data1); + $data2 = $n->serializeToJsonString(); + $this->assertSame($data1, $data2); + + $m = new FieldMask(); + $m->mergeFromJsonString("\"foo.barBaz,qux\""); + $this->assertSame("foo.bar_baz", $m->getPaths()[0]); + $this->assertSame("qux", $m->getPaths()[1]); + } - # public function testEncodeTopLevelFieldMask() - # { - # $m = new FieldMask(); - # $m->setPaths(["foo.bar_baz", "qux"]); - # $this->assertSame("\"foo.barBaz,qux\"", $m->serializeToJsonString()); - # } + public function testEncodeTopLevelFieldMask() + { + $m = new FieldMask(); + $m->setPaths(["foo.bar_baz", "qux"]); + $this->assertSame("\"foo.barBaz,qux\"", $m->serializeToJsonString()); + } - # public function testDecodeEmptyFieldMask() - # { - # $m = new FieldMask(); - # $m->mergeFromJsonString("\"\""); - # $this->assertEquals("", $m->serializeToString()); - # } + public function testDecodeEmptyFieldMask() + { + $m = new FieldMask(); + $m->mergeFromJsonString("\"\""); + $this->assertEquals("", $m->serializeToString()); + } - # public function testJsonDecodeMapWithDefaultValueKey() - # { - # $m = new TestMessage(); - # $m->getMapInt32Int32()[0] = 0; - # $this->assertSame("{\"mapInt32Int32\":{\"0\":0}}", - # $m->serializeToJsonString()); - - # $m = new TestMessage(); - # $m->getMapStringString()[""] = ""; - # $this->assertSame("{\"mapStringString\":{\"\":\"\"}}", - # $m->serializeToJsonString()); - # } + public function testJsonDecodeMapWithDefaultValueKey() + { + $m = new TestMessage(); + $m->getMapInt32Int32()[0] = 0; + $this->assertSame("{\"mapInt32Int32\":{\"0\":0}}", + $m->serializeToJsonString()); + + $m = new TestMessage(); + $m->getMapStringString()[""] = ""; + $this->assertSame("{\"mapStringString\":{\"\":\"\"}}", + $m->serializeToJsonString()); + } - # public function testJsonDecodeNumericStringMapKey() - # { - # $m = new TestMessage(); - # $m->getMapStringString()["1"] = "1"; - # $data = $m->serializeToJsonString(); - # $this->assertSame("{\"mapStringString\":{\"1\":\"1\"}}", $data); - # $n = new TestMessage(); - # $n->mergeFromJsonString($data); - # } + public function testJsonDecodeNumericStringMapKey() + { + $m = new TestMessage(); + $m->getMapStringString()["1"] = "1"; + $data = $m->serializeToJsonString(); + $this->assertSame("{\"mapStringString\":{\"1\":\"1\"}}", $data); + $n = new TestMessage(); + $n->mergeFromJsonString($data); + } - # public function testMessageMapNoValue() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin("CA0700")); - # $m->serializeToString(); - # $this->assertTrue(true); - # } + public function testMessageMapNoValue() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin("CA0700")); + $m->serializeToString(); + $this->assertTrue(true); + } - # public function testAnyMapNoValue() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin("D20700")); - # $m->serializeToString(); - # $this->assertTrue(true); - # } + public function testAnyMapNoValue() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin("D20700")); + $m->serializeToString(); + $this->assertTrue(true); + } - # public function testListValueMapNoValue() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin("DA0700")); - # $m->serializeToString(); - # $this->assertTrue(true); - # } + public function testListValueMapNoValue() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin("DA0700")); + $m->serializeToString(); + $this->assertTrue(true); + } - # public function testStructMapNoValue() - # { - # $m = new TestMessage(); - # $m->mergeFromString(hex2bin("E20700")); - # $m->serializeToString(); - # $this->assertTrue(true); - # } + public function testStructMapNoValue() + { + $m = new TestMessage(); + $m->mergeFromString(hex2bin("E20700")); + $m->serializeToString(); + $this->assertTrue(true); + } /** * @dataProvider wrappersDataProvider From e54fc1b20b1eb63b85c5411db9946a52cdd7a1af Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Sat, 9 Nov 2019 01:02:58 +0000 Subject: [PATCH 18/21] Add type checking for write wrapper value in php7 --- php/ext/google/protobuf/message.c | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c index 305f963ad889..10146ce15402 100644 --- a/php/ext/google/protobuf/message.c +++ b/php/ext/google/protobuf/message.c @@ -620,6 +620,45 @@ PHP_METHOD(Message, writeWrapperValue) { return; } + { + // Type Checking + const upb_msgdef* submsgdef = upb_fielddef_msgsubdef(field); + const upb_fielddef* value_field = upb_msgdef_itof(submsgdef, 1); + upb_fieldtype_t type = upb_fielddef_type(value_field); + switch(type) { + case UPB_TYPE_STRING: + case UPB_TYPE_BYTES: { + if (!protobuf_convert_to_string(value)) { + return; + } + if (type == UPB_TYPE_STRING && + !is_structurally_valid_utf8(Z_STRVAL_P(value), Z_STRLEN_P(value))) { + zend_error(E_USER_ERROR, "Given string is not UTF8 encoded."); + return; + } + } + break; +#define CASE_TYPE(upb_type, type, c_type) \ + case UPB_TYPE_##upb_type: { \ + c_type type##_value; \ + if (!protobuf_convert_to_##type(value, &type##_value)) { \ + return; \ + } \ + break; \ + } + CASE_TYPE(INT32, int32, int32_t) + CASE_TYPE(UINT32, uint32, uint32_t) + CASE_TYPE(ENUM, int32, int32_t) + CASE_TYPE(INT64, int64, int64_t) + CASE_TYPE(UINT64, uint64, uint64_t) + CASE_TYPE(FLOAT, float, float) + CASE_TYPE(DOUBLE, double, double) + CASE_TYPE(BOOL, bool, int8_t) + +#undef CASE_TYPE + } + } + if (upb_fielddef_containingoneof(field)) { uint32_t* oneof_case = slot_oneof_case(msg->descriptor->layout, message_data(msg), field); From 03bb72dd3b7de4f087ab5b99e497e8a655a80f8f Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Sat, 9 Nov 2019 01:23:11 +0000 Subject: [PATCH 19/21] Fix zts build --- php/ext/google/protobuf/encode_decode.c | 34 ++++++++++++++----------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/php/ext/google/protobuf/encode_decode.c b/php/ext/google/protobuf/encode_decode.c index e6422600cdd3..60bdc4ed95d2 100644 --- a/php/ext/google/protobuf/encode_decode.c +++ b/php/ext/google/protobuf/encode_decode.c @@ -1199,18 +1199,19 @@ static void add_handlers_for_oneof_field(upb_handlers *h, } } -#define DEFINE_WRAPPER_HANDLER(utype, type, ctype) \ - static bool type##wrapper_handler( \ - void* closure, const void* hd, ctype val) { \ - wrapperfields_parseframe_t* frame = closure; \ - if (frame->is_msg) { \ - MessageHeader* msg = frame->submsg; \ - const size_t *ofs = hd; \ - DEREF(message_data(msg), *ofs, ctype) = val; \ - } else { \ - native_slot_get(utype, &val, frame->submsg); \ - } \ - return true; \ +#define DEFINE_WRAPPER_HANDLER(utype, type, ctype) \ + static bool type##wrapper_handler( \ + void* closure, const void* hd, ctype val) { \ + wrapperfields_parseframe_t* frame = closure; \ + if (frame->is_msg) { \ + MessageHeader* msg = frame->submsg; \ + const size_t *ofs = hd; \ + DEREF(message_data(msg), *ofs, ctype) = val; \ + } else { \ + TSRMLS_FETCH(); \ + native_slot_get(utype, &val, frame->submsg TSRMLS_CC); \ + } \ + return true; \ } DEFINE_WRAPPER_HANDLER(UPB_TYPE_BOOL, bool, bool) @@ -1248,9 +1249,12 @@ static bool strwrapper_end_handler(void *closure, const void *hd) { static void add_handlers_for_wrapper(const upb_msgdef* msgdef, upb_handlers* h) { const upb_fielddef* f = upb_msgdef_itof(msgdef, 1); - Descriptor* desc = - UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj((void*)msgdef)); - size_t offset = desc->layout->fields[upb_fielddef_index(f)].offset; + Descriptor* desc; + size_t offset; + + TSRMLS_FETCH(); + desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj((void*)msgdef)); + offset = desc->layout->fields[upb_fielddef_index(f)].offset; switch (upb_msgdef_wellknowntype(msgdef)) { #define SET_HANDLER(utype, ltype) \ From be83db73f2570d69df0932a89a892834b3a0aaaa Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Tue, 12 Nov 2019 23:12:59 +0000 Subject: [PATCH 20/21] Fix the bug that readWrapperValue uses parent message's layout to access wrapper value --- php/ext/google/protobuf/message.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c index 10146ce15402..291c2e5dfef6 100644 --- a/php/ext/google/protobuf/message.c +++ b/php/ext/google/protobuf/message.c @@ -589,7 +589,7 @@ PHP_METHOD(Message, readWrapperValue) { const upb_fielddef* value_field = upb_msgdef_itof(submsgdef, 1); MessageHeader* submsg = UNBOX(MessageHeader, cached_zval); CACHED_VALUE* cached_value = find_zval_property(submsg, value_field); - layout_get(msg->descriptor->layout, submsg, value_field, + layout_get(submsg->descriptor->layout, submsg, value_field, cached_value TSRMLS_CC); RETURN_ZVAL(CACHED_PTR_TO_ZVAL_PTR(cached_value), 1, 0); } else { From c61891a5954f61d458109a3f52c61a5d92d78fc4 Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Wed, 13 Nov 2019 02:04:49 +0000 Subject: [PATCH 21/21] Fix wrapper in map --- php/ext/google/protobuf/encode_decode.c | 47 ++++++++++++++++++++++++- php/tests/encode_decode_test.php | 12 +++---- php/tests/proto/test.proto | 1 + 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/php/ext/google/protobuf/encode_decode.c b/php/ext/google/protobuf/encode_decode.c index 60bdc4ed95d2..2612d22d6696 100644 --- a/php/ext/google/protobuf/encode_decode.c +++ b/php/ext/google/protobuf/encode_decode.c @@ -553,6 +553,46 @@ static void *map_submsg_handler(void *closure, const void *hd) { return submsg; } +static void *map_wrapper_submsg_handler(void *closure, const void *hd) { + MessageHeader* msg = closure; + const submsg_handlerdata_t* submsgdata = hd; + TSRMLS_FETCH(); + Descriptor* subdesc = + UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj((void*)submsgdata->md)); + zend_class_entry* subklass = subdesc->klass; + zval* submsg_php; + MessageHeader* submsg; + wrapperfields_parseframe_t* frame = + (wrapperfields_parseframe_t*)malloc(sizeof(wrapperfields_parseframe_t)); + + CACHED_VALUE* cached = + DEREF(message_data(msg), submsgdata->ofs, CACHED_VALUE*); + + if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(cached)) == IS_NULL) { +#if PHP_MAJOR_VERSION < 7 + zval val; + ZVAL_OBJ(&val, subklass->create_object(subklass TSRMLS_CC)); + MessageHeader* intern = UNBOX(MessageHeader, &val); + custom_data_init(subklass, intern PHP_PROTO_TSRMLS_CC); + REPLACE_ZVAL_VALUE(cached, &val, 1); + zval_dtor(&val); +#else + zend_object* obj = subklass->create_object(subklass TSRMLS_CC); + ZVAL_OBJ(cached, obj); + MessageHeader* intern = UNBOX_HASHTABLE_VALUE(MessageHeader, obj); + custom_data_init(subklass, intern PHP_PROTO_TSRMLS_CC); +#endif + } + + submsg_php = CACHED_PTR_TO_ZVAL_PTR(cached); + + submsg = UNBOX(MessageHeader, submsg_php); + frame->closure = closure; + frame->submsg = submsg; + frame->is_msg = true; + return frame; +} + // Handler data for startmap/endmap handlers. typedef struct { const upb_fielddef* fd; @@ -1102,7 +1142,12 @@ static void add_handlers_for_singular_field(upb_handlers *h, upb_handlerattr attr = UPB_HANDLERATTR_INIT; if (is_map) { attr.handler_data = newsubmsghandlerdata(h, offset, f); - upb_handlers_setstartsubmsg(h, f, map_submsg_handler, &attr); + if (is_wrapper_msg(upb_fielddef_msgsubdef(f))) { + upb_handlers_setstartsubmsg(h, f, map_wrapper_submsg_handler, &attr); + upb_handlers_setendsubmsg(h, f, wrapper_submsg_end_handler, &attr); + } else { + upb_handlers_setstartsubmsg(h, f, map_submsg_handler, &attr); + } } else if (is_wrapper_msg(upb_fielddef_msgsubdef(f))) { attr.handler_data = newsubmsghandlerdata(h, 0, f); upb_handlers_setstartsubmsg(h, f, wrapper_submsg_handler, &attr); diff --git a/php/tests/encode_decode_test.php b/php/tests/encode_decode_test.php index 8a41da9ef961..53cd526e4f83 100644 --- a/php/tests/encode_decode_test.php +++ b/php/tests/encode_decode_test.php @@ -188,12 +188,12 @@ public function testDecodeRepeatedStringValue() $this->assertSame("a", $m->getRepeatedField()[0]->getValue()); } - # public function testDecodeMapStringValue() - # { - # $m = new TestStringValue(); - # $m->mergeFromJsonString("{\"map_field\":[\"1\", \"a\"]}"); - # $this->assertSame("a", $m->getMapField()[1]->getValue()); - # } + public function testDecodeMapStringValue() + { + $m = new TestStringValue(); + $m->mergeFromJsonString("{\"map_field\":{\"1\": \"a\"}}"); + $this->assertSame("a", $m->getMapField()[1]->getValue()); + } public function testDecodeTopLevelBytesValue() { diff --git a/php/tests/proto/test.proto b/php/tests/proto/test.proto index f8c200e70e7f..95057090f976 100644 --- a/php/tests/proto/test.proto +++ b/php/tests/proto/test.proto @@ -267,6 +267,7 @@ message TestStringValue { google.protobuf.StringValue oneof_field = 3; int32 int32_field = 4; } + map map_field = 5; } message TestBytesValue {