Implement array constructor in php c extension.
diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c
index c8f4d62..5a8734b 100644
--- a/php/ext/google/protobuf/message.c
+++ b/php/ext/google/protobuf/message.c
@@ -282,15 +282,118 @@
 // 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 53d18ee..fbee29b 100644
--- a/php/tests/generated_class_test.php
+++ b/php/tests/generated_class_test.php
@@ -1171,4 +1171,70 @@
         $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 720af13..6481473 100644
--- a/php/tests/php_implementation_test.php
+++ b/php/tests/php_implementation_test.php
@@ -514,87 +514,6 @@
         $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