Joshua Haberman 5 жил өмнө
parent
commit
cfc0c2b5e0

+ 20 - 1
php/ext/google/protobuf/def.c

@@ -1010,6 +1010,21 @@ static zend_function_entry DescriptorPool_methods[] = {
   ZEND_FE_END
   ZEND_FE_END
 };
 };
 
 
+// -----------------------------------------------------------------------------
+// InternalDescriptorPool
+// -----------------------------------------------------------------------------
+
+// For the C extension, Google\Protobuf\Internal\DescriptorPool is not a
+// separate instantiable object, it just returns a
+// Google\Protobuf\DescriptorPool.
+
+zend_class_entry *InternalDescriptorPool_class_entry;
+
+static zend_function_entry InternalDescriptorPool_methods[] = {
+  PHP_ME(DescriptorPool, getGeneratedPool, NULL,
+         ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+};
+
 // -----------------------------------------------------------------------------
 // -----------------------------------------------------------------------------
 // GPBType
 // GPBType
 // -----------------------------------------------------------------------------
 // -----------------------------------------------------------------------------
@@ -1071,7 +1086,7 @@ void Def_ModuleInit() {
   h = &FieldDescriptor_object_handlers;
   h = &FieldDescriptor_object_handlers;
   memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
   memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
 
 
-  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\DescriptorPool",
+  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\DescriptorPool",
                    DescriptorPool_methods);
                    DescriptorPool_methods);
   DescriptorPool_class_entry = zend_register_internal_class(&tmp_ce);
   DescriptorPool_class_entry = zend_register_internal_class(&tmp_ce);
   DescriptorPool_class_entry->ce_flags |= ZEND_ACC_FINAL;
   DescriptorPool_class_entry->ce_flags |= ZEND_ACC_FINAL;
@@ -1080,6 +1095,10 @@ void Def_ModuleInit() {
   memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
   memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
   h->dtor_obj = DescriptorPool_destructor;
   h->dtor_obj = DescriptorPool_destructor;
 
 
+  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\DescriptorPool",
+                   InternalDescriptorPool_methods);
+  InternalDescriptorPool_class_entry = zend_register_internal_class(&tmp_ce);
+
   // GPBType.
   // GPBType.
 #define STR(str) (str), strlen(str)
 #define STR(str) (str), strlen(str)
   zend_class_entry class_type;
   zend_class_entry class_type;

+ 185 - 73
php/ext/google/protobuf/message.c

@@ -540,6 +540,12 @@ bool Message_InitFromPhp(upb_msg *msg, const upb_msgdef *m, zval *init,
   }
   }
 }
 }
 
 
