Forráskód Böngészése

Merge pull request #3461 from TeBoring/3.4.x

Merge master into 3.4.x
Jisi Liu 8 éve
szülő
commit
3afcded28a
46 módosított fájl, 1627 hozzáadás és 126 törlés
  1. 10 1
      Makefile.am
  2. 2 2
      composer.json
  3. 11 9
      conformance/Makefile.am
  4. 3 1
      conformance/conformance.proto
  5. 14 14
      conformance/conformance_objc.m
  6. 3 1
      csharp/src/Google.Protobuf.Conformance/Conformance.cs
  7. 3 3
      csharp/src/Google.Protobuf.Test/TestProtos/TestMessagesProto3.cs
  8. 1 0
      docs/third_party.md
  9. 1 1
      examples/Makefile
  10. 7 0
      examples/README.txt
  11. 1 1
      jenkins/docker/Dockerfile
  12. 1 1
      jenkins/docker32/Dockerfile
  13. 2 6
      php/composer.json
  14. 477 15
      php/ext/google/protobuf/def.c
  15. 2 2
      php/ext/google/protobuf/encode_decode.c
  16. 5 5
      php/ext/google/protobuf/message.c
  17. 25 6
      php/ext/google/protobuf/protobuf.c
  18. 62 6
      php/ext/google/protobuf/protobuf.h
  19. 67 24
      php/ext/google/protobuf/storage.c
  20. 1 0
      php/phpunit.xml
  21. 100 0
      php/src/Google/Protobuf/Descriptor.php
  22. 76 0
      php/src/Google/Protobuf/DescriptorPool.php
  23. 79 0
      php/src/Google/Protobuf/EnumDescriptor.php
  24. 12 7
      php/src/Google/Protobuf/EnumValueDescriptor.php
  25. 117 0
      php/src/Google/Protobuf/FieldDescriptor.php
  26. 20 1
      php/src/Google/Protobuf/Internal/Descriptor.php
  27. 7 7
      php/src/Google/Protobuf/Internal/DescriptorPool.php
  28. 2 2
      php/src/Google/Protobuf/Internal/EnumBuilderContext.php
  29. 20 0
      php/src/Google/Protobuf/Internal/EnumDescriptor.php
  30. 6 0
      php/src/Google/Protobuf/Internal/FieldDescriptor.php
  31. 1 1
      php/src/Google/Protobuf/Internal/GPBUtil.php
  32. 41 0
      php/src/Google/Protobuf/Internal/GetPublicDescriptorTrait.php
  33. 43 0
      php/src/Google/Protobuf/Internal/HasPublicDescriptorTrait.php
  34. 2 2
      php/src/Google/Protobuf/Internal/MapEntry.php
  35. 2 4
      php/src/Google/Protobuf/Internal/Message.php
  36. 13 2
      php/src/Google/Protobuf/Internal/OneofDescriptor.php
  37. 75 0
      php/src/Google/Protobuf/OneofDescriptor.php
  38. 246 0
      php/tests/descriptors_test.php
  39. 13 0
      php/tests/generated_class_test.php
  40. 1 0
      php/tests/memory_leak_test.php
  41. 5 0
      php/tests/proto/test.proto
  42. 35 0
      php/tests/proto/test_descriptors.proto
  43. 1 1
      php/tests/test.sh
  44. 1 0
      src/google/protobuf/compiler/js/js_generator.cc
  45. 1 0
      src/google/protobuf/test_messages_proto3.proto
  46. 10 1
      tests.sh

+ 10 - 1
Makefile.am

@@ -600,6 +600,12 @@ php_EXTRA_DIST=                                                       \
   php/ext/google/protobuf/upb.c                                       \
   php/ext/google/protobuf/protobuf.c                                  \
   php/src/phpdoc.dist.xml                                             \
+  php/src/Google/Protobuf/Descriptor.php                              \
+  php/src/Google/Protobuf/DescriptorPool.php                          \
+  php/src/Google/Protobuf/EnumDescriptor.php                          \
+  php/src/Google/Protobuf/EnumValueDescriptor.php                     \
+  php/src/Google/Protobuf/FieldDescriptor.php                         \
+  php/src/Google/Protobuf/OneofDescriptor.php                         \
   php/src/Google/Protobuf/Internal/CodedInputStream.php               \
   php/src/Google/Protobuf/Internal/CodedOutputStream.php              \
   php/src/Google/Protobuf/Internal/DescriptorPool.php                 \
@@ -612,7 +618,6 @@ php_EXTRA_DIST=                                                       \
   php/src/Google/Protobuf/Internal/EnumDescriptorProto.php            \
   php/src/Google/Protobuf/Internal/EnumOptions.php                    \
   php/src/Google/Protobuf/Internal/EnumValueDescriptorProto.php       \
-  php/src/Google/Protobuf/Internal/EnumValueDescriptor.php            \
   php/src/Google/Protobuf/Internal/EnumValueOptions.php               \
   php/src/Google/Protobuf/Internal/FieldDescriptorProto_Label.php     \
   php/src/Google/Protobuf/Internal/FieldDescriptorProto.php           \
@@ -628,6 +633,7 @@ php_EXTRA_DIST=                                                       \
   php/src/Google/Protobuf/Internal/FileOptions.php                    \
   php/src/Google/Protobuf/Internal/GeneratedCodeInfo_Annotation.php   \
   php/src/Google/Protobuf/Internal/GeneratedCodeInfo.php              \
+  php/src/Google/Protobuf/Internal/GetPublicDescriptorTrait.php       \
   php/src/Google/Protobuf/Internal/GPBDecodeException.php             \
   php/src/Google/Protobuf/Internal/GPBJsonWire.php                    \
   php/src/Google/Protobuf/Internal/GPBLabel.php                       \
@@ -635,6 +641,7 @@ php_EXTRA_DIST=                                                       \
   php/src/Google/Protobuf/Internal/GPBUtil.php                        \
   php/src/Google/Protobuf/Internal/GPBWireType.php                    \
   php/src/Google/Protobuf/Internal/GPBWire.php                        \
+  php/src/Google/Protobuf/Internal/HasPublicDescriptorTrait.php       \
   php/src/Google/Protobuf/Internal/MapEntry.php                       \
   php/src/Google/Protobuf/Internal/MapFieldIter.php                   \
   php/src/Google/Protobuf/Internal/MapField.php                       \
@@ -661,6 +668,7 @@ php_EXTRA_DIST=                                                       \
   php/tests/array_test.php                                            \
   php/tests/autoload.php                                              \
   php/tests/compatibility_test.sh                                     \
+  php/tests/descriptors_test.php                                      \
   php/tests/encode_decode_test.php                                    \
   php/tests/gdb_test.sh                                               \
   php/tests/generated_class_test.php                                  \
@@ -669,6 +677,7 @@ php_EXTRA_DIST=                                                       \
   php/tests/map_field_test.php                                        \
   php/tests/memory_leak_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_import_descriptor_proto.proto                  \
   php/tests/proto/test_include.proto                                  \

+ 2 - 2
composer.json

@@ -16,8 +16,8 @@
   },
   "autoload": {
     "psr-4": {
-      "Google\\Protobuf\\Internal\\": "php/src/Google/Protobuf/Internal",
-      "GPBMetadata\\Google\\Protobuf\\Internal\\": "php/src/GPBMetadata/Google/Protobuf/Internal"
+      "Google\\Protobuf\\": "php/src/Google/Protobuf",
+      "GPBMetadata\\Google\\Protobuf\\": "php/src/GPBMetadata/Google/Protobuf"
     }
   }
 }

+ 11 - 9
conformance/Makefile.am

@@ -2,12 +2,12 @@
 
 conformance_protoc_inputs =                                    \
   conformance.proto                                            \
-  $(top_srcdir)/src/google/protobuf/test_messages_proto3.proto 
-  
-# proto2 input files, should be separated with proto3, as we 
-# can't generate proto2 files for ruby, php and objc   
+  $(top_srcdir)/src/google/protobuf/test_messages_proto3.proto
+
+# proto2 input files, should be separated with proto3, as we
+# can't generate proto2 files for ruby, php and objc
 conformance_proto2_protoc_inputs =                             \
