Skip to content

Commit a77d3a9

Browse files
committed
Implement array constructor in php c extension.
1 parent ca07512 commit a77d3a9

File tree

3 files changed

+173
-85
lines changed

3 files changed

+173
-85
lines changed

php/ext/google/protobuf/message.c

Lines changed: 107 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -282,15 +282,118 @@ void build_class_from_descriptor(
282282
// PHP Methods
283283
// -----------------------------------------------------------------------------
284284

285+
void Message_construct(zval* msg, zval* array_wrapper) {
286+
zend_class_entry* ce = Z_OBJCE_P(msg);
287+
MessageHeader* intern = NULL;
288+
if (EXPECTED(class_added(ce))) {
289+
intern = UNBOX(MessageHeader, msg);
290+
custom_data_init(ce, intern PHP_PROTO_TSRMLS_CC);
291+
}
292+
293+
if (array_wrapper == NULL) {
294+
return;
295+
}
296+
297+
HashTable* array = Z_ARRVAL_P(array_wrapper);
298+
HashPosition pointer;
299+
zval key;
300+
void* value;
301+
const upb_fielddef* field;
302+
303+
for (zend_hash_internal_pointer_reset_ex(array, &pointer);
304+
php_proto_zend_hash_get_current_data_ex(array, (void**)&value,
305+
&pointer) == SUCCESS;
306+
zend_hash_move_forward_ex(array, &pointer)) {
307+
zend_hash_get_current_key_zval_ex(array, &key, &pointer);
308+
field = upb_msgdef_ntofz(intern->descriptor->msgdef, Z_STRVAL_P(&key));
309+
if (field == NULL) {
310+
zend_error(E_USER_ERROR, "Unknown field: %s", Z_STRVAL_P(&key));
311+
}
312+
if (upb_fielddef_ismap(field)) {
313+
PHP_PROTO_FAKE_SCOPE_BEGIN(Z_OBJCE_P(msg));
314+
zval* submap = message_get_property_internal(msg, &key TSRMLS_CC);
315+
PHP_PROTO_FAKE_SCOPE_END;
316+
HashTable* subtable = HASH_OF(
317+
CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)value));
318+
HashPosition subpointer;
319+
zval subkey;
320+
void* memory;
321+
for (zend_hash_internal_pointer_reset_ex(subtable, &subpointer);
322+
php_proto_zend_hash_get_current_data_ex(subtable, (void**)&memory,
323+
&subpointer) == SUCCESS;
324+
zend_hash_move_forward_ex(subtable, &subpointer)) {
325+
zend_hash_get_current_key_zval_ex(subtable, &subkey, &subpointer);
326+
map_field_handlers->write_dimension(
327+
submap, &subkey,
328+
CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)memory) TSRMLS_CC);
329+
zval_dtor(&subkey);
330+
}
331+
} else if (upb_fielddef_isseq(field)) {
332+
PHP_PROTO_FAKE_SCOPE_BEGIN(Z_OBJCE_P(msg));
333+
zval* subarray = message_get_property_internal(msg, &key TSRMLS_CC);
334+
PHP_PROTO_FAKE_SCOPE_END;
335+
HashTable* subtable = HASH_OF(
336+
CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)value));
337+
HashPosition subpointer;
338+
void* memory;
339+
for (zend_hash_internal_pointer_reset_ex(subtable, &subpointer);
340+
php_proto_zend_hash_get_current_data_ex(subtable, (void**)&memory,
341+
&subpointer) == SUCCESS;
342+
zend_hash_move_forward_ex(subtable, &subpointer)) {
343+
repeated_field_handlers->write_dimension(
344+
subarray, NULL,
345+
CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)memory) TSRMLS_CC);
346+
}
347+
} else if (upb_fielddef_issubmsg(field)) {
348+
const upb_msgdef* submsgdef = upb_fielddef_msgsubdef(field);
349+
PHP_PROTO_HASHTABLE_VALUE desc_php = get_def_obj(submsgdef);
350+
Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, desc_php);
351+
zend_property_info* property_info;
352+
PHP_PROTO_FAKE_SCOPE_BEGIN(Z_OBJCE_P(msg));
353+
#if PHP_MAJOR_VERSION < 7
354+
property_info =
355+
zend_get_property_info(Z_OBJCE_P(msg), &key, true TSRMLS_CC);
356+
#else
357+
property_info =
358+
zend_get_property_info(Z_OBJCE_P(msg), Z_STR_P(&key), true);
359+
#endif
360+
PHP_PROTO_FAKE_SCOPE_END;
361+
CACHED_VALUE* cached = OBJ_PROP(Z_OBJ_P(msg), property_info->offset);
362+
#if PHP_MAJOR_VERSION < 7
363+
SEPARATE_ZVAL_IF_NOT_REF(cached);
364+
#endif
365+
zval* submsg = CACHED_PTR_TO_ZVAL_PTR(cached);
366+
ZVAL_OBJ(submsg, desc->klass->create_object(desc->klass TSRMLS_CC));
367+
Message_construct(submsg, NULL);
368+
MessageHeader* from = UNBOX(MessageHeader,
369+
CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)value));
370+
MessageHeader* to = UNBOX(MessageHeader, submsg);
371+
if(from->descriptor != to->descriptor) {
372+
zend_error(E_USER_ERROR, "Cannot merge messages with different class.");
373+
return;
374+
}
375+
376+
layout_merge(from->descriptor->layout, from, to TSRMLS_CC);
377+
} else {
378+
message_set_property_internal(msg, &key,
379+
CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)value) TSRMLS_CC);
380+
}
381+
zval_dtor(&key);
382+
}
383+
}
384+
285385
// At the first time the message is created, the class entry hasn't been
286386
// modified. As a result, the first created instance will be a normal zend
287387
// object. Here, we manually modify it to our message in such a case.
288388
PHP_METHOD(Message, __construct) {
289-
zend_class_entry* ce = Z_OBJCE_P(getThis());
290-
if (EXPECTED(class_added(ce))) {
291-
MessageHeader* intern = UNBOX(MessageHeader, getThis());
292-
custom_data_init(ce, intern PHP_PROTO_TSRMLS_CC);
389+
// Init message with array
390+
zval* array_wrapper;
391+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a!", &array_wrapper,
392+
message_type) == FAILURE) {
393+
return;
293394
}
395+
396+
Message_construct(getThis(), array_wrapper);
294397
}
295398

