Sfoglia il codice sorgente

Expose descriptor API in php c extension (#3422)

Paul Yang 8 anni fa
parent
commit
c15a3269f9

+ 2 - 0
Makefile.am

@@ -658,6 +658,7 @@ php_EXTRA_DIST=                                                       \
   php/tests/array_test.php                                            \
   php/tests/array_test.php                                            \
   php/tests/autoload.php                                              \
   php/tests/autoload.php                                              \
   php/tests/compatibility_test.sh                                     \
   php/tests/compatibility_test.sh                                     \
+  php/tests/descriptors_test.php                                      \
   php/tests/encode_decode_test.php                                    \
   php/tests/encode_decode_test.php                                    \
   php/tests/gdb_test.sh                                               \
   php/tests/gdb_test.sh                                               \
   php/tests/generated_class_test.php                                  \
   php/tests/generated_class_test.php                                  \
@@ -666,6 +667,7 @@ php_EXTRA_DIST=                                                       \
   php/tests/map_field_test.php                                        \
   php/tests/map_field_test.php                                        \
   php/tests/memory_leak_test.php                                      \
   php/tests/memory_leak_test.php                                      \
   php/tests/php_implementation_test.php                               \
   php/tests/php_implementation_test.php                               \
+  php/tests/proto/test_descriptors.proto                              \
   php/tests/proto/test_empty_php_namespace.proto                      \
   php/tests/proto/test_empty_php_namespace.proto                      \
   php/tests/proto/test_import_descriptor_proto.proto                  \
   php/tests/proto/test_import_descriptor_proto.proto                  \
   php/tests/proto/test_include.proto                                  \
   php/tests/proto/test_include.proto                                  \

+ 466 - 15
php/ext/google/protobuf/def.c

@@ -37,12 +37,27 @@ const int kReservedNamesSize = 3;
 static void descriptor_init_c_instance(Descriptor* intern TSRMLS_DC);
 static void descriptor_init_c_instance(Descriptor* intern TSRMLS_DC);
 static void descriptor_free_c(Descriptor* object TSRMLS_DC);
 static void descriptor_free_c(Descriptor* object TSRMLS_DC);
 
 
+static void field_descriptor_init_c_instance(FieldDescriptor* intern TSRMLS_DC);
+static void field_descriptor_free_c(FieldDescriptor* object TSRMLS_DC);
+
 static void enum_descriptor_init_c_instance(EnumDescriptor* intern TSRMLS_DC);
 static void enum_descriptor_init_c_instance(EnumDescriptor* intern TSRMLS_DC);
 static void enum_descriptor_free_c(EnumDescriptor* object TSRMLS_DC);
 static void enum_descriptor_free_c(EnumDescriptor* object TSRMLS_DC);
 
 
+static void enum_value_descriptor_init_c_instance(
+    EnumValueDescriptor *intern TSRMLS_DC);
+static void enum_value_descriptor_free_c(EnumValueDescriptor *object TSRMLS_DC);
+
 static void descriptor_pool_free_c(DescriptorPool* object TSRMLS_DC);
 static void descriptor_pool_free_c(DescriptorPool* object TSRMLS_DC);
 static void descriptor_pool_init_c_instance(DescriptorPool* pool TSRMLS_DC);
 static void descriptor_pool_init_c_instance(DescriptorPool* pool TSRMLS_DC);
 
 
+static void internal_descriptor_pool_free_c(
+    InternalDescriptorPool *object TSRMLS_DC);
+static void internal_descriptor_pool_init_c_instance(
+    InternalDescriptorPool *pool TSRMLS_DC);
+
+static void oneof_descriptor_free_c(Oneof* object TSRMLS_DC);
+static void oneof_descriptor_init_c_instance(Oneof* pool TSRMLS_DC);
+
 // -----------------------------------------------------------------------------
 // -----------------------------------------------------------------------------
 // Common Utilities
 // Common Utilities
 // -----------------------------------------------------------------------------
 // -----------------------------------------------------------------------------
@@ -169,10 +184,15 @@ void gpb_type_init(TSRMLS_D) {
 // -----------------------------------------------------------------------------
 // -----------------------------------------------------------------------------
 
 
 static zend_function_entry descriptor_methods[] = {
 static zend_function_entry descriptor_methods[] = {
+  PHP_ME(Descriptor, getFullName, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Descriptor, getField, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Descriptor, getFieldCount, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Descriptor, getOneofDecl, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Descriptor, getOneofDeclCount, NULL, ZEND_ACC_PUBLIC)
   ZEND_FE_END
   ZEND_FE_END
 };
 };
 
 
-DEFINE_CLASS(Descriptor, descriptor, "Google\\Protobuf\\Internal\\Descriptor");
+DEFINE_CLASS(Descriptor, descriptor, "Google\\Protobuf\\Descriptor");
 
 
 static void descriptor_free_c(Descriptor *self TSRMLS_DC) {
 static void descriptor_free_c(Descriptor *self TSRMLS_DC) {
   if (self->layout) {
   if (self->layout) {
@@ -203,7 +223,6 @@ static void descriptor_free_c(Descriptor *self TSRMLS_DC) {
 }
 }
 
 
 static void descriptor_init_c_instance(Descriptor *desc TSRMLS_DC) {
 static void descriptor_init_c_instance(Descriptor *desc TSRMLS_DC) {
-  // zend_object_std_init(&desc->std, descriptor_type TSRMLS_CC);
   desc->msgdef = NULL;
   desc->msgdef = NULL;
   desc->layout = NULL;
   desc->layout = NULL;
   desc->klass = NULL;
   desc->klass = NULL;
@@ -215,30 +234,207 @@ static void descriptor_init_c_instance(Descriptor *desc TSRMLS_DC) {
   desc->json_serialize_handlers_preserve = NULL;
   desc->json_serialize_handlers_preserve = NULL;
 }
 }
 
 
+PHP_METHOD(Descriptor, getFullName) {
+  Descriptor *intern = UNBOX(Descriptor, getThis());
+  const char* fullname = upb_msgdef_fullname(intern->msgdef);
+  PHP_PROTO_RETVAL_STRINGL(fullname, strlen(fullname), 1);
+}
+
+PHP_METHOD(Descriptor, getField) {
+  long index;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
+      FAILURE) {
+    zend_error(E_USER_ERROR, "Expect integer for index.\n");
+    return;
+  }
+
+  Descriptor *intern = UNBOX(Descriptor, getThis());
+  int field_num = upb_msgdef_numfields(intern->msgdef);
+  if (index < 0 || index >= field_num) {
+    zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index);
+    return;
+  }
+
+  upb_msg_field_iter iter;
+  int i;
+  for(upb_msg_field_begin(&iter, intern->msgdef), i = 0;
+      !upb_msg_field_done(&iter) && i < index;
+      upb_msg_field_next(&iter), i++);
+  const upb_fielddef *field = upb_msg_iter_field(&iter);
+
+  PHP_PROTO_HASHTABLE_VALUE field_hashtable_value = get_def_obj(field);
+  if (field_hashtable_value == NULL) {
+#if PHP_MAJOR_VERSION < 7
+    MAKE_STD_ZVAL(field_hashtable_value);
+    ZVAL_OBJ(field_hashtable_value, field_descriptor_type->create_object(
+                                        field_descriptor_type TSRMLS_CC));
+#else
+    field_hashtable_value =
+        field_descriptor_type->create_object(field_descriptor_type TSRMLS_CC);
+#endif
+    FieldDescriptor *field_php =
+        UNBOX_HASHTABLE_VALUE(FieldDescriptor, field_hashtable_value);
+    field_php->fielddef = field;
+    add_def_obj(field, field_hashtable_value);
+  }
+
+#if PHP_MAJOR_VERSION < 7
+  RETURN_ZVAL(field_hashtable_value, 1, 0);
+#else
+  ++GC_REFCOUNT(field_hashtable_value);
+  RETURN_OBJ(field_hashtable_value);
+#endif
+}
+
+PHP_METHOD(Descriptor, getFieldCount) {
+  Descriptor *intern = UNBOX(Descriptor, getThis());
+  RETURN_LONG(upb_msgdef_numfields(intern->msgdef));
+}
+
+PHP_METHOD(Descriptor, getOneofDecl) {
+  long index;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
+      FAILURE) {
+    zend_error(E_USER_ERROR, "Expect integer for index.\n");
+    return;
+  }
+
+  Descriptor *intern = UNBOX(Descriptor, getThis());
+  int field_num = upb_msgdef_numoneofs(intern->msgdef);
+  if (index < 0 || index >= field_num) {
+    zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index);
+    return;
+  }
+
+  upb_msg_oneof_iter iter;
+  int i;
+  for(upb_msg_oneof_begin(&iter, intern->msgdef), i = 0;
+      !upb_msg_oneof_done(&iter) && i < index;
+      upb_msg_oneof_next(&iter), i++);
+  upb_oneofdef *oneof = upb_msg_iter_oneof(&iter);
+
+  ZVAL_OBJ(return_value, oneof_descriptor_type->create_object(
+                             oneof_descriptor_type TSRMLS_CC));
+  Oneof *oneof_php = UNBOX(Oneof, return_value);
+  oneof_php->oneofdef = oneof;
+}
+
+PHP_METHOD(Descriptor, getOneofDeclCount) {
+  Descriptor *intern = UNBOX(Descriptor, getThis());
+  RETURN_LONG(upb_msgdef_numoneofs(intern->msgdef));
+}
+
 // -----------------------------------------------------------------------------
 // -----------------------------------------------------------------------------
 // EnumDescriptor
 // EnumDescriptor
 // -----------------------------------------------------------------------------
 // -----------------------------------------------------------------------------
 
 
 static zend_function_entry enum_descriptor_methods[] = {
 static zend_function_entry enum_descriptor_methods[] = {
+  PHP_ME(EnumDescriptor, getValue, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(EnumDescriptor, getValueCount, NULL, ZEND_ACC_PUBLIC)
   ZEND_FE_END
   ZEND_FE_END
 };
 };
 
 
 DEFINE_CLASS(EnumDescriptor, enum_descriptor,
 DEFINE_CLASS(EnumDescriptor, enum_descriptor,
-             "Google\\Protobuf\\Internal\\EnumDescriptor");
+             "Google\\Protobuf\\EnumDescriptor");
 
 
 static void enum_descriptor_free_c(EnumDescriptor *self TSRMLS_DC) {
 static void enum_descriptor_free_c(EnumDescriptor *self TSRMLS_DC) {
 }
 }
 
 
 static void enum_descriptor_init_c_instance(EnumDescriptor *self TSRMLS_DC) {
 static void enum_descriptor_init_c_instance(EnumDescriptor *self TSRMLS_DC) {
-  // zend_object_std_init(&self->std, enum_descriptor_type TSRMLS_CC);
   self->enumdef = NULL;
   self->enumdef = NULL;
   self->klass = NULL;
   self->klass = NULL;
 }
 }
 
 
+PHP_METHOD(EnumDescriptor, getValue) {
+  long index;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
+      FAILURE) {
+    zend_error(E_USER_ERROR, "Expect integer for index.\n");
+    return;
+  }
+
+  EnumDescriptor *intern = UNBOX(EnumDescriptor, getThis());
+  int field_num = upb_enumdef_numvals(intern->enumdef);
+  if (index < 0 || index >= field_num) {
+    zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index);
+    return;
+  }
+
+  upb_enum_iter iter;
+  int i;
+  for(upb_enum_begin(&iter, intern->enumdef), i = 0;
+      !upb_enum_done(&iter) && i < index;
+      upb_enum_next(&iter), i++);
+
+  ZVAL_OBJ(return_value, enum_value_descriptor_type->create_object(
+                             enum_value_descriptor_type TSRMLS_CC));
+  EnumValueDescriptor *enum_value_php =
+      UNBOX(EnumValueDescriptor, return_value);
+  enum_value_php->name = upb_enum_iter_name(&iter);
+  enum_value_php->number = upb_enum_iter_number(&iter);
+}
+
+PHP_METHOD(EnumDescriptor, getValueCount) {
+  EnumDescriptor *intern = UNBOX(EnumDescriptor, getThis());
+  RETURN_LONG(upb_enumdef_numvals(intern->enumdef));
+}
+
+// -----------------------------------------------------------------------------
+// EnumValueDescriptor
+// -----------------------------------------------------------------------------
+
+static zend_function_entry enum_value_descriptor_methods[] = {
+  PHP_ME(EnumValueDescriptor, getName, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(EnumValueDescriptor, getNumber, NULL, ZEND_ACC_PUBLIC)
+  ZEND_FE_END
+};
+
+DEFINE_CLASS(EnumValueDescriptor, enum_value_descriptor,
+             "Google\\Protobuf\\EnumValueDescriptor");
+
+static void enum_value_descriptor_free_c(EnumValueDescriptor *self TSRMLS_DC) {
+}
+
+static void enum_value_descriptor_init_c_instance(EnumValueDescriptor *self TSRMLS_DC) {
+  self->name = NULL;
+  self->number = 0;
+}
+
+PHP_METHOD(EnumValueDescriptor, getName) {
+  EnumValueDescriptor *intern = UNBOX(EnumValueDescriptor, getThis());
+  PHP_PROTO_RETVAL_STRINGL(intern->name, strlen(intern->name), 1);
+}
+
+PHP_METHOD(EnumValueDescriptor, getNumber) {
+  EnumValueDescriptor *intern = UNBOX(EnumValueDescriptor, getThis());
+  RETURN_LONG(intern->number);
+}
+
 // -----------------------------------------------------------------------------
 // -----------------------------------------------------------------------------
 // FieldDescriptor
 // FieldDescriptor
 // -----------------------------------------------------------------------------
 // -----------------------------------------------------------------------------
 
 
+static zend_function_entry field_descriptor_methods[] = {
+  PHP_ME(FieldDescriptor, getName,   NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(FieldDescriptor, getNumber, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(FieldDescriptor, getLabel,  NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(FieldDescriptor, getType,   NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(FieldDescriptor, isMap,     NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(FieldDescriptor, getEnumType, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(FieldDescriptor, getMessageType, NULL, ZEND_ACC_PUBLIC)
+  ZEND_FE_END
+};
+
+DEFINE_CLASS(FieldDescriptor, field_descriptor,
+             "Google\\Protobuf\\FieldDescriptor");
+
+static void field_descriptor_free_c(FieldDescriptor *self TSRMLS_DC) {
+}
+
+static void field_descriptor_init_c_instance(FieldDescriptor *self TSRMLS_DC) {
+  self->fielddef = NULL;
+}
+
 upb_fieldtype_t to_fieldtype(upb_descriptortype_t type) {
 upb_fieldtype_t to_fieldtype(upb_descriptortype_t type) {
   switch (type) {
   switch (type) {
 #define CASE(descriptor_type, type)           \
 #define CASE(descriptor_type, type)           \
@@ -272,6 +468,150 @@ upb_fieldtype_t to_fieldtype(upb_descriptortype_t type) {
   return 0;
   return 0;
 }
 }
 
 
+PHP_METHOD(FieldDescriptor, getName) {
+  FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
+  const char* name = upb_fielddef_name(intern->fielddef);
+  PHP_PROTO_RETVAL_STRINGL(name, strlen(name), 1);
+}
+
+PHP_METHOD(FieldDescriptor, getNumber) {
+  FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
+  RETURN_LONG(upb_fielddef_number(intern->fielddef));
+}
+
+PHP_METHOD(FieldDescriptor, getLabel) {
+  FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
+  RETURN_LONG(upb_fielddef_label(intern->fielddef));
+}
+
+PHP_METHOD(FieldDescriptor, getType) {
+  FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
+  RETURN_LONG(upb_fielddef_descriptortype(intern->fielddef));
+}
+
+PHP_METHOD(FieldDescriptor, isMap) {
+  FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
+  RETURN_BOOL(upb_fielddef_ismap(intern->fielddef));
+}
+
+PHP_METHOD(FieldDescriptor, getEnumType) {
+  FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
+  const upb_enumdef *enumdef = upb_fielddef_enumsubdef(intern->fielddef);
+  if (enumdef == NULL) {
+    char error_msg[100];
+    sprintf(error_msg, "Cannot get enum type for non-enum field '%s'",
+            upb_fielddef_name(intern->fielddef));
+    zend_throw_exception(NULL, error_msg, 0 TSRMLS_CC);
+    return;
+  }
+  PHP_PROTO_HASHTABLE_VALUE desc = get_def_obj(enumdef);
+
+#if PHP_MAJOR_VERSION < 7
+  RETURN_ZVAL(desc, 1, 0);
+#else
+  ++GC_REFCOUNT(desc);
+  RETURN_OBJ(desc);
+#endif
+}
+
+PHP_METHOD(FieldDescriptor, getMessageType) {
+  FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
+  const upb_msgdef *msgdef = upb_fielddef_msgsubdef(intern->fielddef);
+  if (msgdef == NULL) {
+    char error_msg[100];
+    sprintf(error_msg, "Cannot get message type for non-message field '%s'",
+            upb_fielddef_name(intern->fielddef));
+    zend_throw_exception(NULL, error_msg, 0 TSRMLS_CC);
+    return;
+  }
+  PHP_PROTO_HASHTABLE_VALUE desc = get_def_obj(msgdef);
+
+#if PHP_MAJOR_VERSION < 7
+  RETURN_ZVAL(desc, 1, 0);
+#else
+  ++GC_REFCOUNT(desc);
+  RETURN_OBJ(desc);
+#endif
+}
+
+// -----------------------------------------------------------------------------
+// Oneof
+// -----------------------------------------------------------------------------
+
+static zend_function_entry oneof_descriptor_methods[] = {
+  PHP_ME(Oneof, getName,  NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Oneof, getField, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Oneof, getFieldCount, NULL, ZEND_ACC_PUBLIC)
+  ZEND_FE_END
+};
+
+DEFINE_CLASS(Oneof, oneof_descriptor,
+             "Google\\Protobuf\\OneofDescriptor");
+
+static void oneof_descriptor_free_c(Oneof *self TSRMLS_DC) {
+}
+
+static void oneof_descriptor_init_c_instance(Oneof *self TSRMLS_DC) {
+  self->oneofdef = NULL;
+}
+
+PHP_METHOD(Oneof, getName) {
+  Oneof *intern = UNBOX(Oneof, getThis());
+  const char *name = upb_oneofdef_name(intern->oneofdef);
+  PHP_PROTO_RETVAL_STRINGL(name, strlen(name), 1);
+}
+
+PHP_METHOD(Oneof, getField) {
+  long index;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
+      FAILURE) {
+    zend_error(E_USER_ERROR, "Expect integer for index.\n");
+    return;
+  }
+
+  Oneof *intern = UNBOX(Oneof, getThis());
+  int field_num = upb_oneofdef_numfields(intern->oneofdef);
+  if (index < 0 || index >= field_num) {
+    zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index);
+    return;
+  }
+
+  upb_oneof_iter iter;
+  int i;
+  for(upb_oneof_begin(&iter, intern->oneofdef), i = 0;
+      !upb_oneof_done(&iter) && i < index;
+      upb_oneof_next(&iter), i++);
+  const upb_fielddef *field = upb_oneof_iter_field(&iter);
+
+  PHP_PROTO_HASHTABLE_VALUE field_hashtable_value = get_def_obj(field);
+  if (field_hashtable_value == NULL) {
+#if PHP_MAJOR_VERSION < 7
+    MAKE_STD_ZVAL(field_hashtable_value);
+    ZVAL_OBJ(field_hashtable_value, field_descriptor_type->create_object(
+                                        field_descriptor_type TSRMLS_CC));
+#else
+    field_hashtable_value =
+        field_descriptor_type->create_object(field_descriptor_type TSRMLS_CC);
+#endif
+    FieldDescriptor *field_php =
+        UNBOX_HASHTABLE_VALUE(FieldDescriptor, field_hashtable_value);
+    field_php->fielddef = field;
+    add_def_obj(field, field_hashtable_value);
+  }
+
+#if PHP_MAJOR_VERSION < 7
+  RETURN_ZVAL(field_hashtable_value, 1, 0);
+#else
+  ++GC_REFCOUNT(field_hashtable_value);
+  RETURN_OBJ(field_hashtable_value);
+#endif
+}
+
+PHP_METHOD(Oneof, getFieldCount) {
+  Oneof *intern = UNBOX(Oneof, getThis());
+  RETURN_LONG(upb_oneofdef_numfields(intern->oneofdef));
+}
+
 // -----------------------------------------------------------------------------
 // -----------------------------------------------------------------------------
 // DescriptorPool
 // DescriptorPool
 // -----------------------------------------------------------------------------
 // -----------------------------------------------------------------------------
@@ -279,52 +619,79 @@ upb_fieldtype_t to_fieldtype(upb_descriptortype_t type) {
 static zend_function_entry descriptor_pool_methods[] = {
 static zend_function_entry descriptor_pool_methods[] = {
   PHP_ME(DescriptorPool, getGeneratedPool, NULL,
   PHP_ME(DescriptorPool, getGeneratedPool, NULL,
          ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
          ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-  PHP_ME(DescriptorPool, internalAddGeneratedFile, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(DescriptorPool, getDescriptorByClassName, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(DescriptorPool, getEnumDescriptorByClassName, NULL, ZEND_ACC_PUBLIC)
+  ZEND_FE_END
+};
+
+static zend_function_entry internal_descriptor_pool_methods[] = {
+  PHP_ME(InternalDescriptorPool, getGeneratedPool, NULL,
+         ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(InternalDescriptorPool, internalAddGeneratedFile, NULL, ZEND_ACC_PUBLIC)
   ZEND_FE_END
   ZEND_FE_END
 };
 };
 
 
 DEFINE_CLASS(DescriptorPool, descriptor_pool,
 DEFINE_CLASS(DescriptorPool, descriptor_pool,
+             "Google\\Protobuf\\DescriptorPool");
+DEFINE_CLASS(InternalDescriptorPool, internal_descriptor_pool,
              "Google\\Protobuf\\Internal\\DescriptorPool");
              "Google\\Protobuf\\Internal\\DescriptorPool");
 
 
 // wrapper of generated pool
 // wrapper of generated pool
 #if PHP_MAJOR_VERSION < 7
 #if PHP_MAJOR_VERSION < 7
 zval* generated_pool_php;
 zval* generated_pool_php;
+zval* internal_generated_pool_php;
 #else
 #else
 zend_object *generated_pool_php;
 zend_object *generated_pool_php;
+zend_object *internal_generated_pool_php;
 #endif
 #endif
-DescriptorPool *generated_pool;  // The actual generated pool
+InternalDescriptorPool *generated_pool;  // The actual generated pool
 
 
 static void init_generated_pool_once(TSRMLS_D) {
 static void init_generated_pool_once(TSRMLS_D) {
-  if (generated_pool_php == NULL) {
+  if (generated_pool == NULL) {
 #if PHP_MAJOR_VERSION < 7
 #if PHP_MAJOR_VERSION < 7
     MAKE_STD_ZVAL(generated_pool_php);
     MAKE_STD_ZVAL(generated_pool_php);
+    MAKE_STD_ZVAL(internal_generated_pool_php);
+    ZVAL_OBJ(internal_generated_pool_php,
+             internal_descriptor_pool_type->create_object(
+                 internal_descriptor_pool_type TSRMLS_CC));
+    generated_pool = UNBOX(InternalDescriptorPool, internal_generated_pool_php);
     ZVAL_OBJ(generated_pool_php, descriptor_pool_type->create_object(
     ZVAL_OBJ(generated_pool_php, descriptor_pool_type->create_object(
                                      descriptor_pool_type TSRMLS_CC));
                                      descriptor_pool_type TSRMLS_CC));
-    generated_pool = UNBOX(DescriptorPool, generated_pool_php);
 #else
 #else
+    internal_generated_pool_php = internal_descriptor_pool_type->create_object(
+        internal_descriptor_pool_type TSRMLS_CC);
+    generated_pool = (InternalDescriptorPool *)((char *)internal_generated_pool_php -
+                                        XtOffsetOf(InternalDescriptorPool, std));
     generated_pool_php =
     generated_pool_php =
         descriptor_pool_type->create_object(descriptor_pool_type TSRMLS_CC);
         descriptor_pool_type->create_object(descriptor_pool_type TSRMLS_CC);
-    generated_pool = (DescriptorPool *)((char *)generated_pool_php -
-                                        XtOffsetOf(DescriptorPool, std));
 #endif
 #endif
   }
   }
 }
 }
 
 
-static void descriptor_pool_init_c_instance(DescriptorPool *pool TSRMLS_DC) {
-  // zend_object_std_init(&pool->std, descriptor_pool_type TSRMLS_CC);
+static void internal_descriptor_pool_init_c_instance(
+    InternalDescriptorPool *pool TSRMLS_DC) {
   pool->symtab = upb_symtab_new();
   pool->symtab = upb_symtab_new();
 
 
   ALLOC_HASHTABLE(pool->pending_list);
   ALLOC_HASHTABLE(pool->pending_list);
   zend_hash_init(pool->pending_list, 1, NULL, ZVAL_PTR_DTOR, 0);
   zend_hash_init(pool->pending_list, 1, NULL, ZVAL_PTR_DTOR, 0);
 }
 }
 
 
-static void descriptor_pool_free_c(DescriptorPool *pool TSRMLS_DC) {
+static void internal_descriptor_pool_free_c(
+    InternalDescriptorPool *pool TSRMLS_DC) {
   upb_symtab_free(pool->symtab);
   upb_symtab_free(pool->symtab);
 
 
   zend_hash_destroy(pool->pending_list);
   zend_hash_destroy(pool->pending_list);
   FREE_HASHTABLE(pool->pending_list);
   FREE_HASHTABLE(pool->pending_list);
 }
 }
 
 
+static void descriptor_pool_init_c_instance(DescriptorPool *pool TSRMLS_DC) {
+  assert(generated_pool != NULL);
+  pool->intern = generated_pool;
+}
+
+static void descriptor_pool_free_c(DescriptorPool *pool TSRMLS_DC) {
+}
+
 static void validate_enumdef(const upb_enumdef *enumdef) {
 static void validate_enumdef(const upb_enumdef *enumdef) {
   // Verify that an entry exists with integer value 0. (This is the default
   // Verify that an entry exists with integer value 0. (This is the default
   // value.)
   // value.)
@@ -358,6 +725,16 @@ PHP_METHOD(DescriptorPool, getGeneratedPool) {
 #endif
 #endif
 }
 }
 
 
+PHP_METHOD(InternalDescriptorPool, getGeneratedPool) {
+  init_generated_pool_once(TSRMLS_C);
+#if PHP_MAJOR_VERSION < 7
+  RETURN_ZVAL(internal_generated_pool_php, 1, 0);
+#else
+  ++GC_REFCOUNT(internal_generated_pool_php);
+  RETURN_OBJ(internal_generated_pool_php);
+#endif
+}
+
 static void classname_no_prefix(const char *fullname, const char *package_name,
 static void classname_no_prefix(const char *fullname, const char *package_name,
                                 char *class_name) {
                                 char *class_name) {
   size_t i = 0, j;
   size_t i = 0, j;
@@ -455,7 +832,7 @@ static void convert_to_class_name_inplace(const char *package,
   memcpy(classname + i, prefix, prefix_len);
   memcpy(classname + i, prefix, prefix_len);
 }
 }
 
 
-PHP_METHOD(DescriptorPool, internalAddGeneratedFile) {
+PHP_METHOD(InternalDescriptorPool, internalAddGeneratedFile) {
   char *data = NULL;
   char *data = NULL;
   PHP_PROTO_SIZE data_len;
   PHP_PROTO_SIZE data_len;
   upb_filedef **files;
   upb_filedef **files;
@@ -466,7 +843,7 @@ PHP_METHOD(DescriptorPool, internalAddGeneratedFile) {
     return;
     return;
   }
   }
 
 
-  DescriptorPool *pool = UNBOX(DescriptorPool, getThis());
+  InternalDescriptorPool *pool = UNBOX(InternalDescriptorPool, getThis());
   CHECK_UPB(files = upb_loaddescriptor(data, data_len, &pool, &status),
   CHECK_UPB(files = upb_loaddescriptor(data, data_len, &pool, &status),
             "Parse binary descriptors to internal descriptors failed");
             "Parse binary descriptors to internal descriptors failed");
 
 
@@ -550,3 +927,77 @@ PHP_METHOD(DescriptorPool, internalAddGeneratedFile) {
   upb_filedef_unref(files[0], &pool);
   upb_filedef_unref(files[0], &pool);
   upb_gfree(files);
   upb_gfree(files);
 }
 }
+
+PHP_METHOD(DescriptorPool, getDescriptorByClassName) {
+  DescriptorPool *public_pool = UNBOX(DescriptorPool, getThis());
+  InternalDescriptorPool *pool = public_pool->intern;
+
+  char *classname = NULL;
+  PHP_PROTO_SIZE classname_len;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &classname,
+                            &classname_len) == FAILURE) {
+    return;
+  }
+
+  PHP_PROTO_CE_DECLARE pce;
+  if (php_proto_zend_lookup_class(classname, classname_len, &pce) ==
+      FAILURE) {
+    RETURN_NULL();
+  }
+
+  PHP_PROTO_HASHTABLE_VALUE desc = get_ce_obj(PHP_PROTO_CE_UNREF(pce));
+  if (desc == NULL) {
+    RETURN_NULL();
+  }
+
+  zend_class_entry* instance_ce = HASHTABLE_VALUE_CE(desc);
+
+  if (!instanceof_function(instance_ce, descriptor_type TSRMLS_CC)) {
+    RETURN_NULL();
+  }
+
+#if PHP_MAJOR_VERSION < 7
+  RETURN_ZVAL(desc, 1, 0);
+#else
+  ++GC_REFCOUNT(desc);
+  RETURN_OBJ(desc);
+#endif
+}
+
+PHP_METHOD(DescriptorPool, getEnumDescriptorByClassName) {
+  DescriptorPool *public_pool = UNBOX(DescriptorPool, getThis());
+  InternalDescriptorPool *pool = public_pool->intern;
+
+  char *classname = NULL;
+  PHP_PROTO_SIZE classname_len;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &classname,
+                            &classname_len) == FAILURE) {
+    return;
+  }
+
+  PHP_PROTO_CE_DECLARE pce;
+  if (php_proto_zend_lookup_class(classname, classname_len, &pce) ==
+      FAILURE) {
+    RETURN_NULL();
+  }
+
+  PHP_PROTO_HASHTABLE_VALUE desc = get_ce_obj(PHP_PROTO_CE_UNREF(pce));
+  if (desc == NULL) {
+    RETURN_NULL();
+  }
+
+  zend_class_entry* instance_ce = HASHTABLE_VALUE_CE(desc);
+
+  if (!instanceof_function(instance_ce, enum_descriptor_type TSRMLS_CC)) {
+    RETURN_NULL();
+  }
+
+#if PHP_MAJOR_VERSION < 7
+  RETURN_ZVAL(desc, 1, 0);
+#else
+  ++GC_REFCOUNT(desc);
+  RETURN_OBJ(desc);
+#endif
+}

+ 25 - 6
php/ext/google/protobuf/protobuf.c

@@ -63,7 +63,6 @@ static void* get_from_table(const HashTable* t, const void* def) {
   void** value;
   void** value;
   if (php_proto_zend_hash_index_find_mem(t, (zend_ulong)def, (void**)&value) ==
   if (php_proto_zend_hash_index_find_mem(t, (zend_ulong)def, (void**)&value) ==
       FAILURE) {
       FAILURE) {
-    zend_error(E_ERROR, "PHP object not found for given definition.\n");
     return NULL;
     return NULL;
   }
   }
   return *value;
   return *value;
@@ -166,6 +165,7 @@ static PHP_RINIT_FUNCTION(protobuf) {
 
 
   generated_pool = NULL;
   generated_pool = NULL;
   generated_pool_php = NULL;
   generated_pool_php = NULL;
+  internal_generated_pool_php = NULL;
 
 
   return 0;
   return 0;
 }
 }
@@ -182,21 +182,40 @@ static PHP_RSHUTDOWN_FUNCTION(protobuf) {
     zval_dtor(generated_pool_php);
     zval_dtor(generated_pool_php);
     FREE_ZVAL(generated_pool_php);
     FREE_ZVAL(generated_pool_php);
   }
   }
+  if (internal_generated_pool_php != NULL) {
+    zval_dtor(internal_generated_pool_php);
+    FREE_ZVAL(internal_generated_pool_php);
+  }
+#else
+  if (generated_pool_php != NULL) {
+    zval tmp;
+    ZVAL_OBJ(&tmp, generated_pool_php);
+    zval_dtor(&tmp);
+  }
+  if (internal_generated_pool_php != NULL) {
+    zval tmp;
+    ZVAL_OBJ(&tmp, internal_generated_pool_php);
+    zval_dtor(&tmp);
+  }
 #endif
 #endif
 
 
   return 0;
   return 0;
 }
 }
 
 
 static PHP_MINIT_FUNCTION(protobuf) {
 static PHP_MINIT_FUNCTION(protobuf) {
+  descriptor_pool_init(TSRMLS_C);
+  descriptor_init(TSRMLS_C);
+  enum_descriptor_init(TSRMLS_C);
+  enum_value_descriptor_init(TSRMLS_C);
+  field_descriptor_init(TSRMLS_C);
+  gpb_type_init(TSRMLS_C);
+  internal_descriptor_pool_init(TSRMLS_C);
   map_field_init(TSRMLS_C);
   map_field_init(TSRMLS_C);
   map_field_iter_init(TSRMLS_C);
   map_field_iter_init(TSRMLS_C);
+  message_init(TSRMLS_C);
+  oneof_descriptor_init(TSRMLS_C);
   repeated_field_init(TSRMLS_C);
   repeated_field_init(TSRMLS_C);
   repeated_field_iter_init(TSRMLS_C);
   repeated_field_iter_init(TSRMLS_C);
-  gpb_type_init(TSRMLS_C);
-  message_init(TSRMLS_C);
-  descriptor_pool_init(TSRMLS_C);
-  descriptor_init(TSRMLS_C);
-  enum_descriptor_init(TSRMLS_C);
   util_init(TSRMLS_C);
   util_init(TSRMLS_C);
 
 
   return 0;
   return 0;

+ 59 - 4
php/ext/google/protobuf/protobuf.h

@@ -185,6 +185,7 @@
 #define HASHTABLE_VALUE_DTOR ZVAL_PTR_DTOR
 #define HASHTABLE_VALUE_DTOR ZVAL_PTR_DTOR
 
 
 #define PHP_PROTO_HASHTABLE_VALUE zval*
 #define PHP_PROTO_HASHTABLE_VALUE zval*
+#define HASHTABLE_VALUE_CE(val) Z_OBJCE_P(val)
 
 
 #define CREATE_HASHTABLE_VALUE(OBJ, WRAPPED_OBJ, OBJ_TYPE, OBJ_CLASS_ENTRY) \
 #define CREATE_HASHTABLE_VALUE(OBJ, WRAPPED_OBJ, OBJ_TYPE, OBJ_CLASS_ENTRY) \
   OBJ_TYPE* OBJ;                                                            \
   OBJ_TYPE* OBJ;                                                            \
@@ -369,6 +370,7 @@ static inline int php_proto_zend_hash_get_current_data_ex(HashTable* ht,
 #define HASHTABLE_VALUE_DTOR php_proto_hashtable_descriptor_release
 #define HASHTABLE_VALUE_DTOR php_proto_hashtable_descriptor_release
 
 
 #define PHP_PROTO_HASHTABLE_VALUE zend_object*
 #define PHP_PROTO_HASHTABLE_VALUE zend_object*
+#define HASHTABLE_VALUE_CE(val) val->ce
 
 
 #define CREATE_HASHTABLE_VALUE(OBJ, WRAPPED_OBJ, OBJ_TYPE, OBJ_CLASS_ENTRY) \
 #define CREATE_HASHTABLE_VALUE(OBJ, WRAPPED_OBJ, OBJ_TYPE, OBJ_CLASS_ENTRY) \
   OBJ_TYPE* OBJ;                                                            \
   OBJ_TYPE* OBJ;                                                            \
@@ -397,7 +399,9 @@ static inline int php_proto_zend_lookup_class(
 struct DescriptorPool;
 struct DescriptorPool;
 struct Descriptor;
 struct Descriptor;
 struct EnumDescriptor;
 struct EnumDescriptor;
+struct EnumValueDescriptor;
 struct FieldDescriptor;
 struct FieldDescriptor;
+struct InternalDescriptorPool;
 struct MessageField;
 struct MessageField;
 struct MessageHeader;
 struct MessageHeader;
 struct MessageLayout;
 struct MessageLayout;
@@ -410,7 +414,9 @@ struct Oneof;
 typedef struct DescriptorPool DescriptorPool;
 typedef struct DescriptorPool DescriptorPool;
 typedef struct Descriptor Descriptor;
 typedef struct Descriptor Descriptor;
 typedef struct EnumDescriptor EnumDescriptor;
 typedef struct EnumDescriptor EnumDescriptor;
+typedef struct EnumValueDescriptor EnumValueDescriptor;
 typedef struct FieldDescriptor FieldDescriptor;
 typedef struct FieldDescriptor FieldDescriptor;
+typedef struct InternalDescriptorPool InternalDescriptorPool;
 typedef struct MessageField MessageField;
 typedef struct MessageField MessageField;
 typedef struct MessageHeader MessageHeader;
 typedef struct MessageHeader MessageHeader;
 typedef struct MessageLayout MessageLayout;
 typedef struct MessageLayout MessageLayout;
@@ -431,9 +437,12 @@ ZEND_END_MODULE_GLOBALS(protobuf)
 void descriptor_init(TSRMLS_D);
 void descriptor_init(TSRMLS_D);
 void enum_descriptor_init(TSRMLS_D);
 void enum_descriptor_init(TSRMLS_D);
 void descriptor_pool_init(TSRMLS_D);
 void descriptor_pool_init(TSRMLS_D);
+void internal_descriptor_pool_init(TSRMLS_D);
+void field_descriptor_init(TSRMLS_D);
 void gpb_type_init(TSRMLS_D);
 void gpb_type_init(TSRMLS_D);
 void map_field_init(TSRMLS_D);
 void map_field_init(TSRMLS_D);
 void map_field_iter_init(TSRMLS_D);
 void map_field_iter_init(TSRMLS_D);
+void oneof_descriptor_init(TSRMLS_D);
 void repeated_field_init(TSRMLS_D);
 void repeated_field_init(TSRMLS_D);
 void repeated_field_iter_init(TSRMLS_D);
 void repeated_field_iter_init(TSRMLS_D);
 void util_init(TSRMLS_D);
 void util_init(TSRMLS_D);
@@ -458,22 +467,34 @@ extern zend_class_entry* repeated_field_type;
 // -----------------------------------------------------------------------------
 // -----------------------------------------------------------------------------
 
 
 PHP_PROTO_WRAP_OBJECT_START(DescriptorPool)
 PHP_PROTO_WRAP_OBJECT_START(DescriptorPool)
+  InternalDescriptorPool* intern;
+PHP_PROTO_WRAP_OBJECT_END
+
+PHP_METHOD(DescriptorPool, getGeneratedPool);
+PHP_METHOD(DescriptorPool, getDescriptorByClassName);
+PHP_METHOD(DescriptorPool, getEnumDescriptorByClassName);
+
+PHP_PROTO_WRAP_OBJECT_START(InternalDescriptorPool)
   upb_symtab* symtab;
   upb_symtab* symtab;
   HashTable* pending_list;
   HashTable* pending_list;
 PHP_PROTO_WRAP_OBJECT_END
 PHP_PROTO_WRAP_OBJECT_END
 
 
-PHP_METHOD(DescriptorPool, getGeneratedPool);
-PHP_METHOD(DescriptorPool, internalAddGeneratedFile);
+PHP_METHOD(InternalDescriptorPool, getGeneratedPool);
+PHP_METHOD(InternalDescriptorPool, internalAddGeneratedFile);
 
 
 // wrapper of generated pool
 // wrapper of generated pool
 #if PHP_MAJOR_VERSION < 7
 #if PHP_MAJOR_VERSION < 7
 extern zval* generated_pool_php;
 extern zval* generated_pool_php;
+extern zval* internal_generated_pool_php;
 void descriptor_pool_free(void* object TSRMLS_DC);
 void descriptor_pool_free(void* object TSRMLS_DC);
+void internal_descriptor_pool_free(void* object TSRMLS_DC);
 #else
 #else
 extern zend_object *generated_pool_php;
 extern zend_object *generated_pool_php;
+extern zend_object *internal_generated_pool_php;
 void descriptor_pool_free(zend_object* object);
 void descriptor_pool_free(zend_object* object);
+void internal_descriptor_pool_free(zend_object* object);
 #endif
 #endif
-extern DescriptorPool* generated_pool;  // The actual generated pool
+extern InternalDescriptorPool* generated_pool;  // The actual generated pool
 
 
 PHP_PROTO_WRAP_OBJECT_START(Descriptor)
 PHP_PROTO_WRAP_OBJECT_START(Descriptor)
   const upb_msgdef* msgdef;
   const upb_msgdef* msgdef;
@@ -487,6 +508,12 @@ PHP_PROTO_WRAP_OBJECT_START(Descriptor)
   const upb_handlers* json_serialize_handlers_preserve;
   const upb_handlers* json_serialize_handlers_preserve;
 PHP_PROTO_WRAP_OBJECT_END
 PHP_PROTO_WRAP_OBJECT_END
 
 
+PHP_METHOD(Descriptor, getFullName);
+PHP_METHOD(Descriptor, getField);
+PHP_METHOD(Descriptor, getFieldCount);
+PHP_METHOD(Descriptor, getOneofDecl);
+PHP_METHOD(Descriptor, getOneofDeclCount);
+
 extern zend_class_entry* descriptor_type;
 extern zend_class_entry* descriptor_type;
 
 
 void descriptor_name_set(Descriptor *desc, const char *name);
 void descriptor_name_set(Descriptor *desc, const char *name);
@@ -495,14 +522,36 @@ PHP_PROTO_WRAP_OBJECT_START(FieldDescriptor)
   const upb_fielddef* fielddef;
   const upb_fielddef* fielddef;
 PHP_PROTO_WRAP_OBJECT_END
 PHP_PROTO_WRAP_OBJECT_END
 
 
+PHP_METHOD(FieldDescriptor, getName);
+PHP_METHOD(FieldDescriptor, getNumber);
+PHP_METHOD(FieldDescriptor, getLabel);
+PHP_METHOD(FieldDescriptor, getType);
+PHP_METHOD(FieldDescriptor, isMap);
+PHP_METHOD(FieldDescriptor, getEnumType);
+PHP_METHOD(FieldDescriptor, getMessageType);
+
+extern zend_class_entry* field_descriptor_type;
+
 PHP_PROTO_WRAP_OBJECT_START(EnumDescriptor)
 PHP_PROTO_WRAP_OBJECT_START(EnumDescriptor)
   const upb_enumdef* enumdef;
   const upb_enumdef* enumdef;
   zend_class_entry* klass;  // begins as NULL
   zend_class_entry* klass;  // begins as NULL
-  // VALUE module;  // begins as nil
 PHP_PROTO_WRAP_OBJECT_END
 PHP_PROTO_WRAP_OBJECT_END
 
 
+PHP_METHOD(EnumDescriptor, getValue);
+PHP_METHOD(EnumDescriptor, getValueCount);
+
 extern zend_class_entry* enum_descriptor_type;
 extern zend_class_entry* enum_descriptor_type;
 
 
+PHP_PROTO_WRAP_OBJECT_START(EnumValueDescriptor)
+  const char* name;
+  int32_t number;
+PHP_PROTO_WRAP_OBJECT_END
+
+PHP_METHOD(EnumValueDescriptor, getName);
+PHP_METHOD(EnumValueDescriptor, getNumber);
+
+extern zend_class_entry* enum_value_descriptor_type;
+
 // -----------------------------------------------------------------------------
 // -----------------------------------------------------------------------------
 // Message class creation.
 // Message class creation.
 // -----------------------------------------------------------------------------
 // -----------------------------------------------------------------------------
@@ -819,6 +868,12 @@ PHP_PROTO_WRAP_OBJECT_START(Oneof)
   char value[NATIVE_SLOT_MAX_SIZE];
   char value[NATIVE_SLOT_MAX_SIZE];
 PHP_PROTO_WRAP_OBJECT_END
 PHP_PROTO_WRAP_OBJECT_END
 
 
+PHP_METHOD(Oneof, getName);
+PHP_METHOD(Oneof, getField);
+PHP_METHOD(Oneof, getFieldCount);
+
+extern zend_class_entry* oneof_descriptor_type;
+
 // Oneof case slot value to indicate that no oneof case is set. The value `0` is
 // Oneof case slot value to indicate that no oneof case is set. The value `0` is
 // safe because field numbers are used as case identifiers, and no field can
 // safe because field numbers are used as case identifiers, and no field can
 // have a number of 0.
 // have a number of 0.

+ 243 - 0
php/tests/descriptors_test.php

@@ -0,0 +1,243 @@
+<?php
+
+require_once('generated/Descriptors/TestDescriptorsEnum.php');
+require_once('generated/Descriptors/TestDescriptorsMessage.php');
+require_once('test_base.php');
+require_once('test_util.php');
+
+use Google\Protobuf\DescriptorPool;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\MapField;
+use Descriptors\TestDescriptorsEnum;
+use Descriptors\TestDescriptorsMessage;
+use Descriptors\TestDescriptorsMessage_Sub;
+
+class DescriptorsTest extends TestBase
+{
+
+    // Redefine these here for compatibility with c extension
+    const GPBLABEL_OPTIONAL = 1;
+    const GPBLABEL_REQUIRED = 2;
+    const GPBLABEL_REPEATED = 3;
+
+    const GPBTYPE_DOUBLE   =  1;
+    const GPBTYPE_FLOAT    =  2;
+    const GPBTYPE_INT64    =  3;
+    const GPBTYPE_UINT64   =  4;
+    const GPBTYPE_INT32    =  5;
+    const GPBTYPE_FIXED64  =  6;
+    const GPBTYPE_FIXED32  =  7;
+    const GPBTYPE_BOOL     =  8;
+    const GPBTYPE_STRING   =  9;
+    const GPBTYPE_GROUP    = 10;
+    const GPBTYPE_MESSAGE  = 11;
+    const GPBTYPE_BYTES    = 12;
+    const GPBTYPE_UINT32   = 13;
+    const GPBTYPE_ENUM     = 14;
+    const GPBTYPE_SFIXED32 = 15;
+    const GPBTYPE_SFIXED64 = 16;
+    const GPBTYPE_SINT32   = 17;
+    const GPBTYPE_SINT64   = 18;
+
+    #########################################################
+    # Test descriptor pool.
+    #########################################################
+
+    public function testDescriptorPool()
+    {
+        $pool = DescriptorPool::getGeneratedPool();
+
+        $desc = $pool->getDescriptorByClassName(get_class(new TestDescriptorsMessage()));
+        $this->assertInstanceOf('\Google\Protobuf\Descriptor', $desc);
+
+        $enumDesc = $pool->getEnumDescriptorByClassName(get_class(new TestDescriptorsEnum()));
+        $this->assertInstanceOf('\Google\Protobuf\EnumDescriptor', $enumDesc);
+    }
+
+    public function testDescriptorPoolIncorrectArgs()
+    {
+        $pool = DescriptorPool::getGeneratedPool();
+
+        $desc = $pool->getDescriptorByClassName('NotAClass');
+        $this->assertNull($desc);
+
+        $desc = $pool->getDescriptorByClassName(get_class(new TestDescriptorsEnum()));
+        $this->assertNull($desc);
+
+        $enumDesc = $pool->getEnumDescriptorByClassName(get_class(new TestDescriptorsMessage()));
+        $this->assertNull($enumDesc);
+    }
+
+    #########################################################
+    # Test descriptor.
+    #########################################################
+
+    public function testDescriptor()
+    {
+        $pool = DescriptorPool::getGeneratedPool();
+        $desc = $pool->getDescriptorByClassName(get_class(new TestDescriptorsMessage()));
+
+        $this->assertSame('descriptors.TestDescriptorsMessage', $desc->getFullName());
+
+        $this->assertInstanceOf('\Google\Protobuf\FieldDescriptor', $desc->getField(0));
+        $this->assertSame(7, $desc->getFieldCount());
+
+        $this->assertInstanceOf('\Google\Protobuf\OneofDescriptor', $desc->getOneofDecl(0));
+        $this->assertSame(1, $desc->getOneofDeclCount());
+    }
+
+    #########################################################
+    # Test enum descriptor.
+    #########################################################
+
+    public function testEnumDescriptor()
+    {
+        // WARNINIG - we need to do this so that TestDescriptorsEnum is registered!!?
+        new TestDescriptorsMessage();
+
+        $pool = DescriptorPool::getGeneratedPool();
+
+        $enumDesc = $pool->getEnumDescriptorByClassName(get_class(new TestDescriptorsEnum()));
+
+        // Build map of enum values
+        $enumDescMap = [];
+        for ($i = 0; $i < $enumDesc->getValueCount(); $i++) {
+            $enumValueDesc = $enumDesc->getValue($i);
+            $this->assertInstanceOf('\Google\Protobuf\EnumValueDescriptor', $enumValueDesc);
+            $enumDescMap[$enumValueDesc->getNumber()] = $enumValueDesc->getName();
+        }
+
+        $this->assertSame('ZERO', $enumDescMap[0]);
+        $this->assertSame('ONE', $enumDescMap[1]);
+
+        $this->assertSame(2, $enumDesc->getValueCount());
+    }
+
+    #########################################################
+    # Test field descriptor.
+    #########################################################
+
+    public function testFieldDescriptor()
+    {
+        $pool = DescriptorPool::getGeneratedPool();
+        $desc = $pool->getDescriptorByClassName(get_class(new TestDescriptorsMessage()));
+
+        $fieldDescMap = $this->buildFieldMap($desc);
+
+        // Optional int field
+        $fieldDesc = $fieldDescMap[1];
+        $this->assertSame('optional_int32', $fieldDesc->getName());
+        $this->assertSame(1, $fieldDesc->getNumber());
+        $this->assertSame(self::GPBLABEL_OPTIONAL, $fieldDesc->getLabel());
+        $this->assertSame(self::GPBTYPE_INT32, $fieldDesc->getType());
+        $this->assertFalse($fieldDesc->isMap());
+
+        // Optional enum field
+        $fieldDesc = $fieldDescMap[16];
+        $this->assertSame('optional_enum', $fieldDesc->getName());
+        $this->assertSame(16, $fieldDesc->getNumber());
+        $this->assertSame(self::GPBLABEL_OPTIONAL, $fieldDesc->getLabel());
+        $this->assertSame(self::GPBTYPE_ENUM, $fieldDesc->getType());
+        $this->assertInstanceOf('\Google\Protobuf\EnumDescriptor', $fieldDesc->getEnumType());
+        $this->assertFalse($fieldDesc->isMap());
+
+        // Optional message field
+        $fieldDesc = $fieldDescMap[17];
+        $this->assertSame('optional_message', $fieldDesc->getName());
+        $this->assertSame(17, $fieldDesc->getNumber());
+        $this->assertSame(self::GPBLABEL_OPTIONAL, $fieldDesc->getLabel());
+        $this->assertSame(self::GPBTYPE_MESSAGE, $fieldDesc->getType());
+        $this->assertInstanceOf('\Google\Protobuf\Descriptor', $fieldDesc->getMessageType());
+        $this->assertFalse($fieldDesc->isMap());
+
+        // Repeated int field
+        $fieldDesc = $fieldDescMap[31];
+        $this->assertSame('repeated_int32', $fieldDesc->getName());
+        $this->assertSame(31, $fieldDesc->getNumber());
+        $this->assertSame(self::GPBLABEL_REPEATED, $fieldDesc->getLabel());
+        $this->assertSame(self::GPBTYPE_INT32, $fieldDesc->getType());
+        $this->assertFalse($fieldDesc->isMap());
+
+        // Repeated message field
+        $fieldDesc = $fieldDescMap[47];
+        $this->assertSame('repeated_message', $fieldDesc->getName());
+        $this->assertSame(47, $fieldDesc->getNumber());
+        $this->assertSame(self::GPBLABEL_REPEATED, $fieldDesc->getLabel());
+        $this->assertSame(self::GPBTYPE_MESSAGE, $fieldDesc->getType());
+        $this->assertInstanceOf('\Google\Protobuf\Descriptor', $fieldDesc->getMessageType());
+        $this->assertFalse($fieldDesc->isMap());
+
+        // Oneof int field
+        // Tested further in testOneofDescriptor()
+        $fieldDesc = $fieldDescMap[51];
+        $this->assertSame('oneof_int32', $fieldDesc->getName());
+        $this->assertSame(51, $fieldDesc->getNumber());
+        $this->assertSame(self::GPBLABEL_OPTIONAL, $fieldDesc->getLabel());
+        $this->assertSame(self::GPBTYPE_INT32, $fieldDesc->getType());
+        $this->assertFalse($fieldDesc->isMap());
+
+        // Map int-enum field
+        $fieldDesc = $fieldDescMap[71];
+        $this->assertSame('map_int32_enum', $fieldDesc->getName());
+        $this->assertSame(71, $fieldDesc->getNumber());
+        $this->assertSame(self::GPBLABEL_REPEATED, $fieldDesc->getLabel());
+        $this->assertSame(self::GPBTYPE_MESSAGE, $fieldDesc->getType());
+        $this->assertTrue($fieldDesc->isMap());
+        $mapDesc = $fieldDesc->getMessageType();
+        $this->assertSame('descriptors.TestDescriptorsMessage.MapInt32EnumEntry', $mapDesc->getFullName());
+        $this->assertSame(self::GPBTYPE_INT32, $mapDesc->getField(0)->getType());
+        $this->assertSame(self::GPBTYPE_ENUM, $mapDesc->getField(1)->getType());
+    }
+
+    /**
+     * @expectedException \Exception
+     */
+    public function testFieldDescriptorEnumException()
+    {
+        $pool = DescriptorPool::getGeneratedPool();
+        $desc = $pool->getDescriptorByClassName(get_class(new TestDescriptorsMessage()));
+        $fieldDesc = $desc->getField(0);
+        $fieldDesc->getEnumType();
+    }
+
+    /**
+     * @expectedException \Exception
+     */
+    public function testFieldDescriptorMessageException()
+    {
+        $pool = DescriptorPool::getGeneratedPool();
+        $desc = $pool->getDescriptorByClassName(get_class(new TestDescriptorsMessage()));
+        $fieldDesc = $desc->getField(0);
+        $fieldDesc->getMessageType();
+    }
+
+    #########################################################
+    # Test oneof descriptor.
+    #########################################################
+
+    public function testOneofDescriptor()
+    {
+        $pool = DescriptorPool::getGeneratedPool();
+        $desc = $pool->getDescriptorByClassName(get_class(new TestDescriptorsMessage()));
+
+        $fieldDescMap = $this->buildFieldMap($desc);
+        $fieldDesc = $fieldDescMap[51];
+
+        $oneofDesc = $desc->getOneofDecl(0);
+
+        $this->assertSame('my_oneof', $oneofDesc->getName());
+        $fieldDescFromOneof = $oneofDesc->getField(0);
+        $this->assertSame($fieldDesc, $fieldDescFromOneof);
+        $this->assertSame(1, $oneofDesc->getFieldCount());
+    }
+
+    private function buildFieldMap($desc)
+    {
+        $fieldDescMap = [];
+        for ($i = 0; $i < $desc->getFieldCount(); $i++) {
+            $fieldDesc = $desc->getField($i);
+            $fieldDescMap[$fieldDesc->getNumber()] = $fieldDesc;
+        }
+        return $fieldDescMap;
+    }
+}

+ 35 - 0
php/tests/proto/test_descriptors.proto

@@ -0,0 +1,35 @@
+syntax = "proto3";
+
+package descriptors;
+
+message TestDescriptorsMessage {
+  int32 optional_int32 = 1;
+  TestDescriptorsEnum optional_enum = 16;
+  Sub optional_message = 17;
+
+  // Repeated
+  repeated int32 repeated_int32 = 31;
+  repeated Sub repeated_message = 47;
+
+  oneof my_oneof {
+    int32 oneof_int32    = 51;
+  }
+
+  map<int32, EnumSub> map_int32_enum = 71;
+
+  message Sub {
+    int32 a = 1;
+    repeated int32 b = 2;
+  }
+
+  enum EnumSub {
+    ZERO = 0;
+    ONE = 1;
+  }
+}
+
+enum TestDescriptorsEnum {
+  ZERO = 0;
+  ONE  = 1;
+}
+

+ 1 - 1
php/tests/test.sh

@@ -8,7 +8,7 @@ set -e
 phpize && ./configure CFLAGS='-g -O0' && make
 phpize && ./configure CFLAGS='-g -O0' && make
 popd
 popd
 
 
-tests=( array_test.php encode_decode_test.php generated_class_test.php generated_phpdoc_test.php map_field_test.php well_known_test.php generated_service_test.php )
+tests=( array_test.php encode_decode_test.php generated_class_test.php generated_phpdoc_test.php map_field_test.php well_known_test.php generated_service_test.php descriptors_test.php )
 
 
 for t in "${tests[@]}"
 for t in "${tests[@]}"
 do
 do

+ 10 - 1
tests.sh

@@ -347,7 +347,16 @@ generate_php_test_proto() {
   # Generate test file
   # Generate test file
   rm -rf generated
   rm -rf generated
   mkdir generated
   mkdir generated
-  ../../src/protoc --php_out=generated proto/test.proto proto/test_include.proto proto/test_no_namespace.proto proto/test_prefix.proto proto/test_php_namespace.proto proto/test_empty_php_namespace.proto proto/test_service.proto proto/test_service_namespace.proto
+  ../../src/protoc --php_out=generated   \
+    proto/test.proto                     \
+    proto/test_include.proto             \
+    proto/test_no_namespace.proto        \
+    proto/test_prefix.proto              \
+    proto/test_php_namespace.proto       \
+    proto/test_empty_php_namespace.proto \
+    proto/test_service.proto             \
+    proto/test_service_namespace.proto   \
+    proto/test_descriptors.proto
   pushd ../../src
   pushd ../../src
   ./protoc --php_out=../php/tests/generated google/protobuf/empty.proto
   ./protoc --php_out=../php/tests/generated google/protobuf/empty.proto
   ./protoc --php_out=../php/tests/generated -I../php/tests -I. ../php/tests/proto/test_import_descriptor_proto.proto
   ./protoc --php_out=../php/tests/generated -I../php/tests -I. ../php/tests/proto/test_import_descriptor_proto.proto