3 // Protocol Buffers - Google's data interchange format
4 // Copyright 2008 Google Inc. All rights reserved.
5 // https://developers.google.com/protocol-buffers/
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions are
11 // * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 // * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
17 // * Neither the name of Google Inc. nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 * Defines Message, the parent class extended by all protocol message classes.
37 namespace Google\Protobuf\Internal;
39 use Google\Protobuf\Internal\CodedInputStream;
40 use Google\Protobuf\Internal\CodedOutputStream;
41 use Google\Protobuf\Internal\DescriptorPool;
42 use Google\Protobuf\Internal\GPBLabel;
43 use Google\Protobuf\Internal\GPBType;
44 use Google\Protobuf\Internal\GPBWire;
45 use Google\Protobuf\Internal\MapEntry;
46 use Google\Protobuf\Internal\RepeatedField;
47 use Google\Protobuf\ListValue;
48 use Google\Protobuf\Value;
49 use Google\Protobuf\Struct;
50 use Google\Protobuf\NullValue;
53 * Parent class of all proto messages. Users should not instantiate this class
54 * or extend this class or its child classes by their own. See the comment of
55 * specific functions for more details.
64 private $unknown = "";
69 public function __construct($data = NULL)
71 // MapEntry message is shared by all types of map fields, whose
72 // descriptors are different from each other. Thus, we cannot find a
73 // specific descriptor from the descriptor pool.
74 if ($this instanceof MapEntry) {
75 $this->initWithDescriptor($data);
77 $this->initWithGeneratedPool();
78 if (is_array($data)) {
79 $this->mergeFromArray($data);
80 } else if (!empty($data)) {
81 throw new \InvalidArgumentException(
82 'Message constructor must be an array or null.'
91 private function initWithGeneratedPool()
93 $pool = DescriptorPool::getGeneratedPool();
94 $this->desc = $pool->getDescriptorByClassName(get_class($this));
95 if (is_null($this->desc)) {
96 user_error(get_class($this) . " is not found in descriptor pool.");
98 foreach ($this->desc->getField() as $field) {
99 $setter = $field->getSetter();
100 if ($field->isMap()) {
101 $message_type = $field->getMessageType();
102 $key_field = $message_type->getFieldByNumber(1);
103 $value_field = $message_type->getFieldByNumber(2);
104 switch ($value_field->getType()) {
105 case GPBType::MESSAGE:
107 $map_field = new MapField(
108 $key_field->getType(),
109 $value_field->getType(),
110 $value_field->getMessageType()->getClass());
111 $this->$setter($map_field);
114 $map_field = new MapField(
115 $key_field->getType(),
116 $value_field->getType(),
117 $value_field->getEnumType()->getClass());
118 $this->$setter($map_field);
121 $map_field = new MapField(
122 $key_field->getType(),
123 $value_field->getType());
124 $this->$setter($map_field);
127 } else if ($field->getLabel() === GPBLabel::REPEATED) {
128 switch ($field->getType()) {
129 case GPBType::MESSAGE:
131 $repeated_field = new RepeatedField(
133 $field->getMessageType()->getClass());
134 $this->$setter($repeated_field);
137 $repeated_field = new RepeatedField(
139 $field->getEnumType()->getClass());
140 $this->$setter($repeated_field);
143 $repeated_field = new RepeatedField($field->getType());
144 $this->$setter($repeated_field);
147 } else if ($field->getOneofIndex() !== -1) {
148 $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
149 $oneof_name = $oneof->getName();
150 $this->$oneof_name = new OneofField($oneof);
151 } else if ($field->getLabel() === GPBLabel::OPTIONAL &&
153 switch ($field->getType()) {
155 case GPBType::UINT64:
156 case GPBType::FIXED64:
157 case GPBType::SFIXED64:
158 case GPBType::SINT64:
168 private function initWithDescriptor(Descriptor $desc)
171 foreach ($desc->getField() as $field) {
172 $setter = $field->getSetter();
173 $defaultValue = $this->defaultValue($field);
174 $this->$setter($defaultValue);
178 protected function readOneof($number)
180 $field = $this->desc->getFieldByNumber($number);
181 $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
182 $oneof_name = $oneof->getName();
183 $oneof_field = $this->$oneof_name;
184 if ($number === $oneof_field->getNumber()) {
185 return $oneof_field->getValue();
187 return $this->defaultValue($field);
191 protected function writeOneof($number, $value)
193 $field = $this->desc->getFieldByNumber($number);
194 $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
195 $oneof_name = $oneof->getName();
196 $oneof_field = $this->$oneof_name;
197 $oneof_field->setValue($value);
198 $oneof_field->setFieldName($field->getName());
199 $oneof_field->setNumber($number);
202 protected function whichOneof($oneof_name)
204 $oneof_field = $this->$oneof_name;
205 $number = $oneof_field->getNumber();
209 $field = $this->desc->getFieldByNumber($number);
210 return $field->getName();
216 private function defaultValue($field)
220 switch ($field->getType()) {
221 case GPBType::DOUBLE:
224 case GPBType::UINT32:
226 case GPBType::FIXED32:
227 case GPBType::SFIXED32:
228 case GPBType::SINT32:
232 case GPBType::UINT64:
233 case GPBType::FIXED64:
234 case GPBType::SFIXED64:
235 case GPBType::SINT64:
236 if (PHP_INT_SIZE === 4) {
243 case GPBType::STRING:
247 case GPBType::MESSAGE:
250 user_error("Unsupported type.");
258 private function skipField($input, $tag)
260 $number = GPBWire::getTagFieldNumber($tag);
262 throw new GPBDecodeException("Illegal field number zero.");
265 $start = $input->current();
266 switch (GPBWire::getTagWireType($tag)) {
267 case GPBWireType::VARINT:
269 if (!$input->readVarint64($uint64)) {
270 throw new GPBDecodeException(
271 "Unexpected EOF inside varint.");
274 case GPBWireType::FIXED64:
276 if (!$input->readLittleEndian64($uint64)) {
277 throw new GPBDecodeException(
278 "Unexpected EOF inside fixed64.");
281 case GPBWireType::FIXED32:
283 if (!$input->readLittleEndian32($uint32)) {
284 throw new GPBDecodeException(
285 "Unexpected EOF inside fixed32.");
288 case GPBWireType::LENGTH_DELIMITED:
290 if (!$input->readVarint32($length)) {
291 throw new GPBDecodeException(
292 "Unexpected EOF inside length.");
295 if (!$input->readRaw($length, $data)) {
296 throw new GPBDecodeException(
297 "Unexpected EOF inside length delimited data.");
300 case GPBWireType::START_GROUP:
301 case GPBWireType::END_GROUP:
302 throw new GPBDecodeException("Unexpected wire type.");
304 throw new GPBDecodeException("Unexpected wire type.");
306 $end = $input->current();
308 $bytes = str_repeat(chr(0), CodedOutputStream::MAX_VARINT64_BYTES);
309 $size = CodedOutputStream::writeVarintToArray($tag, $bytes, true);
310 $this->unknown .= substr($bytes, 0, $size) . $input->substr($start, $end);
316 private static function parseFieldFromStreamNoTag($input, $field, &$value)
318 switch ($field->getType()) {
319 case GPBType::DOUBLE:
320 if (!GPBWire::readDouble($input, $value)) {
321 throw new GPBDecodeException(
322 "Unexpected EOF inside double field.");
326 if (!GPBWire::readFloat($input, $value)) {
327 throw new GPBDecodeException(
328 "Unexpected EOF inside float field.");
332 if (!GPBWire::readInt64($input, $value)) {
333 throw new GPBDecodeException(
334 "Unexpected EOF inside int64 field.");
337 case GPBType::UINT64:
338 if (!GPBWire::readUint64($input, $value)) {
339 throw new GPBDecodeException(
340 "Unexpected EOF inside uint64 field.");
344 if (!GPBWire::readInt32($input, $value)) {
345 throw new GPBDecodeException(
346 "Unexpected EOF inside int32 field.");
349 case GPBType::FIXED64:
350 if (!GPBWire::readFixed64($input, $value)) {
351 throw new GPBDecodeException(
352 "Unexpected EOF inside fixed64 field.");
355 case GPBType::FIXED32:
356 if (!GPBWire::readFixed32($input, $value)) {
357 throw new GPBDecodeException(
358 "Unexpected EOF inside fixed32 field.");
362 if (!GPBWire::readBool($input, $value)) {
363 throw new GPBDecodeException(
364 "Unexpected EOF inside bool field.");
367 case GPBType::STRING:
368 // TODO(teboring): Add utf-8 check.
369 if (!GPBWire::readString($input, $value)) {
370 throw new GPBDecodeException(
371 "Unexpected EOF inside string field.");
375 trigger_error("Not implemented.", E_ERROR);
377 case GPBType::MESSAGE:
378 if ($field->isMap()) {
379 $value = new MapEntry($field->getMessageType());
381 $klass = $field->getMessageType()->getClass();
384 if (!GPBWire::readMessage($input, $value)) {
385 throw new GPBDecodeException(
386 "Unexpected EOF inside message.");
390 if (!GPBWire::readString($input, $value)) {
391 throw new GPBDecodeException(
392 "Unexpected EOF inside bytes field.");
395 case GPBType::UINT32:
396 if (!GPBWire::readUint32($input, $value)) {
397 throw new GPBDecodeException(
398 "Unexpected EOF inside uint32 field.");
402 // TODO(teboring): Check unknown enum value.
403 if (!GPBWire::readInt32($input, $value)) {
404 throw new GPBDecodeException(
405 "Unexpected EOF inside enum field.");
408 case GPBType::SFIXED32:
409 if (!GPBWire::readSfixed32($input, $value)) {
410 throw new GPBDecodeException(
411 "Unexpected EOF inside sfixed32 field.");
414 case GPBType::SFIXED64:
415 if (!GPBWire::readSfixed64($input, $value)) {
416 throw new GPBDecodeException(
417 "Unexpected EOF inside sfixed64 field.");
420 case GPBType::SINT32:
421 if (!GPBWire::readSint32($input, $value)) {
422 throw new GPBDecodeException(
423 "Unexpected EOF inside sint32 field.");
426 case GPBType::SINT64:
427 if (!GPBWire::readSint64($input, $value)) {
428 throw new GPBDecodeException(
429 "Unexpected EOF inside sint64 field.");
433 user_error("Unsupported type.");
442 private function parseFieldFromStream($tag, $input, $field)
446 if (is_null($field)) {
447 $value_format = GPBWire::UNKNOWN;
448 } elseif (GPBWire::getTagWireType($tag) ===
449 GPBWire::getWireType($field->getType())) {
450 $value_format = GPBWire::NORMAL_FORMAT;
451 } elseif ($field->isPackable() &&
452 GPBWire::getTagWireType($tag) ===
453 GPBWire::WIRETYPE_LENGTH_DELIMITED) {
454 $value_format = GPBWire::PACKED_FORMAT;
456 // the wire type doesn't match. Put it in our unknown field set.
457 $value_format = GPBWire::UNKNOWN;
460 if ($value_format === GPBWire::UNKNOWN) {
461 $this->skipField($input, $tag);
463 } elseif ($value_format === GPBWire::NORMAL_FORMAT) {
464 self::parseFieldFromStreamNoTag($input, $field, $value);
465 } elseif ($value_format === GPBWire::PACKED_FORMAT) {
467 if (!GPBWire::readInt32($input, $length)) {
468 throw new GPBDecodeException(
469 "Unexpected EOF inside packed length.");
471 $limit = $input->pushLimit($length);
472 $getter = $field->getGetter();
473 while ($input->bytesUntilLimit() > 0) {
474 self::parseFieldFromStreamNoTag($input, $field, $value);
475 $this->appendHelper($field, $value);
477 $input->popLimit($limit);
483 if ($field->isMap()) {
484 $this->kvUpdateHelper($field, $value->getKey(), $value->getValue());
485 } else if ($field->isRepeated()) {
486 $this->appendHelper($field, $value);
488 $setter = $field->getSetter();
489 $this->$setter($value);
494 * Clear all containing fields.
497 public function clear()
500 foreach ($this->desc->getField() as $field) {
501 $setter = $field->getSetter();
502 if ($field->isMap()) {
503 $message_type = $field->getMessageType();
504 $key_field = $message_type->getFieldByNumber(1);
505 $value_field = $message_type->getFieldByNumber(2);
506 switch ($value_field->getType()) {
507 case GPBType::MESSAGE:
509 $map_field = new MapField(
510 $key_field->getType(),
511 $value_field->getType(),
512 $value_field->getMessageType()->getClass());
513 $this->$setter($map_field);
516 $map_field = new MapField(
517 $key_field->getType(),
518 $value_field->getType(),
519 $value_field->getEnumType()->getClass());
520 $this->$setter($map_field);
523 $map_field = new MapField(
524 $key_field->getType(),
525 $value_field->getType());
526 $this->$setter($map_field);
529 } else if ($field->getLabel() === GPBLabel::REPEATED) {
530 switch ($field->getType()) {
531 case GPBType::MESSAGE:
533 $repeated_field = new RepeatedField(
535 $field->getMessageType()->getClass());
536 $this->$setter($repeated_field);
539 $repeated_field = new RepeatedField(
541 $field->getEnumType()->getClass());
542 $this->$setter($repeated_field);
545 $repeated_field = new RepeatedField($field->getType());
546 $this->$setter($repeated_field);
549 } else if ($field->getOneofIndex() !== -1) {
550 $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
551 $oneof_name = $oneof->getName();
552 $this->$oneof_name = new OneofField($oneof);
553 } else if ($field->getLabel() === GPBLabel::OPTIONAL) {
554 switch ($field->getType()) {
555 case GPBType::DOUBLE :
556 case GPBType::FLOAT :
559 case GPBType::INT32 :
560 case GPBType::FIXED32 :
561 case GPBType::UINT32 :
562 case GPBType::SFIXED32 :
563 case GPBType::SINT32 :
568 $this->$setter(false);
570 case GPBType::STRING :
571 case GPBType::BYTES :
574 case GPBType::GROUP :
575 case GPBType::MESSAGE :
577 $this->$setter($null);
580 if (PHP_INT_SIZE == 4) {
581 switch ($field->getType()) {
583 case GPBType::UINT64:
584 case GPBType::FIXED64:
585 case GPBType::SFIXED64:
586 case GPBType::SINT64:
590 switch ($field->getType()) {
592 case GPBType::UINT64:
593 case GPBType::FIXED64:
594 case GPBType::SFIXED64:
595 case GPBType::SINT64:
604 * Clear all unknown fields previously parsed.
607 public function discardUnknownFields()
610 foreach ($this->desc->getField() as $field) {
611 if ($field->getType() != GPBType::MESSAGE) {
614 if ($field->isMap()) {
615 $value_field = $field->getMessageType()->getFieldByNumber(2);
616 if ($value_field->getType() != GPBType::MESSAGE) {
619 $getter = $field->getGetter();
620 $map = $this->$getter();
621 foreach ($map as $key => $value) {
622 $value->discardUnknownFields();
624 } else if ($field->getLabel() === GPBLabel::REPEATED) {
625 $getter = $field->getGetter();
626 $arr = $this->$getter();
627 foreach ($arr as $sub) {
628 $sub->discardUnknownFields();
630 } else if ($field->getLabel() === GPBLabel::OPTIONAL) {
631 $getter = $field->getGetter();
632 $sub = $this->$getter();
633 if (!is_null($sub)) {
634 $sub->discardUnknownFields();
641 * Merges the contents of the specified message into current message.
643 * This method merges the contents of the specified message into the
644 * current message. Singular fields that are set in the specified message
645 * overwrite the corresponding fields in the current message. Repeated
646 * fields are appended. Map fields key-value pairs are overritten.
647 * Singular/Oneof sub-messages are recursively merged. All overritten
648 * sub-messages are deep-copied.
650 * @param object $msg Protobuf message to be merged from.
653 public function mergeFrom($msg)
655 if (get_class($this) !== get_class($msg)) {
656 user_error("Cannot merge messages with different class.");
660 foreach ($this->desc->getField() as $field) {
661 $setter = $field->getSetter();
662 $getter = $field->getGetter();
663 if ($field->isMap()) {
664 if (count($msg->$getter()) != 0) {
665 $value_field = $field->getMessageType()->getFieldByNumber(2);
666 foreach ($msg->$getter() as $key => $value) {
667 if ($value_field->getType() == GPBType::MESSAGE) {
668 $klass = $value_field->getMessageType()->getClass();
670 $copy->mergeFrom($value);
672 $this->kvUpdateHelper($field, $key, $copy);
674 $this->kvUpdateHelper($field, $key, $value);
678 } else if ($field->getLabel() === GPBLabel::REPEATED) {
679 if (count($msg->$getter()) != 0) {
680 foreach ($msg->$getter() as $tmp) {
681 if ($field->getType() == GPBType::MESSAGE) {
682 $klass = $field->getMessageType()->getClass();
684 $copy->mergeFrom($tmp);
685 $this->appendHelper($field, $copy);
687 $this->appendHelper($field, $tmp);
691 } else if ($field->getLabel() === GPBLabel::OPTIONAL) {
692 if($msg->$getter() !== $this->defaultValue($field)) {
693 $tmp = $msg->$getter();
694 if ($field->getType() == GPBType::MESSAGE) {
695 if (is_null($this->$getter())) {
696 $klass = $field->getMessageType()->getClass();
697 $new_msg = new $klass;
698 $this->$setter($new_msg);
700 $this->$getter()->mergeFrom($tmp);
702 $this->$setter($tmp);
710 * Parses a protocol buffer contained in a string.
712 * This function takes a string in the (non-human-readable) binary wire
713 * format, matching the encoding output by serializeToString().
714 * See mergeFrom() for merging behavior, if the field is already set in the
717 * @param string $data Binary protobuf data.
719 * @throws \Exception Invalid data.
721 public function mergeFromString($data)
723 $input = new CodedInputStream($data);
724 $this->parseFromStream($input);
728 * Parses a json string to protobuf message.
730 * This function takes a string in the json wire format, matching the
731 * encoding output by serializeToJsonString().
732 * See mergeFrom() for merging behavior, if the field is already set in the
735 * @param string $data Json protobuf data.
737 * @throws \Exception Invalid data.
739 public function mergeFromJsonString($data)
741 $input = new RawInputStream($data);
742 $this->parseFromJsonStream($input);
748 public function parseFromStream($input)
751 $tag = $input->readTag();
752 // End of input. This is a valid place to end, so return true.
757 $number = GPBWire::getTagFieldNumber($tag);
758 $field = $this->desc->getFieldByNumber($number);
760 $this->parseFieldFromStream($tag, $input, $field);
764 private function convertJsonValueToProtoValue(
769 switch ($field->getType()) {
770 case GPBType::MESSAGE:
771 $klass = $field->getMessageType()->getClass();
772 $submsg = new $klass;
774 if (is_a($submsg, "Google\Protobuf\Duration")) {
775 if (is_null($value)) {
776 return $this->defaultValue($field);
777 } else if (!is_string($value)) {
778 throw new GPBDecodeException("Expect string.");
780 return GPBUtil::parseDuration($value);
781 } else if ($field->isTimestamp()) {
782 if (is_null($value)) {
783 return $this->defaultValue($field);
784 } else if (!is_string($value)) {
785 throw new GPBDecodeException("Expect string.");
788 $timestamp = GPBUtil::parseTimestamp($value);
789 } catch (\Exception $e) {
790 throw new GPBDecodeException(
791 "Invalid RFC 3339 timestamp: ".$e->getMessage());
794 $submsg->setSeconds($timestamp->getSeconds());
795 $submsg->setNanos($timestamp->getNanos());
796 } else if (is_a($submsg, "Google\Protobuf\FieldMask")) {
797 if (is_null($value)) {
798 return $this->defaultValue($field);
801 return GPBUtil::parseFieldMask($value);
802 } catch (\Exception $e) {
803 throw new GPBDecodeException(
804 "Invalid FieldMask: ".$e->getMessage());
807 if (is_null($value) &&
808 !is_a($submsg, "Google\Protobuf\Value")) {
809 return $this->defaultValue($field);
811 if (GPBUtil::hasSpecialJsonMapping($submsg)) {
812 } elseif (!is_object($value) && !is_array($value)) {
813 throw new GPBDecodeException("Expect message.");
815 $submsg->mergeFromJsonArray($value);
819 if (is_null($value)) {
820 return $this->defaultValue($field);
822 if (is_integer($value)) {
825 $enum_value = $field->getEnumType()->getValueByName($value);
826 if (!is_null($enum_value)) {
827 return $enum_value->getNumber();
829 throw new GPBDecodeException(
830 "Enum field only accepts integer or enum value name");
831 case GPBType::STRING:
832 if (is_null($value)) {
833 return $this->defaultValue($field);
835 if (is_numeric($value)) {
836 return strval($value);
838 if (!is_string($value)) {
839 throw new GPBDecodeException(
840 "String field only accepts string value");
844 if (is_null($value)) {
845 return $this->defaultValue($field);
847 if (!is_string($value)) {
848 throw new GPBDecodeException(
849 "Byte field only accepts string value");
851 $proto_value = base64_decode($value, true);
852 if ($proto_value === false) {
853 throw new GPBDecodeException("Invalid base64 characters");
857 if (is_null($value)) {
858 return $this->defaultValue($field);
861 if ($value === "true") {
864 if ($value === "false") {
867 throw new GPBDecodeException(
868 "Bool field only accepts bool value");
870 if (!is_bool($value)) {
871 throw new GPBDecodeException(
872 "Bool field only accepts bool value");
876 case GPBType::DOUBLE:
877 if (is_null($value)) {
878 return $this->defaultValue($field);
880 if ($value === "Infinity") {
883 if ($value === "-Infinity") {
886 if ($value === "NaN") {
891 case GPBType::SINT32:
892 case GPBType::SFIXED32:
893 if (is_null($value)) {
894 return $this->defaultValue($field);
896 if (!is_numeric($value)) {
897 throw new GPBDecodeException(
898 "Invalid data type for int32 field");
900 if (bccomp($value, "2147483647") > 0) {
901 throw new GPBDecodeException(
904 if (bccomp($value, "-2147483648") < 0) {
905 throw new GPBDecodeException(
909 case GPBType::UINT32:
910 case GPBType::FIXED32:
911 if (is_null($value)) {
912 return $this->defaultValue($field);
914 if (!is_numeric($value)) {
915 throw new GPBDecodeException(
916 "Invalid data type for uint32 field");
918 if (bccomp($value, 4294967295) > 0) {
919 throw new GPBDecodeException(
924 case GPBType::SINT64:
925 case GPBType::SFIXED64:
926 if (is_null($value)) {
927 return $this->defaultValue($field);
929 if (!is_numeric($value)) {
930 throw new GPBDecodeException(
931 "Invalid data type for int64 field");
933 if (bccomp($value, "9223372036854775807") > 0) {
934 throw new GPBDecodeException(
937 if (bccomp($value, "-9223372036854775808") < 0) {
938 throw new GPBDecodeException(
942 case GPBType::UINT64:
943 case GPBType::FIXED64:
944 if (is_null($value)) {
945 return $this->defaultValue($field);
947 if (!is_numeric($value)) {
948 throw new GPBDecodeException(
949 "Invalid data type for int64 field");
951 if (bccomp($value, "18446744073709551615") > 0) {
952 throw new GPBDecodeException(
955 if (bccomp($value, "9223372036854775807") > 0) {
956 $value = bcsub($value, "18446744073709551616");
965 * Populates the message from a user-supplied PHP array. Array keys
966 * correspond to Message properties and nested message properties.
970 * $message->mergeFromArray([
971 * 'name' => 'This is a message name',
973 * 'startTime' => time() - 60,
974 * 'endTime' => time(),
979 * This method will trigger an error if it is passed data that cannot
980 * be converted to the correct type. For example, a StringValue field
981 * must receive data that is either a string or a StringValue object.
983 * @param array $array An array containing message properties and values.
986 protected function mergeFromArray(array $array)
988 // Just call the setters for the field names
989 foreach ($array as $key => $value) {
990 $field = $this->desc->getFieldByName($key);
991 if (is_null($field)) {
992 throw new \UnexpectedValueException(
993 'Invalid message property: ' . $key);
995 $setter = $field->getSetter();
996 if ($field->isMap()) {
997 $valueField = $field->getMessageType()->getFieldByName('value');
998 if (!is_null($valueField) && $valueField->isWrapperType()) {
999 self::normalizeArrayElementsToMessageType($value, $valueField->getMessageType()->getClass());
1001 } elseif ($field->isWrapperType()) {
1002 $class = $field->getMessageType()->getClass();
1003 if ($field->isRepeated()) {
1004 self::normalizeArrayElementsToMessageType($value, $class);
1006 self::normalizeToMessageType($value, $class);
1009 $this->$setter($value);
1014 * Tries to normalize the elements in $value into a provided protobuf
1015 * wrapper type $class. If $value is any type other than array, we do
1016 * not do any conversion, and instead rely on the existing protobuf
1017 * type checking. If $value is an array, we process each element and
1018 * try to convert it to an instance of $class.
1020 * @param mixed $value The array of values to normalize.
1021 * @param string $class The expected wrapper class name
1023 private static function normalizeArrayElementsToMessageType(&$value, $class)
1025 if (!is_array($value)) {
1026 // In the case that $value is not an array, we do not want to
1027 // attempt any conversion. Note that this includes the cases
1028 // when $value is a RepeatedField of MapField. In those cases,
1029 // we do not need to convert the elements, as they should
1030 // already be the correct types.
1033 // Normalize each element in the array.
1034 foreach ($value as $key => &$elementValue) {
1035 self::normalizeToMessageType($elementValue, $class);
1041 * Tries to normalize $value into a provided protobuf wrapper type $class.
1042 * If $value is any type other than an object, we attempt to construct an
1043 * instance of $class and assign $value to it using the setValue method
1044 * shared by all wrapper types.
1046 * This method will raise an error if it receives a type that cannot be
1047 * assigned to the wrapper type via setValue.
1049 * @param mixed $value The value to normalize.
1050 * @param string $class The expected wrapper class name
1052 private static function normalizeToMessageType(&$value, $class)
1054 if (is_null($value) || is_object($value)) {
1055 // This handles the case that $value is an instance of $class. We
1056 // choose not to do any more strict checking here, relying on the
1057 // existing type checking done by GPBUtil.
1060 // Try to instantiate $class and set the value
1063 $msg->setValue($value);
1066 } catch (\Exception $exception) {
1068 "Error normalizing value to type '$class': " . $exception->getMessage(),
1075 protected function mergeFromJsonArray($array)
1077 if (is_a($this, "Google\Protobuf\Any")) {
1079 $this->setTypeUrl($array["@type"]);
1080 $msg = $this->unpack();
1081 if (GPBUtil::hasSpecialJsonMapping($msg)) {
1082 $msg->mergeFromJsonArray($array["value"]);
1084 unset($array["@type"]);
1085 $msg->mergeFromJsonArray($array);
1087 $this->setValue($msg->serializeToString());
1090 if (is_a($this, "Google\Protobuf\DoubleValue") ||
1091 is_a($this, "Google\Protobuf\FloatValue") ||
1092 is_a($this, "Google\Protobuf\Int64Value") ||
1093 is_a($this, "Google\Protobuf\UInt64Value") ||
1094 is_a($this, "Google\Protobuf\Int32Value") ||
1095 is_a($this, "Google\Protobuf\UInt32Value") ||
1096 is_a($this, "Google\Protobuf\BoolValue") ||
1097 is_a($this, "Google\Protobuf\StringValue")) {
1098 $this->setValue($array);
1101 if (is_a($this, "Google\Protobuf\BytesValue")) {
1102 $this->setValue(base64_decode($array));
1105 if (is_a($this, "Google\Protobuf\Duration")) {
1106 $this->mergeFrom(GPBUtil::parseDuration($array));
1109 if (is_a($this, "Google\Protobuf\FieldMask")) {
1110 $this->mergeFrom(GPBUtil::parseFieldMask($array));
1113 if (is_a($this, "Google\Protobuf\Timestamp")) {
1114 $this->mergeFrom(GPBUtil::parseTimestamp($array));
1117 if (is_a($this, "Google\Protobuf\Struct")) {
1118 $fields = $this->getFields();
1119 foreach($array as $key => $value) {
1121 $v->mergeFromJsonArray($value);
1125 if (is_a($this, "Google\Protobuf\Value")) {
1126 if (is_bool($array)) {
1127 $this->setBoolValue($array);
1128 } elseif (is_string($array)) {
1129 $this->setStringValue($array);
1130 } elseif (is_null($array)) {
1131 $this->setNullValue(0);
1132 } elseif (is_double($array) || is_integer($array)) {
1133 $this->setNumberValue($array);
1134 } elseif (is_array($array)) {
1135 if (array_values($array) !== $array) {
1136 // Associative array
1137 $struct_value = $this->getStructValue();
1138 if (is_null($struct_value)) {
1139 $struct_value = new Struct();
1140 $this->setStructValue($struct_value);
1142 foreach ($array as $key => $v) {
1143 $value = new Value();
1144 $value->mergeFromJsonArray($v);
1145 $values = $struct_value->getFields();
1146 $values[$key]= $value;
1150 $list_value = $this->getListValue();
1151 if (is_null($list_value)) {
1152 $list_value = new ListValue();
1153 $this->setListValue($list_value);
1155 foreach ($array as $v) {
1156 $value = new Value();
1157 $value->mergeFromJsonArray($v);
1158 $values = $list_value->getValues();
1163 throw new GPBDecodeException("Invalid type for Value.");
1167 $this->mergeFromArrayJsonImpl($array);
1170 private function mergeFromArrayJsonImpl($array)
1172 foreach ($array as $key => $value) {
1173 $field = $this->desc->getFieldByJsonName($key);
1174 if (is_null($field)) {
1175 $field = $this->desc->getFieldByName($key);
1176 if (is_null($field)) {
1180 if ($field->isMap()) {
1181 if (is_null($value)) {
1184 $key_field = $field->getMessageType()->getFieldByNumber(1);
1185 $value_field = $field->getMessageType()->getFieldByNumber(2);
1186 foreach ($value as $tmp_key => $tmp_value) {
1187 if (is_null($tmp_value)) {
1188 throw new \Exception(
1189 "Map value field element cannot be null.");
1191 $proto_key = $this->convertJsonValueToProtoValue(
1195 $proto_value = $this->convertJsonValueToProtoValue(
1198 self::kvUpdateHelper($field, $proto_key, $proto_value);
1200 } else if ($field->isRepeated()) {
1201 if (is_null($value)) {
1204 foreach ($value as $tmp) {
1205 if (is_null($tmp)) {
1206 throw new \Exception(
1207 "Repeated field elements cannot be null.");
1209 $proto_value = $this->convertJsonValueToProtoValue(
1212 self::appendHelper($field, $proto_value);
1215 $setter = $field->getSetter();
1216 $proto_value = $this->convertJsonValueToProtoValue(
1219 if ($field->getType() === GPBType::MESSAGE) {
1220 if (is_null($proto_value)) {
1223 $getter = $field->getGetter();
1224 $submsg = $this->$getter();
1225 if (!is_null($submsg)) {
1226 $submsg->mergeFrom($proto_value);
1230 $this->$setter($proto_value);
1238 public function parseFromJsonStream($input)
1240 $array = json_decode($input->getData(), true, 512, JSON_BIGINT_AS_STRING);
1241 if ($this instanceof \Google\Protobuf\ListValue) {
1242 $array = ["values"=>$array];
1244 if (is_null($array)) {
1245 if ($this instanceof \Google\Protobuf\Value) {
1246 $this->setNullValue(\Google\Protobuf\NullValue::NULL_VALUE);
1249 throw new GPBDecodeException(
1250 "Cannot decode json string: " . $input->getData());
1254 $this->mergeFromJsonArray($array);
1255 } catch (\Exception $e) {
1256 throw new GPBDecodeException($e->getMessage());
1263 private function serializeSingularFieldToStream($field, &$output)
1265 if (!$this->existField($field)) {
1268 $getter = $field->getGetter();
1269 $value = $this->$getter();
1270 if (!GPBWire::serializeFieldToStream($value, $field, true, $output)) {
1279 private function serializeRepeatedFieldToStream($field, &$output)
1281 $getter = $field->getGetter();
1282 $values = $this->$getter();
1283 $count = count($values);
1288 $packed = $field->getPacked();
1290 if (!GPBWire::writeTag(
1292 GPBWire::makeTag($field->getNumber(), GPBType::STRING))) {
1296 foreach ($values as $value) {
1297 $size += $this->fieldDataOnlyByteSize($field, $value);
1299 if (!$output->writeVarint32($size, true)) {
1304 foreach ($values as $value) {
1305 if (!GPBWire::serializeFieldToStream(
1319 private function serializeMapFieldToStream($field, $output)
1321 $getter = $field->getGetter();
1322 $values = $this->$getter();
1323 $count = count($values);
1328 foreach ($values as $key => $value) {
1329 $map_entry = new MapEntry($field->getMessageType());
1330 $map_entry->setKey($key);
1331 $map_entry->setValue($value);
1332 if (!GPBWire::serializeFieldToStream(
1346 private function serializeFieldToStream(&$output, $field)
1348 if ($field->isMap()) {
1349 return $this->serializeMapFieldToStream($field, $output);
1350 } elseif ($field->isRepeated()) {
1351 return $this->serializeRepeatedFieldToStream($field, $output);
1353 return $this->serializeSingularFieldToStream($field, $output);
1360 private function serializeFieldToJsonStream(&$output, $field)
1362 $getter = $field->getGetter();
1363 $values = $this->$getter();
1364 return GPBJsonWire::serializeFieldToStream(
1365 $values, $field, $output, !GPBUtil::hasSpecialJsonMapping($this));
1371 public function serializeToStream(&$output)
1373 $fields = $this->desc->getField();
1374 foreach ($fields as $field) {
1375 if (!$this->serializeFieldToStream($output, $field)) {
1379 $output->writeRaw($this->unknown, strlen($this->unknown));
1386 public function serializeToJsonStream(&$output)
1388 if (is_a($this, 'Google\Protobuf\Any')) {
1389 $output->writeRaw("{", 1);
1390 $type_field = $this->desc->getFieldByNumber(1);
1391 $value_msg = $this->unpack();
1393 // Serialize type url.
1394 $output->writeRaw("\"@type\":", 8);
1395 $output->writeRaw("\"", 1);
1396 $output->writeRaw($this->getTypeUrl(), strlen($this->getTypeUrl()));
1397 $output->writeRaw("\"", 1);
1400 if (GPBUtil::hasSpecialJsonMapping($value_msg)) {
1401 $output->writeRaw(",\"value\":", 9);
1402 $value_msg->serializeToJsonStream($output);
1404 $value_fields = $value_msg->desc->getField();
1405 foreach ($value_fields as $field) {
1406 if ($value_msg->existField($field)) {
1407 $output->writeRaw(",", 1);
1408 if (!$value_msg->serializeFieldToJsonStream($output, $field)) {
1415 $output->writeRaw("}", 1);
1416 } elseif (is_a($this, 'Google\Protobuf\FieldMask')) {
1417 $field_mask = GPBUtil::formatFieldMask($this);
1418 $output->writeRaw("\"", 1);
1419 $output->writeRaw($field_mask, strlen($field_mask));
1420 $output->writeRaw("\"", 1);
1421 } elseif (is_a($this, 'Google\Protobuf\Duration')) {
1422 $duration = GPBUtil::formatDuration($this) . "s";
1423 $output->writeRaw("\"", 1);
1424 $output->writeRaw($duration, strlen($duration));
1425 $output->writeRaw("\"", 1);
1426 } elseif (get_class($this) === 'Google\Protobuf\Timestamp') {
1427 $timestamp = GPBUtil::formatTimestamp($this);
1428 $timestamp = json_encode($timestamp);
1429 $output->writeRaw($timestamp, strlen($timestamp));
1430 } elseif (get_class($this) === 'Google\Protobuf\ListValue') {
1431 $field = $this->desc->getField()[1];
1432 if (!$this->existField($field)) {
1433 $output->writeRaw("[]", 2);
1435 if (!$this->serializeFieldToJsonStream($output, $field)) {
1439 } elseif (get_class($this) === 'Google\Protobuf\Struct') {
1440 $field = $this->desc->getField()[1];
1441 if (!$this->existField($field)) {
1442 $output->writeRaw("{}", 2);
1444 if (!$this->serializeFieldToJsonStream($output, $field)) {
1449 if (!GPBUtil::hasSpecialJsonMapping($this)) {
1450 $output->writeRaw("{", 1);
1452 $fields = $this->desc->getField();
1454 foreach ($fields as $field) {
1455 if ($this->existField($field) ||
1456 GPBUtil::hasJsonValue($this)) {
1460 $output->writeRaw(",", 1);
1462 if (!$this->serializeFieldToJsonStream($output, $field)) {
1467 if (!GPBUtil::hasSpecialJsonMapping($this)) {
1468 $output->writeRaw("}", 1);
1475 * Serialize the message to string.
1476 * @return string Serialized binary protobuf data.
1478 public function serializeToString()
1480 $output = new CodedOutputStream($this->byteSize());
1481 $this->serializeToStream($output);
1482 return $output->getData();
1486 * Serialize the message to json string.
1487 * @return string Serialized json protobuf data.
1489 public function serializeToJsonString()
1491 $output = new CodedOutputStream($this->jsonByteSize());
1492 $this->serializeToJsonStream($output);
1493 return $output->getData();
1499 private function existField($field)
1501 $oneof_index = $field->getOneofIndex();
1502 if ($oneof_index !== -1) {
1503 $oneof = $this->desc->getOneofDecl()[$oneof_index];
1504 $oneof_name = $oneof->getName();
1505 return $this->$oneof_name->getNumber() === $field->getNumber();
1508 $getter = $field->getGetter();
1509 $values = $this->$getter();
1510 if ($field->isMap()) {
1511 return count($values) !== 0;
1512 } elseif ($field->isRepeated()) {
1513 return count($values) !== 0;
1515 return $values !== $this->defaultValue($field);
1522 private function repeatedFieldDataOnlyByteSize($field)
1526 $getter = $field->getGetter();
1527 $values = $this->$getter();
1528 $count = count($values);
1530 $size += $count * GPBWire::tagSize($field);
1531 foreach ($values as $value) {
1532 $size += $this->singularFieldDataOnlyByteSize($field);
1540 private function fieldDataOnlyByteSize($field, $value)
1544 switch ($field->getType()) {
1548 case GPBType::FLOAT:
1549 case GPBType::FIXED32:
1550 case GPBType::SFIXED32:
1553 case GPBType::DOUBLE:
1554 case GPBType::FIXED64:
1555 case GPBType::SFIXED64:
1558 case GPBType::INT32:
1560 $size += GPBWire::varint32Size($value, true);
1562 case GPBType::UINT32:
1563 $size += GPBWire::varint32Size($value);
1565 case GPBType::UINT64:
1566 case GPBType::INT64:
1567 $size += GPBWire::varint64Size($value);
1569 case GPBType::SINT32:
1570 $size += GPBWire::sint32Size($value);
1572 case GPBType::SINT64:
1573 $size += GPBWire::sint64Size($value);
1575 case GPBType::STRING:
1576 case GPBType::BYTES:
1577 $size += strlen($value);
1578 $size += GPBWire::varint32Size($size);
1580 case GPBType::MESSAGE:
1581 $size += $value->byteSize();
1582 $size += GPBWire::varint32Size($size);
1584 case GPBType::GROUP:
1585 // TODO(teboring): Add support.
1586 user_error("Unsupported type.");
1589 user_error("Unsupported type.");
1599 private function fieldDataOnlyJsonByteSize($field, $value)
1603 switch ($field->getType()) {
1604 case GPBType::SFIXED32:
1605 case GPBType::SINT32:
1606 case GPBType::INT32:
1607 $size += strlen(strval($value));
1609 case GPBType::FIXED32:
1610 case GPBType::UINT32:
1612 $value = bcadd($value, "4294967296");
1614 $size += strlen(strval($value));
1616 case GPBType::FIXED64:
1617 case GPBType::UINT64:
1619 $value = bcadd($value, "18446744073709551616");
1621 // Intentional fall through.
1622 case GPBType::SFIXED64:
1623 case GPBType::INT64:
1624 case GPBType::SINT64:
1625 $size += 2; // size for ""
1626 $size += strlen(strval($value));
1628 case GPBType::FLOAT:
1629 if (is_nan($value)) {
1630 $size += strlen("NaN") + 2;
1631 } elseif ($value === INF) {
1632 $size += strlen("Infinity") + 2;
1633 } elseif ($value === -INF) {
1634 $size += strlen("-Infinity") + 2;
1636 $size += strlen(sprintf("%.8g", $value));
1639 case GPBType::DOUBLE:
1640 if (is_nan($value)) {
1641 $size += strlen("NaN") + 2;
1642 } elseif ($value === INF) {
1643 $size += strlen("Infinity") + 2;
1644 } elseif ($value === -INF) {
1645 $size += strlen("-Infinity") + 2;
1647 $size += strlen(sprintf("%.17g", $value));
1651 $enum_desc = $field->getEnumType();
1652 if ($enum_desc->getClass() === "Google\Protobuf\NullValue") {
1656 $enum_value_desc = $enum_desc->getValueByNumber($value);
1657 if (!is_null($enum_value_desc)) {
1658 $size += 2; // size for ""
1659 $size += strlen($enum_value_desc->getName());
1661 $str_value = strval($value);
1662 $size += strlen($str_value);
1672 case GPBType::STRING:
1673 $value = json_encode($value, JSON_UNESCAPED_UNICODE);
1674 $size += strlen($value);
1676 case GPBType::BYTES:
1677 # if (is_a($this, "Google\Protobuf\BytesValue")) {
1678 # $size += strlen(json_encode($value));
1680 # $size += strlen(base64_encode($value));
1681 # $size += 2; // size for \"\"
1683 $size += strlen(base64_encode($value));
1684 $size += 2; // size for \"\"
1686 case GPBType::MESSAGE:
1687 $size += $value->jsonByteSize();
1689 # case GPBType::GROUP:
1690 # // TODO(teboring): Add support.
1691 # user_error("Unsupported type.");
1694 user_error("Unsupported type " . $field->getType());
1704 private function fieldByteSize($field)
1707 if ($field->isMap()) {
1708 $getter = $field->getGetter();
1709 $values = $this->$getter();
1710 $count = count($values);
1712 $size += $count * GPBWire::tagSize($field);
1713 $message_type = $field->getMessageType();
1714 $key_field = $message_type->getFieldByNumber(1);
1715 $value_field = $message_type->getFieldByNumber(2);
1716 foreach ($values as $key => $value) {
1718 if ($key != $this->defaultValue($key_field)) {
1719 $data_size += $this->fieldDataOnlyByteSize(
1722 $data_size += GPBWire::tagSize($key_field);
1724 if ($value != $this->defaultValue($value_field)) {
1725 $data_size += $this->fieldDataOnlyByteSize(
1728 $data_size += GPBWire::tagSize($value_field);
1730 $size += GPBWire::varint32Size($data_size) + $data_size;
1733 } elseif ($field->isRepeated()) {
1734 $getter = $field->getGetter();
1735 $values = $this->$getter();
1736 $count = count($values);
1738 if ($field->getPacked()) {
1740 foreach ($values as $value) {
1741 $data_size += $this->fieldDataOnlyByteSize($field, $value);
1743 $size += GPBWire::tagSize($field);
1744 $size += GPBWire::varint32Size($data_size);
1745 $size += $data_size;
1747 $size += $count * GPBWire::tagSize($field);
1748 foreach ($values as $value) {
1749 $size += $this->fieldDataOnlyByteSize($field, $value);
1753 } elseif ($this->existField($field)) {
1754 $size += GPBWire::tagSize($field);
1755 $getter = $field->getGetter();
1756 $value = $this->$getter();
1757 $size += $this->fieldDataOnlyByteSize($field, $value);
1765 private function fieldJsonByteSize($field)
1769 if ($field->isMap()) {
1770 $getter = $field->getGetter();
1771 $values = $this->$getter();
1772 $count = count($values);
1774 if (!GPBUtil::hasSpecialJsonMapping($this)) {
1775 $size += 3; // size for "\"\":".
1776 $size += strlen($field->getJsonName()); // size for field name
1778 $size += 2; // size for "{}".
1779 $size += $count - 1; // size for commas
1780 $getter = $field->getGetter();
1781 $map_entry = $field->getMessageType();
1782 $key_field = $map_entry->getFieldByNumber(1);
1783 $value_field = $map_entry->getFieldByNumber(2);
1784 switch ($key_field->getType()) {
1785 case GPBType::STRING:
1786 case GPBType::SFIXED64:
1787 case GPBType::INT64:
1788 case GPBType::SINT64:
1789 case GPBType::FIXED64:
1790 case GPBType::UINT64:
1791 $additional_quote = false;
1794 $additional_quote = true;
1796 foreach ($values as $key => $value) {
1797 if ($additional_quote) {
1798 $size += 2; // size for ""
1800 $size += $this->fieldDataOnlyJsonByteSize($key_field, $key);
1801 $size += $this->fieldDataOnlyJsonByteSize($value_field, $value);
1802 $size += 1; // size for :
1805 } elseif ($field->isRepeated()) {
1806 $getter = $field->getGetter();
1807 $values = $this->$getter();
1808 $count = count($values);
1810 if (!GPBUtil::hasSpecialJsonMapping($this)) {
1811 $size += 3; // size for "\"\":".
1812 $size += strlen($field->getJsonName()); // size for field name
1814 $size += 2; // size for "[]".
1815 $size += $count - 1; // size for commas
1816 $getter = $field->getGetter();
1817 foreach ($values as $value) {
1818 $size += $this->fieldDataOnlyJsonByteSize($field, $value);
1821 } elseif ($this->existField($field) || GPBUtil::hasJsonValue($this)) {
1822 if (!GPBUtil::hasSpecialJsonMapping($this)) {
1823 $size += 3; // size for "\"\":".
1824 $size += strlen($field->getJsonName()); // size for field name
1826 $getter = $field->getGetter();
1827 $value = $this->$getter();
1828 $size += $this->fieldDataOnlyJsonByteSize($field, $value);
1836 public function byteSize()
1840 $fields = $this->desc->getField();
1841 foreach ($fields as $field) {
1842 $size += $this->fieldByteSize($field);
1844 $size += strlen($this->unknown);
1848 private function appendHelper($field, $append_value)
1850 $getter = $field->getGetter();
1851 $setter = $field->getSetter();
1853 $field_arr_value = $this->$getter();
1854 $field_arr_value[] = $append_value;
1856 if (!is_object($field_arr_value)) {
1857 $this->$setter($field_arr_value);
1861 private function kvUpdateHelper($field, $update_key, $update_value)
1863 $getter = $field->getGetter();
1864 $setter = $field->getSetter();
1866 $field_arr_value = $this->$getter();
1867 $field_arr_value[$update_key] = $update_value;
1869 if (!is_object($field_arr_value)) {
1870 $this->$setter($field_arr_value);
1877 public function jsonByteSize()
1880 if (is_a($this, 'Google\Protobuf\Any')) {
1884 // Size for "\"@type\":".
1887 // Size for url. +2 for "" /.
1888 $size += strlen($this->getTypeUrl()) + 2;
1890 $value_msg = $this->unpack();
1891 if (GPBUtil::hasSpecialJsonMapping($value_msg)) {
1892 // Size for "\",value\":".
1894 $size += $value_msg->jsonByteSize();
1896 // Size for value. +1 for comma, -2 for "{}".
1897 $size += $value_msg->jsonByteSize() -1;
1899 } elseif (get_class($this) === 'Google\Protobuf\FieldMask') {
1900 $field_mask = GPBUtil::formatFieldMask($this);
1901 $size += strlen($field_mask) + 2; // 2 for ""
1902 } elseif (get_class($this) === 'Google\Protobuf\Duration') {
1903 $duration = GPBUtil::formatDuration($this) . "s";
1904 $size += strlen($duration) + 2; // 2 for ""
1905 } elseif (get_class($this) === 'Google\Protobuf\Timestamp') {
1906 $timestamp = GPBUtil::formatTimestamp($this);
1907 $timestamp = json_encode($timestamp);
1908 $size += strlen($timestamp);
1909 } elseif (get_class($this) === 'Google\Protobuf\ListValue') {
1910 $field = $this->desc->getField()[1];
1911 if ($this->existField($field)) {
1912 $field_size = $this->fieldJsonByteSize($field);
1913 $size += $field_size;
1918 } elseif (get_class($this) === 'Google\Protobuf\Struct') {
1919 $field = $this->desc->getField()[1];
1920 if ($this->existField($field)) {
1921 $field_size = $this->fieldJsonByteSize($field);
1922 $size += $field_size;
1928 if (!GPBUtil::hasSpecialJsonMapping($this)) {
1933 $fields = $this->desc->getField();
1935 foreach ($fields as $field) {
1936 $field_size = $this->fieldJsonByteSize($field);
1937 $size += $field_size;
1938 if ($field_size != 0) {
1943 $size += $count > 0 ? ($count - 1) : 0;