+static void Message_Initialize(Message *intern, const Descriptor *desc) {
+  intern->desc = desc;
+  intern->msg = upb_msg_new(desc->msgdef, Arena_Get(&intern->arena));
+  ObjCache_Add(intern->msg, &intern->std);
+}
+
 /**
 /**
  * Message::__construct()
  * Message::__construct()
  *
  *
@@ -549,13 +555,10 @@ bool Message_InitFromPhp(upb_msg *msg, const upb_msgdef *m, zval *init,
 PHP_METHOD(Message, __construct) {
 PHP_METHOD(Message, __construct) {
   Message* intern = (Message*)Z_OBJ_P(getThis());
   Message* intern = (Message*)Z_OBJ_P(getThis());
   const Descriptor* desc = Descriptor_GetFromClassEntry(Z_OBJCE_P(getThis()));
   const Descriptor* desc = Descriptor_GetFromClassEntry(Z_OBJCE_P(getThis()));
-  const upb_msgdef *msgdef = desc->msgdef;
   upb_arena *arena = Arena_Get(&intern->arena);
   upb_arena *arena = Arena_Get(&intern->arena);
   zval *init_arr = NULL;
   zval *init_arr = NULL;
 
 
-  intern->desc = desc;
-  intern->msg = upb_msg_new(msgdef, arena);
-  ObjCache_Add(intern->msg, &intern->std);
+  Message_Initialize(intern, desc);
 
 
   if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!", &init_arr) == FAILURE) {
   if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!", &init_arr) == FAILURE) {
     return;
     return;
@@ -1024,31 +1027,55 @@ static const char TYPE_URL_PREFIX[] = "type.googleapis.com/";
 
 
 static upb_msgval Message_getval(Message *intern, const char *field_name) {
 static upb_msgval Message_getval(Message *intern, const char *field_name) {
   const upb_fielddef *f = upb_msgdef_ntofz(intern->desc->msgdef, field_name);
   const upb_fielddef *f = upb_msgdef_ntofz(intern->desc->msgdef, field_name);
+  PBPHP_ASSERT(f);
   return upb_msg_get(intern->msg, f);
   return upb_msg_get(intern->msg, f);
 }
 }
 
 
+static void Message_setval(Message *intern, const char *field_name,
+                           upb_msgval val) {
+  const upb_fielddef *f = upb_msgdef_ntofz(intern->desc->msgdef, field_name);
+  PBPHP_ASSERT(f);
+  return upb_msg_set(intern->msg, f, val, Arena_Get(&intern->arena));
+}
+
+static upb_msgval StringVal(upb_strview view) {
+  upb_msgval ret;
+  ret.str_val = view;
+  return ret;
+}
+
+static bool TryStripUrlPrefix(upb_strview *str) {
+  size_t size = strlen(TYPE_URL_PREFIX);
+  if (str->size < size || memcmp(TYPE_URL_PREFIX, str->data, size) != 0) {
+    return false;
+  }
+  str->data += size;
+  str->size -= size;
+  return true;
+}
+
+static bool StrViewEq(upb_strview view, const char *str) {
+  size_t size = strlen(str);
+  return view.size == size && memcmp(view.data, str, size) == 0;
+}
+
 PHP_METHOD(google_protobuf_Any, unpack) {
 PHP_METHOD(google_protobuf_Any, unpack) {
   Message* intern = (Message*)Z_OBJ_P(getThis());
   Message* intern = (Message*)Z_OBJ_P(getThis());
   upb_strview type_url = Message_getval(intern, "type_url").str_val;
   upb_strview type_url = Message_getval(intern, "type_url").str_val;
   upb_strview value = Message_getval(intern, "value").str_val;
   upb_strview value = Message_getval(intern, "value").str_val;
-  size_t prefix_len = strlen(TYPE_URL_PREFIX);
   upb_symtab *symtab = DescriptorPool_GetSymbolTable();
   upb_symtab *symtab = DescriptorPool_GetSymbolTable();
   const upb_msgdef *m;
   const upb_msgdef *m;
   Descriptor *desc;
   Descriptor *desc;
-  zend_class_entry *klass;
   zval ret;
   zval ret;
 
 
   // Ensure that type_url has TYPE_URL_PREFIX as a prefix.
   // Ensure that type_url has TYPE_URL_PREFIX as a prefix.
-  if (type_url.size < prefix_len ||
-      strncmp(TYPE_URL_PREFIX, type_url.data, prefix_len) != 0) {
+  if (!TryStripUrlPrefix(&type_url)) {
     zend_throw_exception(
     zend_throw_exception(
         NULL, "Type url needs to be type.googleapis.com/fully-qualified",
         NULL, "Type url needs to be type.googleapis.com/fully-qualified",
         0 TSRMLS_CC);
         0 TSRMLS_CC);
     return;
     return;
   }
   }
 
 
-  type_url.size -= prefix_len;
-  type_url.data += prefix_len;
   m = upb_symtab_lookupmsg2(symtab, type_url.data, type_url.size);
   m = upb_symtab_lookupmsg2(symtab, type_url.data, type_url.size);
 
 
   if (m == NULL) {
   if (m == NULL) {
@@ -1059,9 +1086,11 @@ PHP_METHOD(google_protobuf_Any, unpack) {
   }
   }
 
 
   desc = Descriptor_GetFromMessageDef(m);
   desc = Descriptor_GetFromMessageDef(m);
-  klass = desc->class_entry;
-  ZVAL_OBJ(&ret, klass->create_object(klass));
-  Message *msg = (Message*)Z_OBJ_P(&ret);
+  PBPHP_ASSERT(desc->class_entry->create_object == Message_create);
+  zend_object *obj = Message_create(desc->class_entry);
+  Message *msg = (Message*)obj;
+  Message_Initialize(msg, desc);
+  ZVAL_OBJ(&ret, obj);
 
 
   // Get value.
   // Get value.
   if (!upb_decode(value.data, value.size, msg->msg,
   if (!upb_decode(value.data, value.size, msg->msg,
@@ -1076,9 +1105,15 @@ PHP_METHOD(google_protobuf_Any, unpack) {
   RETURN_ZVAL(&ret, 1, 0);
   RETURN_ZVAL(&ret, 1, 0);
 }
 }
 
 
-/*
-PHP_METHOD(Any, pack) {
-  zval* val;
+PHP_METHOD(google_protobuf_Any, pack) {
+  Message* intern = (Message*)Z_OBJ_P(getThis());
+  upb_arena *arena = Arena_Get(&intern->arena);
+  zval *val;
+  Message *msg;
+  upb_strview value;
+  upb_strview type_url;
+  const char *full_name;
+  char *buf;
 
 
   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &val) ==
   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &val) ==
       FAILURE) {
       FAILURE) {
@@ -1090,76 +1125,153 @@ PHP_METHOD(Any, pack) {
     return;
     return;
   }
   }
 
 
-  // Set value by serialized data.
-  zval data;
-  serialize_to_string(val, &data TSRMLS_CC);
-
-  zval member;
-  PHP_PROTO_ZVAL_STRING(&member, "value", 1);
-
-  PHP_PROTO_FAKE_SCOPE_BEGIN(any_type);
-  message_handlers->write_property(getThis(), &member, &data,
-                                   NULL PHP_PROTO_TSRMLS_CC);
-  zval_dtor(&data);
-  zval_dtor(&member);
-  PHP_PROTO_FAKE_SCOPE_END;
-
-  // Set type url.
-  DescriptorInternal* desc = get_ce_desc(Z_OBJCE_P(val));
-  const char* fully_qualified_name = upb_msgdef_fullname(desc->msgdef);
-  size_t type_url_len =
-      strlen(TYPE_URL_PREFIX) + strlen(fully_qualified_name) + 1;
-  char* type_url = ALLOC_N(char, type_url_len);
-  sprintf(type_url, "%s%s", TYPE_URL_PREFIX, fully_qualified_name);
-  zval type_url_php;
-  PHP_PROTO_ZVAL_STRING(&type_url_php, type_url, 1);
-  PHP_PROTO_ZVAL_STRING(&member, "type_url", 1);
-
-  PHP_PROTO_FAKE_SCOPE_RESTART(any_type);
-  message_handlers->write_property(getThis(), &member, &type_url_php,
-                                   NULL PHP_PROTO_TSRMLS_CC);
-  zval_dtor(&type_url_php);
-  zval_dtor(&member);
-  PHP_PROTO_FAKE_SCOPE_END;
-  FREE(type_url);
+  msg = (Message*)Z_OBJ_P(val);
+
+  // Serialize and set value.
+  value.data = upb_encode(msg->msg, upb_msgdef_layout(msg->desc->msgdef), arena,
+                          &value.size);
+  Message_setval(intern, "value", StringVal(value));
+
+  // Set type url: type_url_prefix + fully_qualified_name
+  full_name = upb_msgdef_fullname(msg->desc->msgdef);
+  type_url.size = strlen(TYPE_URL_PREFIX) + strlen(full_name);
+  buf = upb_arena_malloc(arena, type_url.size + 1);
+  memcpy(buf, TYPE_URL_PREFIX, strlen(TYPE_URL_PREFIX));
+  memcpy(buf + strlen(TYPE_URL_PREFIX), full_name, strlen(full_name));
+  type_url.data = buf;
+  Message_setval(intern, "type_url", StringVal(type_url));
 }
 }
 
 
-PHP_METHOD(Any, is) {
+PHP_METHOD(google_protobuf_Any, is) {
+  Message* intern = (Message*)Z_OBJ_P(getThis());
+  upb_strview type_url = Message_getval(intern, "type_url").str_val;
   zend_class_entry *klass = NULL;
   zend_class_entry *klass = NULL;
+  const upb_msgdef *m;
 
 
   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "C", &klass) ==
   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "C", &klass) ==
       FAILURE) {
       FAILURE) {
     return;
     return;
   }
   }
 
 
-  DescriptorInternal* desc = get_ce_desc(klass);
-  if (desc == NULL) {
+  m = NameMap_GetMessage(klass);
+
+  if (m == NULL) {
     RETURN_BOOL(false);
     RETURN_BOOL(false);
   }
   }
 
 
-  // Create corresponded type url.
-  const char* fully_qualified_name = upb_msgdef_fullname(desc->msgdef);
-  size_t type_url_len =
-      strlen(TYPE_URL_PREFIX) + strlen(fully_qualified_name) + 1;
-  char* type_url = ALLOC_N(char, type_url_len);
-  sprintf(type_url, "%s%s", TYPE_URL_PREFIX, fully_qualified_name);
-
-  // Fetch stored type url.
-  zval member;
-  PHP_PROTO_ZVAL_STRING(&member, "type_url", 1);
-  PHP_PROTO_FAKE_SCOPE_BEGIN(any_type);
-  zval* value =
-      php_proto_message_read_property(getThis(), &member PHP_PROTO_TSRMLS_CC);
-  zval_dtor(&member);
-  PHP_PROTO_FAKE_SCOPE_END;
-
-  // Compare two type url.
-  bool is = strcmp(type_url, Z_STRVAL_P(value)) == 0;
-  FREE(type_url);
-
-  RETURN_BOOL(is);
+  RETURN_BOOL(TryStripUrlPrefix(&type_url) &&
+              StrViewEq(type_url, upb_msgdef_fullname(m)));
+}
+
+PHP_METHOD(google_protobuf_Timestamp, fromDateTime) {
+  Message* intern = (Message*)Z_OBJ_P(getThis());
+  zval* datetime;
+  const char *classname = "\\DatetimeInterface";
+  zend_string *classname_str = zend_string_init(classname, strlen(classname), 0);
+  zend_class_entry *date_interface_ce = zend_lookup_class(classname_str);
+
+  if (date_interface_ce == NULL) {
+    zend_error(E_ERROR, "Make sure date extension is enabled.");
+    return;
+  }
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &datetime,
+                            date_interface_ce) == FAILURE) {
+    zend_error(E_USER_ERROR, "Expect DatetimeInterface.");
+    return;
+  }
+
+  upb_msgval timestamp_seconds;
+  {
+    zval retval;
+    zval function_name;
+
+    ZVAL_STRING(&function_name, "date_timestamp_get");
+
+    if (call_user_function(EG(function_table), NULL, &function_name, &retval, 1,
+                           datetime) == FAILURE ||
+        !Convert_PhpToUpb(&retval, &timestamp_seconds, UPB_TYPE_INT64, NULL,
+                          NULL)) {
+      zend_error(E_ERROR, "Cannot get timestamp from DateTime.");
+      return;
+    }
+
+    zval_dtor(&retval);
+    zval_dtor(&function_name);
+  }
+
+  upb_msgval timestamp_nanos;
+  {
+    zval retval;
+    zval function_name;
+    zval format_string;
+
+    ZVAL_STRING(&function_name, "date_format");
+    ZVAL_STRING(&format_string, "u");
+
+    zval params[2] = {
+        *datetime,
+        format_string,
+    };
+
+    if (call_user_function(EG(function_table), NULL, &function_name, &retval, 2,
+                           params) == FAILURE ||
+        !Convert_PhpToUpb(&retval, &timestamp_nanos, UPB_TYPE_INT32, NULL,
+                          NULL)) {
+      zend_error(E_ERROR, "Cannot format DateTime.");
+      return;
+    }
+
+    timestamp_nanos.int32_val *= 1000;
+
+    zval_dtor(&retval);
+    zval_dtor(&function_name);
+    zval_dtor(&format_string);
+  }
+
+  Message_setval(intern, "seconds", timestamp_seconds);
+  Message_setval(intern, "nanos", timestamp_nanos);
+
+  RETURN_NULL();
+}
+
+PHP_METHOD(google_protobuf_Timestamp, toDateTime) {
+  Message* intern = (Message*)Z_OBJ_P(getThis());
+  upb_msgval seconds = Message_getval(intern, "seconds");
+  upb_msgval nanos = Message_getval(intern, "nanos");
+
+  // Get formatted time string.
+  char formatted_time[32];
+  snprintf(formatted_time, sizeof(formatted_time), "%" PRId64 ".%06" PRId32,
+           seconds.int64_val, nanos.int32_val / 1000);
+
+  // Create Datetime object.
+  zval datetime;
+  zval function_name;
+  zval format_string;
+  zval formatted_time_php;
+
+  ZVAL_STRING(&function_name, "date_create_from_format");
+  ZVAL_STRING(&format_string, "U.u");
+  ZVAL_STRING(&formatted_time_php, formatted_time);
+
+  zval params[2] = {
+    format_string,
+    formatted_time_php,
+  };
+
+  if (call_user_function(EG(function_table), NULL, &function_name, &datetime, 2,
+                         params) == FAILURE) {
+    zend_error(E_ERROR, "Cannot create DateTime.");
+    return;
+  }
+
+  zval_dtor(&function_name);
+  zval_dtor(&format_string);
+  zval_dtor(&formatted_time_php);
+
+  ZVAL_OBJ(return_value, Z_OBJ(datetime));
 }
 }
-*/
 
 
 #include "wkt.inc"
 #include "wkt.inc"
 
 