296399
PHP_METHOD(Message, clear) {

php/tests/generated_class_test.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,4 +1171,70 @@ public function testLowerCase()
11711171
$m = new testLowerCaseMessage();
11721172
$n = testLowerCaseEnum::VALUE;
11731173
}
1174+
1175+
#########################################################
1176+
# Test Array Constructor.
1177+
#########################################################
1178+
1179+
public function testArrayConstructor()
1180+
{
1181+
$m = new TestMessage([
1182+
'optional_int32' => -42,
1183+
'optional_int64' => -43,
1184+
'optional_uint32' => 42,
1185+
'optional_uint64' => 43,
1186+
'optional_sint32' => -44,
1187+
'optional_sint64' => -45,
1188+
'optional_fixed32' => 46,
1189+
'optional_fixed64' => 47,
1190+
'optional_sfixed32' => -46,
1191+
'optional_sfixed64' => -47,
1192+
'optional_float' => 1.5,
1193+
'optional_double' => 1.6,
1194+
'optional_bool' => true,
1195+
'optional_string' => 'a',
1196+
'optional_bytes' => 'b',
1197+
'optional_enum' => TestEnum::ONE,
1198+
'optional_message' => new TestMessage_Sub([
1199+
'a' => 33
1200+
]),
1201+
'repeated_int32' => [-42, -52],
1202+
'repeated_int64' => [-43, -53],
1203+
'repeated_uint32' => [42, 52],
1204+
'repeated_uint64' => [43, 53],
1205+
'repeated_sint32' => [-44, -54],
1206+
'repeated_sint64' => [-45, -55],
1207+
'repeated_fixed32' => [46, 56],
1208+
'repeated_fixed64' => [47, 57],
1209+
'repeated_sfixed32' => [-46, -56],
1210+
'repeated_sfixed64' => [-47, -57],
1211+
'repeated_float' => [1.5, 2.5],
1212+
'repeated_double' => [1.6, 2.6],
1213+
'repeated_bool' => [true, false],
1214+
'repeated_string' => ['a', 'c'],
1215+
'repeated_bytes' => ['b', 'd'],
1216+
'repeated_enum' => [TestEnum::ZERO, TestEnum::ONE],
1217+
'repeated_message' => [new TestMessage_Sub(['a' => 34]),
1218+
new TestMessage_Sub(['a' => 35])],
1219+
'map_int32_int32' => [-62 => -62],
1220+
'map_int64_int64' => [-63 => -63],
1221+
'map_uint32_uint32' => [62 => 62],
1222+
'map_uint64_uint64' => [63 => 63],
1223+
'map_sint32_sint32' => [-64 => -64],
1224+
'map_sint64_sint64' => [-65 => -65],
1225+
'map_fixed32_fixed32' => [66 => 66],
1226+
'map_fixed64_fixed64' => [67 => 67],
1227+
'map_sfixed32_sfixed32' => [-68 => -68],
1228+
'map_sfixed64_sfixed64' => [-69 => -69],
1229+
'map_int32_float' => [1 => 3.5],
1230+
'map_int32_double' => [1 => 3.6],
1231+
'map_bool_bool' => [true => true],
1232+
'map_string_string' => ['e' => 'e'],
1233+
'map_int32_bytes' => [1 => 'f'],
1234+
'map_int32_enum' => [1 => TestEnum::ONE],
1235+
'map_int32_message' => [1 => new TestMessage_Sub(['a' => 36])],
1236+
]);
1237+
1238+
TestUtil::assertTestMessage($m);
1239+
}
11741240
}

