瀏覽代碼

PHP array constructors for protobuf messages (#4530)

* PHP array constructors for protobuf messages

* removes Descriptor from error message

* allows mergeFrom to accept an array

* only use initWithDescriptor if instanceof MapEntry

* adds doc comments

* removes ability for constructors to take arrays for submessages

* Revert "allows mergeFrom to accept an array"

This reverts commit b7b72182d561634af12c5c5c56a7cda3b33241f9.

* makes mergeFromArray protected and fixes mergeFrom whitespace

* Separates merging from JSON and merging from PHP array

* removes well-known types and json keys from array construction

* Addresses PR review comments

* cleans up tests

* fixes exception messages
Brent Shaffer 7 年之前
父節點
當前提交
c9b404d23b
共有 31 個文件被更改,包括 1082 次插入161 次删除
  1. 22 2
      php/src/Google/Protobuf/Internal/DescriptorProto.php
  2. 13 2
      php/src/Google/Protobuf/Internal/DescriptorProto_ExtensionRange.php
  3. 14 2
      php/src/Google/Protobuf/Internal/DescriptorProto_ReservedRange.php
  4. 20 2
      php/src/Google/Protobuf/Internal/EnumDescriptorProto.php
  5. 14 2
      php/src/Google/Protobuf/Internal/EnumDescriptorProto_EnumReservedRange.php
  6. 20 2
      php/src/Google/Protobuf/Internal/EnumOptions.php
  7. 13 2
      php/src/Google/Protobuf/Internal/EnumValueDescriptorProto.php
  8. 17 2
      php/src/Google/Protobuf/Internal/EnumValueOptions.php
  9. 12 2
      php/src/Google/Protobuf/Internal/ExtensionRangeOptions.php
  10. 40 2
      php/src/Google/Protobuf/Internal/FieldDescriptorProto.php
  11. 66 2
      php/src/Google/Protobuf/Internal/FieldOptions.php
  12. 35 2
      php/src/Google/Protobuf/Internal/FileDescriptorProto.php
  13. 11 2
      php/src/Google/Protobuf/Internal/FileDescriptorSet.php
  14. 85 2
      php/src/Google/Protobuf/Internal/FileOptions.php
  15. 13 2
      php/src/Google/Protobuf/Internal/GeneratedCodeInfo.php
  16. 22 2
      php/src/Google/Protobuf/Internal/GeneratedCodeInfo_Annotation.php
  17. 150 98
      php/src/Google/Protobuf/Internal/Message.php
  18. 56 2
      php/src/Google/Protobuf/Internal/MessageOptions.php
  19. 20 2
      php/src/Google/Protobuf/Internal/MethodDescriptorProto.php
  20. 18 2
      php/src/Google/Protobuf/Internal/MethodOptions.php
  21. 12 2
      php/src/Google/Protobuf/Internal/OneofDescriptorProto.php
  22. 12 2
      php/src/Google/Protobuf/Internal/OneofOptions.php
  23. 13 2
      php/src/Google/Protobuf/Internal/ServiceDescriptorProto.php
  24. 17 2
      php/src/Google/Protobuf/Internal/ServiceOptions.php
  25. 52 2
      php/src/Google/Protobuf/Internal/SourceCodeInfo.php
  26. 77 2
      php/src/Google/Protobuf/Internal/SourceCodeInfo_Location.php
  27. 19 2
      php/src/Google/Protobuf/Internal/UninterpretedOption.php
  28. 12 2
      php/src/Google/Protobuf/Internal/UninterpretedOption_NamePart.php
  29. 8 0
      php/tests/generated_phpdoc_test.php
  30. 153 1
      php/tests/php_implementation_test.php
  31. 46 8
      src/google/protobuf/compiler/php/php_generator.cc

+ 22 - 2
php/src/Google/Protobuf/Internal/DescriptorProto.php

@@ -71,9 +71,29 @@ class DescriptorProto extends \Google\Protobuf\Internal\Message
     private $reserved_name;
     private $has_reserved_name = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $name
+     *     @type \Google\Protobuf\Internal\FieldDescriptorProto[]|\Google\Protobuf\Internal\RepeatedField $field
+     *     @type \Google\Protobuf\Internal\FieldDescriptorProto[]|\Google\Protobuf\Internal\RepeatedField $extension
+     *     @type \Google\Protobuf\Internal\DescriptorProto[]|\Google\Protobuf\Internal\RepeatedField $nested_type
+     *     @type \Google\Protobuf\Internal\EnumDescriptorProto[]|\Google\Protobuf\Internal\RepeatedField $enum_type
+     *     @type \Google\Protobuf\Internal\DescriptorProto_ExtensionRange[]|\Google\Protobuf\Internal\RepeatedField $extension_range
+     *     @type \Google\Protobuf\Internal\OneofDescriptorProto[]|\Google\Protobuf\Internal\RepeatedField $oneof_decl
+     *     @type \Google\Protobuf\Internal\MessageOptions $options
+     *     @type \Google\Protobuf\Internal\DescriptorProto_ReservedRange[]|\Google\Protobuf\Internal\RepeatedField $reserved_range
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $reserved_name
+     *           Reserved field names, which may not be used by fields in the same message.
+     *           A given name may only be reserved once.
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

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

@@ -31,9 +31,20 @@ class DescriptorProto_ExtensionRange extends \Google\Protobuf\Internal\Message
     private $options = null;
     private $has_options = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int $start
+     *     @type int $end
+     *     @type \Google\Protobuf\Internal\ExtensionRangeOptions $options
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 14 - 2
php/src/Google/Protobuf/Internal/DescriptorProto_ReservedRange.php

@@ -34,9 +34,21 @@ class DescriptorProto_ReservedRange extends \Google\Protobuf\Internal\Message
     private $end = 0;
     private $has_end = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int $start
+     *           Inclusive.
+     *     @type int $end
+     *           Exclusive.
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 20 - 2
php/src/Google/Protobuf/Internal/EnumDescriptorProto.php

@@ -50,9 +50,27 @@ class EnumDescriptorProto extends \Google\Protobuf\Internal\Message
     private $reserved_name;
     private $has_reserved_name = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $name
+     *     @type \Google\Protobuf\Internal\EnumValueDescriptorProto[]|\Google\Protobuf\Internal\RepeatedField $value
+     *     @type \Google\Protobuf\Internal\EnumOptions $options
+     *     @type \Google\Protobuf\Internal\EnumDescriptorProto_EnumReservedRange[]|\Google\Protobuf\Internal\RepeatedField $reserved_range
+     *           Range of reserved numeric values. Reserved numeric values may not be used
+     *           by enum values in the same enum declaration. Reserved ranges may not
+     *           overlap.
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $reserved_name
+     *           Reserved enum value names, which may not be reused. A given name may only
+     *           be reserved once.
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 14 - 2
php/src/Google/Protobuf/Internal/EnumDescriptorProto_EnumReservedRange.php

@@ -36,9 +36,21 @@ class EnumDescriptorProto_EnumReservedRange extends \Google\Protobuf\Internal\Me
     private $end = 0;
     private $has_end = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int $start
+     *           Inclusive.
+     *     @type int $end
+     *           Inclusive.
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 20 - 2
php/src/Google/Protobuf/Internal/EnumOptions.php

@@ -41,9 +41,27 @@ class EnumOptions extends \Google\Protobuf\Internal\Message
     private $uninterpreted_option;
     private $has_uninterpreted_option = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type bool $allow_alias
+     *           Set this option to true to allow mapping different tag names to the same
+     *           value.
+     *     @type bool $deprecated
+     *           Is this enum deprecated?
+     *           Depending on the target platform, this can emit Deprecated annotations
+     *           for the enum, or it will be completely ignored; in the very least, this
+     *           is a formalization for deprecating enums.
+     *     @type \Google\Protobuf\Internal\UninterpretedOption[]|\Google\Protobuf\Internal\RepeatedField $uninterpreted_option
+     *           The parser stores options it doesn't recognize here. See above.
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

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

@@ -33,9 +33,20 @@ class EnumValueDescriptorProto extends \Google\Protobuf\Internal\Message
     private $options = null;
     private $has_options = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $name
+     *     @type int $number
+     *     @type \Google\Protobuf\Internal\EnumValueOptions $options
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 17 - 2
php/src/Google/Protobuf/Internal/EnumValueOptions.php

@@ -33,9 +33,24 @@ class EnumValueOptions extends \Google\Protobuf\Internal\Message
     private $uninterpreted_option;
     private $has_uninterpreted_option = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type bool $deprecated
+     *           Is this enum value deprecated?
+     *           Depending on the target platform, this can emit Deprecated annotations
+     *           for the enum value, or it will be completely ignored; in the very least,
+     *           this is a formalization for deprecating enum values.
+     *     @type \Google\Protobuf\Internal\UninterpretedOption[]|\Google\Protobuf\Internal\RepeatedField $uninterpreted_option
+     *           The parser stores options it doesn't recognize here. See above.
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 12 - 2
php/src/Google/Protobuf/Internal/ExtensionRangeOptions.php

@@ -23,9 +23,19 @@ class ExtensionRangeOptions extends \Google\Protobuf\Internal\Message
     private $uninterpreted_option;
     private $has_uninterpreted_option = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Google\Protobuf\Internal\UninterpretedOption[]|\Google\Protobuf\Internal\RepeatedField $uninterpreted_option
+     *           The parser stores options it doesn't recognize here. See above.
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 40 - 2
php/src/Google/Protobuf/Internal/FieldDescriptorProto.php

@@ -94,9 +94,47 @@ class FieldDescriptorProto extends \Google\Protobuf\Internal\Message
     private $options = null;
     private $has_options = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $name
+     *     @type int $number
+     *     @type int $label
+     *     @type int $type
+     *           If type_name is set, this need not be set.  If both this and type_name
+     *           are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP.
+     *     @type string $type_name
+     *           For message and enum types, this is the name of the type.  If the name
+     *           starts with a '.', it is fully-qualified.  Otherwise, C++-like scoping
+     *           rules are used to find the type (i.e. first the nested types within this
+     *           message are searched, then within the parent, on up to the root
+     *           namespace).
+     *     @type string $extendee
+     *           For extensions, this is the name of the type being extended.  It is
+     *           resolved in the same manner as type_name.
+     *     @type string $default_value
+     *           For numeric types, contains the original text representation of the value.
+     *           For booleans, "true" or "false".
+     *           For strings, contains the default text contents (not escaped in any way).
+     *           For bytes, contains the C escaped value.  All bytes >= 128 are escaped.
+     *           TODO(kenton):  Base-64 encode?
+     *     @type int $oneof_index
+     *           If set, gives the index of a oneof in the containing type's oneof_decl
+     *           list.  This field is a member of that oneof.
+     *     @type string $json_name
+     *           JSON name of this field. The value is set by protocol compiler. If the
+     *           user has set a "json_name" option on this field, that option's value
+     *           will be used. Otherwise, it's deduced from the field's name by converting
+     *           it to camelCase.
+     *     @type \Google\Protobuf\Internal\FieldOptions $options
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 66 - 2
php/src/Google/Protobuf/Internal/FieldOptions.php

@@ -107,9 +107,73 @@ class FieldOptions extends \Google\Protobuf\Internal\Message
     private $uninterpreted_option;
     private $has_uninterpreted_option = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int $ctype
+     *           The ctype option instructs the C++ code generator to use a different
+     *           representation of the field than it normally would.  See the specific
+     *           options below.  This option is not yet implemented in the open source
+     *           release -- sorry, we'll try to include it in a future version!
+     *     @type bool $packed
+     *           The packed option can be enabled for repeated primitive fields to enable
+     *           a more efficient representation on the wire. Rather than repeatedly
+     *           writing the tag and type for each element, the entire array is encoded as
+     *           a single length-delimited blob. In proto3, only explicit setting it to
+     *           false will avoid using packed encoding.
+     *     @type int $jstype
+     *           The jstype option determines the JavaScript type used for values of the
+     *           field.  The option is permitted only for 64 bit integral and fixed types
+     *           (int64, uint64, sint64, fixed64, sfixed64).  A field with jstype JS_STRING
+     *           is represented as JavaScript string, which avoids loss of precision that
+     *           can happen when a large value is converted to a floating point JavaScript.
+     *           Specifying JS_NUMBER for the jstype causes the generated JavaScript code to
+     *           use the JavaScript "number" type.  The behavior of the default option
+     *           JS_NORMAL is implementation dependent.
+     *           This option is an enum to permit additional types to be added, e.g.
+     *           goog.math.Integer.
+     *     @type bool $lazy
+     *           Should this field be parsed lazily?  Lazy applies only to message-type
+     *           fields.  It means that when the outer message is initially parsed, the
+     *           inner message's contents will not be parsed but instead stored in encoded
+     *           form.  The inner message will actually be parsed when it is first accessed.
+     *           This is only a hint.  Implementations are free to choose whether to use
+     *           eager or lazy parsing regardless of the value of this option.  However,
+     *           setting this option true suggests that the protocol author believes that
+     *           using lazy parsing on this field is worth the additional bookkeeping
+     *           overhead typically needed to implement it.
+     *           This option does not affect the public interface of any generated code;
+     *           all method signatures remain the same.  Furthermore, thread-safety of the
+     *           interface is not affected by this option; const methods remain safe to
+     *           call from multiple threads concurrently, while non-const methods continue
+     *           to require exclusive access.
+     *           Note that implementations may choose not to check required fields within
+     *           a lazy sub-message.  That is, calling IsInitialized() on the outer message
+     *           may return true even if the inner message has missing required fields.
+     *           This is necessary because otherwise the inner message would have to be
+     *           parsed in order to perform the check, defeating the purpose of lazy
+     *           parsing.  An implementation which chooses not to check required fields
+     *           must be consistent about it.  That is, for any particular sub-message, the
+     *           implementation must either *always* check its required fields, or *never*
+     *           check its required fields, regardless of whether or not the message has
+     *           been parsed.
+     *     @type bool $deprecated
+     *           Is this field deprecated?
+     *           Depending on the target platform, this can emit Deprecated annotations
+     *           for accessors, or it will be completely ignored; in the very least, this
+     *           is a formalization for deprecating fields.
+     *     @type bool $weak
+     *           For Google-internal migration only. Do not use.
+     *     @type \Google\Protobuf\Internal\UninterpretedOption[]|\Google\Protobuf\Internal\RepeatedField $uninterpreted_option
+     *           The parser stores options it doesn't recognize here. See above.
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 35 - 2
php/src/Google/Protobuf/Internal/FileDescriptorProto.php

@@ -99,9 +99,42 @@ class FileDescriptorProto extends \Google\Protobuf\Internal\Message
     private $syntax = '';
     private $has_syntax = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $name
+     *           file name, relative to root of source tree
+     *     @type string $package
+     *           e.g. "foo", "foo.bar", etc.
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $dependency
+     *           Names of files imported by this file.
+     *     @type int[]|\Google\Protobuf\Internal\RepeatedField $public_dependency
+     *           Indexes of the public imported files in the dependency list above.
+     *     @type int[]|\Google\Protobuf\Internal\RepeatedField $weak_dependency
+     *           Indexes of the weak imported files in the dependency list.
+     *           For Google-internal migration only. Do not use.
+     *     @type \Google\Protobuf\Internal\DescriptorProto[]|\Google\Protobuf\Internal\RepeatedField $message_type
+     *           All top-level definitions in this file.
+     *     @type \Google\Protobuf\Internal\EnumDescriptorProto[]|\Google\Protobuf\Internal\RepeatedField $enum_type
+     *     @type \Google\Protobuf\Internal\ServiceDescriptorProto[]|\Google\Protobuf\Internal\RepeatedField $service
+     *     @type \Google\Protobuf\Internal\FieldDescriptorProto[]|\Google\Protobuf\Internal\RepeatedField $extension
+     *     @type \Google\Protobuf\Internal\FileOptions $options
+     *     @type \Google\Protobuf\Internal\SourceCodeInfo $source_code_info
+     *           This field contains optional information about the original source code.
+     *           You may safely remove this entire field without harming runtime
+     *           functionality of the descriptors -- the information is needed only by
+     *           development tools.
+     *     @type string $syntax
+     *           The syntax of the proto file.
+     *           The supported values are "proto2" and "proto3".
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 11 - 2
php/src/Google/Protobuf/Internal/FileDescriptorSet.php

@@ -24,9 +24,18 @@ class FileDescriptorSet extends \Google\Protobuf\Internal\Message
     private $file;
     private $has_file = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Google\Protobuf\Internal\FileDescriptorProto[]|\Google\Protobuf\Internal\RepeatedField $file
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 85 - 2
php/src/Google/Protobuf/Internal/FileOptions.php

@@ -200,9 +200,92 @@ class FileOptions extends \Google\Protobuf\Internal\Message
     private $uninterpreted_option;
     private $has_uninterpreted_option = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $java_package
+     *           Sets the Java package where classes generated from this .proto will be
+     *           placed.  By default, the proto package is used, but this is often
+     *           inappropriate because proto packages do not normally start with backwards
+     *           domain names.
+     *     @type string $java_outer_classname
+     *           If set, all the classes from the .proto file are wrapped in a single
+     *           outer class with the given name.  This applies to both Proto1
+     *           (equivalent to the old "--one_java_file" option) and Proto2 (where
+     *           a .proto always translates to a single class, but you may want to
+     *           explicitly choose the class name).
+     *     @type bool $java_multiple_files
+     *           If set true, then the Java code generator will generate a separate .java
+     *           file for each top-level message, enum, and service defined in the .proto
+     *           file.  Thus, these types will *not* be nested inside the outer class
+     *           named by java_outer_classname.  However, the outer class will still be
+     *           generated to contain the file's getDescriptor() method as well as any
+     *           top-level extensions defined in the file.
+     *     @type bool $java_generate_equals_and_hash
+     *           This option does nothing.
+     *     @type bool $java_string_check_utf8
+     *           If set true, then the Java2 code generator will generate code that
+     *           throws an exception whenever an attempt is made to assign a non-UTF-8
+     *           byte sequence to a string field.
+     *           Message reflection will do the same.
+     *           However, an extension field still accepts non-UTF-8 byte sequences.
+     *           This option has no effect on when used with the lite runtime.
+     *     @type int $optimize_for
+     *     @type string $go_package
+     *           Sets the Go package where structs generated from this .proto will be
+     *           placed. If omitted, the Go package will be derived from the following:
+     *             - The basename of the package import path, if provided.
+     *             - Otherwise, the package statement in the .proto file, if present.
+     *             - Otherwise, the basename of the .proto file, without extension.
+     *     @type bool $cc_generic_services
+     *           Should generic services be generated in each language?  "Generic" services
+     *           are not specific to any particular RPC system.  They are generated by the
+     *           main code generators in each language (without additional plugins).
+     *           Generic services were the only kind of service generation supported by
+     *           early versions of google.protobuf.
+     *           Generic services are now considered deprecated in favor of using plugins
+     *           that generate code specific to your particular RPC system.  Therefore,
+     *           these default to false.  Old code which depends on generic services should
+     *           explicitly set them to true.
+     *     @type bool $java_generic_services
+     *     @type bool $py_generic_services
+     *     @type bool $php_generic_services
+     *     @type bool $deprecated
+     *           Is this file deprecated?
+     *           Depending on the target platform, this can emit Deprecated annotations
+     *           for everything in the file, or it will be completely ignored; in the very
+     *           least, this is a formalization for deprecating files.
+     *     @type bool $cc_enable_arenas
+     *           Enables the use of arenas for the proto messages in this file. This applies
+     *           only to generated classes for C++.
+     *     @type string $objc_class_prefix
+     *           Sets the objective c class prefix which is prepended to all objective c
+     *           generated classes from this .proto. There is no default.
+     *     @type string $csharp_namespace
+     *           Namespace for generated classes; defaults to the package.
+     *     @type string $swift_prefix
+     *           By default Swift generators will take the proto package and CamelCase it
+     *           replacing '.' with underscore and use that to prefix the types/symbols
+     *           defined. When this options is provided, they will use this value instead
+     *           to prefix the types/symbols defined.
+     *     @type string $php_class_prefix
+     *           Sets the php class prefix which is prepended to all php generated classes
+     *           from this .proto. Default is empty.
+     *     @type string $php_namespace
+     *           Use this option to change the namespace of php generated classes. Default
+     *           is empty. When this option is empty, the package name will be used for
+     *           determining the namespace.
+     *     @type \Google\Protobuf\Internal\UninterpretedOption[]|\Google\Protobuf\Internal\RepeatedField $uninterpreted_option
+     *           The parser stores options it doesn't recognize here.
+     *           See the documentation for the "Options" section above.
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

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

@@ -28,9 +28,20 @@ class GeneratedCodeInfo extends \Google\Protobuf\Internal\Message
     private $annotation;
     private $has_annotation = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Google\Protobuf\Internal\GeneratedCodeInfo_Annotation[]|\Google\Protobuf\Internal\RepeatedField $annotation
+     *           An Annotation connects some span of text in generated code to an element
+     *           of its generating .proto file.
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 22 - 2
php/src/Google/Protobuf/Internal/GeneratedCodeInfo_Annotation.php

@@ -48,9 +48,29 @@ class GeneratedCodeInfo_Annotation extends \Google\Protobuf\Internal\Message
     private $end = 0;
     private $has_end = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int[]|\Google\Protobuf\Internal\RepeatedField $path
+     *           Identifies the element in the original source .proto file. This field
+     *           is formatted the same as SourceCodeInfo.Location.path.
+     *     @type string $source_file
+     *           Identifies the filesystem path to the original source .proto.
+     *     @type int $begin
+     *           Identifies the starting offset in bytes in the generated code
+     *           that relates to the identified object.
+     *     @type int $end
+     *           Identifies the ending offset in bytes in the generated code that
+     *           relates to the identified offset. The end offset should be one past
+     *           the last relevant byte (so the length of the text = end - begin).
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 150 - 98
php/src/Google/Protobuf/Internal/Message.php

@@ -66,19 +66,30 @@ class Message
     /**
      * @ignore
      */
-    public function __construct($desc = NULL)
+    public function __construct($data = NULL)
     {
         // MapEntry message is shared by all types of map fields, whose
         // descriptors are different from each other. Thus, we cannot find a
         // specific descriptor from the descriptor pool.
-        if (get_class($this) === 'Google\Protobuf\Internal\MapEntry') {
-            $this->desc = $desc;
-            foreach ($desc->getField() as $field) {
-                $setter = $field->getSetter();
-                $this->$setter($this->defaultValue($field));
+        if ($this instanceof MapEntry) {
+            $this->initWithDescriptor($data);
+        } else {
+            $this->initWithGeneratedPool();
+            if (is_array($data)) {
+                $this->mergeFromArray($data);
+            } else if (!empty($data)) {
+                throw new \InvalidArgumentException(
+                    'Message constructor must be an array or null.'
+                );
             }
-            return;
         }
+    }
+
+    /**
+     * @ignore
+     */
+    private function initWithGeneratedPool()
+    {
         $pool = DescriptorPool::getGeneratedPool();
         $this->desc = $pool->getDescriptorByClassName(get_class($this));
         if (is_null($this->desc)) {
@@ -151,6 +162,19 @@ class Message
         }
     }
 
+    /**
+     * @ignore
+     */
+    private function initWithDescriptor(Descriptor $desc)
+    {
+        $this->desc = $desc;
+        foreach ($desc->getField() as $field) {
+            $setter = $field->getSetter();
+            $defaultValue = $this->defaultValue($field);
+            $this->$setter($defaultValue);
+        }
+    }
+
     protected function readOneof($number)
     {
         $field = $this->desc->getFieldByNumber($number);
@@ -628,58 +652,58 @@ class Message
      */
     public function mergeFrom($msg)
     {
-      if (get_class($this) !== get_class($msg)) {
-          user_error("Cannot merge messages with different class.");
-          return;
-      }
-
-      foreach ($this->desc->getField() as $field) {
-          $setter = $field->getSetter();
-          $getter = $field->getGetter();
-          if ($field->isMap()) {
-              if (count($msg->$getter()) != 0) {
-                  $value_field = $field->getMessageType()->getFieldByNumber(2);
-                  foreach ($msg->$getter() as $key => $value) {
-                      if ($value_field->getType() == GPBType::MESSAGE) {
-                          $klass = $value_field->getMessageType()->getClass();
-                          $copy = new $klass;
-                          $copy->mergeFrom($value);
-
-                          $this->kvUpdateHelper($field, $key, $copy);
-                      } else {
-                          $this->kvUpdateHelper($field, $key, $value);
-                      }
-                  }
-              }
-          } else if ($field->getLabel() === GPBLabel::REPEATED) {
-              if (count($msg->$getter()) != 0) {
-                  foreach ($msg->$getter() as $tmp) {
-                      if ($field->getType() == GPBType::MESSAGE) {
-                          $klass = $field->getMessageType()->getClass();
-                          $copy = new $klass;
-                          $copy->mergeFrom($tmp);
-                          $this->appendHelper($field, $copy);
-                      } else {
-                          $this->appendHelper($field, $tmp);
-                      }
-                  }
-              }
-          } else if ($field->getLabel() === GPBLabel::OPTIONAL) {
-              if($msg->$getter() !== $this->defaultValue($field)) {
-                  $tmp = $msg->$getter();
-                  if ($field->getType() == GPBType::MESSAGE) {
-                      if (is_null($this->$getter())) {
-                          $klass = $field->getMessageType()->getClass();
-                          $new_msg = new $klass;
-                          $this->$setter($new_msg);
-                      }
-                      $this->$getter()->mergeFrom($tmp);
-                  } else {
-                      $this->$setter($tmp);
-                  }
-              }
-          }
-      }
+        if (get_class($this) !== get_class($msg)) {
+            user_error("Cannot merge messages with different class.");
+            return;
+        }
+
+        foreach ($this->desc->getField() as $field) {
+            $setter = $field->getSetter();
+            $getter = $field->getGetter();
+            if ($field->isMap()) {
+                if (count($msg->$getter()) != 0) {
+                    $value_field = $field->getMessageType()->getFieldByNumber(2);
+                    foreach ($msg->$getter() as $key => $value) {
+                        if ($value_field->getType() == GPBType::MESSAGE) {
+                            $klass = $value_field->getMessageType()->getClass();
+                            $copy = new $klass;
+                            $copy->mergeFrom($value);
+
+                            $this->kvUpdateHelper($field, $key, $copy);
+                        } else {
+                            $this->kvUpdateHelper($field, $key, $value);
+                        }
+                    }
+                }
+            } else if ($field->getLabel() === GPBLabel::REPEATED) {
+                if (count($msg->$getter()) != 0) {
+                    foreach ($msg->$getter() as $tmp) {
+                        if ($field->getType() == GPBType::MESSAGE) {
+                            $klass = $field->getMessageType()->getClass();
+                            $copy = new $klass;
+                            $copy->mergeFrom($tmp);
+                            $this->appendHelper($field, $copy);
+                        } else {
+                            $this->appendHelper($field, $tmp);
+                        }
+                    }
+                }
+            } else if ($field->getLabel() === GPBLabel::OPTIONAL) {
+                if($msg->$getter() !== $this->defaultValue($field)) {
+                    $tmp = $msg->$getter();
+                    if ($field->getType() == GPBType::MESSAGE) {
+                        if (is_null($this->$getter())) {
+                            $klass = $field->getMessageType()->getClass();
+                            $new_msg = new $klass;
+                            $this->$setter($new_msg);
+                        }
+                        $this->$getter()->mergeFrom($tmp);
+                    } else {
+                        $this->$setter($tmp);
+                    }
+                }
+            }
+        }
     }
 
     /**
@@ -763,7 +787,8 @@ class Message
                     try {
                         $timestamp = GPBUtil::parseTimestamp($value);
                     } catch (\Exception $e) {
-                        throw new GPBDecodeException("Invalid RFC 3339 timestamp: ".$e->getMessage());
+                        throw new GPBDecodeException(
+                            "Invalid RFC 3339 timestamp: ".$e->getMessage());
                     }
 
                     $submsg->setSeconds($timestamp->getSeconds());
@@ -775,7 +800,8 @@ class Message
                     try {
                         return GPBUtil::parseFieldMask($value);
                     } catch (\Exception $e) {
-                        throw new GPBDecodeException("Invalid FieldMask: ".$e->getMessage());
+                        throw new GPBDecodeException(
+                            "Invalid FieldMask: ".$e->getMessage());
                     }
                 } else {
                     if (is_null($value) &&
@@ -792,21 +818,23 @@ class Message
             case GPBType::ENUM:
                 if (is_null($value)) {
                     return $this->defaultValue($field);
-                } else if (is_integer($value)) {
+                }
+                if (is_integer($value)) {
                     return $value;
-                } else {
-                    $enum_value =
-                        $field->getEnumType()->getValueByName($value);
                 }
+                $enum_value = $field->getEnumType()->getValueByName($value);
                 if (!is_null($enum_value)) {
                     return $enum_value->getNumber();
                 }
+                throw new GPBDecodeException(
+                        "Enum field only accepts integer or enum value name");
             case GPBType::STRING:
                 if (is_null($value)) {
                     return $this->defaultValue($field);
                 }
                 if (!is_string($value)) {
-                    throw new GPBDecodeException("Expect string");
+                    throw new GPBDecodeException(
+                        "String field only accepts string value");
                 }
                 return $value;
             case GPBType::BYTES:
@@ -814,12 +842,12 @@ class Message
                     return $this->defaultValue($field);
                 }
                 if (!is_string($value)) {
-                    throw new GPBDecodeException("Expect string");
+                    throw new GPBDecodeException(
+                        "Byte field only accepts string value");
                 }
                 $proto_value = base64_decode($value, true);
                 if ($proto_value === false) {
-                    throw new GPBDecodeException(
-                        "Invalid base64 characters");
+                    throw new GPBDecodeException("Invalid base64 characters");
                 }
                 return $proto_value;
             case GPBType::BOOL:
@@ -834,27 +862,14 @@ class Message
                         return false;
                     }
                     throw new GPBDecodeException(
-                        "Bool field only accept bool value");
+                        "Bool field only accepts bool value");
                 }
                 if (!is_bool($value)) {
                     throw new GPBDecodeException(
-                        "Bool field only accept bool value");
+                        "Bool field only accepts bool value");
                 }
                 return $value;
             case GPBType::FLOAT:
-                if (is_null($value)) {
-                    return $this->defaultValue($field);
-                }
-                if ($value === "Infinity") {
-                    return INF;
-                }
-                if ($value === "-Infinity") {
-                    return -INF;
-                }
-                if ($value === "NaN") {
-                    return NAN;
-                }
-                return $value;
             case GPBType::DOUBLE:
                 if (is_null($value)) {
                     return $this->defaultValue($field);
@@ -943,6 +958,39 @@ class Message
         }
     }
 
+    /**
+     * Populates the message from a user-supplied PHP array. Array keys
+     * correspond to Message properties and nested message properties.
+     *
+     * Example:
+     * ```
+     * $message->mergeFromArray([
+     *     'name' => 'This is a message name',
+     *     'interval' => [
+     *          'startTime' => time() - 60,
+     *          'endTime' => time(),
+     *     ]
+     * ]);
+     * ```
+     *
+     * @param array $array An array containing message properties and values.
+     * @return null.
+     * @throws Exception Invalid data.
+     */
+    protected function mergeFromArray(array $array)
+    {
+        // Just call the setters for the field names
+        foreach ($array as $key => $value) {
+            $field = $this->desc->getFieldByName($key);
+            if (is_null($field)) {
+                throw new \UnexpectedValueException(
+                    'Invalid message property: ' . $key);
+            }
+            $setter = $field->getSetter();
+            $this->$setter($value);
+        }
+    }
+
     protected function mergeFromJsonArray($array)
     {
         if (is_a($this, "Google\Protobuf\Any")) {
@@ -1035,6 +1083,11 @@ class Message
             }
             return;
         }
+        $this->mergeFromArrayJsonImpl($array);
+    }
+
+    private function mergeFromArrayJsonImpl($array)
+    {
         foreach ($array as $key => $value) {
             $field = $this->desc->getFieldByJsonName($key);
             if (is_null($field)) {
@@ -1043,7 +1096,6 @@ class Message
                     continue;
                 }
             }
-            $setter = $field->getSetter();
             if ($field->isMap()) {
                 if (is_null($value)) {
                     continue;
@@ -1055,15 +1107,13 @@ class Message
                         throw new \Exception(
                             "Map value field element cannot be null.");
                     }
-                    $proto_key =
-                        $this->convertJsonValueToProtoValue(
-                            $tmp_key,
-                            $key_field,
-                            true);
-                    $proto_value =
-                        $this->convertJsonValueToProtoValue(
-                            $tmp_value,
-                            $value_field);
+                    $proto_key = $this->convertJsonValueToProtoValue(
+                        $tmp_key,
+                        $key_field,
+                        true);
+                    $proto_value = $this->convertJsonValueToProtoValue(
+                        $tmp_value,
+                        $value_field);
                     self::kvUpdateHelper($field, $proto_key, $proto_value);
                 }
             } else if ($field->isRepeated()) {
@@ -1075,14 +1125,16 @@ class Message
                         throw new \Exception(
                             "Repeated field elements cannot be null.");
                     }
-                    $proto_value =
-                        $this->convertJsonValueToProtoValue($tmp, $field);
+                    $proto_value = $this->convertJsonValueToProtoValue(
+                        $tmp,
+                        $field);
                     self::appendHelper($field, $proto_value);
                 }
             } else {
                 $setter = $field->getSetter();
-                $proto_value =
-                    $this->convertJsonValueToProtoValue($value, $field);
+                $proto_value = $this->convertJsonValueToProtoValue(
+                    $value,
+                    $field);
                 if ($field->getType() === GPBType::MESSAGE) {
                     if (is_null($proto_value)) {
                         continue;

+ 56 - 2
php/src/Google/Protobuf/Internal/MessageOptions.php

@@ -87,9 +87,63 @@ class MessageOptions extends \Google\Protobuf\Internal\Message
     private $uninterpreted_option;
     private $has_uninterpreted_option = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type bool $message_set_wire_format
+     *           Set true to use the old proto1 MessageSet wire format for extensions.
+     *           This is provided for backwards-compatibility with the MessageSet wire
+     *           format.  You should not use this for any other reason:  It's less
+     *           efficient, has fewer features, and is more complicated.
+     *           The message must be defined exactly as follows:
+     *             message Foo {
+     *               option message_set_wire_format = true;
+     *               extensions 4 to max;
+     *             }
+     *           Note that the message cannot have any defined fields; MessageSets only
+     *           have extensions.
+     *           All extensions of your type must be singular messages; e.g. they cannot
+     *           be int32s, enums, or repeated messages.
+     *           Because this is an option, the above two restrictions are not enforced by
+     *           the protocol compiler.
+     *     @type bool $no_standard_descriptor_accessor
+     *           Disables the generation of the standard "descriptor()" accessor, which can
+     *           conflict with a field of the same name.  This is meant to make migration
+     *           from proto1 easier; new code should avoid fields named "descriptor".
+     *     @type bool $deprecated
+     *           Is this message deprecated?
+     *           Depending on the target platform, this can emit Deprecated annotations
+     *           for the message, or it will be completely ignored; in the very least,
+     *           this is a formalization for deprecating messages.
+     *     @type bool $map_entry
+     *           Whether the message is an automatically generated map entry type for the
+     *           maps field.
+     *           For maps fields:
+     *               map<KeyType, ValueType> map_field = 1;
+     *           The parsed descriptor looks like:
+     *               message MapFieldEntry {
+     *                   option map_entry = true;
+     *                   optional KeyType key = 1;
+     *                   optional ValueType value = 2;
+     *               }
+     *               repeated MapFieldEntry map_field = 1;
+     *           Implementations may choose not to generate the map_entry=true message, but
+     *           use a native map in the target language to hold the keys and values.
+     *           The reflection APIs in such implementions still need to work as
+     *           if the field is a repeated message field.
+     *           NOTE: Do not set the option in .proto files. Always use the maps syntax
+     *           instead. The option should only be implicitly set by the proto compiler
+     *           parser.
+     *     @type \Google\Protobuf\Internal\UninterpretedOption[]|\Google\Protobuf\Internal\RepeatedField $uninterpreted_option
+     *           The parser stores options it doesn't recognize here. See above.
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 20 - 2
php/src/Google/Protobuf/Internal/MethodDescriptorProto.php

@@ -55,9 +55,27 @@ class MethodDescriptorProto extends \Google\Protobuf\Internal\Message
     private $server_streaming = false;
     private $has_server_streaming = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $name
+     *     @type string $input_type
+     *           Input and output type names.  These are resolved in the same way as
+     *           FieldDescriptorProto.type_name, but must refer to a message type.
+     *     @type string $output_type
+     *     @type \Google\Protobuf\Internal\MethodOptions $options
+     *     @type bool $client_streaming
+     *           Identifies if client streams multiple client messages
+     *     @type bool $server_streaming
+     *           Identifies if server streams multiple server messages
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 18 - 2
php/src/Google/Protobuf/Internal/MethodOptions.php

@@ -38,9 +38,25 @@ class MethodOptions extends \Google\Protobuf\Internal\Message
     private $uninterpreted_option;
     private $has_uninterpreted_option = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type bool $deprecated
+     *           Is this method deprecated?
+     *           Depending on the target platform, this can emit Deprecated annotations
+     *           for the method, or it will be completely ignored; in the very least,
+     *           this is a formalization for deprecating methods.
+     *     @type int $idempotency_level
+     *     @type \Google\Protobuf\Internal\UninterpretedOption[]|\Google\Protobuf\Internal\RepeatedField $uninterpreted_option
+     *           The parser stores options it doesn't recognize here. See above.
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 12 - 2
php/src/Google/Protobuf/Internal/OneofDescriptorProto.php

@@ -28,9 +28,19 @@ class OneofDescriptorProto extends \Google\Protobuf\Internal\Message
     private $options = null;
     private $has_options = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $name
+     *     @type \Google\Protobuf\Internal\OneofOptions $options
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 12 - 2
php/src/Google/Protobuf/Internal/OneofOptions.php

@@ -23,9 +23,19 @@ class OneofOptions extends \Google\Protobuf\Internal\Message
     private $uninterpreted_option;
     private $has_uninterpreted_option = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Google\Protobuf\Internal\UninterpretedOption[]|\Google\Protobuf\Internal\RepeatedField $uninterpreted_option
+     *           The parser stores options it doesn't recognize here. See above.
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

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

@@ -33,9 +33,20 @@ class ServiceDescriptorProto extends \Google\Protobuf\Internal\Message
     private $options = null;
     private $has_options = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $name
+     *     @type \Google\Protobuf\Internal\MethodDescriptorProto[]|\Google\Protobuf\Internal\RepeatedField $method
+     *     @type \Google\Protobuf\Internal\ServiceOptions $options
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 17 - 2
php/src/Google/Protobuf/Internal/ServiceOptions.php

@@ -33,9 +33,24 @@ class ServiceOptions extends \Google\Protobuf\Internal\Message
     private $uninterpreted_option;
     private $has_uninterpreted_option = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type bool $deprecated
+     *           Is this service deprecated?
+     *           Depending on the target platform, this can emit Deprecated annotations
+     *           for the service, or it will be completely ignored; in the very least,
+     *           this is a formalization for deprecating services.
+     *     @type \Google\Protobuf\Internal\UninterpretedOption[]|\Google\Protobuf\Internal\RepeatedField $uninterpreted_option
+     *           The parser stores options it doesn't recognize here. See above.
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 52 - 2
php/src/Google/Protobuf/Internal/SourceCodeInfo.php

@@ -66,9 +66,59 @@ class SourceCodeInfo extends \Google\Protobuf\Internal\Message
     private $location;
     private $has_location = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Google\Protobuf\Internal\SourceCodeInfo_Location[]|\Google\Protobuf\Internal\RepeatedField $location
+     *           A Location identifies a piece of source code in a .proto file which
+     *           corresponds to a particular definition.  This information is intended
+     *           to be useful to IDEs, code indexers, documentation generators, and similar
+     *           tools.
+     *           For example, say we have a file like:
+     *             message Foo {
+     *               optional string foo = 1;
+     *             }
+     *           Let's look at just the field definition:
+     *             optional string foo = 1;
+     *             ^       ^^     ^^  ^  ^^^
+     *             a       bc     de  f  ghi
+     *           We have the following locations:
+     *             span   path               represents
+     *             [a,i)  [ 4, 0, 2, 0 ]     The whole field definition.
+     *             [a,b)  [ 4, 0, 2, 0, 4 ]  The label (optional).
+     *             [c,d)  [ 4, 0, 2, 0, 5 ]  The type (string).
+     *             [e,f)  [ 4, 0, 2, 0, 1 ]  The name (foo).
+     *             [g,h)  [ 4, 0, 2, 0, 3 ]  The number (1).
+     *           Notes:
+     *           - A location may refer to a repeated field itself (i.e. not to any
+     *             particular index within it).  This is used whenever a set of elements are
+     *             logically enclosed in a single code segment.  For example, an entire
+     *             extend block (possibly containing multiple extension definitions) will
+     *             have an outer location whose path refers to the "extensions" repeated
+     *             field without an index.
+     *           - Multiple locations may have the same path.  This happens when a single
+     *             logical declaration is spread out across multiple places.  The most
+     *             obvious example is the "extend" block again -- there may be multiple
+     *             extend blocks in the same scope, each of which will have the same path.
+     *           - A location's span is not always a subset of its parent's span.  For
+     *             example, the "extendee" of an extension declaration appears at the
+     *             beginning of the "extend" block and is shared by all extensions within
+     *             the block.
+     *           - Just because a location's span is a subset of some other location's span
+     *             does not mean that it is a descendent.  For example, a "group" defines
+     *             both a type and a field in a single declaration.  Thus, the locations
+     *             corresponding to the type and field and their components will overlap.
+     *           - Code which tries to interpret locations should probably be designed to
+     *             ignore those that it doesn't understand, as more types of locations could
+     *             be recorded in the future.
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 77 - 2
php/src/Google/Protobuf/Internal/SourceCodeInfo_Location.php

@@ -106,9 +106,84 @@ class SourceCodeInfo_Location extends \Google\Protobuf\Internal\Message
     private $leading_detached_comments;
     private $has_leading_detached_comments = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int[]|\Google\Protobuf\Internal\RepeatedField $path
+     *           Identifies which part of the FileDescriptorProto was defined at this
+     *           location.
+     *           Each element is a field number or an index.  They form a path from
+     *           the root FileDescriptorProto to the place where the definition.  For
+     *           example, this path:
+     *             [ 4, 3, 2, 7, 1 ]
+     *           refers to:
+     *             file.message_type(3)  // 4, 3
+     *                 .field(7)         // 2, 7
+     *                 .name()           // 1
+     *           This is because FileDescriptorProto.message_type has field number 4:
+     *             repeated DescriptorProto message_type = 4;
+     *           and DescriptorProto.field has field number 2:
+     *             repeated FieldDescriptorProto field = 2;
+     *           and FieldDescriptorProto.name has field number 1:
+     *             optional string name = 1;
+     *           Thus, the above path gives the location of a field name.  If we removed
+     *           the last element:
+     *             [ 4, 3, 2, 7 ]
+     *           this path refers to the whole field declaration (from the beginning
+     *           of the label to the terminating semicolon).
+     *     @type int[]|\Google\Protobuf\Internal\RepeatedField $span
+     *           Always has exactly three or four elements: start line, start column,
+     *           end line (optional, otherwise assumed same as start line), end column.
+     *           These are packed into a single field for efficiency.  Note that line
+     *           and column numbers are zero-based -- typically you will want to add
+     *           1 to each before displaying to a user.
+     *     @type string $leading_comments
+     *           If this SourceCodeInfo represents a complete declaration, these are any
+     *           comments appearing before and after the declaration which appear to be
+     *           attached to the declaration.
+     *           A series of line comments appearing on consecutive lines, with no other
+     *           tokens appearing on those lines, will be treated as a single comment.
+     *           leading_detached_comments will keep paragraphs of comments that appear
+     *           before (but not connected to) the current element. Each paragraph,
+     *           separated by empty lines, will be one comment element in the repeated
+     *           field.
+     *           Only the comment content is provided; comment markers (e.g. //) are
+     *           stripped out.  For block comments, leading whitespace and an asterisk
+     *           will be stripped from the beginning of each line other than the first.
+     *           Newlines are included in the output.
+     *           Examples:
+     *             optional int32 foo = 1;  // Comment attached to foo.
+     *             // Comment attached to bar.
+     *             optional int32 bar = 2;
+     *             optional string baz = 3;
+     *             // Comment attached to baz.
+     *             // Another line attached to baz.
+     *             // Comment attached to qux.
+     *             //
+     *             // Another line attached to qux.
+     *             optional double qux = 4;
+     *             // Detached comment for corge. This is not leading or trailing comments
+     *             // to qux or corge because there are blank lines separating it from
+     *             // both.
+     *             // Detached comment for corge paragraph 2.
+     *             optional string corge = 5;
+     *             /&#42; Block comment attached
+     *              * to corge.  Leading asterisks
+     *              * will be removed. *&#47;
+     *             /&#42; Block comment attached to
+     *              * grault. *&#47;
+     *             optional int32 grault = 6;
+     *             // ignored detached comments.
+     *     @type string $trailing_comments
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $leading_detached_comments
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 19 - 2
php/src/Google/Protobuf/Internal/UninterpretedOption.php

@@ -61,9 +61,26 @@ class UninterpretedOption extends \Google\Protobuf\Internal\Message
     private $aggregate_value = '';
     private $has_aggregate_value = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Google\Protobuf\Internal\UninterpretedOption_NamePart[]|\Google\Protobuf\Internal\RepeatedField $name
+     *     @type string $identifier_value
+     *           The value of the uninterpreted option, in whatever type the tokenizer
+     *           identified it as during parsing. Exactly one of these should be set.
+     *     @type int|string $positive_int_value
+     *     @type int|string $negative_int_value
+     *     @type float $double_value
+     *     @type string $string_value
+     *     @type string $aggregate_value
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 12 - 2
php/src/Google/Protobuf/Internal/UninterpretedOption_NamePart.php

@@ -32,9 +32,19 @@ class UninterpretedOption_NamePart extends \Google\Protobuf\Internal\Message
     private $is_extension = false;
     private $has_is_extension = false;
 
-    public function __construct() {
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $name_part
+     *     @type bool $is_extension
+     * }
+     */
+    public function __construct($data = NULL) {
         \GPBMetadata\Google\Protobuf\Internal\Descriptor::initOnce();
-        parent::__construct();
+        parent::__construct($data);
     }
 
     /**

+ 8 - 0
php/tests/generated_phpdoc_test.php

@@ -16,6 +16,14 @@ class GeneratedPhpdocTest extends TestBase
         $this->assertContains('foo.TestMessage', $doc);
     }
 
+    public function testPhpDocForConstructor()
+    {
+        $class = new ReflectionClass('Foo\TestMessage');
+        $doc = $class->getMethod('__construct')->getDocComment();
+        $this->assertContains('@param array $data', $doc);
+        $this->assertContains('@type int $optional_int32', $doc);
+    }
+
     /**
      * @dataProvider providePhpDocForGettersAndSetters
      */

+ 153 - 1
php/tests/php_implementation_test.php

@@ -3,6 +3,7 @@
 require_once('test_base.php');
 require_once('test_util.php');
 
+use Foo\TestEnum;
 use Foo\TestMessage;
 use Foo\TestMessage_Sub;
 use Foo\TestPackedMessage;
@@ -15,7 +16,6 @@ use Google\Protobuf\Internal\CodedOutputStream;
 
 class ImplementationTest extends TestBase
 {
-
     public function testReadInt32()
     {
         $value = null;
@@ -513,4 +513,156 @@ class ImplementationTest extends TestBase
         TestUtil::setTestPackedMessage($m);
         $this->assertSame(166, $m->byteSize());
     }
+
+    public function testArrayConstructor()
+    {
+        $m = new TestMessage([
+            'optional_int32' => -42,
+            'optional_int64' => -43,
+            'optional_uint32' => 42,
+            'optional_uint64' => 43,
+            'optional_sint32' => -44,
+            'optional_sint64' => -45,
+            'optional_fixed32' => 46,
+            'optional_fixed64' => 47,
+            'optional_sfixed32' => -46,
+            'optional_sfixed64' => -47,
+            'optional_float' => 1.5,
+            'optional_double' => 1.6,
+            'optional_bool' => true,
+            'optional_string' => 'a',
+            'optional_bytes' => 'b',
+            'optional_enum' => TestEnum::ONE,
+            'optional_message' => new TestMessage_Sub([
+                'a' => 33
+            ]),
+            'repeated_int32' => [-42, -52],
+            'repeated_int64' => [-43, -53],
+            'repeated_uint32' => [42, 52],
+            'repeated_uint64' => [43, 53],
+            'repeated_sint32' => [-44, -54],
+            'repeated_sint64' => [-45, -55],
+            'repeated_fixed32' => [46, 56],
+            'repeated_fixed64' => [47, 57],
+            'repeated_sfixed32' => [-46, -56],
+            'repeated_sfixed64' => [-47, -57],
+            'repeated_float' => [1.5, 2.5],
+            'repeated_double' => [1.6, 2.6],
+            'repeated_bool' => [true, false],
+            'repeated_string' => ['a', 'c'],
+            'repeated_bytes' => ['b', 'd'],
+            'repeated_enum' => [TestEnum::ZERO, TestEnum::ONE],
+            'repeated_message' => [
+                new TestMessage_Sub(['a' => 34]),
+                new TestMessage_Sub(['a' => 35]),
+            ],
+            'map_int32_int32' => [-62 => -62],
+            'map_int64_int64' => [-63 => -63],
+            'map_uint32_uint32' => [62 => 62],
+            'map_uint64_uint64' => [63 => 63],
+            'map_sint32_sint32' => [-64 => -64],
+            'map_sint64_sint64' => [-65 => -65],
+            'map_fixed32_fixed32' => [66 => 66],
+            'map_fixed64_fixed64' => [67 => 67],
+            'map_sfixed32_sfixed32' => [-68 => -68],
+            'map_sfixed64_sfixed64' => [-69 => -69],
+            'map_int32_float' => [1 => 3.5],
+            'map_int32_double' => [1 => 3.6],
+            'map_bool_bool' => [true => true],
+            'map_string_string' => ['e' => 'e'],
+            'map_int32_bytes' => [1 => 'f'],
+            'map_int32_enum' => [1 => TestEnum::ONE],
+            'map_int32_message' => [1 => new TestMessage_Sub(['a' => 36])],
+        ]);
+
+        TestUtil::assertTestMessage($m);
+
+        // Using message objects
+        $m = new TestMessage([
+            'optional_message' => new TestMessage_Sub(['a' => 33]),
+            'repeated_message' => [
+                new TestMessage_Sub(['a' => 34]),
+                new TestMessage_Sub(['a' => 35]),
+            ],
+            'map_int32_message' => [
+                1 => new TestMessage_Sub(['a' => 36])
+            ],
+        ]);
+
+        $this->assertEquals(33, $m->getOptionalMessage()->getA());
+        $this->assertEquals(34, $m->getRepeatedMessage()[0]->getA());
+        $this->assertEquals(35, $m->getRepeatedMessage()[1]->getA());
+        $this->assertEquals(36, $m->getMapInt32Message()[1]->getA());
+    }
+
+    /**
+     * @expectedException UnexpectedValueException
+     * @expectedExceptionMessage Invalid message property: optionalInt32
+     */
+    public function testArrayConstructorJsonCaseThrowsException()
+    {
+        $m = new TestMessage([
+            'optionalInt32' => -42,
+        ]);
+    }
+
+    /**
+     * @expectedException Exception
+     * @expectedExceptionMessage Expect message.
+     */
+    public function testArraysForMessagesThrowsException()
+    {
+        $m = new TestMessage([
+            'optional_message' => [
+                'a' => 33
+            ]
+        ]);
+    }
+
+    public function testArrayConstructorWithNullValues()
+    {
+        $requestData = [
+            'optional_bool' => null,
+            'optional_string' => null,
+            'optional_bytes' => null,
+            'optional_message' => null,
+        ];
+
+        $m = new TestMessage($requestData);
+
+        $this->assertSame(false, $m->getOptionalBool());
+        $this->assertSame('', $m->getOptionalString());
+        $this->assertSame('', $m->getOptionalBytes());
+        $this->assertSame(null, $m->getOptionalMessage());
+    }
+
+    /**
+     * @dataProvider provideArrayConstructorWithNullValuesThrowsException
+     * @expectedException Exception
+     */
+    public function testArrayConstructorWithNullValuesThrowsException($requestData)
+    {
+        $m = new TestMessage($requestData);
+    }
+
+    public function provideArrayConstructorWithNullValuesThrowsException()
+    {
+        return [
+            [['optional_int32' => null]],
+            [['optional_int64' => null]],
+            [['optional_uint32' => null]],
+            [['optional_uint64' => null]],
+            [['optional_sint32' => null]],
+            [['optional_sint64' => null]],
+            [['optional_fixed32' => null]],
+            [['optional_fixed64' => null]],
+            [['optional_sfixed32' => null]],
+            [['optional_sfixed64' => null]],
+            [['optional_float' => null]],
+            [['optional_double' => null]],
+            [['optional_enum' => null]],
+            [['repeated_int32' => null]],
+            [['map_int32_int32' => null]],
+        ];
+    }
 }

+ 46 - 8
src/google/protobuf/compiler/php/php_generator.cc

@@ -94,6 +94,9 @@ void Indent(io::Printer* printer);
 void Outdent(io::Printer* printer);
 void GenerateMessageDocComment(io::Printer* printer, const Descriptor* message,
                                int is_descriptor);
+void GenerateMessageConstructorDocComment(io::Printer* printer,
+                                          const Descriptor* message,
+                                          int is_descriptor);
 void GenerateFieldDocComment(io::Printer* printer, const FieldDescriptor* field,
                              int is_descriptor, int function_type);
 void GenerateEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_,
@@ -1109,8 +1112,9 @@ void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message,
   }
   printer.Print("\n");
 
+  GenerateMessageConstructorDocComment(&printer, message, is_descriptor);
   printer.Print(
-      "public function __construct() {\n");
+      "public function __construct($data = NULL) {\n");
   Indent(&printer);
 
   std::string metadata_filename =
@@ -1118,7 +1122,7 @@ void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message,
   std::string metadata_fullname = FilenameToClassname(metadata_filename);
   printer.Print(
       "\\^fullname^::initOnce();\n"
-      "parent::__construct();\n",
+      "parent::__construct($data);\n",
       "fullname", metadata_fullname);
 
   Outdent(&printer);
@@ -1271,7 +1275,8 @@ static string EscapePhpdoc(const string& input) {
 }
 
 static void GenerateDocCommentBodyForLocation(
-    io::Printer* printer, const SourceLocation& location) {
+    io::Printer* printer, const SourceLocation& location, bool trailingNewline,
+    int indentCount) {
   string comments = location.leading_comments.empty() ?
       location.trailing_comments : location.leading_comments;
   if (!comments.empty()) {
@@ -1292,14 +1297,16 @@ static void GenerateDocCommentBodyForLocation(
       // Most lines should start with a space.  Watch out for lines that start
       // with a /, since putting that right after the leading asterisk will
       // close the comment.
-      if (!lines[i].empty() && lines[i][0] == '/') {
+      if (indentCount == 0 && !lines[i].empty() && lines[i][0] == '/') {
         printer->Print(" * ^line^\n", "line", lines[i]);
       } else {
-        printer->Print(" *^line^\n", "line", lines[i]);
+        std::string indent = std::string(indentCount, ' ');
+        printer->Print(" *^ind^^line^\n", "ind", indent, "line", lines[i]);
       }
     }
-    printer->Print(
-        " *\n");
+    if (trailingNewline) {
+      printer->Print(" *\n");
+    }
   }
 }
 
@@ -1308,7 +1315,7 @@ static void GenerateDocCommentBody(
     io::Printer* printer, const DescriptorType* descriptor) {
   SourceLocation location;
   if (descriptor->GetSourceLocation(&location)) {
-    GenerateDocCommentBodyForLocation(printer, location);
+    GenerateDocCommentBodyForLocation(printer, location, true, 0);
   }
 }
 
@@ -1334,6 +1341,37 @@ void GenerateMessageDocComment(io::Printer* printer,
     "messagename", EscapePhpdoc(message->full_name()));
 }
 
+void GenerateMessageConstructorDocComment(io::Printer* printer,
+                                          const Descriptor* message,
+                                          int is_descriptor) {
+  // In theory we should have slightly different comments for setters, getters,
+  // etc., but in practice everyone already knows the difference between these
+  // so it's redundant information.
+
+  // We start the comment with the main body based on the comments from the
+  // .proto file (if present). We then end with the field declaration, e.g.:
+  //   optional string foo = 5;
+  // If the field is a group, the debug string might end with {.
+  printer->Print("/**\n");
+  printer->Print(" * Constructor.\n");
+  printer->Print(" *\n");
+  printer->Print(" * @param array $data {\n");
+  printer->Print(" *     Optional. Data for populating the Message object.\n");
+  printer->Print(" *\n");
+  for (int i = 0; i < message->field_count(); i++) {
+    const FieldDescriptor* field = message->field(i);
+    printer->Print(" *     @type ^php_type^ $^var^\n",
+      "php_type", PhpSetterTypeName(field, is_descriptor),
+      "var", field->name());
+    SourceLocation location;
+    if (field->GetSourceLocation(&location)) {
+      GenerateDocCommentBodyForLocation(printer, location, false, 10);
+    }
+  }
+  printer->Print(" * }\n");
+  printer->Print(" */\n");
+}
+
 void GenerateServiceDocComment(io::Printer* printer,
                                const ServiceDescriptor* service) {
   printer->Print("/**\n");