-  $(top_srcdir)/src/google/protobuf/test_messages_proto2.proto                            
+  $(top_srcdir)/src/google/protobuf/test_messages_proto2.proto
 
 well_known_type_protoc_inputs =                                \
   $(top_srcdir)/src/google/protobuf/any.proto                  \
@@ -86,6 +86,8 @@ other_language_protoc_outputs =                                \
   google/protobuf/struct.pb.h                                  \
   google/protobuf/struct.rb                                    \
   google/protobuf/struct_pb2.py                                \
+  google/protobuf/TestMessagesProto2.pbobjc.h                  \
+  google/protobuf/TestMessagesProto2.pbobjc.m                  \
   google/protobuf/TestMessagesProto3.pbobjc.h                  \
   google/protobuf/TestMessagesProto3.pbobjc.m                  \
   google/protobuf/test_messages_proto3.pb.cc                   \
@@ -228,7 +230,7 @@ if OBJC_CONFORMANCE_TEST
 bin_PROGRAMS += conformance-objc
 
 conformance_objc_SOURCES = conformance_objc.m ../objectivec/GPBProtocolBuffers.m
-nodist_conformance_objc_SOURCES = Conformance.pbobjc.m google/protobuf/TestMessagesProto3.pbobjc.m
+nodist_conformance_objc_SOURCES = Conformance.pbobjc.m google/protobuf/TestMessagesProto2.pbobjc.m google/protobuf/TestMessagesProto3.pbobjc.m
 # On travis, the build fails without the isysroot because whatever system
 # headers are being found don't include generics support for
 # NSArray/NSDictionary, the only guess is their image at one time had an odd