+ 312 - 0
php/ext/google/protobuf/wkt.inc

@@ -110,6 +110,8 @@ static zend_function_entry google_protobuf_Timestamp_phpmethods[] = {
   PHP_ME(google_protobuf_Timestamp, setSeconds, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Timestamp, setSeconds, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Timestamp, getNanos, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Timestamp, getNanos, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Timestamp, setNanos, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Timestamp, setNanos, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(google_protobuf_Timestamp, fromDateTime, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(google_protobuf_Timestamp, toDateTime, NULL, ZEND_ACC_PUBLIC)
   ZEND_FE_END
   ZEND_FE_END
 };
 };
 
 
@@ -724,6 +726,8 @@ static zend_function_entry google_protobuf_Any_phpmethods[] = {
   PHP_ME(google_protobuf_Any, setTypeUrl, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Any, setTypeUrl, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Any, getValue, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Any, getValue, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Any, setValue, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Any, setValue, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(google_protobuf_Any, is, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(google_protobuf_Any, pack, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Any, unpack, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Any, unpack, NULL, ZEND_ACC_PUBLIC)
   ZEND_FE_END
   ZEND_FE_END
 };
 };
@@ -1979,6 +1983,174 @@ static void google_protobuf_Field_ModuleInit() {
   zend_do_inheritance(google_protobuf_Field_ce, message_ce);
   zend_do_inheritance(google_protobuf_Field_ce, message_ce);
 }
 }
 
 
+/* google_protobuf_Field_Kind */
+
+zend_class_entry* google_protobuf_Field_Kind_ce;
+
+PHP_METHOD(google_protobuf_Field_Kind, name) {
+  google_protobuf_type_proto_AddDescriptor();
+  const upb_symtab *symtab = DescriptorPool_GetSymbolTable();
+  const upb_enumdef *e = upb_symtab_lookupenum(symtab, "google.protobuf.Field.Kind");
+  const char *name;
+  zend_long value;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &value) ==
+      FAILURE) {
+    return;
+  }
+  name = upb_enumdef_iton(e, value);
+  if (!name) {
+    zend_throw_exception_ex(NULL, 0,
+                            "Google\\Protobuf\\Field\\Kind has no name "
+                            "defined for value " ZEND_LONG_FMT ".",
+                            value);
+    return;
+  }
+  RETURN_STRING(name);
+}
+
+PHP_METHOD(google_protobuf_Field_Kind, value) {
+  google_protobuf_type_proto_AddDescriptor();
+  const upb_symtab *symtab = DescriptorPool_GetSymbolTable();
+  const upb_enumdef *e = upb_symtab_lookupenum(symtab, "google.protobuf.Field.Kind");
+  char *name = NULL;
+  size_t name_len;
+  int32_t num;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name,
+                            &name_len) == FAILURE) {
+    return;
+  }
+  if (!upb_enumdef_ntoi(e, name, name_len, &num)) {
+    zend_throw_exception_ex(NULL, 0,
+                            "Google\\Protobuf\\Field\\Kind has no value "
+                            "defined for name %s.",
+                            name);
+    return;
+  }
+  RETURN_LONG(num);
+}
+
+static zend_function_entry google_protobuf_Field_Kind_phpmethods[] = {
+  PHP_ME(google_protobuf_Field_Kind, name, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
+  PHP_ME(google_protobuf_Field_Kind, value, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
+  ZEND_FE_END
+};
+
+static void google_protobuf_Field_Kind_ModuleInit() {
+  zend_class_entry tmp_ce;
+
+  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Field\\Kind",
+                   google_protobuf_Field_Kind_phpmethods);
+
+  google_protobuf_Field_Kind_ce = zend_register_internal_class(&tmp_ce);
+  zend_declare_class_constant_long(google_protobuf_Field_Kind_ce, "TYPE_UNKNOWN",
+                                   strlen("TYPE_UNKNOWN"), 0);
+  zend_declare_class_constant_long(google_protobuf_Field_Kind_ce, "TYPE_DOUBLE",
+                                   strlen("TYPE_DOUBLE"), 1);
+  zend_declare_class_constant_long(google_protobuf_Field_Kind_ce, "TYPE_FLOAT",
+                                   strlen("TYPE_FLOAT"), 2);
+  zend_declare_class_constant_long(google_protobuf_Field_Kind_ce, "TYPE_INT64",
+                                   strlen("TYPE_INT64"), 3);
+  zend_declare_class_constant_long(google_protobuf_Field_Kind_ce, "TYPE_UINT64",
+                                   strlen("TYPE_UINT64"), 4);
+  zend_declare_class_constant_long(google_protobuf_Field_Kind_ce, "TYPE_INT32",
+                                   strlen("TYPE_INT32"), 5);
+  zend_declare_class_constant_long(google_protobuf_Field_Kind_ce, "TYPE_FIXED64",
+                                   strlen("TYPE_FIXED64"), 6);
+  zend_declare_class_constant_long(google_protobuf_Field_Kind_ce, "TYPE_FIXED32",
+                                   strlen("TYPE_FIXED32"), 7);
+  zend_declare_class_constant_long(google_protobuf_Field_Kind_ce, "TYPE_BOOL",
+                                   strlen("TYPE_BOOL"), 8);
+  zend_declare_class_constant_long(google_protobuf_Field_Kind_ce, "TYPE_STRING",
+                                   strlen("TYPE_STRING"), 9);
+  zend_declare_class_constant_long(google_protobuf_Field_Kind_ce, "TYPE_GROUP",
+                                   strlen("TYPE_GROUP"), 10);
+  zend_declare_class_constant_long(google_protobuf_Field_Kind_ce, "TYPE_MESSAGE",
+                                   strlen("TYPE_MESSAGE"), 11);
+  zend_declare_class_constant_long(google_protobuf_Field_Kind_ce, "TYPE_BYTES",
+                                   strlen("TYPE_BYTES"), 12);
+  zend_declare_class_constant_long(google_protobuf_Field_Kind_ce, "TYPE_UINT32",
+                                   strlen("TYPE_UINT32"), 13);
+  zend_declare_class_constant_long(google_protobuf_Field_Kind_ce, "TYPE_ENUM",
+                                   strlen("TYPE_ENUM"), 14);
+  zend_declare_class_constant_long(google_protobuf_Field_Kind_ce, "TYPE_SFIXED32",
+                                   strlen("TYPE_SFIXED32"), 15);
+  zend_declare_class_constant_long(google_protobuf_Field_Kind_ce, "TYPE_SFIXED64",
+                                   strlen("TYPE_SFIXED64"), 16);
+  zend_declare_class_constant_long(google_protobuf_Field_Kind_ce, "TYPE_SINT32",
+                                   strlen("TYPE_SINT32"), 17);
+  zend_declare_class_constant_long(google_protobuf_Field_Kind_ce, "TYPE_SINT64",
+                                   strlen("TYPE_SINT64"), 18);
+}
+
+/* google_protobuf_Field_Cardinality */
+
+zend_class_entry* google_protobuf_Field_Cardinality_ce;
+
+PHP_METHOD(google_protobuf_Field_Cardinality, name) {
+  google_protobuf_type_proto_AddDescriptor();
+  const upb_symtab *symtab = DescriptorPool_GetSymbolTable();
+  const upb_enumdef *e = upb_symtab_lookupenum(symtab, "google.protobuf.Field.Cardinality");
+  const char *name;
+  zend_long value;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &value) ==
+      FAILURE) {
+    return;
+  }
+  name = upb_enumdef_iton(e, value);
+  if (!name) {
+    zend_throw_exception_ex(NULL, 0,
+                            "Google\\Protobuf\\Field\\Cardinality has no name "
+                            "defined for value " ZEND_LONG_FMT ".",
+                            value);
+    return;
+  }
+  RETURN_STRING(name);
+}
+
+PHP_METHOD(google_protobuf_Field_Cardinality, value) {
+  google_protobuf_type_proto_AddDescriptor();
+  const upb_symtab *symtab = DescriptorPool_GetSymbolTable();
+  const upb_enumdef *e = upb_symtab_lookupenum(symtab, "google.protobuf.Field.Cardinality");
+  char *name = NULL;
+  size_t name_len;
+  int32_t num;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name,
+                            &name_len) == FAILURE) {
+    return;
+  }
+  if (!upb_enumdef_ntoi(e, name, name_len, &num)) {
+    zend_throw_exception_ex(NULL, 0,
+                            "Google\\Protobuf\\Field\\Cardinality has no value "
+                            "defined for name %s.",
+                            name);
+    return;
+  }
+  RETURN_LONG(num);
+}
+
+static zend_function_entry google_protobuf_Field_Cardinality_phpmethods[] = {
+  PHP_ME(google_protobuf_Field_Cardinality, name, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
+  PHP_ME(google_protobuf_Field_Cardinality, value, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
+  ZEND_FE_END
+};
+
+static void google_protobuf_Field_Cardinality_ModuleInit() {
+  zend_class_entry tmp_ce;
+
+  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Field\\Cardinality",
+                   google_protobuf_Field_Cardinality_phpmethods);
+
+  google_protobuf_Field_Cardinality_ce = zend_register_internal_class(&tmp_ce);
+  zend_declare_class_constant_long(google_protobuf_Field_Cardinality_ce, "CARDINALITY_UNKNOWN",
+                                   strlen("CARDINALITY_UNKNOWN"), 0);
+  zend_declare_class_constant_long(google_protobuf_Field_Cardinality_ce, "CARDINALITY_OPTIONAL",
+                                   strlen("CARDINALITY_OPTIONAL"), 1);
+  zend_declare_class_constant_long(google_protobuf_Field_Cardinality_ce, "CARDINALITY_REQUIRED",
+                                   strlen("CARDINALITY_REQUIRED"), 2);
+  zend_declare_class_constant_long(google_protobuf_Field_Cardinality_ce, "CARDINALITY_REPEATED",
+                                   strlen("CARDINALITY_REPEATED"), 3);
+}
+
 /* google_protobuf_Enum */
 /* google_protobuf_Enum */
 
 
 zend_class_entry* google_protobuf_Enum_ce;
 zend_class_entry* google_protobuf_Enum_ce;
@@ -2297,6 +2469,71 @@ static void google_protobuf_Option_ModuleInit() {
   zend_do_inheritance(google_protobuf_Option_ce, message_ce);
   zend_do_inheritance(google_protobuf_Option_ce, message_ce);
 }
 }
 
 