php/tests/php_implementation_test.php

Lines changed: 0 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -514,87 +514,6 @@ public function testPackedByteSize()
514514
$this->assertSame(166, $m->byteSize());
515515
}
516516

517-
public function testArrayConstructor()
518-
{
519-
$m = new TestMessage([
520-
'optional_int32' => -42,
521-
'optional_int64' => -43,
522-
'optional_uint32' => 42,
523-
'optional_uint64' => 43,
524-
'optional_sint32' => -44,
525-
'optional_sint64' => -45,
526-
'optional_fixed32' => 46,
527-
'optional_fixed64' => 47,
528-
'optional_sfixed32' => -46,
529-
'optional_sfixed64' => -47,
530-
'optional_float' => 1.5,
531-
'optional_double' => 1.6,
532-
'optional_bool' => true,
533-
'optional_string' => 'a',
534-
'optional_bytes' => 'b',
535-
'optional_enum' => TestEnum::ONE,
536-
'optional_message' => new TestMessage_Sub([
537-
'a' => 33
538-
]),
539-
'repeated_int32' => [-42, -52],
540-
'repeated_int64' => [-43, -53],
541-
'repeated_uint32' => [42, 52],
542-
'repeated_uint64' => [43, 53],
543-
'repeated_sint32' => [-44, -54],
544-
'repeated_sint64' => [-45, -55],
545-
'repeated_fixed32' => [46, 56],
546-
'repeated_fixed64' => [47, 57],
547-
'repeated_sfixed32' => [-46, -56],
548-
'repeated_sfixed64' => [-47, -57],
549-
'repeated_float' => [1.5, 2.5],
550-
'repeated_double' => [1.6, 2.6],
551-
'repeated_bool' => [true, false],
552-
'repeated_string' => ['a', 'c'],
553-
'repeated_bytes' => ['b', 'd'],
554-
'repeated_enum' => [TestEnum::ZERO, TestEnum::ONE],
555-
'repeated_message' => [
556-
new TestMessage_Sub(['a' => 34]),
557-
new TestMessage_Sub(['a' => 35]),
558-
],
559-
'map_int32_int32' => [-62 => -62],
560-
'map_int64_int64' => [-63 => -63],
561-
'map_uint32_uint32' => [62 => 62],
562-
'map_uint64_uint64' => [63 => 63],
563-
'map_sint32_sint32' => [-64 => -64],
564-
'map_sint64_sint64' => [-65 => -65],
565-
'map_fixed32_fixed32' => [66 => 66],
566-
'map_fixed64_fixed64' => [67 => 67],
567-
'map_sfixed32_sfixed32' => [-68 => -68],
568-
'map_sfixed64_sfixed64' => [-69 => -69],
569-
'map_int32_float' => [1 => 3.5],
570-
'map_int32_double' => [1 => 3.6],
571-
'map_bool_bool' => [true => true],
572-
'map_string_string' => ['e' => 'e'],
573-
'map_int32_bytes' => [1 => 'f'],
574-
'map_int32_enum' => [1 => TestEnum::ONE],
575-
'map_int32_message' => [1 => new TestMessage_Sub(['a' => 36])],
576-
]);
577-
578-
TestUtil::assertTestMessage($m);
579-
580-
// Using message objects
581-
$m = new TestMessage([
582-
'optional_message' => new TestMessage_Sub(['a' => 33]),
583-
'repeated_message' => [
584-
new TestMessage_Sub(['a' => 34]),
585-
new TestMessage_Sub(['a' => 35]),
586-
],
587-
'map_int32_message' => [
588-
1 => new TestMessage_Sub(['a' => 36])
589-
],
590-
]);
591-
592-
$this->assertEquals(33, $m->getOptionalMessage()->getA());
593-
$this->assertEquals(34, $m->getRepeatedMessage()[0]->getA());
594-
$this->assertEquals(35, $m->getRepeatedMessage()[1]->getA());
595-
$this->assertEquals(36, $m->getMapInt32Message()[1]->getA());
596-
}
597-
598517
/**
599518
* @expectedException UnexpectedValueException
600519
* @expectedExceptionMessage Invalid message property: optionalInt32

0 commit comments

Comments
 (0)