@@ -237,7 +239,7 @@ conformance_objc_CPPFLAGS = -I$(top_srcdir)/objectivec -isysroot `xcrun --sdk ma
 conformance_objc_LDFLAGS = -framework Foundation
 # Explicit dep beacuse BUILT_SOURCES are only done before a "make all/check"
 # so a direct "make test_objc" could fail if parallel enough.
-conformance_objc-conformance_objc.$(OBJEXT): Conformance.pbobjc.h google/protobuf/TestMessagesProto3.pbobjc.h
+conformance_objc-conformance_objc.$(OBJEXT): Conformance.pbobjc.h google/protobuf/TestMessagesProto2.pbobjc.h google/protobuf/TestMessagesProto3.pbobjc.h
 
 endif
 
@@ -253,7 +255,7 @@ if USE_EXTERNAL_PROTOC
 # Some implementations include pre-generated versions of well-known types.
 protoc_middleman: $(conformance_protoc_inputs) $(conformance_proto2_protoc_inputs) $(well_known_type_protoc_inputs) google-protobuf
 	$(PROTOC) -I$(srcdir) -I$(top_srcdir) --cpp_out=. --java_out=. --ruby_out=. --objc_out=. --python_out=. --php_out=. --js_out=import_style=commonjs,binary:. $(conformance_protoc_inputs)
-	$(PROTOC) -I$(srcdir) -I$(top_srcdir) --cpp_out=. --java_out=. --python_out=. --js_out=import_style=commonjs,binary:. $(conformance_proto2_protoc_inputs)
+	$(PROTOC) -I$(srcdir) -I$(top_srcdir) --cpp_out=. --java_out=. --objc_out=. --python_out=. --js_out=import_style=commonjs,binary:. $(conformance_proto2_protoc_inputs)
 	$(PROTOC) -I$(srcdir) -I$(top_srcdir) --cpp_out=. --java_out=. --ruby_out=. --python_out=. --php_out=. --js_out=import_style=commonjs,binary:google-protobuf $(well_known_type_protoc_inputs)
 	## $(PROTOC) -I$(srcdir) -I$(top_srcdir) --java_out=lite:lite $(conformance_protoc_inputs) $(well_known_type_protoc_inputs)
 	touch protoc_middleman
@@ -265,7 +267,7 @@ else
 # building out-of-tree.
 protoc_middleman: $(top_srcdir)/src/protoc$(EXEEXT) $(conformance_protoc_inputs) $(conformance_proto2_protoc_inputs) $(well_known_type_protoc_inputs) google-protobuf
 	oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --cpp_out=$$oldpwd --java_out=$$oldpwd --ruby_out=$$oldpwd --objc_out=$$oldpwd --python_out=$$oldpwd --php_out=$$oldpwd --js_out=import_style=commonjs,binary:$$oldpwd $(conformance_protoc_inputs) )
-	oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --cpp_out=$$oldpwd --java_out=$$oldpwd --python_out=$$oldpwd --js_out=import_style=commonjs,binary:$$oldpwd $(conformance_proto2_protoc_inputs) )
+	oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --cpp_out=$$oldpwd --java_out=$$oldpwd --objc_out=. --python_out=$$oldpwd --js_out=import_style=commonjs,binary:$$oldpwd $(conformance_proto2_protoc_inputs) )
 	oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --cpp_out=$$oldpwd --java_out=$$oldpwd --ruby_out=$$oldpwd --python_out=$$oldpwd --php_out=$$oldpwd --js_out=import_style=commonjs,binary:$$oldpwd/google-protobuf $(well_known_type_protoc_inputs) )
 	## @mkdir -p lite
 	## oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --java_out=lite:$$oldpwd/lite $(conformance_protoc_inputs) $(well_known_type_protoc_inputs) )

+ 3 - 1
conformance/conformance.proto

@@ -78,7 +78,9 @@ message ConformanceRequest {
   // Which format should the testee serialize its message to?
   WireFormat requested_output_format = 3;
 
-  // should be set to either "proto2" or "proto3"
+  // The full name for the test message to use; for the moment, either:
+  // protobuf_test_messages.proto3.TestAllTypesProto3 or
+  // protobuf_test_messages.proto2.TestAllTypesProto2.
   string message_type = 4;
 }
 

+ 14 - 14
conformance/conformance_objc.m

@@ -31,6 +31,7 @@
 #import <Foundation/Foundation.h>
 
 #import "Conformance.pbobjc.h"
+#import "google/protobuf/TestMessagesProto2.pbobjc.h"
 #import "google/protobuf/TestMessagesProto3.pbobjc.h"
 
 static void Die(NSString *format, ...) __dead2;
@@ -63,7 +64,7 @@ static NSData *CheckedReadDataOfLength(NSFileHandle *handle, NSUInteger numBytes
 
 static ConformanceResponse *DoTest(ConformanceRequest *request) {
   ConformanceResponse *response = [ConformanceResponse message];
-  TestAllTypesProto3 *testMessage = nil;
+  GPBMessage *testMessage = nil;
 
   switch (request.payloadOneOfCase) {
     case ConformanceRequest_Payload_OneOfCase_GPBUnsetOneOfCase:
@@ -71,20 +72,19 @@ static ConformanceResponse *DoTest(ConformanceRequest *request) {
       break;
 
     case ConformanceRequest_Payload_OneOfCase_ProtobufPayload: {
-      if ([request.messageType isEqualToString:@"protobuf_test_messages.proto3.TestAllTypesProto3"]) {
-        NSError *error = nil;
-        testMessage = [TestAllTypesProto3 parseFromData:request.protobufPayload
-                                            error:&error];
-        if (!testMessage) {
-          response.parseError =
-              [NSString stringWithFormat:@"Parse error: %@", error];
-        }
-      } else if ([request.messageType isEqualToString:@"protobuf_test_messages.proto2.TestAllTypesProto2"]) {
-	response.skipped = @"ObjC doesn't support proto2";
-	break;
+      Class msgClass = nil;
+      if ([request.messageType isEqual:@"protobuf_test_messages.proto3.TestAllTypesProto3"]) {
+        msgClass = [Proto3TestAllTypesProto3 class];
+      } else if ([request.messageType isEqual:@"protobuf_test_messages.proto2.TestAllTypesProto2"]) {
+        msgClass = [TestAllTypesProto2 class];
       } else {
-	Die(@"Protobuf request doesn't have specific payload type");
-	break;
+        Die(@"Protobuf request had an unknown message_type: %@", request.messageType);
+      }
+      NSError *error = nil;
+      testMessage = [msgClass parseFromData:request.protobufPayload error:&error];
+      if (!testMessage) {
+        response.parseError =
+            [NSString stringWithFormat:@"Parse error: %@", error];
       }
       break;
     }

+ 3 - 1
csharp/src/Google.Protobuf.Conformance/Conformance.cs

@@ -142,7 +142,9 @@ namespace Conformance {
     public const int MessageTypeFieldNumber = 4;
     private string messageType_ = "";
     /// <summary>
-    /// should be set to either "proto2" or "proto3"
+    /// The full name for the test message to use; for the moment, either:
+    /// protobuf_test_messages.proto3.TestAllTypesProto3 or
+    /// protobuf_test_messages.proto2.TestAllTypesProto2.
     /// </summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public string MessageType {

+ 3 - 3
csharp/src/Google.Protobuf.Test/TestProtos/TestMessagesProto3.cs

@@ -198,9 +198,9 @@ namespace ProtobufTestMessages.Proto3 {
             "AjgBIjkKCk5lc3RlZEVudW0SBwoDRk9PEAASBwoDQkFSEAESBwoDQkFaEAIS",
             "EAoDTkVHEP///////////wFCDQoLb25lb2ZfZmllbGQiGwoORm9yZWlnbk1l",
             "c3NhZ2USCQoBYxgBIAEoBSpACgtGb3JlaWduRW51bRIPCgtGT1JFSUdOX0ZP",
-            "TxAAEg8KC0ZPUkVJR05fQkFSEAESDwoLRk9SRUlHTl9CQVoQAkIvCihjb20u",
-            "Z29vZ2xlLnByb3RvYnVmX3Rlc3RfbWVzc2FnZXMucHJvdG8zSAH4AQFiBnBy",
-            "b3RvMw=="));
+            "TxAAEg8KC0ZPUkVJR05fQkFSEAESDwoLRk9SRUlHTl9CQVoQAkI4Cihjb20u",
+            "Z29vZ2xlLnByb3RvYnVmX3Rlc3RfbWVzc2FnZXMucHJvdG8zSAH4AQGiAgZQ",
+            "cm90bzNiBnByb3RvMw=="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.AnyReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.DurationReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.FieldMaskReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.StructReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.WrappersReflection.Descriptor, },
           new pbr::GeneratedClrTypeInfo(new[] {typeof(global::ProtobufTestMessages.Proto3.ForeignEnum), }, new pbr::GeneratedClrTypeInfo[] {

+ 1 - 0
docs/third_party.md

@@ -156,3 +156,4 @@ There are miscellaneous other things you may find useful as a Protocol Buffers d
 * [Linter for .proto files](https://github.com/ckaznocha/protoc-gen-lint)
 * [Protocol Buffers Dynamic Schema - create protobuf schemas programmatically (Java)] (https://github.com/os72/protobuf-dynamic)
 * [Make protoc plugins in NodeJS](https://github.com/konsumer/node-protoc-plugin)
+* [ProfaneDB - A Protocol Buffers database](https://profanedb.gitlab.io)

+ 1 - 1
examples/Makefile

@@ -51,7 +51,7 @@ list_people_gotest: list_people.go list_people_go
 	go test list_people.go list_people_test.go
 
 javac_middleman: AddPerson.java ListPeople.java protoc_middleman
-	javac AddPerson.java ListPeople.java com/example/tutorial/AddressBookProtos.java
+	javac -cp ../java/core/target/*.jar AddPerson.java ListPeople.java com/example/tutorial/AddressBookProtos.java
 	@touch javac_middleman
 
 add_person_java: javac_middleman

+ 7 - 0
examples/README.txt

@@ -28,6 +28,13 @@ These examples are part of the Protocol Buffers tutorial, located at:
 "-lpthread" from the linker commands (perhaps replacing it with something else).
 We didn't do this automatically because we wanted to keep the example simple.
 
+## Java ##
+
+protobuf-java-*.jar can be generated by:
+  cd ../java
+  mvn package
+and will be used by "make java"
+
 ## Go ##
 
 The Go example requires a plugin to the protocol buffer compiler, so it is not

+ 1 - 1
jenkins/docker/Dockerfile

@@ -182,7 +182,7 @@ RUN cd /tmp && \
   rm -rf protobuf && \
   git clone https://github.com/google/protobuf.git && \
   cd protobuf && \
-  git reset --hard 8d97b3d8b5a33650e822460b3b561802c969e86e && \
+  git reset --hard 49b44bff2b6257a119f9c6a342d6151c736586b8 && \
   cd php && \
   ln -sfn /usr/local/php-5.5/bin/php /usr/bin/php && \
   ln -sfn /usr/local/php-5.5/bin/php-config /usr/bin/php-config && \

+ 1 - 1
jenkins/docker32/Dockerfile

@@ -98,7 +98,7 @@ RUN composer config -g -- secure-http false
 RUN cd /tmp && \
   git clone https://github.com/google/protobuf.git && \
   cd protobuf/php && \
-  git reset --hard 8d97b3d8b5a33650e822460b3b561802c969e86e && \
+  git reset --hard 49b44bff2b6257a119f9c6a342d6151c736586b8 && \
   ln -sfn /usr/local/php-5.5/bin/php /usr/bin/php && \
   ln -sfn /usr/local/php-5.5/bin/php-config /usr/bin/php-config && \
   ln -sfn /usr/local/php-5.5/bin/phpize /usr/bin/phpize && \

+ 2 - 6
php/composer.json

@@ -13,12 +13,8 @@
   },
   "autoload": {
     "psr-4": {
-      "Foo\\": "tests/generated/Foo",
-      "Bar\\": "tests/generated/Bar",
-      "Google\\Protobuf\\": "tests/generated/Google/Protobuf",
-      "Google\\Protobuf\\Internal\\": "src/Google/Protobuf/Internal",
-      "GPBMetadata\\": "tests/generated/GPBMetadata",
-      "GPBMetadata\\Google\\Protobuf\\Internal\\": "src/GPBMetadata/Google/Protobuf/Internal",
+      "Google\\Protobuf\\": "src/Google/Protobuf",
+      "GPBMetadata\\Google\\Protobuf\\": "src/GPBMetadata/Google/Protobuf",
       "": "tests/generated"
     }
   }

+ 477 - 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_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_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_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
 // -----------------------------------------------------------------------------
@@ -169,10 +184,16 @@ void gpb_type_init(TSRMLS_D) {
 // -----------------------------------------------------------------------------
 
 static zend_function_entry descriptor_methods[] = {
+  PHP_ME(Descriptor, getClass, NULL, ZEND_ACC_PUBLIC)
+  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
 };
 
-DEFINE_CLASS(Descriptor, descriptor, "Google\\Protobuf\\Internal\\Descriptor");
+DEFINE_CLASS(Descriptor, descriptor, "Google\\Protobuf\\Descriptor");
 
 static void descriptor_free_c(Descriptor *self TSRMLS_DC) {
   if (self->layout) {
@@ -203,7 +224,6 @@ static void descriptor_free_c(Descriptor *self 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->layout = NULL;
   desc->klass = NULL;
@@ -215,30 +235,217 @@ static void descriptor_init_c_instance(Descriptor *desc TSRMLS_DC) {
   desc->json_serialize_handlers_preserve = NULL;
 }
 
+PHP_METHOD(Descriptor, getClass) {
+  Descriptor *intern = UNBOX(Descriptor, getThis());
+#if PHP_MAJOR_VERSION < 7
+  const char* classname = intern->klass->name;
+#else
+  const char* classname = ZSTR_VAL(intern->klass->name);
+#endif
+  PHP_PROTO_RETVAL_STRINGL(classname, strlen(classname), 1);
+}
+
+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
 // -----------------------------------------------------------------------------
 
 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
 };
 
 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_init_c_instance(EnumDescriptor *self TSRMLS_DC) {
-  // zend_object_std_init(&self->std, enum_descriptor_type TSRMLS_CC);
   self->enumdef = 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
 // -----------------------------------------------------------------------------
 
+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) {
   switch (type) {
 #define CASE(descriptor_type, type)           \
@@ -272,6 +479,150 @@ upb_fieldtype_t to_fieldtype(upb_descriptortype_t type) {
   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
 // -----------------------------------------------------------------------------
@@ -279,52 +630,79 @@ upb_fieldtype_t to_fieldtype(upb_descriptortype_t type) {
 static zend_function_entry descriptor_pool_methods[] = {
   PHP_ME(DescriptorPool, getGeneratedPool, NULL,
          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
 };
 
 DEFINE_CLASS(DescriptorPool, descriptor_pool,
+             "Google\\Protobuf\\DescriptorPool");
+DEFINE_CLASS(InternalDescriptorPool, internal_descriptor_pool,
              "Google\\Protobuf\\Internal\\DescriptorPool");
 
 // wrapper of generated pool
 #if PHP_MAJOR_VERSION < 7
 zval* generated_pool_php;
+zval* internal_generated_pool_php;
 #else
 zend_object *generated_pool_php;
+zend_object *internal_generated_pool_php;
 #endif
-DescriptorPool *generated_pool;  // The actual generated pool
+InternalDescriptorPool *generated_pool;  // The actual generated pool
 
 static void init_generated_pool_once(TSRMLS_D) {
-  if (generated_pool_php == NULL) {
+  if (generated_pool == NULL) {
 #if PHP_MAJOR_VERSION < 7
     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(
                                      descriptor_pool_type TSRMLS_CC));
-    generated_pool = UNBOX(DescriptorPool, generated_pool_php);
 #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 =
         descriptor_pool_type->create_object(descriptor_pool_type TSRMLS_CC);
-    generated_pool = (DescriptorPool *)((char *)generated_pool_php -
-                                        XtOffsetOf(DescriptorPool, std));
 #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();
 
   ALLOC_HASHTABLE(pool->pending_list);
   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);
 
   zend_hash_destroy(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) {
   // Verify that an entry exists with integer value 0. (This is the default
   // value.)
@@ -358,6 +736,16 @@ PHP_METHOD(DescriptorPool, getGeneratedPool) {
 #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,
                                 char *class_name) {
   size_t i = 0, j;
@@ -455,7 +843,7 @@ static void convert_to_class_name_inplace(const char *package,
   memcpy(classname + i, prefix, prefix_len);
 }
 
-PHP_METHOD(DescriptorPool, internalAddGeneratedFile) {
+PHP_METHOD(InternalDescriptorPool, internalAddGeneratedFile) {
   char *data = NULL;
   PHP_PROTO_SIZE data_len;
   upb_filedef **files;
@@ -466,7 +854,7 @@ PHP_METHOD(DescriptorPool, internalAddGeneratedFile) {
     return;
   }
 
-  DescriptorPool *pool = UNBOX(DescriptorPool, getThis());
+  InternalDescriptorPool *pool = UNBOX(InternalDescriptorPool, getThis());
   CHECK_UPB(files = upb_loaddescriptor(data, data_len, &pool, &status),
             "Parse binary descriptors to internal descriptors failed");
 
@@ -550,3 +938,77 @@ PHP_METHOD(DescriptorPool, internalAddGeneratedFile) {
   upb_filedef_unref(files[0], &pool);
   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
+}

+ 2 - 2
php/ext/google/protobuf/encode_decode.c

@@ -716,7 +716,7 @@ static void *oneofbytes_handler(void *closure,
   DEREF(message_data(msg), oneofdata->case_ofs, uint32_t) =
       oneofdata->oneof_case_num;
   DEREF(message_data(msg), oneofdata->ofs, CACHED_VALUE*) =
-      &(msg->std.properties_table)[oneofdata->property_ofs];
+      OBJ_PROP(&msg->std, oneofdata->property_ofs);
 
    return empty_php_string(DEREF(
        message_data(msg), oneofdata->ofs, CACHED_VALUE*));
@@ -747,7 +747,7 @@ static void* oneofsubmsg_handler(void* closure, const void* hd) {
 
     // Create new message.
     DEREF(message_data(msg), oneofdata->ofs, CACHED_VALUE*) =
-        &(msg->std.properties_table)[oneofdata->property_ofs];
+        OBJ_PROP(&msg->std, oneofdata->property_ofs);
     ZVAL_OBJ(CACHED_PTR_TO_ZVAL_PTR(
         DEREF(message_data(msg), oneofdata->ofs, CACHED_VALUE*)),
         subklass->create_object(subklass TSRMLS_CC));

+ 5 - 5
php/ext/google/protobuf/message.c

@@ -172,7 +172,7 @@ static zval* message_get_property(zval* object, zval* member, int type,
       zend_get_property_info(Z_OBJCE_P(object), member, true TSRMLS_CC);
   return layout_get(
       self->descriptor->layout, message_data(self), field,
-      &Z_OBJ_P(object)->properties_table[property_info->offset] TSRMLS_CC);
+      OBJ_PROP(Z_OBJ_P(object), property_info->offset) TSRMLS_CC);
 #else
   property_info =
       zend_get_property_info(Z_OBJCE_P(object), Z_STR_P(member), true);
@@ -222,7 +222,7 @@ void custom_data_init(const zend_class_entry* ce,
   // case a collection happens during object creation in layout_init().
   intern->descriptor = desc;
   layout_init(desc->layout, message_data(intern),
-              intern->std.properties_table PHP_PROTO_TSRMLS_CC);
+              &intern->std PHP_PROTO_TSRMLS_CC);
 }
 
 void build_class_from_descriptor(
@@ -265,8 +265,7 @@ PHP_METHOD(Message, clear) {
   zend_class_entry* ce = desc->klass;
 
   object_properties_init(&msg->std, ce);
-  layout_init(desc->layout, message_data(msg),
-              msg->std.properties_table TSRMLS_CC);
+  layout_init(desc->layout, message_data(msg), &msg->std TSRMLS_CC);
 }
 
 PHP_METHOD(Message, mergeFrom) {
@@ -301,7 +300,8 @@ PHP_METHOD(Message, readOneof) {
 
   int property_cache_index =
       msg->descriptor->layout->fields[upb_fielddef_index(field)].cache_index;
-  zval* property_ptr = OBJ_PROP(Z_OBJ_P(getThis()), property_cache_index);
+  zval* property_ptr = CACHED_PTR_TO_ZVAL_PTR(
+      OBJ_PROP(Z_OBJ_P(getThis()), property_cache_index));
 
   // Unlike singular fields, oneof fields share cached property. So we cannot
   // let lay_get modify the cached property. Instead, we pass in the return

+ 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;
   if (php_proto_zend_hash_index_find_mem(t, (zend_ulong)def, (void**)&value) ==
       FAILURE) {
-    zend_error(E_ERROR, "PHP object not found for given definition.\n");
     return NULL;
   }
   return *value;
@@ -166,6 +165,7 @@ static PHP_RINIT_FUNCTION(protobuf) {
 
   generated_pool = NULL;
   generated_pool_php = NULL;
+  internal_generated_pool_php = NULL;
 
   return 0;
 }
@@ -182,21 +182,40 @@ static PHP_RSHUTDOWN_FUNCTION(protobuf) {
     zval_dtor(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
 
   return 0;
 }
 
 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_iter_init(TSRMLS_C);
+  message_init(TSRMLS_C);
+  oneof_descriptor_init(TSRMLS_C);
   repeated_field_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);
 
   return 0;

+ 62 - 6
php/ext/google/protobuf/protobuf.h

@@ -151,7 +151,7 @@
 
 #define PHP_PROTO_GLOBAL_UNINITIALIZED_ZVAL EG(uninitialized_zval_ptr)
 
-#define OBJ_PROP(PROPERTIES, OFFSET) (PROPERTIES)->properties_table[OFFSET]
+#define OBJ_PROP(OBJECT, OFFSET) &((OBJECT)->properties_table[OFFSET])
 
 #define php_proto_zval_ptr_dtor(zval_ptr) \
   zval_ptr_dtor(&(zval_ptr))
@@ -185,6 +185,7 @@
 #define HASHTABLE_VALUE_DTOR ZVAL_PTR_DTOR
 
 #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) \
   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 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) \
   OBJ_TYPE* OBJ;                                                            \
@@ -397,7 +399,9 @@ static inline int php_proto_zend_lookup_class(
 struct DescriptorPool;
 struct Descriptor;
 struct EnumDescriptor;
+struct EnumValueDescriptor;
 struct FieldDescriptor;
+struct InternalDescriptorPool;
 struct MessageField;
 struct MessageHeader;
 struct MessageLayout;
@@ -410,7 +414,9 @@ struct Oneof;
 typedef struct DescriptorPool DescriptorPool;
 typedef struct Descriptor Descriptor;
 typedef struct EnumDescriptor EnumDescriptor;
+typedef struct EnumValueDescriptor EnumValueDescriptor;
 typedef struct FieldDescriptor FieldDescriptor;
+typedef struct InternalDescriptorPool InternalDescriptorPool;
 typedef struct MessageField MessageField;
 typedef struct MessageHeader MessageHeader;
 typedef struct MessageLayout MessageLayout;
@@ -431,9 +437,12 @@ ZEND_END_MODULE_GLOBALS(protobuf)
 void descriptor_init(TSRMLS_D);
 void enum_descriptor_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 map_field_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_iter_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)
+  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;
   HashTable* pending_list;
 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
 #if PHP_MAJOR_VERSION < 7
 extern zval* generated_pool_php;
+extern zval* internal_generated_pool_php;
 void descriptor_pool_free(void* object TSRMLS_DC);
+void internal_descriptor_pool_free(void* object TSRMLS_DC);
 #else
 extern zend_object *generated_pool_php;
+extern zend_object *internal_generated_pool_php;
 void descriptor_pool_free(zend_object* object);
+void internal_descriptor_pool_free(zend_object* object);
 #endif
-extern DescriptorPool* generated_pool;  // The actual generated pool
+extern InternalDescriptorPool* generated_pool;  // The actual generated pool
 
 PHP_PROTO_WRAP_OBJECT_START(Descriptor)
   const upb_msgdef* msgdef;
@@ -487,6 +508,13 @@ PHP_PROTO_WRAP_OBJECT_START(Descriptor)
   const upb_handlers* json_serialize_handlers_preserve;
 PHP_PROTO_WRAP_OBJECT_END
 
+PHP_METHOD(Descriptor, getClass);
+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;
 
 void descriptor_name_set(Descriptor *desc, const char *name);
@@ -495,14 +523,36 @@ PHP_PROTO_WRAP_OBJECT_START(FieldDescriptor)
   const upb_fielddef* fielddef;
 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)
   const upb_enumdef* enumdef;
   zend_class_entry* klass;  // begins as NULL
-  // VALUE module;  // begins as nil
 PHP_PROTO_WRAP_OBJECT_END
 
+PHP_METHOD(EnumDescriptor, getValue);
+PHP_METHOD(EnumDescriptor, getValueCount);
+
 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.
 // -----------------------------------------------------------------------------
@@ -594,7 +644,7 @@ PHP_PROTO_WRAP_OBJECT_END
 
 MessageLayout* create_layout(const upb_msgdef* msgdef);
 void layout_init(MessageLayout* layout, void* storage,
-                 CACHED_VALUE* properties_table PHP_PROTO_TSRMLS_DC);
+                 zend_object* object PHP_PROTO_TSRMLS_DC);
 zval* layout_get(MessageLayout* layout, const void* storage,
                  const upb_fielddef* field, CACHED_VALUE* cache TSRMLS_DC);
 void layout_set(MessageLayout* layout, MessageHeader* header,
@@ -819,6 +869,12 @@ PHP_PROTO_WRAP_OBJECT_START(Oneof)
   char value[NATIVE_SLOT_MAX_SIZE];
 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
 // safe because field numbers are used as case identifiers, and no field can
 // have a number of 0.

+ 67 - 24
php/ext/google/protobuf/storage.c

@@ -275,9 +275,6 @@ void native_slot_init(upb_fieldtype_t type, void* memory, CACHED_VALUE* cache) {
       break;
     case UPB_TYPE_STRING:
     case UPB_TYPE_BYTES:
-      DEREF(memory, CACHED_VALUE*) = cache;
-      ZVAL_EMPTY_STRING(CACHED_PTR_TO_ZVAL_PTR(cache));
-      break;
     case UPB_TYPE_MESSAGE:
       DEREF(memory, CACHED_VALUE*) = cache;
       break;
@@ -586,6 +583,8 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) {
   upb_msg_oneof_iter oit;
   size_t off = 0;
   int i = 0;
+  TSRMLS_FETCH();
+  Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj(msgdef));
 
   layout->fields = ALLOC_N(MessageField, nfields);
 
@@ -612,7 +611,37 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) {
     layout->fields[upb_fielddef_index(field)].offset = off;
     layout->fields[upb_fielddef_index(field)].case_offset =
         MESSAGE_FIELD_NO_CASE;
-    layout->fields[upb_fielddef_index(field)].cache_index = i++;
+
+    const char* fieldname = upb_fielddef_name(field);
+
+#if PHP_MAJOR_VERSION < 7 || (PHP_MAJOR_VERSION == 7 && PHP_MINOR_VERSION == 0)
+    zend_class_entry* old_scope = EG(scope);
+    EG(scope) = desc->klass;
+#else
+    zend_class_entry* old_scope = EG(fake_scope);
+    EG(fake_scope) = desc->klass;
+#endif
+
+#if PHP_MAJOR_VERSION < 7
+    zval member;
+    ZVAL_STRINGL(&member, fieldname, strlen(fieldname), 0);
+    zend_property_info* property_info =
+        zend_get_property_info(desc->klass, &member, true TSRMLS_CC);
+#else
+    zend_string* member = zend_string_init(fieldname, strlen(fieldname), 1);
+    zend_property_info* property_info =
+        zend_get_property_info(desc->klass, member, true);
+    zend_string_release(member);
+#endif
+
+#if PHP_MAJOR_VERSION < 7 || (PHP_MAJOR_VERSION == 7 && PHP_MINOR_VERSION == 0)
+    EG(scope) = old_scope;
+#else
+    EG(fake_scope) = old_scope;
+#endif
+
+    layout->fields[upb_fielddef_index(field)].cache_index =
+        property_info->offset;
     off += field_size;
   }
 
@@ -640,11 +669,40 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) {
     // Align the offset .
     off = align_up_to( off, field_size);
     // Assign all fields in the oneof this same offset.
+    const char* oneofname = upb_oneofdef_name(oneof);
     for (upb_oneof_begin(&fit, oneof); !upb_oneof_done(&fit);
          upb_oneof_next(&fit)) {
       const upb_fielddef* field = upb_oneof_iter_field(&fit);
       layout->fields[upb_fielddef_index(field)].offset = off;
-      layout->fields[upb_fielddef_index(field)].cache_index = i;
+
+#if PHP_MAJOR_VERSION < 7 || (PHP_MAJOR_VERSION == 7 && PHP_MINOR_VERSION == 0)
+      zend_class_entry* old_scope = EG(scope);
+      EG(scope) = desc->klass;
+#else
+      zend_class_entry* old_scope = EG(fake_scope);
+      EG(fake_scope) = desc->klass;
+#endif
+
+#if PHP_MAJOR_VERSION < 7
+      zval member;
+      ZVAL_STRINGL(&member, oneofname, strlen(oneofname), 0);
+      zend_property_info* property_info =
+          zend_get_property_info(desc->klass, &member, true TSRMLS_CC);
+#else
+      zend_string* member = zend_string_init(oneofname, strlen(oneofname), 1);
+      zend_property_info* property_info =
+          zend_get_property_info(desc->klass, member, true);
+      zend_string_release(member);
+#endif
+
+#if PHP_MAJOR_VERSION < 7 || (PHP_MAJOR_VERSION == 7 && PHP_MINOR_VERSION == 0)
+      EG(scope) = old_scope;
+#else
+      EG(fake_scope) = old_scope;
+#endif
+
+      layout->fields[upb_fielddef_index(field)].cache_index =
+          property_info->offset;
     }
     i++;
     off += field_size;
@@ -683,7 +741,7 @@ void free_layout(MessageLayout* layout) {
 }
 
 void layout_init(MessageLayout* layout, void* storage,
-                 CACHED_VALUE* properties_table PHP_PROTO_TSRMLS_DC) {
+                 zend_object* object PHP_PROTO_TSRMLS_DC) {
   int i;
   upb_msg_field_iter it;
   for (upb_msg_field_begin(&it, layout->msgdef), i = 0; !upb_msg_field_done(&it);
@@ -692,22 +750,7 @@ void layout_init(MessageLayout* layout, void* storage,
     void* memory = slot_memory(layout, storage, field);
     uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
     int cache_index = slot_property_cache(layout, storage, field);
-    CACHED_VALUE* property_ptr = &properties_table[cache_index];
-
-    // Clean up initial value by generated code. In the generated code of
-    // previous versions, each php field is given an initial value. However, the
-    // order to initialize these fields may not be consistent with the order of
-    // upb fields.
-    if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(property_ptr)) == IS_STRING) {
-#if PHP_MAJOR_VERSION < 7
-      if (!IS_INTERNED(Z_STRVAL_PP(property_ptr))) {
-        FREE(Z_STRVAL_PP(property_ptr));
-      }
-#else
-      zend_string_release(Z_STR_P(property_ptr));
-#endif
-    }
-    ZVAL_NULL(CACHED_PTR_TO_ZVAL_PTR(property_ptr));
+    CACHED_VALUE* property_ptr = OBJ_PROP(object, cache_index);
 
     if (upb_fielddef_containingoneof(field)) {
       memset(memory, 0, NATIVE_SLOT_MAX_SIZE);
@@ -797,7 +840,7 @@ void layout_set(MessageLayout* layout, MessageHeader* header,
             header->descriptor->layout->fields[upb_fielddef_index(field)]
                 .cache_index;
         DEREF(memory, CACHED_VALUE*) =
-            &(header->std.properties_table)[property_cache_index];
+            OBJ_PROP(&header->std, property_cache_index);
         memory = DEREF(memory, CACHED_VALUE*);
         break;
       }
@@ -964,7 +1007,7 @@ void layout_merge(MessageLayout* layout, MessageHeader* from,
           int property_cache_index =
               layout->fields[upb_fielddef_index(field)].cache_index;
           DEREF(to_memory, CACHED_VALUE*) =
-              &(to->std.properties_table)[property_cache_index];
+              OBJ_PROP(&to->std, property_cache_index);
           break;
         }
         default:

+ 1 - 0
php/phpunit.xml

@@ -10,6 +10,7 @@
       <file>tests/generated_phpdoc_test.php</file>
       <file>tests/map_field_test.php</file>
       <file>tests/well_known_test.php</file>
+      <file>tests/descriptors_test.php</file>
       <file>tests/generated_service_test.php</file>
     </testsuite>
   </testsuites>

+ 100 - 0
php/src/Google/Protobuf/Descriptor.php

@@ -0,0 +1,100 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2017 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf;
+
+use Google\Protobuf\Internal\GetPublicDescriptorTrait;
+
+class Descriptor
+{
+    use GetPublicDescriptorTrait;
+
+    private $internal_desc;
+
+    /**
+     * @internal
+     */
+    public function __construct($internal_desc)
+    {
+        $this->internal_desc = $internal_desc;
+    }
+
+    /**
+     * @return string Full protobuf message name
+     */
+    public function getFullName()
+    {
+        return trim($this->internal_desc->getFullName(), ".");
+    }
+
+    /**
+     * @return string PHP class name
+     */
+    public function getClass()
+    {
+        return $this->internal_desc->getClass();
+    }
+
+    /**
+     * @param int $index Must be >= 0 and < getFieldCount()
+     * @return FieldDescriptor
+     */
+    public function getField($index)
+    {
+        return $this->getPublicDescriptor($this->internal_desc->getFieldByIndex($index));
+    }
+
+    /**
+     * @return int Number of fields in message
+     */
+    public function getFieldCount()
+    {
+        return count($this->internal_desc->getField());
+    }
+
+    /**
+     * @param int $index Must be >= 0 and < getOneofDeclCount()
+     * @return OneofDescriptor
+     */
+    public function getOneofDecl($index)
+    {
+        return $this->getPublicDescriptor($this->internal_desc->getOneofDecl()[$index]);
+    }
+
+    /**
+     * @return int Number of oneofs in message
+     */
+    public function getOneofDeclCount()
+    {
+        return count($this->internal_desc->getOneofDecl());
+    }
+}

+ 76 - 0
php/src/Google/Protobuf/DescriptorPool.php

@@ -0,0 +1,76 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2017 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf;
+
+class DescriptorPool
+{
+    private static $pool;
+
+    private $internal_pool;
+
+    /**
+     * @return DescriptorPool
+     */
+    public static function getGeneratedPool()
+    {
+        if (!isset(self::$pool)) {
+            self::$pool = new DescriptorPool(\Google\Protobuf\Internal\DescriptorPool::getGeneratedPool());
+        }
+        return self::$pool;
+    }
+
+    private function __construct($internal_pool)
+    {
+        $this->internal_pool = $internal_pool;
+    }
+
+    /**
+     * @param string $className A fully qualified protobuf class name
+     * @return Descriptor
+     */
+    public function getDescriptorByClassName($className)
+    {
+        $desc = $this->internal_pool->getDescriptorByClassName($className);
+        return is_null($desc) ? null : $desc->getPublicDescriptor();
+    }
+
+    /**
+     * @param string $className A fully qualified protobuf class name
+     * @return EnumDescriptor
+     */
+    public function getEnumDescriptorByClassName($className)
+    {
+        $desc = $this->internal_pool->getEnumDescriptorByClassName($className);
+        return is_null($desc) ? null : $desc->getPublicDescriptor();
+    }
+}

+ 79 - 0
php/src/Google/Protobuf/EnumDescriptor.php

@@ -0,0 +1,79 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2017 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf;
+
+class EnumDescriptor
+{
+    private $internal_desc;
+
+    /**
+     * @internal
+     */
+    public function __construct($internal_desc)
+    {
+        $this->internal_desc = $internal_desc;
+    }
+
+    /**
+     * @return string Full protobuf message name
+     */
+    public function getFullName()
+    {
+        return $this->internal_desc->getFullName();
+    }
+
+    /**
+     * @return string PHP class name
+     */
+    public function getClass()
+    {
+        return $this->internal_desc->getClass();
+    }
+
+    /**
+     * @param int $index Must be >= 0 and < getValueCount()
+     * @return EnumValueDescriptor
+     */
+    public function getValue($index)
+    {
+        return $this->internal_desc->getValueDescriptorByIndex($index);
+    }
+
+    /**
+     * @return int Number of values in enum
+     */
+    public function getValueCount()
+    {
+        return $this->internal_desc->getValueCount();
+    }
+}

+ 12 - 7
php/src/Google/Protobuf/Internal/EnumValueDescriptor.php → php/src/Google/Protobuf/EnumValueDescriptor.php

@@ -30,28 +30,33 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-namespace Google\Protobuf\Internal;
+namespace Google\Protobuf;
 
 class EnumValueDescriptor
 {
     private $name;
     private $number;
 
-    public function setName($name)
+    /**
+     * @internal
+     */
+    public function __construct($name, $number)
     {
         $this->name = $name;
+        $this->number = $number;
     }
 
+    /**
+     * @return string
+     */
     public function getName()
     {
         return $this->name;
     }
 
-    public function setNumber($number)
-    {
-        $this->number = $number;
-    }
-
+    /**
+     * @return int
+     */
     public function getNumber()
     {
         return $this->number;

+ 117 - 0
php/src/Google/Protobuf/FieldDescriptor.php

@@ -0,0 +1,117 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2017 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf;
+
+use Google\Protobuf\Internal\GetPublicDescriptorTrait;
+use Google\Protobuf\Internal\GPBType;
+
+class FieldDescriptor
+{
+    use GetPublicDescriptorTrait;
+
+    private $internal_desc;
+
+    /**
+     * @internal
+     */
+    public function __construct($internal_desc)
+    {
+        $this->internal_desc = $internal_desc;
+    }
+
+    /**
+     * @return string Field name
+     */
+    public function getName()
+    {
+        return $this->internal_desc->getName();
+    }
+
+    /**
+     * @return int Protobuf field number
+     */
+    public function getNumber()
+    {
+        return $this->internal_desc->getNumber();
+    }
+
+    /**
+     * @return int
+     */
+    public function getLabel()
+    {
+        return $this->internal_desc->getLabel();
+    }
+
+    /**
+     * @return int
+     */
+    public function getType()
+    {
+        return $this->internal_desc->getType();
+    }
+
+    /**
+     * @return Descriptor Returns a descriptor for the field type if the field type is a message, otherwise throws \Exception
+     * @throws \Exception
+     */
+    public function getMessageType()
+    {
+        if ($this->getType() == GPBType::MESSAGE) {
+            return $this->getPublicDescriptor($this->internal_desc->getMessageType());
+        } else {
+            throw new \Exception("Cannot get message type for non-message field '" . $this->getName() . "'");
+        }
+    }
+
+    /**
+     * @return EnumDescriptor Returns an enum descriptor if the field type is an enum, otherwise throws \Exception
+     * @throws \Exception
+     */
+    public function getEnumType()
+    {
+        if ($this->getType() == GPBType::ENUM) {
+            return $this->getPublicDescriptor($this->internal_desc->getEnumType());
+        } else {
+            throw new \Exception("Cannot get enum type for non-enum field '" . $this->getName() . "'");
+        }
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isMap()
+    {
+        return $this->internal_desc->isMap();
+    }
+}

+ 20 - 1
php/src/Google/Protobuf/Internal/Descriptor.php

@@ -34,17 +34,24 @@ namespace Google\Protobuf\Internal;
 
 class Descriptor
 {
+    use HasPublicDescriptorTrait;
 
     private $full_name;
     private $field = [];
     private $json_to_field = [];
     private $name_to_field = [];
+    private $index_to_field = [];
     private $nested_type = [];
     private $enum_type = [];
     private $klass;
     private $options;
     private $oneof_decl = [];
 
+    public function __construct()
+    {
+        $this->public_desc = new \Google\Protobuf\Descriptor($this);
+    }
+
     public function addOneofDecl($oneof)
     {
         $this->oneof_decl[] = $oneof;
@@ -70,6 +77,7 @@ class Descriptor
         $this->field[$field->getNumber()] = $field;
         $this->json_to_field[$field->getJsonName()] = $field;
         $this->name_to_field[$field->getName()] = $field;
+        $this->index_to_field[] = $field;
     }
 
     public function getField()
@@ -124,6 +132,15 @@ class Descriptor
         }
     }
 
+    public function getFieldByIndex($index)
+    {
+        if (count($this->index_to_field) <= $index) {
+            return NULL;
+        } else {
+            return $this->index_to_field[$index];
+        }
+    }
+
     public function setClass($klass)
     {
         $this->klass = $klass;
@@ -179,9 +196,11 @@ class Descriptor
         }
 
         // Handle oneof fields.
+        $index = 0;
         foreach ($proto->getOneofDecl() as $oneof_proto) {
             $desc->addOneofDecl(
-                OneofDescriptor::buildFromProto($oneof_proto, $desc));
+                OneofDescriptor::buildFromProto($oneof_proto, $desc, $index));
+            $index++;
         }
 
         return $desc;

+ 7 - 7
php/src/Google/Protobuf/Internal/DescriptorPool.php

@@ -61,17 +61,17 @@ class DescriptorPool
         $files->mergeFromString($data);
         $file = FileDescriptor::buildFromProto($files->getFile()[0]);
 
-        foreach ($file->getMessageType() as &$desc) {
+        foreach ($file->getMessageType() as $desc) {
             $this->addDescriptor($desc);
         }
         unset($desc);
 
-        foreach ($file->getEnumType() as &$desc) {
+        foreach ($file->getEnumType() as $desc) {
             $this->addEnumDescriptor($desc);
         }
         unset($desc);
 
-        foreach ($file->getMessageType() as &$desc) {
+        foreach ($file->getMessageType() as $desc) {
             $this->crossLink($desc);
         }
         unset($desc);
@@ -129,9 +129,9 @@ class DescriptorPool
         return $this->class_to_enum_desc[$klass];
     }
 
-    private function crossLink(&$desc)
+    private function crossLink(Descriptor $desc)
     {
-        foreach ($desc->getField() as &$field) {
+        foreach ($desc->getField() as $field) {
             switch ($field->getType()) {
                 case GPBType::MESSAGE:
                     $proto = $field->getMessageType();
@@ -149,7 +149,7 @@ class DescriptorPool
         }
         unset($field);
 
-        foreach ($desc->getNestedType() as &$nested_type) {
+        foreach ($desc->getNestedType() as $nested_type) {
             $this->crossLink($nested_type);
         }
         unset($nested_type);
@@ -157,7 +157,7 @@ class DescriptorPool
 
     public function finish()
     {
-        foreach ($this->class_to_desc as $klass => &$desc) {
+        foreach ($this->class_to_desc as $klass => $desc) {
             $this->crossLink($desc);
         }
         unset($desc);

+ 2 - 2
php/src/Google/Protobuf/Internal/EnumBuilderContext.php

@@ -33,7 +33,7 @@
 namespace Google\Protobuf\Internal;
 
 use Google\Protobuf\Internal\EnumDescriptor;
-use Google\Protobuf\Internal\EnumValueDescriptor;
+use Google\Protobuf\EnumValueDescriptor;
 
 class EnumBuilderContext
 {
@@ -51,7 +51,7 @@ class EnumBuilderContext
 
     public function value($name, $number)
     {
-        $value = new EnumValueDescriptor();
+        $value = new EnumValueDescriptor($name, $number);
         $this->descriptor->addValue($number, $value);
         return $this;
     }

+ 20 - 0
php/src/Google/Protobuf/Internal/EnumDescriptor.php

@@ -2,13 +2,22 @@
 
 namespace Google\Protobuf\Internal;
 
+use Google\Protobuf\EnumValueDescriptor;
+
 class EnumDescriptor
 {
+    use HasPublicDescriptorTrait;
 
     private $klass;
     private $full_name;
     private $value;
     private $name_to_value;
+    private $value_descriptor = [];
+
+    public function __construct()
+    {
+        $this->public_desc = new \Google\Protobuf\EnumDescriptor($this);
+    }
 
     public function setFullName($full_name)
     {
@@ -24,6 +33,7 @@ class EnumDescriptor
     {
         $this->value[$number] = $value;
         $this->name_to_value[$value->getName()] = $value;
+        $this->value_descriptor[] = new EnumValueDescriptor($value->getName(), $number);
     }
 
     public function getValueByNumber($number)
@@ -36,6 +46,16 @@ class EnumDescriptor
         return $this->name_to_value[$name];
     }
 
+    public function getValueDescriptorByIndex($index)
+    {
+        return $this->value_descriptor[$index];
+    }
+
+    public function getValueCount()
+    {
+        return count($this->value);
+    }
+
     public function setClass($klass)
     {
         $this->klass = $klass;

+ 6 - 0
php/src/Google/Protobuf/Internal/FieldDescriptor.php

@@ -34,6 +34,7 @@ namespace Google\Protobuf\Internal;
 
 class FieldDescriptor
 {
+    use HasPublicDescriptorTrait;
 
     private $name;
     private $json_name;
@@ -48,6 +49,11 @@ class FieldDescriptor
     private $is_map;
     private $oneof_index = -1;
 
+    public function __construct()
+    {
+        $this->public_desc = new \Google\Protobuf\FieldDescriptor($this);
+    }
+
     public function setOneofIndex($index)
     {
         $this->oneof_index = $index;

+ 1 - 1
php/src/Google/Protobuf/Internal/GPBUtil.php

@@ -38,7 +38,7 @@ use Google\Protobuf\Internal\MapField;
 
 class GPBUtil
 {
-    public function divideInt64ToInt32($value, &$high, &$low, $trim = false)
+    public static function divideInt64ToInt32($value, &$high, &$low, $trim = false)
     {
         $isNeg = (bccomp($value, 0) < 0);
         if ($isNeg) {

+ 41 - 0
php/src/Google/Protobuf/Internal/GetPublicDescriptorTrait.php

@@ -0,0 +1,41 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2017 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf\Internal;
+
+trait GetPublicDescriptorTrait
+{
+    private function getPublicDescriptor($desc)
+    {
+        return is_null($desc) ? null : $desc->getPublicDescriptor();
+    }
+}

+ 43 - 0
php/src/Google/Protobuf/Internal/HasPublicDescriptorTrait.php

@@ -0,0 +1,43 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf\Internal;
+
+trait HasPublicDescriptorTrait
+{
+    private $public_desc;
+
+    public function getPublicDescriptor()
+    {
+        return $this->public_desc;
+    }
+}

+ 2 - 2
php/src/Google/Protobuf/Internal/MapEntry.php

@@ -39,7 +39,7 @@ class MapEntry extends Message
     public $key;
     public $value;
 
-    public function setKey(&$key) {
+    public function setKey($key) {
       $this->key = $key;
     }
 
@@ -47,7 +47,7 @@ class MapEntry extends Message
       return $this->key;
     }
 
-    public function setValue(&$value) {
+    public function setValue($value) {
       $this->value = $value;
     }
 

+ 2 - 4
php/src/Google/Protobuf/Internal/Message.php

@@ -841,7 +841,6 @@ class Message
                 if (is_null($value)) {
                     continue;
                 }
-                $getter = $field->getGetter();
                 $key_field = $field->getMessageType()->getFieldByNumber(1);
                 $value_field = $field->getMessageType()->getFieldByNumber(2);
                 foreach ($value as $tmp_key => $tmp_value) {
@@ -858,13 +857,12 @@ class Message
                         $this->convertJsonValueToProtoValue(
                             $tmp_value,
                             $value_field);
-                    $this->$getter()[$proto_key] = $proto_value;
+                    self::kvUpdateHelper($field, $proto_key, $proto_value);
                 }
             } else if ($field->isRepeated()) {
                 if (is_null($value)) {
                     continue;
                 }
-                $getter = $field->getGetter();
                 foreach ($value as $tmp) {
                     if (is_null($tmp)) {
                         throw new \Exception(
@@ -872,7 +870,7 @@ class Message
                     }
                     $proto_value =
                         $this->convertJsonValueToProtoValue($tmp, $field);
-                    $this->$getter()[] = $proto_value;
+                    self::appendHelper($field, $proto_value);
                 }
             } else {
                 $setter = $field->getSetter();

+ 13 - 2
php/src/Google/Protobuf/Internal/OneofDescriptor.php

@@ -34,10 +34,16 @@ namespace Google\Protobuf\Internal;
 
 class OneofDescriptor
 {
+    use HasPublicDescriptorTrait;
 
     private $name;
     private $fields;
 
+    public function __construct()
+    {
+        $this->public_desc = new \Google\Protobuf\OneofDescriptor($this);
+    }
+
     public function setName($name)
     {
         $this->name = $name;
@@ -48,7 +54,7 @@ class OneofDescriptor
         return $this->name;
     }
 
-    public function addField(&$field)
+    public function addField(FieldDescriptor $field)
     {
         $this->fields[] = $field;
     }
@@ -58,10 +64,15 @@ class OneofDescriptor
         return $this->fields;
     }
 
-    public static function buildFromProto($oneof_proto)
+    public static function buildFromProto($oneof_proto, $desc, $index)
     {
         $oneof = new OneofDescriptor();
         $oneof->setName($oneof_proto->getName());
+        foreach ($desc->getField() as $field) {
+            if ($field->getOneofIndex() == $index) {
+                $oneof->addField($field);
+            }
+        }
         return $oneof;
     }
 }

+ 75 - 0
php/src/Google/Protobuf/OneofDescriptor.php

@@ -0,0 +1,75 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2017 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf;
+
+use Google\Protobuf\Internal\GetPublicDescriptorTrait;
+
+class OneofDescriptor
+{
+    use GetPublicDescriptorTrait;
+
+    private $internal_desc;
+
+    /**
+     * @internal
+     */
+    public function __construct($internal_desc)
+    {
+        $this->internal_desc = $internal_desc;
+    }
+
+    /**
+     * @return string The name of the oneof
+     */
+    public function getName()
+    {
+        return $this->internal_desc->getName();
+    }
+
+    /**
+     * @param int $index Must be >= 0 and < getFieldCount()
+     * @return FieldDescriptor
+     */
+    public function getField($index)
+    {
+        return $this->getPublicDescriptor($this->internal_desc->getFields()[$index]);
+    }
+
+    /**
+     * @return int Number of fields in the oneof
+     */
+    public function getFieldCount()
+    {
+        return count($this->internal_desc->getFields());
+    }
+}

+ 246 - 0
php/tests/descriptors_test.php

@@ -0,0 +1,246 @@
+<?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();
+        $class = get_class(new TestDescriptorsMessage());
+        $this->assertSame('Descriptors\TestDescriptorsMessage', $class);
+        $desc = $pool->getDescriptorByClassName($class);
+
+        $this->assertSame('descriptors.TestDescriptorsMessage', $desc->getFullName());
+        $this->assertSame($class, $desc->getClass());
+
+        $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;
+    }
+}

+ 13 - 0
php/tests/generated_class_test.php

@@ -13,6 +13,7 @@ use Foo\TestIncludeNamespaceMessage;
 use Foo\TestIncludePrefixMessage;
 use Foo\TestMessage;
 use Foo\TestMessage_Sub;
+use Foo\TestReverseFieldOrder;
 use Php\Test\TestNamespace;
 
 class GeneratedClassTest extends TestBase
@@ -702,4 +703,16 @@ class GeneratedClassTest extends TestBase
         $this->assertSame(1, $m->getOptionalInt32());
         $this->assertSame(2, $m->getOptionalUInt32());
     }
+
+    #########################################################
+    # Test Reverse Field Order.
+    #########################################################
+
+    public function testReverseFieldOrder()
+    {
+        $m = new TestReverseFieldOrder();
+        $m->setB("abc");
+        $this->assertSame("abc", $m->getB());
+        $this->assertNotSame("abc", $m->getA());
+    }
 }

+ 1 - 0
php/tests/memory_leak_test.php

@@ -21,6 +21,7 @@ require_once('generated/Foo/TestMessage_Sub.php');
 require_once('generated/Foo/TestPackedMessage.php');
 require_once('generated/Foo/TestPhpDoc.php');
 require_once('generated/Foo/TestRandomFieldOrder.php');
+require_once('generated/Foo/TestReverseFieldOrder.php');
 require_once('generated/Foo/TestUnpackedMessage.php');
 require_once('generated/GPBMetadata/Proto/Test.php');
 require_once('generated/GPBMetadata/Proto/TestEmptyPhpNamespace.php');

+ 5 - 0
php/tests/proto/test.proto

@@ -187,3 +187,8 @@ message TestRandomFieldOrder {
   int64 tag13 = 150;
   string tag14 = 160;
 }
+
+message TestReverseFieldOrder {
+  repeated int32 a = 2;
+  string b = 1;
+}

+ 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
 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[@]}"
 do

+ 1 - 0
src/google/protobuf/compiler/js/js_generator.cc

@@ -197,6 +197,7 @@ string ModuleAlias(const string& filename) {
   string basename = StripProto(filename);
   StripString(&basename, "-", '$');
   StripString(&basename, "/", '_');
+  StripString(&basename, ".", '_');
   return basename + "_pb";
 }
 

+ 1 - 0
src/google/protobuf/test_messages_proto3.proto

@@ -39,6 +39,7 @@ syntax = "proto3";
 
 package protobuf_test_messages.proto3;
 option java_package = "com.google.protobuf_test_messages.proto3";
+option objc_class_prefix = "Proto3";
 
 // This is the default, but we specify it here explicitly.
 option optimize_for = SPEED;

+ 10 - 1
tests.sh

@@ -347,7 +347,16 @@ generate_php_test_proto() {
   # Generate test file
   rm -rf 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
   ./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