+/* google_protobuf_Syntax */
+
+zend_class_entry* google_protobuf_Syntax_ce;
+
+PHP_METHOD(google_protobuf_Syntax, name) {
+  google_protobuf_type_proto_AddDescriptor();
+  const upb_symtab *symtab = DescriptorPool_GetSymbolTable();
+  const upb_enumdef *e = upb_symtab_lookupenum(symtab, "google.protobuf.Syntax");
+  const char *name;
+  zend_long value;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &value) ==
+      FAILURE) {
+    return;
+  }
+  name = upb_enumdef_iton(e, value);
+  if (!name) {
+    zend_throw_exception_ex(NULL, 0,
+                            "Google\\Protobuf\\Syntax has no name "
+                            "defined for value " ZEND_LONG_FMT ".",
+                            value);
+    return;
+  }
+  RETURN_STRING(name);
+}
+
+PHP_METHOD(google_protobuf_Syntax, value) {
+  google_protobuf_type_proto_AddDescriptor();
+  const upb_symtab *symtab = DescriptorPool_GetSymbolTable();
+  const upb_enumdef *e = upb_symtab_lookupenum(symtab, "google.protobuf.Syntax");
+  char *name = NULL;
+  size_t name_len;
+  int32_t num;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name,
+                            &name_len) == FAILURE) {
+    return;
+  }
+  if (!upb_enumdef_ntoi(e, name, name_len, &num)) {
+    zend_throw_exception_ex(NULL, 0,
+                            "Google\\Protobuf\\Syntax has no value "
+                            "defined for name %s.",
+                            name);
+    return;
+  }
+  RETURN_LONG(num);
+}
+
+static zend_function_entry google_protobuf_Syntax_phpmethods[] = {
+  PHP_ME(google_protobuf_Syntax, name, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
+  PHP_ME(google_protobuf_Syntax, value, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
+  ZEND_FE_END
+};
+
+static void google_protobuf_Syntax_ModuleInit() {
+  zend_class_entry tmp_ce;
+
+  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Syntax",
+                   google_protobuf_Syntax_phpmethods);
+
+  google_protobuf_Syntax_ce = zend_register_internal_class(&tmp_ce);
+  zend_declare_class_constant_long(google_protobuf_Syntax_ce, "SYNTAX_PROTO2",
+                                   strlen("SYNTAX_PROTO2"), 0);
+  zend_declare_class_constant_long(google_protobuf_Syntax_ce, "SYNTAX_PROTO3",
+                                   strlen("SYNTAX_PROTO3"), 1);
+}
+
 /* google/protobuf/struct.proto */
 /* google/protobuf/struct.proto */
 
 
 zend_class_entry* GPBMetadata_Google_Protobuf_Struct_ce;
 zend_class_entry* GPBMetadata_Google_Protobuf_Struct_ce;
@@ -2619,6 +2856,13 @@ static PHP_METHOD(google_protobuf_Value, setListValue) {
   RETURN_ZVAL(getThis(), 1, 0);
   RETURN_ZVAL(getThis(), 1, 0);
 }
 }
 
 
