Imported Upstream version 3.8.0
[platform/upstream/protobuf.git] / php / src / Google / Protobuf / Internal / Message.php
1 <?php
2
3 // Protocol Buffers - Google's data interchange format
4 // Copyright 2008 Google Inc.  All rights reserved.
5 // https://developers.google.com/protocol-buffers/
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions are
9 // met:
10 //
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
16 // distribution.
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.
20 //
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.
32
33 /**
34  * Defines Message, the parent class extended by all protocol message classes.
35  */
36
37 namespace Google\Protobuf\Internal;
38
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;
51
52 /**
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.
56  */
57 class Message
58 {
59
60     /**
61      * @ignore
62      */
63     private $desc;
64     private $unknown = "";
65
66     /**
67      * @ignore
68      */
69     public function __construct($data = NULL)
70     {
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);
76         } else {
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.'
83                 );
84             }
85         }
86     }
87
88     /**
89      * @ignore
90      */
91     private function initWithGeneratedPool()
92     {
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.");
97         }
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:
106                     case GPBType::GROUP:
107                         $map_field = new MapField(
108                             $key_field->getType(),
109                             $value_field->getType(),
110                             $value_field->getMessageType()->getClass());
111                         $this->$setter($map_field);
112                         break;
113                     case GPBType::ENUM:
114                         $map_field = new MapField(
115                             $key_field->getType(),
116                             $value_field->getType(),
117                             $value_field->getEnumType()->getClass());
118                         $this->$setter($map_field);
119                         break;
120                     default:
121                         $map_field = new MapField(
122                             $key_field->getType(),
123                             $value_field->getType());
124                         $this->$setter($map_field);
125                         break;
126                 }
127             } else if ($field->getLabel() === GPBLabel::REPEATED) {
128                 switch ($field->getType()) {
129                     case GPBType::MESSAGE:
130                     case GPBType::GROUP:
131                         $repeated_field = new RepeatedField(
132                             $field->getType(),
133                             $field->getMessageType()->getClass());
134                         $this->$setter($repeated_field);
135                         break;
136                     case GPBType::ENUM:
137                         $repeated_field = new RepeatedField(
138                             $field->getType(),
139                             $field->getEnumType()->getClass());
140                         $this->$setter($repeated_field);
141                         break;
142                     default:
143                         $repeated_field = new RepeatedField($field->getType());
144                         $this->$setter($repeated_field);
145                         break;
146                 }
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 &&
152                        PHP_INT_SIZE == 4) {
153                 switch ($field->getType()) {
154                     case GPBType::INT64:
155                     case GPBType::UINT64:
156                     case GPBType::FIXED64:
157                     case GPBType::SFIXED64:
158                     case GPBType::SINT64:
159                         $this->$setter("0");
160                 }
161             }
162         }
163     }
164
165     /**
166      * @ignore
167      */
168     private function initWithDescriptor(Descriptor $desc)
169     {
170         $this->desc = $desc;
171         foreach ($desc->getField() as $field) {
172             $setter = $field->getSetter();
173             $defaultValue = $this->defaultValue($field);
174             $this->$setter($defaultValue);
175         }
176     }
177
178     protected function readOneof($number)
179     {
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();
186         } else {
187             return $this->defaultValue($field);
188         }
189     }
190
191     protected function writeOneof($number, $value)
192     {
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);
200     }
201
202     protected function whichOneof($oneof_name)
203     {
204         $oneof_field = $this->$oneof_name;
205         $number = $oneof_field->getNumber();
206         if ($number == 0) {
207           return "";
208         }
209         $field = $this->desc->getFieldByNumber($number);
210         return $field->getName();
211     }
212
213     /**
214      * @ignore
215      */
216     private function defaultValue($field)
217     {
218         $value = null;
219
220         switch ($field->getType()) {
221             case GPBType::DOUBLE:
222             case GPBType::FLOAT:
223                 return 0.0;
224             case GPBType::UINT32:
225             case GPBType::INT32:
226             case GPBType::FIXED32:
227             case GPBType::SFIXED32:
228             case GPBType::SINT32:
229             case GPBType::ENUM:
230                 return 0;
231             case GPBType::INT64:
232             case GPBType::UINT64:
233             case GPBType::FIXED64:
234             case GPBType::SFIXED64:
235             case GPBType::SINT64:
236                 if (PHP_INT_SIZE === 4) {
237                     return '0';
238                 } else {
239                     return 0;
240                 }
241             case GPBType::BOOL:
242                 return false;
243             case GPBType::STRING:
244             case GPBType::BYTES:
245                 return "";
246             case GPBType::GROUP:
247             case GPBType::MESSAGE:
248                 return null;
249             default:
250                 user_error("Unsupported type.");
251                 return false;
252         }
253     }
254
255     /**
256      * @ignore
257      */
258     private function skipField($input, $tag)
259     {
260         $number = GPBWire::getTagFieldNumber($tag);
261         if ($number === 0) {
262             throw new GPBDecodeException("Illegal field number zero.");
263         }
264
265         $start = $input->current();
266         switch (GPBWire::getTagWireType($tag)) {
267             case GPBWireType::VARINT:
268                 $uint64 = 0;
269                 if (!$input->readVarint64($uint64)) {
270                     throw new GPBDecodeException(
271                         "Unexpected EOF inside varint.");
272                 }
273                 break;
274             case GPBWireType::FIXED64:
275                 $uint64 = 0;
276                 if (!$input->readLittleEndian64($uint64)) {
277                     throw new GPBDecodeException(
278                         "Unexpected EOF inside fixed64.");
279                 }
280                 break;
281             case GPBWireType::FIXED32:
282                 $uint32 = 0;
283                 if (!$input->readLittleEndian32($uint32)) {
284                     throw new GPBDecodeException(
285                         "Unexpected EOF inside fixed32.");
286                 }
287                 break;
288             case GPBWireType::LENGTH_DELIMITED:
289                 $length = 0;
290                 if (!$input->readVarint32($length)) {
291                     throw new GPBDecodeException(
292                         "Unexpected EOF inside length.");
293                 }
294                 $data = NULL;
295                 if (!$input->readRaw($length, $data)) {
296                     throw new GPBDecodeException(
297                         "Unexpected EOF inside length delimited data.");
298                 }
299                 break;
300             case GPBWireType::START_GROUP:
301             case GPBWireType::END_GROUP:
302                 throw new GPBDecodeException("Unexpected wire type.");
303             default:
304                 throw new GPBDecodeException("Unexpected wire type.");
305         }
306         $end = $input->current();
307
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);
311     }
312
313     /**
314      * @ignore
315      */
316     private static function parseFieldFromStreamNoTag($input, $field, &$value)
317     {
318         switch ($field->getType()) {
319             case GPBType::DOUBLE:
320                 if (!GPBWire::readDouble($input, $value)) {
321                     throw new GPBDecodeException(
322                         "Unexpected EOF inside double field.");
323                 }
324                 break;
325             case GPBType::FLOAT:
326                 if (!GPBWire::readFloat($input, $value)) {
327                     throw new GPBDecodeException(
328                         "Unexpected EOF inside float field.");
329                 }
330                 break;
331             case GPBType::INT64:
332                 if (!GPBWire::readInt64($input, $value)) {
333                     throw new GPBDecodeException(
334                         "Unexpected EOF inside int64 field.");
335                 }
336                 break;
337             case GPBType::UINT64:
338                 if (!GPBWire::readUint64($input, $value)) {
339                     throw new GPBDecodeException(
340                         "Unexpected EOF inside uint64 field.");
341                 }
342                 break;
343             case GPBType::INT32:
344                 if (!GPBWire::readInt32($input, $value)) {
345                     throw new GPBDecodeException(
346                         "Unexpected EOF inside int32 field.");
347                 }
348                 break;
349             case GPBType::FIXED64:
350                 if (!GPBWire::readFixed64($input, $value)) {
351                     throw new GPBDecodeException(
352                         "Unexpected EOF inside fixed64 field.");
353                 }
354                 break;
355             case GPBType::FIXED32:
356                 if (!GPBWire::readFixed32($input, $value)) {
357                     throw new GPBDecodeException(
358                         "Unexpected EOF inside fixed32 field.");
359                 }
360                 break;
361             case GPBType::BOOL:
362                 if (!GPBWire::readBool($input, $value)) {
363                     throw new GPBDecodeException(
364                         "Unexpected EOF inside bool field.");
365                 }
366                 break;
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.");
372                 }
373                 break;
374             case GPBType::GROUP:
375                 trigger_error("Not implemented.", E_ERROR);
376                 break;
377             case GPBType::MESSAGE:
378                 if ($field->isMap()) {
379                     $value = new MapEntry($field->getMessageType());
380                 } else {
381                     $klass = $field->getMessageType()->getClass();
382                     $value = new $klass;
383                 }
384                 if (!GPBWire::readMessage($input, $value)) {
385                     throw new GPBDecodeException(
386                         "Unexpected EOF inside message.");
387                 }
388                 break;
389             case GPBType::BYTES:
390                 if (!GPBWire::readString($input, $value)) {
391                     throw new GPBDecodeException(
392                         "Unexpected EOF inside bytes field.");
393                 }
394                 break;
395             case GPBType::UINT32:
396                 if (!GPBWire::readUint32($input, $value)) {
397                     throw new GPBDecodeException(
398                         "Unexpected EOF inside uint32 field.");
399                 }
400                 break;
401             case GPBType::ENUM:
402                 // TODO(teboring): Check unknown enum value.
403                 if (!GPBWire::readInt32($input, $value)) {
404                     throw new GPBDecodeException(
405                         "Unexpected EOF inside enum field.");
406                 }
407                 break;
408             case GPBType::SFIXED32:
409                 if (!GPBWire::readSfixed32($input, $value)) {
410                     throw new GPBDecodeException(
411                         "Unexpected EOF inside sfixed32 field.");
412                 }
413                 break;
414             case GPBType::SFIXED64:
415                 if (!GPBWire::readSfixed64($input, $value)) {
416                     throw new GPBDecodeException(
417                         "Unexpected EOF inside sfixed64 field.");
418                 }
419                 break;
420             case GPBType::SINT32:
421                 if (!GPBWire::readSint32($input, $value)) {
422                     throw new GPBDecodeException(
423                         "Unexpected EOF inside sint32 field.");
424                 }
425                 break;
426             case GPBType::SINT64:
427                 if (!GPBWire::readSint64($input, $value)) {
428                     throw new GPBDecodeException(
429                         "Unexpected EOF inside sint64 field.");
430                 }
431                 break;
432             default:
433                 user_error("Unsupported type.");
434                 return false;
435         }
436         return true;
437     }
438
439     /**
440      * @ignore
441      */
442     private function parseFieldFromStream($tag, $input, $field)
443     {
444         $value = null;
445
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;
455         } else {
456             // the wire type doesn't match. Put it in our unknown field set.
457             $value_format = GPBWire::UNKNOWN;
458         }
459
460         if ($value_format === GPBWire::UNKNOWN) {
461             $this->skipField($input, $tag);
462             return;
463         } elseif ($value_format === GPBWire::NORMAL_FORMAT) {
464             self::parseFieldFromStreamNoTag($input, $field, $value);
465         } elseif ($value_format === GPBWire::PACKED_FORMAT) {
466             $length = 0;
467             if (!GPBWire::readInt32($input, $length)) {
468                 throw new GPBDecodeException(
469                     "Unexpected EOF inside packed length.");
470             }
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);
476             }
477             $input->popLimit($limit);
478             return;
479         } else {
480             return;
481         }
482
483         if ($field->isMap()) {
484             $this->kvUpdateHelper($field, $value->getKey(), $value->getValue());
485         } else if ($field->isRepeated()) {
486             $this->appendHelper($field, $value);
487         } else {
488             $setter = $field->getSetter();
489             $this->$setter($value);
490         }
491     }
492
493     /**
494      * Clear all containing fields.
495      * @return null.
496      */
497     public function clear()
498     {
499         $this->unknown = "";
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:
508                     case GPBType::GROUP:
509                         $map_field = new MapField(
510                             $key_field->getType(),
511                             $value_field->getType(),
512                             $value_field->getMessageType()->getClass());
513                         $this->$setter($map_field);
514                         break;
515                     case GPBType::ENUM:
516                         $map_field = new MapField(
517                             $key_field->getType(),
518                             $value_field->getType(),
519                             $value_field->getEnumType()->getClass());
520                         $this->$setter($map_field);
521                         break;
522                     default:
523                         $map_field = new MapField(
524                             $key_field->getType(),
525                             $value_field->getType());
526                         $this->$setter($map_field);
527                         break;
528                 }
529             } else if ($field->getLabel() === GPBLabel::REPEATED) {
530                 switch ($field->getType()) {
531                     case GPBType::MESSAGE:
532                     case GPBType::GROUP:
533                         $repeated_field = new RepeatedField(
534                             $field->getType(),
535                             $field->getMessageType()->getClass());
536                         $this->$setter($repeated_field);
537                         break;
538                     case GPBType::ENUM:
539                         $repeated_field = new RepeatedField(
540                             $field->getType(),
541                             $field->getEnumType()->getClass());
542                         $this->$setter($repeated_field);
543                         break;
544                     default:
545                         $repeated_field = new RepeatedField($field->getType());
546                         $this->$setter($repeated_field);
547                         break;
548                 }
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    :
557                         $this->$setter(0.0);
558                         break;
559                     case GPBType::INT32    :
560                     case GPBType::FIXED32  :
561                     case GPBType::UINT32   :
562                     case GPBType::SFIXED32 :
563                     case GPBType::SINT32   :
564                     case GPBType::ENUM     :
565                         $this->$setter(0);
566                         break;
567                     case GPBType::BOOL     :
568                         $this->$setter(false);
569                         break;
570                     case GPBType::STRING   :
571                     case GPBType::BYTES    :
572                         $this->$setter("");
573                         break;
574                     case GPBType::GROUP    :
575                     case GPBType::MESSAGE  :
576                         $null = null;
577                         $this->$setter($null);
578                         break;
579                 }
580                 if (PHP_INT_SIZE == 4) {
581                     switch ($field->getType()) {
582                         case GPBType::INT64:
583                         case GPBType::UINT64:
584                         case GPBType::FIXED64:
585                         case GPBType::SFIXED64:
586                         case GPBType::SINT64:
587                             $this->$setter("0");
588                     }
589                 } else {
590                     switch ($field->getType()) {
591                         case GPBType::INT64:
592                         case GPBType::UINT64:
593                         case GPBType::FIXED64:
594                         case GPBType::SFIXED64:
595                         case GPBType::SINT64:
596                             $this->$setter(0);
597                     }
598                 }
599             }
600         }
601     }
602
603     /**
604      * Clear all unknown fields previously parsed.
605      * @return null.
606      */
607     public function discardUnknownFields()
608     {
609         $this->unknown = "";
610         foreach ($this->desc->getField() as $field) {
611             if ($field->getType() != GPBType::MESSAGE) {
612                 continue;
613             }
614             if ($field->isMap()) {
615                 $value_field = $field->getMessageType()->getFieldByNumber(2);
616                 if ($value_field->getType() != GPBType::MESSAGE) {
617                     continue;
618                 }
619                 $getter = $field->getGetter();
620                 $map = $this->$getter();
621                 foreach ($map as $key => $value) {
622                     $value->discardUnknownFields();
623                 }
624             } else if ($field->getLabel() === GPBLabel::REPEATED) {
625                 $getter = $field->getGetter();
626                 $arr = $this->$getter();
627                 foreach ($arr as $sub) {
628                     $sub->discardUnknownFields();
629                 }
630             } else if ($field->getLabel() === GPBLabel::OPTIONAL) {
631                 $getter = $field->getGetter();
632                 $sub = $this->$getter();
633                 if (!is_null($sub)) {
634                     $sub->discardUnknownFields();
635                 }
636             }
637         }
638     }
639
640     /**
641      * Merges the contents of the specified message into current message.
642      *
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.
649      *
650      * @param object $msg Protobuf message to be merged from.
651      * @return null.
652      */
653     public function mergeFrom($msg)
654     {
655         if (get_class($this) !== get_class($msg)) {
656             user_error("Cannot merge messages with different class.");
657             return;
658         }
659
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();
669                             $copy = new $klass;
670                             $copy->mergeFrom($value);
671
672                             $this->kvUpdateHelper($field, $key, $copy);
673                         } else {
674                             $this->kvUpdateHelper($field, $key, $value);
675                         }
676                     }
677                 }
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();
683                             $copy = new $klass;
684                             $copy->mergeFrom($tmp);
685                             $this->appendHelper($field, $copy);
686                         } else {
687                             $this->appendHelper($field, $tmp);
688                         }
689                     }
690                 }
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);
699                         }
700                         $this->$getter()->mergeFrom($tmp);
701                     } else {
702                         $this->$setter($tmp);
703                     }
704                 }
705             }
706         }
707     }
708
709     /**
710      * Parses a protocol buffer contained in a string.
711      *
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
715      * specified message.
716      *
717      * @param string $data Binary protobuf data.
718      * @return null.
719      * @throws \Exception Invalid data.
720      */
721     public function mergeFromString($data)
722     {
723         $input = new CodedInputStream($data);
724         $this->parseFromStream($input);
725     }
726
727     /**
728      * Parses a json string to protobuf message.
729      *
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
733      * specified message.
734      *
735      * @param string $data Json protobuf data.
736      * @return null.
737      * @throws \Exception Invalid data.
738      */
739     public function mergeFromJsonString($data)
740     {
741         $input = new RawInputStream($data);
742         $this->parseFromJsonStream($input);
743     }
744
745     /**
746      * @ignore
747      */
748     public function parseFromStream($input)
749     {
750         while (true) {
751             $tag = $input->readTag();
752             // End of input.  This is a valid place to end, so return true.
753             if ($tag === 0) {
754                 return true;
755             }
756
757             $number = GPBWire::getTagFieldNumber($tag);
758             $field = $this->desc->getFieldByNumber($number);
759
760             $this->parseFieldFromStream($tag, $input, $field);
761         }
762     }
763
764     private function convertJsonValueToProtoValue(
765         $value,
766         $field,
767         $is_map_key = false)
768     {
769         switch ($field->getType()) {
770             case GPBType::MESSAGE:
771                 $klass = $field->getMessageType()->getClass();
772                 $submsg = new $klass;
773
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.");
779                     }
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.");
786                     }
787                     try {
788                         $timestamp = GPBUtil::parseTimestamp($value);
789                     } catch (\Exception $e) {
790                         throw new GPBDecodeException(
791                             "Invalid RFC 3339 timestamp: ".$e->getMessage());
792                     }
793
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);
799                     }
800                     try {
801                         return GPBUtil::parseFieldMask($value);
802                     } catch (\Exception $e) {
803                         throw new GPBDecodeException(
804                             "Invalid FieldMask: ".$e->getMessage());
805                     }
806                 } else {
807                     if (is_null($value) &&
808                         !is_a($submsg, "Google\Protobuf\Value")) {
809                         return $this->defaultValue($field);
810                     }
811                     if (GPBUtil::hasSpecialJsonMapping($submsg)) {
812                     } elseif (!is_object($value) && !is_array($value)) {
813                         throw new GPBDecodeException("Expect message.");
814                     }
815                     $submsg->mergeFromJsonArray($value);
816                 }
817                 return $submsg;
818             case GPBType::ENUM:
819                 if (is_null($value)) {
820                     return $this->defaultValue($field);
821                 }
822                 if (is_integer($value)) {
823                     return $value;
824                 }
825                 $enum_value = $field->getEnumType()->getValueByName($value);
826                 if (!is_null($enum_value)) {
827                     return $enum_value->getNumber();
828                 }
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);
834                 }
835                 if (is_numeric($value)) {
836                     return strval($value);
837                 }
838                 if (!is_string($value)) {
839                     throw new GPBDecodeException(
840                         "String field only accepts string value");
841                 }
842                 return $value;
843             case GPBType::BYTES:
844                 if (is_null($value)) {
845                     return $this->defaultValue($field);
846                 }
847                 if (!is_string($value)) {
848                     throw new GPBDecodeException(
849                         "Byte field only accepts string value");
850                 }
851                 $proto_value = base64_decode($value, true);
852                 if ($proto_value === false) {
853                     throw new GPBDecodeException("Invalid base64 characters");
854                 }
855                 return $proto_value;
856             case GPBType::BOOL:
857                 if (is_null($value)) {
858                     return $this->defaultValue($field);
859                 }
860                 if ($is_map_key) {
861                     if ($value === "true") {
862                         return true;
863                     }
864                     if ($value === "false") {
865                         return false;
866                     }
867                     throw new GPBDecodeException(
868                         "Bool field only accepts bool value");
869                 }
870                 if (!is_bool($value)) {
871                     throw new GPBDecodeException(
872                         "Bool field only accepts bool value");
873                 }
874                 return $value;
875             case GPBType::FLOAT:
876             case GPBType::DOUBLE:
877                 if (is_null($value)) {
878                     return $this->defaultValue($field);
879                 }
880                 if ($value === "Infinity") {
881                     return INF;
882                 }
883                 if ($value === "-Infinity") {
884                     return -INF;
885                 }
886                 if ($value === "NaN") {
887                     return NAN;
888                 }
889                 return $value;
890             case GPBType::INT32:
891             case GPBType::SINT32:
892             case GPBType::SFIXED32:
893                 if (is_null($value)) {
894                     return $this->defaultValue($field);
895                 }
896                 if (!is_numeric($value)) {
897                    throw new GPBDecodeException(
898                        "Invalid data type for int32 field");
899                 }
900                 if (bccomp($value, "2147483647") > 0) {
901                    throw new GPBDecodeException(
902                        "Int32 too large");
903                 }
904                 if (bccomp($value, "-2147483648") < 0) {
905                    throw new GPBDecodeException(
906                        "Int32 too small");
907                 }
908                 return $value;
909             case GPBType::UINT32:
910             case GPBType::FIXED32:
911                 if (is_null($value)) {
912                     return $this->defaultValue($field);
913                 }
914                 if (!is_numeric($value)) {
915                    throw new GPBDecodeException(
916                        "Invalid data type for uint32 field");
917                 }
918                 if (bccomp($value, 4294967295) > 0) {
919                     throw new GPBDecodeException(
920                         "Uint32 too large");
921                 }
922                 return $value;
923             case GPBType::INT64:
924             case GPBType::SINT64:
925             case GPBType::SFIXED64:
926                 if (is_null($value)) {
927                     return $this->defaultValue($field);
928                 }
929                 if (!is_numeric($value)) {
930                    throw new GPBDecodeException(
931                        "Invalid data type for int64 field");
932                 }
933                 if (bccomp($value, "9223372036854775807") > 0) {
934                     throw new GPBDecodeException(
935                         "Int64 too large");
936                 }
937                 if (bccomp($value, "-9223372036854775808") < 0) {
938                     throw new GPBDecodeException(
939                         "Int64 too small");
940                 }
941                 return $value;
942             case GPBType::UINT64:
943             case GPBType::FIXED64:
944                 if (is_null($value)) {
945                     return $this->defaultValue($field);
946                 }
947                 if (!is_numeric($value)) {
948                    throw new GPBDecodeException(
949                        "Invalid data type for int64 field");
950                 }
951                 if (bccomp($value, "18446744073709551615") > 0) {
952                     throw new GPBDecodeException(
953                         "Uint64 too large");
954                 }
955                 if (bccomp($value, "9223372036854775807") > 0) {
956                     $value = bcsub($value, "18446744073709551616");
957                 }
958                 return $value;
959             default:
960                 return $value;
961         }
962     }
963
964     /**
965      * Populates the message from a user-supplied PHP array. Array keys
966      * correspond to Message properties and nested message properties.
967      *
968      * Example:
969      * ```
970      * $message->mergeFromArray([
971      *     'name' => 'This is a message name',
972      *     'interval' => [
973      *          'startTime' => time() - 60,
974      *          'endTime' => time(),
975      *     ]
976      * ]);
977      * ```
978      *
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.
982      *
983      * @param array $array An array containing message properties and values.
984      * @return null.
985      */
986     protected function mergeFromArray(array $array)
987     {
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);
994             }
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());
1000                 }
1001             } elseif ($field->isWrapperType()) {
1002                 $class = $field->getMessageType()->getClass();
1003                 if ($field->isRepeated()) {
1004                     self::normalizeArrayElementsToMessageType($value, $class);
1005                 } else {
1006                     self::normalizeToMessageType($value, $class);
1007                 }
1008             }
1009             $this->$setter($value);
1010         }
1011     }
1012
1013     /**
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.
1019      *
1020      * @param mixed $value The array of values to normalize.
1021      * @param string $class The expected wrapper class name
1022      */
1023     private static function normalizeArrayElementsToMessageType(&$value, $class)
1024     {
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.
1031             return;
1032         } else {
1033             // Normalize each element in the array.
1034             foreach ($value as $key => &$elementValue) {
1035               self::normalizeToMessageType($elementValue, $class);
1036             }
1037         }
1038     }
1039
1040     /**
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.
1045      *
1046      * This method will raise an error if it receives a type that cannot be
1047      * assigned to the wrapper type via setValue.
1048      *
1049      * @param mixed $value The value to normalize.
1050      * @param string $class The expected wrapper class name
1051      */
1052     private static function normalizeToMessageType(&$value, $class)
1053     {
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.
1058             return;
1059         } else {
1060             // Try to instantiate $class and set the value
1061             try {
1062                 $msg = new $class;
1063                 $msg->setValue($value);
1064                 $value = $msg;
1065                 return;
1066             } catch (\Exception $exception) {
1067                 trigger_error(
1068                     "Error normalizing value to type '$class': " . $exception->getMessage(),
1069                     E_USER_ERROR
1070                 );
1071             }
1072         }
1073     }
1074
1075     protected function mergeFromJsonArray($array)
1076     {
1077         if (is_a($this, "Google\Protobuf\Any")) {
1078             $this->clear();
1079             $this->setTypeUrl($array["@type"]);
1080             $msg = $this->unpack();
1081             if (GPBUtil::hasSpecialJsonMapping($msg)) {
1082                 $msg->mergeFromJsonArray($array["value"]);
1083             } else {
1084                 unset($array["@type"]);
1085                 $msg->mergeFromJsonArray($array);
1086             }
1087             $this->setValue($msg->serializeToString());
1088             return;
1089         }
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);
1099             return;
1100         }
1101         if (is_a($this, "Google\Protobuf\BytesValue")) {
1102             $this->setValue(base64_decode($array));
1103             return;
1104         }
1105         if (is_a($this, "Google\Protobuf\Duration")) {
1106             $this->mergeFrom(GPBUtil::parseDuration($array));
1107             return;
1108         }
1109         if (is_a($this, "Google\Protobuf\FieldMask")) {
1110             $this->mergeFrom(GPBUtil::parseFieldMask($array));
1111             return;
1112         }
1113         if (is_a($this, "Google\Protobuf\Timestamp")) {
1114             $this->mergeFrom(GPBUtil::parseTimestamp($array));
1115             return;
1116         }
1117         if (is_a($this, "Google\Protobuf\Struct")) {
1118             $fields = $this->getFields();
1119             foreach($array as $key => $value) {
1120                 $v = new Value();
1121                 $v->mergeFromJsonArray($value);
1122                 $fields[$key] = $v;
1123             }
1124         }
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);
1141                     }
1142                     foreach ($array as $key => $v) {
1143                         $value = new Value();
1144                         $value->mergeFromJsonArray($v);
1145                         $values = $struct_value->getFields();
1146                         $values[$key]= $value;
1147                     }
1148                 } else {
1149                     // Array
1150                     $list_value = $this->getListValue();
1151                     if (is_null($list_value)) {
1152                         $list_value = new ListValue();
1153                         $this->setListValue($list_value);
1154                     }
1155                     foreach ($array as $v) {
1156                         $value = new Value();
1157                         $value->mergeFromJsonArray($v);
1158                         $values = $list_value->getValues();
1159                         $values[]= $value;
1160                     }
1161                 }
1162             } else {
1163                 throw new GPBDecodeException("Invalid type for Value.");
1164             }
1165             return;
1166         }
1167         $this->mergeFromArrayJsonImpl($array);
1168     }
1169
1170     private function mergeFromArrayJsonImpl($array)
1171     {
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)) {
1177                     continue;
1178                 }
1179             }
1180             if ($field->isMap()) {
1181                 if (is_null($value)) {
1182                     continue;
1183                 }
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.");
1190                     }
1191                     $proto_key = $this->convertJsonValueToProtoValue(
1192                         $tmp_key,
1193                         $key_field,
1194                         true);
1195                     $proto_value = $this->convertJsonValueToProtoValue(
1196                         $tmp_value,
1197                         $value_field);
1198                     self::kvUpdateHelper($field, $proto_key, $proto_value);
1199                 }
1200             } else if ($field->isRepeated()) {
1201                 if (is_null($value)) {
1202                     continue;
1203                 }
1204                 foreach ($value as $tmp) {
1205                     if (is_null($tmp)) {
1206                         throw new \Exception(
1207                             "Repeated field elements cannot be null.");
1208                     }
1209                     $proto_value = $this->convertJsonValueToProtoValue(
1210                         $tmp,
1211                         $field);
1212                     self::appendHelper($field, $proto_value);
1213                 }
1214             } else {
1215                 $setter = $field->getSetter();
1216                 $proto_value = $this->convertJsonValueToProtoValue(
1217                     $value,
1218                     $field);
1219                 if ($field->getType() === GPBType::MESSAGE) {
1220                     if (is_null($proto_value)) {
1221                         continue;
1222                     }
1223                     $getter = $field->getGetter();
1224                     $submsg = $this->$getter();
1225                     if (!is_null($submsg)) {
1226                         $submsg->mergeFrom($proto_value);
1227                         continue;
1228                     }
1229                 }
1230                 $this->$setter($proto_value);
1231             }
1232         }
1233     }
1234
1235     /**
1236      * @ignore
1237      */
1238     public function parseFromJsonStream($input)
1239     {
1240         $array = json_decode($input->getData(), true, 512, JSON_BIGINT_AS_STRING);
1241         if ($this instanceof \Google\Protobuf\ListValue) {
1242             $array = ["values"=>$array];
1243         }
1244         if (is_null($array)) {
1245             if ($this instanceof \Google\Protobuf\Value) {
1246               $this->setNullValue(\Google\Protobuf\NullValue::NULL_VALUE);
1247               return;
1248             } else {
1249               throw new GPBDecodeException(
1250                   "Cannot decode json string: " . $input->getData());
1251             }
1252         }
1253         try {
1254             $this->mergeFromJsonArray($array);
1255         } catch (\Exception $e) {
1256             throw new GPBDecodeException($e->getMessage());
1257         }
1258     }
1259
1260     /**
1261      * @ignore
1262      */
1263     private function serializeSingularFieldToStream($field, &$output)
1264     {
1265         if (!$this->existField($field)) {
1266             return true;
1267         }
1268         $getter = $field->getGetter();
1269         $value = $this->$getter();
1270         if (!GPBWire::serializeFieldToStream($value, $field, true, $output)) {
1271             return false;
1272         }
1273         return true;
1274     }
1275
1276     /**
1277      * @ignore
1278      */
1279     private function serializeRepeatedFieldToStream($field, &$output)
1280     {
1281         $getter = $field->getGetter();
1282         $values = $this->$getter();
1283         $count = count($values);
1284         if ($count === 0) {
1285             return true;
1286         }
1287
1288         $packed = $field->getPacked();
1289         if ($packed) {
1290             if (!GPBWire::writeTag(
1291                 $output,
1292                 GPBWire::makeTag($field->getNumber(), GPBType::STRING))) {
1293                 return false;
1294             }
1295             $size = 0;
1296             foreach ($values as $value) {
1297                 $size += $this->fieldDataOnlyByteSize($field, $value);
1298             }
1299             if (!$output->writeVarint32($size, true)) {
1300                 return false;
1301             }
1302         }
1303
1304         foreach ($values as $value) {
1305             if (!GPBWire::serializeFieldToStream(
1306                 $value,
1307                 $field,
1308                 !$packed,
1309                 $output)) {
1310                 return false;
1311             }
1312         }
1313         return true;
1314     }
1315
1316     /**
1317      * @ignore
1318      */
1319     private function serializeMapFieldToStream($field, $output)
1320     {
1321         $getter = $field->getGetter();
1322         $values = $this->$getter();
1323         $count = count($values);
1324         if ($count === 0) {
1325             return true;
1326         }
1327
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(
1333                 $map_entry,
1334                 $field,
1335                 true,
1336                 $output)) {
1337                 return false;
1338             }
1339         }
1340         return true;
1341     }
1342
1343     /**
1344      * @ignore
1345      */
1346     private function serializeFieldToStream(&$output, $field)
1347     {
1348         if ($field->isMap()) {
1349             return $this->serializeMapFieldToStream($field, $output);
1350         } elseif ($field->isRepeated()) {
1351             return $this->serializeRepeatedFieldToStream($field, $output);
1352         } else {
1353             return $this->serializeSingularFieldToStream($field, $output);
1354         }
1355     }
1356
1357     /**
1358      * @ignore
1359      */
1360     private function serializeFieldToJsonStream(&$output, $field)
1361     {
1362         $getter = $field->getGetter();
1363         $values = $this->$getter();
1364         return GPBJsonWire::serializeFieldToStream(
1365             $values, $field, $output, !GPBUtil::hasSpecialJsonMapping($this));
1366     }
1367
1368     /**
1369      * @ignore
1370      */
1371     public function serializeToStream(&$output)
1372     {
1373         $fields = $this->desc->getField();
1374         foreach ($fields as $field) {
1375             if (!$this->serializeFieldToStream($output, $field)) {
1376                 return false;
1377             }
1378         }
1379         $output->writeRaw($this->unknown, strlen($this->unknown));
1380         return true;
1381     }
1382
1383     /**
1384      * @ignore
1385      */
1386     public function serializeToJsonStream(&$output)
1387     {
1388         if (is_a($this, 'Google\Protobuf\Any')) {
1389             $output->writeRaw("{", 1);
1390             $type_field = $this->desc->getFieldByNumber(1);
1391             $value_msg = $this->unpack();
1392
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);
1398
1399             // Serialize value
1400             if (GPBUtil::hasSpecialJsonMapping($value_msg)) {
1401                 $output->writeRaw(",\"value\":", 9);
1402                 $value_msg->serializeToJsonStream($output);
1403             } else {
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)) {
1409                             return false;
1410                         }
1411                     }
1412                 }
1413             }
1414
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);
1434             } else {
1435                 if (!$this->serializeFieldToJsonStream($output, $field)) {
1436                     return false;
1437                 }
1438             }
1439         } elseif (get_class($this) === 'Google\Protobuf\Struct') {
1440             $field = $this->desc->getField()[1];
1441             if (!$this->existField($field)) {
1442                 $output->writeRaw("{}", 2);
1443             } else {
1444                 if (!$this->serializeFieldToJsonStream($output, $field)) {
1445                     return false;
1446                 }
1447             }
1448         } else {
1449             if (!GPBUtil::hasSpecialJsonMapping($this)) {
1450                 $output->writeRaw("{", 1);
1451             }
1452             $fields = $this->desc->getField();
1453             $first = true;
1454             foreach ($fields as $field) {
1455                 if ($this->existField($field) ||
1456                     GPBUtil::hasJsonValue($this)) {
1457                     if ($first) {
1458                         $first = false;
1459                     } else {
1460                         $output->writeRaw(",", 1);
1461                     }
1462                     if (!$this->serializeFieldToJsonStream($output, $field)) {
1463                         return false;
1464                     }
1465                 }
1466             }
1467             if (!GPBUtil::hasSpecialJsonMapping($this)) {
1468                 $output->writeRaw("}", 1);
1469             }
1470         }
1471         return true;
1472     }
1473
1474     /**
1475      * Serialize the message to string.
1476      * @return string Serialized binary protobuf data.
1477      */
1478     public function serializeToString()
1479     {
1480         $output = new CodedOutputStream($this->byteSize());
1481         $this->serializeToStream($output);
1482         return $output->getData();
1483     }
1484
1485     /**
1486      * Serialize the message to json string.
1487      * @return string Serialized json protobuf data.
1488      */
1489     public function serializeToJsonString()
1490     {
1491         $output = new CodedOutputStream($this->jsonByteSize());
1492         $this->serializeToJsonStream($output);
1493         return $output->getData();
1494     }
1495
1496     /**
1497      * @ignore
1498      */
1499     private function existField($field)
1500     {
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();
1506         }
1507
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;
1514         } else {
1515             return $values !== $this->defaultValue($field);
1516         }
1517     }
1518
1519     /**
1520      * @ignore
1521      */
1522     private function repeatedFieldDataOnlyByteSize($field)
1523     {
1524         $size = 0;
1525
1526         $getter = $field->getGetter();
1527         $values = $this->$getter();
1528         $count = count($values);
1529         if ($count !== 0) {
1530             $size += $count * GPBWire::tagSize($field);
1531             foreach ($values as $value) {
1532                 $size += $this->singularFieldDataOnlyByteSize($field);
1533             }
1534         }
1535     }
1536
1537     /**
1538      * @ignore
1539      */
1540     private function fieldDataOnlyByteSize($field, $value)
1541     {
1542         $size = 0;
1543
1544         switch ($field->getType()) {
1545             case GPBType::BOOL:
1546                 $size += 1;
1547                 break;
1548             case GPBType::FLOAT:
1549             case GPBType::FIXED32:
1550             case GPBType::SFIXED32:
1551                 $size += 4;
1552                 break;
1553             case GPBType::DOUBLE:
1554             case GPBType::FIXED64:
1555             case GPBType::SFIXED64:
1556                 $size += 8;
1557                 break;
1558             case GPBType::INT32:
1559             case GPBType::ENUM:
1560                 $size += GPBWire::varint32Size($value, true);
1561                 break;
1562             case GPBType::UINT32:
1563                 $size += GPBWire::varint32Size($value);
1564                 break;
1565             case GPBType::UINT64:
1566             case GPBType::INT64:
1567                 $size += GPBWire::varint64Size($value);
1568                 break;
1569             case GPBType::SINT32:
1570                 $size += GPBWire::sint32Size($value);
1571                 break;
1572             case GPBType::SINT64:
1573                 $size += GPBWire::sint64Size($value);
1574                 break;
1575             case GPBType::STRING:
1576             case GPBType::BYTES:
1577                 $size += strlen($value);
1578                 $size += GPBWire::varint32Size($size);
1579                 break;
1580             case GPBType::MESSAGE:
1581                 $size += $value->byteSize();
1582                 $size += GPBWire::varint32Size($size);
1583                 break;
1584             case GPBType::GROUP:
1585                 // TODO(teboring): Add support.
1586                 user_error("Unsupported type.");
1587                 break;
1588             default:
1589                 user_error("Unsupported type.");
1590                 return 0;
1591         }
1592
1593         return $size;
1594     }
1595
1596     /**
1597      * @ignore
1598      */
1599     private function fieldDataOnlyJsonByteSize($field, $value)
1600     {
1601         $size = 0;
1602
1603         switch ($field->getType()) {
1604             case GPBType::SFIXED32:
1605             case GPBType::SINT32:
1606             case GPBType::INT32:
1607                 $size += strlen(strval($value));
1608                 break;
1609             case GPBType::FIXED32:
1610             case GPBType::UINT32:
1611                 if ($value < 0) {
1612                     $value = bcadd($value, "4294967296");
1613                 }
1614                 $size += strlen(strval($value));
1615                 break;
1616             case GPBType::FIXED64:
1617             case GPBType::UINT64:
1618                 if ($value < 0) {
1619                     $value = bcadd($value, "18446744073709551616");
1620                 }
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));
1627                 break;
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;
1635                 } else {
1636                     $size += strlen(sprintf("%.8g", $value));
1637                 }
1638                 break;
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;
1646                 } else {
1647                     $size += strlen(sprintf("%.17g", $value));
1648                 }
1649                 break;
1650             case GPBType::ENUM:
1651                 $enum_desc = $field->getEnumType();
1652                 if ($enum_desc->getClass() === "Google\Protobuf\NullValue") {
1653                     $size += 4;
1654                     break;
1655                 }
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());
1660                 } else {
1661                     $str_value = strval($value);
1662                     $size += strlen($str_value);
1663                 }
1664                 break;
1665             case GPBType::BOOL:
1666                 if ($value) {
1667                     $size += 4;
1668                 } else {
1669                     $size += 5;
1670                 }
1671                 break;
1672             case GPBType::STRING:
1673                 $value = json_encode($value, JSON_UNESCAPED_UNICODE);
1674                 $size += strlen($value);
1675                 break;
1676             case GPBType::BYTES:
1677                 # if (is_a($this, "Google\Protobuf\BytesValue")) {
1678                 #     $size += strlen(json_encode($value));
1679                 # } else {
1680                 #     $size += strlen(base64_encode($value));
1681                 #     $size += 2;  // size for \"\"
1682                 # }
1683                 $size += strlen(base64_encode($value));
1684                 $size += 2;  // size for \"\"
1685                 break;
1686             case GPBType::MESSAGE:
1687                 $size += $value->jsonByteSize();
1688                 break;
1689 #             case GPBType::GROUP:
1690 #                 // TODO(teboring): Add support.
1691 #                 user_error("Unsupported type.");
1692 #                 break;
1693             default:
1694                 user_error("Unsupported type " . $field->getType());
1695                 return 0;
1696         }
1697
1698         return $size;
1699     }
1700
1701     /**
1702      * @ignore
1703      */
1704     private function fieldByteSize($field)
1705     {
1706         $size = 0;
1707         if ($field->isMap()) {
1708             $getter = $field->getGetter();
1709             $values = $this->$getter();
1710             $count = count($values);
1711             if ($count !== 0) {
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) {
1717                     $data_size = 0;
1718                     if ($key != $this->defaultValue($key_field)) {
1719                         $data_size += $this->fieldDataOnlyByteSize(
1720                             $key_field,
1721                             $key);
1722                         $data_size += GPBWire::tagSize($key_field);
1723                     }
1724                     if ($value != $this->defaultValue($value_field)) {
1725                         $data_size += $this->fieldDataOnlyByteSize(
1726                             $value_field,
1727                             $value);
1728                         $data_size += GPBWire::tagSize($value_field);
1729                     }
1730                     $size += GPBWire::varint32Size($data_size) + $data_size;
1731                 }
1732             }
1733         } elseif ($field->isRepeated()) {
1734             $getter = $field->getGetter();
1735             $values = $this->$getter();
1736             $count = count($values);
1737             if ($count !== 0) {
1738                 if ($field->getPacked()) {
1739                     $data_size = 0;
1740                     foreach ($values as $value) {
1741                         $data_size += $this->fieldDataOnlyByteSize($field, $value);
1742                     }
1743                     $size += GPBWire::tagSize($field);
1744                     $size += GPBWire::varint32Size($data_size);
1745                     $size += $data_size;
1746                 } else {
1747                     $size += $count * GPBWire::tagSize($field);
1748                     foreach ($values as $value) {
1749                         $size += $this->fieldDataOnlyByteSize($field, $value);
1750                     }
1751                 }
1752             }
1753         } elseif ($this->existField($field)) {
1754             $size += GPBWire::tagSize($field);
1755             $getter = $field->getGetter();
1756             $value = $this->$getter();
1757             $size += $this->fieldDataOnlyByteSize($field, $value);
1758         }
1759         return $size;
1760     }
1761
1762     /**
1763      * @ignore
1764      */
1765     private function fieldJsonByteSize($field)
1766     {
1767         $size = 0;
1768
1769         if ($field->isMap()) {
1770             $getter = $field->getGetter();
1771             $values = $this->$getter();
1772             $count = count($values);
1773             if ($count !== 0) {
1774                 if (!GPBUtil::hasSpecialJsonMapping($this)) {
1775                     $size += 3;                              // size for "\"\":".
1776                     $size += strlen($field->getJsonName());  // size for field name
1777                 }
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;
1792                     break;
1793                 default:
1794                     $additional_quote = true;
1795                 }
1796                 foreach ($values as $key => $value) {
1797                     if ($additional_quote) {
1798                         $size += 2;  // size for ""
1799                     }
1800                     $size += $this->fieldDataOnlyJsonByteSize($key_field, $key);
1801                     $size += $this->fieldDataOnlyJsonByteSize($value_field, $value);
1802                     $size += 1;  // size for :
1803                 }
1804             }
1805         } elseif ($field->isRepeated()) {
1806             $getter = $field->getGetter();
1807             $values = $this->$getter();
1808             $count = count($values);
1809             if ($count !== 0) {
1810                 if (!GPBUtil::hasSpecialJsonMapping($this)) {
1811                     $size += 3;                              // size for "\"\":".
1812                     $size += strlen($field->getJsonName());  // size for field name
1813                 }
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);
1819                 }
1820             }
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
1825             }
1826             $getter = $field->getGetter();
1827             $value = $this->$getter();
1828             $size += $this->fieldDataOnlyJsonByteSize($field, $value);
1829         }
1830         return $size;
1831     }
1832
1833     /**
1834      * @ignore
1835      */
1836     public function byteSize()
1837     {
1838         $size = 0;
1839
1840         $fields = $this->desc->getField();
1841         foreach ($fields as $field) {
1842             $size += $this->fieldByteSize($field);
1843         }
1844         $size += strlen($this->unknown);
1845         return $size;
1846     }
1847
1848     private function appendHelper($field, $append_value)
1849     {
1850         $getter = $field->getGetter();
1851         $setter = $field->getSetter();
1852
1853         $field_arr_value = $this->$getter();
1854         $field_arr_value[] = $append_value;
1855
1856         if (!is_object($field_arr_value)) {
1857             $this->$setter($field_arr_value);
1858         }
1859     }
1860
1861     private function kvUpdateHelper($field, $update_key, $update_value)
1862     {
1863         $getter = $field->getGetter();
1864         $setter = $field->getSetter();
1865
1866         $field_arr_value = $this->$getter();
1867         $field_arr_value[$update_key] = $update_value;
1868
1869         if (!is_object($field_arr_value)) {
1870             $this->$setter($field_arr_value);
1871         }
1872     }
1873
1874     /**
1875      * @ignore
1876      */
1877     public function jsonByteSize()
1878     {
1879         $size = 0;
1880         if (is_a($this, 'Google\Protobuf\Any')) {
1881             // Size for "{}".
1882             $size += 2;
1883
1884             // Size for "\"@type\":".
1885             $size += 8;
1886
1887             // Size for url. +2 for "" /.
1888             $size += strlen($this->getTypeUrl()) + 2;
1889
1890             $value_msg = $this->unpack();
1891             if (GPBUtil::hasSpecialJsonMapping($value_msg)) {
1892                 // Size for "\",value\":".
1893                 $size += 9;
1894                 $size += $value_msg->jsonByteSize();
1895             } else {
1896                 // Size for value. +1 for comma, -2 for "{}".
1897                 $size += $value_msg->jsonByteSize() -1;
1898             }
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;
1914             } else {
1915                 // Size for "[]".
1916                 $size += 2;
1917             }
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;
1923             } else {
1924                 // Size for "{}".
1925                 $size += 2;
1926             }
1927         } else {
1928             if (!GPBUtil::hasSpecialJsonMapping($this)) {
1929                 // Size for "{}".
1930                 $size += 2;
1931             }
1932
1933             $fields = $this->desc->getField();
1934             $count = 0;
1935             foreach ($fields as $field) {
1936                 $field_size = $this->fieldJsonByteSize($field);
1937                 $size += $field_size;
1938                 if ($field_size != 0) {
1939                   $count++;
1940                 }
1941             }
1942             // size for comma
1943             $size += $count > 0 ? ($count - 1) : 0;
1944         }
1945         return $size;
1946     }
1947 }