From a77d3a981a253cc89881434dd0c68f816b45a60e Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Thu, 3 May 2018 10:29:23 -0700 Subject: Implement array constructor in php c extension. --- php/ext/google/protobuf/message.c | 111 ++++++++++++++++++++++++++++++++-- php/tests/generated_class_test.php | 66 ++++++++++++++++++++ php/tests/php_implementation_test.php | 81 ------------------------- 3 files changed, 173 insertions(+), 85 deletions(-) diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c index c8f4d62b..5a8734bc 100644 --- a/php/ext/google/protobuf/message.c +++ b/php/ext/google/protobuf/message.c @@ -282,15 +282,118 @@ void build_class_from_descriptor( // PHP Methods // ----------------------------------------------------------------------------- +void Message_construct(zval* msg, zval* array_wrapper) { + zend_class_entry* ce = Z_OBJCE_P(msg); + MessageHeader* intern = NULL; + if (EXPECTED(class_added(ce))) { + intern = UNBOX(MessageHeader, msg); + custom_data_init(ce, intern PHP_PROTO_TSRMLS_CC); + } + + if (array_wrapper == NULL) { + return; + } + + HashTable* array = Z_ARRVAL_P(array_wrapper); + HashPosition pointer; + zval key; + void* value; + const upb_fielddef* field; + + for (zend_hash_internal_pointer_reset_ex(array, &pointer); + php_proto_zend_hash_get_current_data_ex(array, (void**)&value, + &pointer) == SUCCESS; + zend_hash_move_forward_ex(array, &pointer)) { + zend_hash_get_current_key_zval_ex(array, &key, &pointer); + field = upb_msgdef_ntofz(intern->descriptor->msgdef, Z_STRVAL_P(&key)); + if (field == NULL) { + zend_error(E_USER_ERROR, "Unknown field: %s", Z_STRVAL_P(&key)); + } + if (upb_fielddef_ismap(field)) { + PHP_PROTO_FAKE_SCOPE_BEGIN(Z_OBJCE_P(msg)); + zval* submap = message_get_property_internal(msg, &key TSRMLS_CC); + PHP_PROTO_FAKE_SCOPE_END; + HashTable* subtable = HASH_OF( + CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)value)); + HashPosition subpointer; + zval subkey; + void* memory; + for (zend_hash_internal_pointer_reset_ex(subtable, &subpointer); + php_proto_zend_hash_get_current_data_ex(subtable, (void**)&memory, + &subpointer) == SUCCESS; + zend_hash_move_forward_ex(subtable, &subpointer)) { + zend_hash_get_current_key_zval_ex(subtable, &subkey, &subpointer); + map_field_handlers->write_dimension( + submap, &subkey, + CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)memory) TSRMLS_CC); + zval_dtor(&subkey); + } + } else if (upb_fielddef_isseq(field)) { + PHP_PROTO_FAKE_SCOPE_BEGIN(Z_OBJCE_P(msg)); + zval* subarray = message_get_property_internal(msg, &key TSRMLS_CC); + PHP_PROTO_FAKE_SCOPE_END; + HashTable* subtable = HASH_OF( + CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)value)); + HashPosition subpointer; + void* memory; + for (zend_hash_internal_pointer_reset_ex(subtable, &subpointer); + php_proto_zend_hash_get_current_data_ex(subtable, (void**)&memory, + &subpointer) == SUCCESS; + zend_hash_move_forward_ex(subtable, &subpointer)) { + repeated_field_handlers->write_dimension( + subarray, NULL, + CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)memory) TSRMLS_CC); + } + } else if (upb_fielddef_issubmsg(field)) { + const upb_msgdef* submsgdef = upb_fielddef_msgsubdef(field); + PHP_PROTO_HASHTABLE_VALUE desc_php = get_def_obj(submsgdef); + Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, desc_php); + zend_property_info* property_info; + PHP_PROTO_FAKE_SCOPE_BEGIN(Z_OBJCE_P(msg)); +#if PHP_MAJOR_VERSION < 7 + property_info = + zend_get_property_info(Z_OBJCE_P(msg), &key, true TSRMLS_CC); +#else + property_info = + zend_get_property_info(Z_OBJCE_P(msg), Z_STR_P(&key), true); +#endif + PHP_PROTO_FAKE_SCOPE_END; + CACHED_VALUE* cached = OBJ_PROP(Z_OBJ_P(msg), property_info->offset); +#if PHP_MAJOR_VERSION < 7 + SEPARATE_ZVAL_IF_NOT_REF(cached); +#endif + zval* submsg = CACHED_PTR_TO_ZVAL_PTR(cached); + ZVAL_OBJ(submsg, desc->klass->create_object(desc->klass TSRMLS_CC)); + Message_construct(submsg, NULL); + MessageHeader* from = UNBOX(MessageHeader, + CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)value)); + MessageHeader* to = UNBOX(MessageHeader, submsg); + if(from->descriptor != to->descriptor) { + zend_error(E_USER_ERROR, "Cannot merge messages with different class."); + return; + } + + layout_merge(from->descriptor->layout, from, to TSRMLS_CC); + } else { + message_set_property_internal(msg, &key, + CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)value) TSRMLS_CC); + } + zval_dtor(&key); + } +} + // At the first time the message is created, the class entry hasn't been // modified. As a result, the first created instance will be a normal zend // object. Here, we manually modify it to our message in such a case. PHP_METHOD(Message, __construct) { - zend_class_entry* ce = Z_OBJCE_P(getThis()); - if (EXPECTED(class_added(ce))) { - MessageHeader* intern = UNBOX(MessageHeader, getThis()); - custom_data_init(ce, intern PHP_PROTO_TSRMLS_CC); + // Init message with array + zval* array_wrapper; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a!", &array_wrapper, + message_type) == FAILURE) { + return; } + + Message_construct(getThis(), array_wrapper); } PHP_METHOD(Message, clear) { diff --git a/php/tests/generated_class_test.php b/php/tests/generated_class_test.php index 53d18ee7..fbee29bc 100644 --- a/php/tests/generated_class_test.php +++ b/php/tests/generated_class_test.php @@ -1171,4 +1171,70 @@ class GeneratedClassTest extends TestBase $m = new testLowerCaseMessage(); $n = testLowerCaseEnum::VALUE; } + + ######################################################### + # Test Array Constructor. + ######################################################### + + public function testArrayConstructor() + { + $m = new TestMessage([ + 'optional_int32' => -42, + 'optional_int64' => -43, + 'optional_uint32' => 42, + 'optional_uint64' => 43, + 'optional_sint32' => -44, + 'optional_sint64' => -45, + 'optional_fixed32' => 46, + 'optional_fixed64' => 47, + 'optional_sfixed32' => -46, + 'optional_sfixed64' => -47, + 'optional_float' => 1.5, + 'optional_double' => 1.6, + 'optional_bool' => true, + 'optional_string' => 'a', + 'optional_bytes' => 'b', + 'optional_enum' => TestEnum::ONE, + 'optional_message' => new TestMessage_Sub([ + 'a' => 33 + ]), + 'repeated_int32' => [-42, -52], + 'repeated_int64' => [-43, -53], + 'repeated_uint32' => [42, 52], + 'repeated_uint64' => [43, 53], + 'repeated_sint32' => [-44, -54], + 'repeated_sint64' => [-45, -55], + 'repeated_fixed32' => [46, 56], + 'repeated_fixed64' => [47, 57], + 'repeated_sfixed32' => [-46, -56], + 'repeated_sfixed64' => [-47, -57], + 'repeated_float' => [1.5, 2.5], + 'repeated_double' => [1.6, 2.6], + 'repeated_bool' => [true, false], + 'repeated_string' => ['a', 'c'], + 'repeated_bytes' => ['b', 'd'], + 'repeated_enum' => [TestEnum::ZERO, TestEnum::ONE], + 'repeated_message' => [new TestMessage_Sub(['a' => 34]), + new TestMessage_Sub(['a' => 35])], + 'map_int32_int32' => [-62 => -62], + 'map_int64_int64' => [-63 => -63], + 'map_uint32_uint32' => [62 => 62], + 'map_uint64_uint64' => [63 => 63], + 'map_sint32_sint32' => [-64 => -64], + 'map_sint64_sint64' => [-65 => -65], + 'map_fixed32_fixed32' => [66 => 66], + 'map_fixed64_fixed64' => [67 => 67], + 'map_sfixed32_sfixed32' => [-68 => -68], + 'map_sfixed64_sfixed64' => [-69 => -69], + 'map_int32_float' => [1 => 3.5], + 'map_int32_double' => [1 => 3.6], + 'map_bool_bool' => [true => true], + 'map_string_string' => ['e' => 'e'], + 'map_int32_bytes' => [1 => 'f'], + 'map_int32_enum' => [1 => TestEnum::ONE], + 'map_int32_message' => [1 => new TestMessage_Sub(['a' => 36])], + ]); + + TestUtil::assertTestMessage($m); + } } diff --git a/php/tests/php_implementation_test.php b/php/tests/php_implementation_test.php index 720af132..6481473e 100644 --- a/php/tests/php_implementation_test.php +++ b/php/tests/php_implementation_test.php @@ -514,87 +514,6 @@ class ImplementationTest extends TestBase $this->assertSame(166, $m->byteSize()); } - public function testArrayConstructor() - { - $m = new TestMessage([ - 'optional_int32' => -42, - 'optional_int64' => -43, - 'optional_uint32' => 42, - 'optional_uint64' => 43, - 'optional_sint32' => -44, - 'optional_sint64' => -45, - 'optional_fixed32' => 46, - 'optional_fixed64' => 47, - 'optional_sfixed32' => -46, - 'optional_sfixed64' => -47, - 'optional_float' => 1.5, - 'optional_double' => 1.6, - 'optional_bool' => true, - 'optional_string' => 'a', - 'optional_bytes' => 'b', - 'optional_enum' => TestEnum::ONE, - 'optional_message' => new TestMessage_Sub([ - 'a' => 33 - ]), - 'repeated_int32' => [-42, -52], - 'repeated_int64' => [-43, -53], - 'repeated_uint32' => [42, 52], - 'repeated_uint64' => [43, 53], - 'repeated_sint32' => [-44, -54], - 'repeated_sint64' => [-45, -55], - 'repeated_fixed32' => [46, 56], - 'repeated_fixed64' => [47, 57], - 'repeated_sfixed32' => [-46, -56], - 'repeated_sfixed64' => [-47, -57], - 'repeated_float' => [1.5, 2.5], - 'repeated_double' => [1.6, 2.6], - 'repeated_bool' => [true, false], - 'repeated_string' => ['a', 'c'], - 'repeated_bytes' => ['b', 'd'], - 'repeated_enum' => [TestEnum::ZERO, TestEnum::ONE], - 'repeated_message' => [ - new TestMessage_Sub(['a' => 34]), - new TestMessage_Sub(['a' => 35]), - ], - 'map_int32_int32' => [-62 => -62], - 'map_int64_int64' => [-63 => -63], - 'map_uint32_uint32' => [62 => 62], - 'map_uint64_uint64' => [63 => 63], - 'map_sint32_sint32' => [-64 => -64], - 'map_sint64_sint64' => [-65 => -65], - 'map_fixed32_fixed32' => [66 => 66], - 'map_fixed64_fixed64' => [67 => 67], - 'map_sfixed32_sfixed32' => [-68 => -68], - 'map_sfixed64_sfixed64' => [-69 => -69], - 'map_int32_float' => [1 => 3.5], - 'map_int32_double' => [1 => 3.6], - 'map_bool_bool' => [true => true], - 'map_string_string' => ['e' => 'e'], - 'map_int32_bytes' => [1 => 'f'], - 'map_int32_enum' => [1 => TestEnum::ONE], - 'map_int32_message' => [1 => new TestMessage_Sub(['a' => 36])], - ]); - - TestUtil::assertTestMessage($m); - - // Using message objects - $m = new TestMessage([ - 'optional_message' => new TestMessage_Sub(['a' => 33]), - 'repeated_message' => [ - new TestMessage_Sub(['a' => 34]), - new TestMessage_Sub(['a' => 35]), - ], - 'map_int32_message' => [ - 1 => new TestMessage_Sub(['a' => 36]) - ], - ]); - - $this->assertEquals(33, $m->getOptionalMessage()->getA()); - $this->assertEquals(34, $m->getRepeatedMessage()[0]->getA()); - $this->assertEquals(35, $m->getRepeatedMessage()[1]->getA()); - $this->assertEquals(36, $m->getMapInt32Message()[1]->getA()); - } - /** * @expectedException UnexpectedValueException * @expectedExceptionMessage Invalid message property: optionalInt32 -- cgit v1.2.3