+static PHP_METHOD(google_protobuf_Value, getKind) {
+  Message* intern = (Message*)Z_OBJ_P(getThis());
+  const upb_oneofdef *oneof = upb_msgdef_ntooz(intern->desc->msgdef,
+                                              "kind");
+  const upb_fielddef *field = upb_msg_whichoneof(intern->msg, oneof);
+  RETURN_STRING(field ? upb_fielddef_name(field) : "");
+}
 static zend_function_entry google_protobuf_Value_phpmethods[] = {
 static zend_function_entry google_protobuf_Value_phpmethods[] = {
   PHP_ME(google_protobuf_Value, __construct, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Value, __construct, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Value, getNullValue, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Value, getNullValue, NULL, ZEND_ACC_PUBLIC)
@@ -2633,6 +2877,7 @@ static zend_function_entry google_protobuf_Value_phpmethods[] = {
   PHP_ME(google_protobuf_Value, setStructValue, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Value, setStructValue, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Value, getListValue, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Value, getListValue, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Value, setListValue, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(google_protobuf_Value, setListValue, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(google_protobuf_Value, getKind, NULL, ZEND_ACC_PUBLIC)
   ZEND_FE_END
   ZEND_FE_END
 };
 };
 
 
@@ -2698,6 +2943,69 @@ static void google_protobuf_ListValue_ModuleInit() {
   zend_do_inheritance(google_protobuf_ListValue_ce, message_ce);
   zend_do_inheritance(google_protobuf_ListValue_ce, message_ce);
 }
 }
 
 
+/* google_protobuf_NullValue */
+
+zend_class_entry* google_protobuf_NullValue_ce;
+
+PHP_METHOD(google_protobuf_NullValue, name) {
+  google_protobuf_struct_proto_AddDescriptor();
+  const upb_symtab *symtab = DescriptorPool_GetSymbolTable();
+  const upb_enumdef *e = upb_symtab_lookupenum(symtab, "google.protobuf.NullValue");
+  const char *name;
+  zend_long value;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &value) ==
+      FAILURE) {
+    return;
+  }
+  name = upb_enumdef_iton(e, value);
+  if (!name) {
+    zend_throw_exception_ex(NULL, 0,
+                            "Google\\Protobuf\\NullValue has no name "
+                            "defined for value " ZEND_LONG_FMT ".",
+                            value);
+    return;
+  }
+  RETURN_STRING(name);
+}
+
+PHP_METHOD(google_protobuf_NullValue, value) {
+  google_protobuf_struct_proto_AddDescriptor();
+  const upb_symtab *symtab = DescriptorPool_GetSymbolTable();
+  const upb_enumdef *e = upb_symtab_lookupenum(symtab, "google.protobuf.NullValue");
+  char *name = NULL;
+  size_t name_len;
+  int32_t num;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name,
+                            &name_len) == FAILURE) {
+    return;
+  }
+  if (!upb_enumdef_ntoi(e, name, name_len, &num)) {
+    zend_throw_exception_ex(NULL, 0,
+                            "Google\\Protobuf\\NullValue has no value "
+                            "defined for name %s.",
+                            name);
+    return;
+  }
+  RETURN_LONG(num);
+}
+
+static zend_function_entry google_protobuf_NullValue_phpmethods[] = {
+  PHP_ME(google_protobuf_NullValue, name, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
+  PHP_ME(google_protobuf_NullValue, value, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
+  ZEND_FE_END
+};
+
+static void google_protobuf_NullValue_ModuleInit() {
+  zend_class_entry tmp_ce;
+
+  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\NullValue",
+                   google_protobuf_NullValue_phpmethods);
+
+  google_protobuf_NullValue_ce = zend_register_internal_class(&tmp_ce);
+  zend_declare_class_constant_long(google_protobuf_NullValue_ce, "NULL_VALUE",
+                                   strlen("NULL_VALUE"), 0);
+}
+
 /* google/protobuf/source_context.proto */
 /* google/protobuf/source_context.proto */
 
 
 zend_class_entry* GPBMetadata_Google_Protobuf_SourceContext_ce;
 zend_class_entry* GPBMetadata_Google_Protobuf_SourceContext_ce;
@@ -2905,14 +3213,18 @@ static void WellKnownTypes_ModuleInit() {
   GPBMetadata_Google_Protobuf_Type_ModuleInit();
   GPBMetadata_Google_Protobuf_Type_ModuleInit();
   google_protobuf_Type_ModuleInit();
   google_protobuf_Type_ModuleInit();
   google_protobuf_Field_ModuleInit();
   google_protobuf_Field_ModuleInit();
+  google_protobuf_Field_Kind_ModuleInit();
+  google_protobuf_Field_Cardinality_ModuleInit();
   google_protobuf_Enum_ModuleInit();
   google_protobuf_Enum_ModuleInit();
   google_protobuf_EnumValue_ModuleInit();
   google_protobuf_EnumValue_ModuleInit();
   google_protobuf_Option_ModuleInit();
   google_protobuf_Option_ModuleInit();
+  google_protobuf_Syntax_ModuleInit();
   GPBMetadata_Google_Protobuf_Struct_ModuleInit();
   GPBMetadata_Google_Protobuf_Struct_ModuleInit();
   google_protobuf_Struct_ModuleInit();
   google_protobuf_Struct_ModuleInit();
   google_protobuf_Struct_FieldsEntry_ModuleInit();
   google_protobuf_Struct_FieldsEntry_ModuleInit();
   google_protobuf_Value_ModuleInit();
   google_protobuf_Value_ModuleInit();
   google_protobuf_ListValue_ModuleInit();
   google_protobuf_ListValue_ModuleInit();
+  google_protobuf_NullValue_ModuleInit();
   GPBMetadata_Google_Protobuf_SourceContext_ModuleInit();
   GPBMetadata_Google_Protobuf_SourceContext_ModuleInit();
   google_protobuf_SourceContext_ModuleInit();
   google_protobuf_SourceContext_ModuleInit();
   GPBMetadata_Google_Protobuf_FieldMask_ModuleInit();
   GPBMetadata_Google_Protobuf_FieldMask_ModuleInit();

+ 11 - 0
php/generate_descriptor_protos.sh

@@ -13,4 +13,15 @@ fi
 
 
 pushd src
 pushd src
 ./protoc --php_out=internal:../php/src google/protobuf/descriptor.proto
 ./protoc --php_out=internal:../php/src google/protobuf/descriptor.proto
+./protoc --php_out=internal_generate_c_wkt:src \
+  google/protobuf/any.proto \
+  google/protobuf/api.proto \
+  google/protobuf/duration.proto \
+  google/protobuf/empty.proto \
+  google/protobuf/field_mask.proto \
+  google/protobuf/source_context.proto \
+  google/protobuf/struct.proto \
+  google/protobuf/type.proto \
+  google/protobuf/timestamp.proto \
+  google/protobuf/wrappers.proto
 popd
 popd

+ 145 - 3
src/google/protobuf/compiler/php/php_generator.cc

@@ -1815,6 +1815,91 @@ std::string FilenameCName(const FileDescriptor* file) {
   return c_name;
   return c_name;
 }
 }
 
 
+void GenerateCEnum(const EnumDescriptor* desc, io::Printer* printer) {
+  std::string c_name = desc->full_name();
+  c_name = StringReplace(c_name, ".", "_", true);
+  std::string php_name = FullClassName(desc, Options());
+  php_name = StringReplace(php_name, "\\", "\\\\", true);
+  printer->Print(
+      "/* $c_name$ */\n"
+      "\n"
+      "zend_class_entry* $c_name$_ce;\n"
+      "\n"
+      "PHP_METHOD($c_name$, name) {\n"
+      "  $file_c_name$_AddDescriptor();\n"
+      "  const upb_symtab *symtab = DescriptorPool_GetSymbolTable();\n"
+      "  const upb_enumdef *e = upb_symtab_lookupenum(symtab, \"$name$\");\n"
+      "  const char *name;\n"
+      "  zend_long value;\n"
+      "  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, \"l\", &value) ==\n"
+      "      FAILURE) {\n"
+      "    return;\n"
+      "  }\n"
+      "  name = upb_enumdef_iton(e, value);\n"
+      "  if (!name) {\n"
+      "    zend_throw_exception_ex(NULL, 0,\n"
+      "                            \"$php_name$ has no name \"\n"
+      "                            \"defined for value \" ZEND_LONG_FMT \".\",\n"
+      "                            value);\n"
+      "    return;\n"
+      "  }\n"
+      "  RETURN_STRING(name);\n"
+      "}\n"
+      "\n"
+      "PHP_METHOD($c_name$, value) {\n"
+      "  $file_c_name$_AddDescriptor();\n"
+      "  const upb_symtab *symtab = DescriptorPool_GetSymbolTable();\n"
+      "  const upb_enumdef *e = upb_symtab_lookupenum(symtab, \"$name$\");\n"
+      "  char *name = NULL;\n"
+      "  size_t name_len;\n"
+      "  int32_t num;\n"
+      "  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, \"s\", &name,\n"
+      "                            &name_len) == FAILURE) {\n"
+      "    return;\n"
+      "  }\n"
+      "  if (!upb_enumdef_ntoi(e, name, name_len, &num)) {\n"
+      "    zend_throw_exception_ex(NULL, 0,\n"
+      "                            \"$php_name$ has no value \"\n"
+      "                            \"defined for name %s.\",\n"
+      "                            name);\n"
+      "    return;\n"
+      "  }\n"
+      "  RETURN_LONG(num);\n"
+      "}\n"
+      "\n"
+      "static zend_function_entry $c_name$_phpmethods[] = {\n"
+      "  PHP_ME($c_name$, name, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n"
+      "  PHP_ME($c_name$, value, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n"
+      "  ZEND_FE_END\n"
+      "};\n"
+      "\n"
+      "static void $c_name$_ModuleInit() {\n"
+      "  zend_class_entry tmp_ce;\n"
+      "\n"
+      "  INIT_CLASS_ENTRY(tmp_ce, \"$php_name$\",\n"
+      "                   $c_name$_phpmethods);\n"
+      "\n"
+      "  $c_name$_ce = zend_register_internal_class(&tmp_ce);\n",
+      "name", desc->full_name(),
+      "file_c_name", FilenameCName(desc->file()),
+      "c_name", c_name,
+      "php_name", php_name);
+
+  for (int i = 0; i < desc->value_count(); i++) {
+    const EnumValueDescriptor* value = desc->value(i);
+    printer->Print(
+        "  zend_declare_class_constant_long($c_name$_ce, \"$name$\",\n"
+        "                                   strlen(\"$name$\"), $num$);\n",
+        "c_name", c_name,
+        "name", value->name(),
+        "num", std::to_string(value->number()));
+  }
+
+  printer->Print(
+      "}\n"
+      "\n");
+}
+
 void GenerateCMessage(const Descriptor* message, io::Printer* printer) {
 void GenerateCMessage(const Descriptor* message, io::Printer* printer) {
   std::string c_name = message->full_name();
   std::string c_name = message->full_name();
   c_name = StringReplace(c_name, ".", "_", true);
   c_name = StringReplace(c_name, ".", "_", true);
@@ -1863,6 +1948,21 @@ void GenerateCMessage(const Descriptor* message, io::Printer* printer) {
       "camel_name", UnderscoresToCamelCase(field->name(), true));
       "camel_name", UnderscoresToCamelCase(field->name(), true));
   }
   }
 
 
