aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBo Yang <teboring@teboring0.svl.corp.google.com>2018-05-03 10:29:23 -0700
committerBo Yang <teboring@google.com>2018-05-25 13:04:08 -0700
commit8b336f8c5a77ca4932b00203e485f9266f6f0c30 (patch)
tree401667c4e48bd64f789cb812af8ee4e2c2df4bc9
parentc9b404d23bdf3dd8f1556b19a27dcba40565e3e4 (diff)
downloadprotobuf-8b336f8c5a77ca4932b00203e485f9266f6f0c30.tar.gz
protobuf-8b336f8c5a77ca4932b00203e485f9266f6f0c30.tar.bz2
protobuf-8b336f8c5a77ca4932b00203e485f9266f6f0c30.zip
Implement array constructor in php c extension.
-rw-r--r--php/ext/google/protobuf/message.c111
-rw-r--r--php/tests/generated_class_test.php66
-rw-r--r--php/tests/php_implementation_test.php81
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