+  for (int i = 0; i < message->real_oneof_decl_count(); i++) {
+    auto oneof = message->oneof_decl(i);
+    printer->Print(
+      "static PHP_METHOD($c_name$, get$camel_name$) {\n"
+      "  Message* intern = (Message*)Z_OBJ_P(getThis());\n"
+      "  const upb_oneofdef *oneof = upb_msgdef_ntooz(intern->desc->msgdef,\n"
+      "                                              \"$name$\");\n"
+      "  const upb_fielddef *field = upb_msg_whichoneof(intern->msg, oneof);\n"
+      "  RETURN_STRING(field ? upb_fielddef_name(field) : \"\");\n"
+      "}\n",
+      "c_name", c_name,
+      "name", oneof->name(),
+      "camel_name", UnderscoresToCamelCase(oneof->name(), true));
+  }
+
   printer->Print(
   printer->Print(
       "static zend_function_entry $c_name$_phpmethods[] = {\n"
       "static zend_function_entry $c_name$_phpmethods[] = {\n"
       "  PHP_ME($c_name$, __construct, NULL, ZEND_ACC_PUBLIC)\n",
       "  PHP_ME($c_name$, __construct, NULL, ZEND_ACC_PUBLIC)\n",
@@ -1877,12 +1977,33 @@ void GenerateCMessage(const Descriptor* message, io::Printer* printer) {
       "camel_name", UnderscoresToCamelCase(field->name(), true));
       "camel_name", UnderscoresToCamelCase(field->name(), true));
   }
   }
 
 
-  if (message->well_known_type() == Descriptor::WELLKNOWNTYPE_ANY) {
+  for (int i = 0; i < message->real_oneof_decl_count(); i++) {
+    auto oneof = message->oneof_decl(i);
     printer->Print(
     printer->Print(
-      "  PHP_ME($c_name$, unpack, NULL, ZEND_ACC_PUBLIC)\n",
-      "c_name", c_name);
+      "  PHP_ME($c_name$, get$camel_name$, NULL, ZEND_ACC_PUBLIC)\n",
+      "c_name", c_name,
+      "camel_name", UnderscoresToCamelCase(oneof->name(), true));
   }
   }
 
 
+  // Extra hand-written functions added to the well-known types.
+  switch (message->well_known_type()) {
+    case Descriptor::WELLKNOWNTYPE_ANY:
+      printer->Print(
+        "  PHP_ME($c_name$, is, NULL, ZEND_ACC_PUBLIC)\n"
+        "  PHP_ME($c_name$, pack, NULL, ZEND_ACC_PUBLIC)\n"
+        "  PHP_ME($c_name$, unpack, NULL, ZEND_ACC_PUBLIC)\n",
+        "c_name", c_name);
+      break;
+    case Descriptor::WELLKNOWNTYPE_TIMESTAMP:
+      printer->Print(
+        "  PHP_ME($c_name$, fromDateTime, NULL, ZEND_ACC_PUBLIC)\n"
+        "  PHP_ME($c_name$, toDateTime, NULL, ZEND_ACC_PUBLIC)\n",
+        "c_name", c_name);
+      break;
+    default:
+      break;
+  } 
+
   printer->Print(
   printer->Print(
       "  ZEND_FE_END\n"
       "  ZEND_FE_END\n"
       "};\n"
       "};\n"
@@ -1905,6 +2026,18 @@ void GenerateCMessage(const Descriptor* message, io::Printer* printer) {
   for (int i = 0; i < message->nested_type_count(); i++) {
   for (int i = 0; i < message->nested_type_count(); i++) {
     GenerateCMessage(message->nested_type(i), printer);
     GenerateCMessage(message->nested_type(i), printer);
   }
   }
+  for (int i = 0; i < message->enum_type_count(); i++) {
+    GenerateCEnum(message->enum_type(i), printer);
+  }
+}
+
+void GenerateEnumCInit(const EnumDescriptor* desc, io::Printer* printer) {
+  std::string c_name = desc->full_name();
+  c_name = StringReplace(c_name, ".", "_", true);
+
+  printer->Print(
+      "  $c_name$_ModuleInit();\n",
+      "c_name", c_name);
 }
 }
 
 
 void GenerateCInit(const Descriptor* message, io::Printer* printer) {
 void GenerateCInit(const Descriptor* message, io::Printer* printer) {
@@ -1918,6 +2051,9 @@ void GenerateCInit(const Descriptor* message, io::Printer* printer) {
   for (int i = 0; i < message->nested_type_count(); i++) {
   for (int i = 0; i < message->nested_type_count(); i++) {
     GenerateCInit(message->nested_type(i), printer);
     GenerateCInit(message->nested_type(i), printer);
   }
   }
+  for (int i = 0; i < message->enum_type_count(); i++) {
+    GenerateEnumCInit(message->enum_type(i), printer);
+  }
 }
 }
 
 
 void GenerateCWellKnownTypes(const std::vector<const FileDescriptor*>& files,
 void GenerateCWellKnownTypes(const std::vector<const FileDescriptor*>& files,
@@ -2011,6 +2147,9 @@ void GenerateCWellKnownTypes(const std::vector<const FileDescriptor*>& files,
     for (int i = 0; i < file->message_type_count(); i++) {
     for (int i = 0; i < file->message_type_count(); i++) {
       GenerateCMessage(file->message_type(i), &printer);
       GenerateCMessage(file->message_type(i), &printer);
     }
     }
+    for (int i = 0; i < file->enum_type_count(); i++) {
+      GenerateCEnum(file->enum_type(i), &printer);
+    }
   }
   }
 
 
   printer.Print(
   printer.Print(
@@ -2027,6 +2166,9 @@ void GenerateCWellKnownTypes(const std::vector<const FileDescriptor*>& files,
     for (int i = 0; i < file->message_type_count(); i++) {
     for (int i = 0; i < file->message_type_count(); i++) {
       GenerateCInit(file->message_type(i), &printer);
       GenerateCInit(file->message_type(i), &printer);
     }
     }
+    for (int i = 0; i < file->enum_type_count(); i++) {
+      GenerateEnumCInit(file->enum_type(i), &printer);
+    }
   }
   }
 
 
   printer.Print(
   printer.Print(