• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<?php
2
3// Protocol Buffers - Google's data interchange format
4// Copyright 2008 Google Inc.  All rights reserved.
5//
6// Use of this source code is governed by a BSD-style
7// license that can be found in the LICENSE file or at
8// https://developers.google.com/open-source/licenses/bsd
9
10/**
11 * Defines Message, the parent class extended by all protocol message classes.
12 */
13
14namespace Google\Protobuf\Internal;
15
16use Google\Protobuf\Internal\CodedInputStream;
17use Google\Protobuf\Internal\CodedOutputStream;
18use Google\Protobuf\Internal\DescriptorPool;
19use Google\Protobuf\Internal\GPBLabel;
20use Google\Protobuf\Internal\GPBType;
21use Google\Protobuf\Internal\GPBWire;
22use Google\Protobuf\Internal\MapEntry;
23use Google\Protobuf\Internal\RepeatedField;
24use Google\Protobuf\ListValue;
25use Google\Protobuf\Value;
26use Google\Protobuf\Struct;
27use Google\Protobuf\NullValue;
28
29/**
30 * Parent class of all proto messages. Users should not instantiate this class
31 * or extend this class or its child classes by their own.  See the comment of
32 * specific functions for more details.
33 */
34#[\AllowDynamicProperties]
35class Message
36{
37
38    /**
39     * @ignore
40     */
41    private $desc;
42    private $unknown = "";
43
44    /**
45     * @ignore
46     */
47    public function __construct($data = NULL)
48    {
49        // MapEntry message is shared by all types of map fields, whose
50        // descriptors are different from each other. Thus, we cannot find a
51        // specific descriptor from the descriptor pool.
52        if ($this instanceof MapEntry) {
53            $this->initWithDescriptor($data);
54        } else {
55            $this->initWithGeneratedPool();
56            if (is_array($data)) {
57                $this->mergeFromArray($data);
58            } else if (!empty($data)) {
59                throw new \InvalidArgumentException(
60                    'Message constructor must be an array or null.'
61                );
62            }
63        }
64    }
65
66    /**
67     * @ignore
68     */
69    private function initWithGeneratedPool()
70    {
71        $pool = DescriptorPool::getGeneratedPool();
72        $this->desc = $pool->getDescriptorByClassName(get_class($this));
73        if (is_null($this->desc)) {
74          throw new \InvalidArgumentException(
75            get_class($this) ." is not found in descriptor pool. " .
76            'Only generated classes may derive from Message.');
77        }
78        foreach ($this->desc->getField() as $field) {
79            $setter = $field->getSetter();
80            if ($field->isMap()) {
81                $message_type = $field->getMessageType();
82                $key_field = $message_type->getFieldByNumber(1);
83                $value_field = $message_type->getFieldByNumber(2);
84                switch ($value_field->getType()) {
85                    case GPBType::MESSAGE:
86                    case GPBType::GROUP:
87                        $map_field = new MapField(
88                            $key_field->getType(),
89                            $value_field->getType(),
90                            $value_field->getMessageType()->getClass());
91                        $this->$setter($map_field);
92                        break;
93                    case GPBType::ENUM:
94                        $map_field = new MapField(
95                            $key_field->getType(),
96                            $value_field->getType(),
97                            $value_field->getEnumType()->getClass());
98                        $this->$setter($map_field);
99                        break;
100                    default:
101                        $map_field = new MapField(
102                            $key_field->getType(),
103                            $value_field->getType());
104                        $this->$setter($map_field);
105                        break;
106                }
107            } else if ($field->getLabel() === GPBLabel::REPEATED) {
108                switch ($field->getType()) {
109                    case GPBType::MESSAGE:
110                    case GPBType::GROUP:
111                        $repeated_field = new RepeatedField(
112                            $field->getType(),
113                            $field->getMessageType()->getClass());
114                        $this->$setter($repeated_field);
115                        break;
116                    case GPBType::ENUM:
117                        $repeated_field = new RepeatedField(
118                            $field->getType(),
119                            $field->getEnumType()->getClass());
120                        $this->$setter($repeated_field);
121                        break;
122                    default:
123                        $repeated_field = new RepeatedField($field->getType());
124                        $this->$setter($repeated_field);
125                        break;
126                }
127            } else if ($field->getOneofIndex() !== -1) {
128                $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
129                $oneof_name = $oneof->getName();
130                $this->$oneof_name = new OneofField($oneof);
131            } else if ($field->getLabel() === GPBLabel::OPTIONAL &&
132                       PHP_INT_SIZE == 4) {
133                switch ($field->getType()) {
134                    case GPBType::INT64:
135                    case GPBType::UINT64:
136                    case GPBType::FIXED64:
137                    case GPBType::SFIXED64:
138                    case GPBType::SINT64:
139                        $this->$setter("0");
140                }
141            }
142        }
143    }
144
145    /**
146     * @ignore
147     */
148    private function initWithDescriptor(Descriptor $desc)
149    {
150        $this->desc = $desc;
151        foreach ($desc->getField() as $field) {
152            $setter = $field->getSetter();
153            $defaultValue = $this->defaultValue($field);
154            $this->$setter($defaultValue);
155        }
156    }
157
158    protected function readWrapperValue($member)
159    {
160        $field = $this->desc->getFieldByName($member);
161        $oneof_index = $field->getOneofIndex();
162        if ($oneof_index === -1) {
163            $wrapper = $this->$member;
164        } else {
165            $wrapper = $this->readOneof($field->getNumber());
166        }
167
168        if (is_null($wrapper)) {
169            return NULL;
170        } else {
171            return $wrapper->getValue();
172        }
173    }
174
175    protected function writeWrapperValue($member, $value)
176    {
177        $field = $this->desc->getFieldByName($member);
178        $wrapped_value = $value;
179        if (!is_null($value)) {
180            $desc = $field->getMessageType();
181            $klass = $desc->getClass();
182            $wrapped_value = new $klass;
183            $wrapped_value->setValue($value);
184        }
185
186        $oneof_index = $field->getOneofIndex();
187        if ($oneof_index === -1) {
188            $this->$member = $wrapped_value;
189        } else {
190            $this->writeOneof($field->getNumber(), $wrapped_value);
191        }
192    }
193
194    protected function readOneof($number)
195    {
196        $field = $this->desc->getFieldByNumber($number);
197        $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
198        $oneof_name = $oneof->getName();
199        $oneof_field = $this->$oneof_name;
200        if ($number === $oneof_field->getNumber()) {
201            return $oneof_field->getValue();
202        } else {
203            return $this->defaultValue($field);
204        }
205    }
206
207    protected function hasOneof($number)
208    {
209        $field = $this->desc->getFieldByNumber($number);
210        $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
211        $oneof_name = $oneof->getName();
212        $oneof_field = $this->$oneof_name;
213        return $number === $oneof_field->getNumber();
214    }
215
216    protected function writeOneof($number, $value)
217    {
218        $field = $this->desc->getFieldByNumber($number);
219        $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
220        $oneof_name = $oneof->getName();
221        if ($value === null) {
222            $this->$oneof_name = new OneofField($oneof);
223        } else {
224            $oneof_field = $this->$oneof_name;
225            $oneof_field->setValue($value);
226            $oneof_field->setFieldName($field->getName());
227            $oneof_field->setNumber($number);
228        }
229    }
230
231    protected function whichOneof($oneof_name)
232    {
233        $oneof_field = $this->$oneof_name;
234        $number = $oneof_field->getNumber();
235        if ($number == 0) {
236          return "";
237        }
238        $field = $this->desc->getFieldByNumber($number);
239        return $field->getName();
240    }
241
242    /**
243     * @ignore
244     */
245    private function defaultValue($field)
246    {
247        $value = null;
248
249        switch ($field->getType()) {
250            case GPBType::DOUBLE:
251            case GPBType::FLOAT:
252                return 0.0;
253            case GPBType::UINT32:
254            case GPBType::INT32:
255            case GPBType::FIXED32:
256            case GPBType::SFIXED32:
257            case GPBType::SINT32:
258            case GPBType::ENUM:
259                return 0;
260            case GPBType::INT64:
261            case GPBType::UINT64:
262            case GPBType::FIXED64:
263            case GPBType::SFIXED64:
264            case GPBType::SINT64:
265                if (PHP_INT_SIZE === 4) {
266                    return '0';
267                } else {
268                    return 0;
269                }
270            case GPBType::BOOL:
271                return false;
272            case GPBType::STRING:
273            case GPBType::BYTES:
274                return "";
275            case GPBType::GROUP:
276            case GPBType::MESSAGE:
277                return null;
278            default:
279                user_error("Unsupported type.");
280                return false;
281        }
282    }
283
284    /**
285     * @ignore
286     */
287    private function skipField($input, $tag)
288    {
289        $number = GPBWire::getTagFieldNumber($tag);
290        if ($number === 0) {
291            throw new GPBDecodeException("Illegal field number zero.");
292        }
293
294        $start = $input->current();
295        switch (GPBWire::getTagWireType($tag)) {
296            case GPBWireType::VARINT:
297                $uint64 = 0;
298                if (!$input->readVarint64($uint64)) {
299                    throw new GPBDecodeException(
300                        "Unexpected EOF inside varint.");
301                }
302                break;
303            case GPBWireType::FIXED64:
304                $uint64 = 0;
305                if (!$input->readLittleEndian64($uint64)) {
306                    throw new GPBDecodeException(
307                        "Unexpected EOF inside fixed64.");
308                }
309                break;
310            case GPBWireType::FIXED32:
311                $uint32 = 0;
312                if (!$input->readLittleEndian32($uint32)) {
313                    throw new GPBDecodeException(
314                        "Unexpected EOF inside fixed32.");
315                }
316                break;
317            case GPBWireType::LENGTH_DELIMITED:
318                $length = 0;
319                if (!$input->readVarint32($length)) {
320                    throw new GPBDecodeException(
321                        "Unexpected EOF inside length.");
322                }
323                $data = NULL;
324                if (!$input->readRaw($length, $data)) {
325                    throw new GPBDecodeException(
326                        "Unexpected EOF inside length delimited data.");
327                }
328                break;
329            case GPBWireType::START_GROUP:
330            case GPBWireType::END_GROUP:
331                throw new GPBDecodeException("Unexpected wire type.");
332            default:
333                throw new GPBDecodeException("Unexpected wire type.");
334        }
335        $end = $input->current();
336
337        $bytes = str_repeat(chr(0), CodedOutputStream::MAX_VARINT64_BYTES);
338        $size = CodedOutputStream::writeVarintToArray($tag, $bytes, true);
339        $this->unknown .= substr($bytes, 0, $size) . $input->substr($start, $end);
340    }
341
342    /**
343     * @ignore
344     */
345    private static function parseFieldFromStreamNoTag($input, $field, &$value)
346    {
347        switch ($field->getType()) {
348            case GPBType::DOUBLE:
349                if (!GPBWire::readDouble($input, $value)) {
350                    throw new GPBDecodeException(
351                        "Unexpected EOF inside double field.");
352                }
353                break;
354            case GPBType::FLOAT:
355                if (!GPBWire::readFloat($input, $value)) {
356                    throw new GPBDecodeException(
357                        "Unexpected EOF inside float field.");
358                }
359                break;
360            case GPBType::INT64:
361                if (!GPBWire::readInt64($input, $value)) {
362                    throw new GPBDecodeException(
363                        "Unexpected EOF inside int64 field.");
364                }
365                break;
366            case GPBType::UINT64:
367                if (!GPBWire::readUint64($input, $value)) {
368                    throw new GPBDecodeException(
369                        "Unexpected EOF inside uint64 field.");
370                }
371                break;
372            case GPBType::INT32:
373                if (!GPBWire::readInt32($input, $value)) {
374                    throw new GPBDecodeException(
375                        "Unexpected EOF inside int32 field.");
376                }
377                break;
378            case GPBType::FIXED64:
379                if (!GPBWire::readFixed64($input, $value)) {
380                    throw new GPBDecodeException(
381                        "Unexpected EOF inside fixed64 field.");
382                }
383                break;
384            case GPBType::FIXED32:
385                if (!GPBWire::readFixed32($input, $value)) {
386                    throw new GPBDecodeException(
387                        "Unexpected EOF inside fixed32 field.");
388                }
389                break;
390            case GPBType::BOOL:
391                if (!GPBWire::readBool($input, $value)) {
392                    throw new GPBDecodeException(
393                        "Unexpected EOF inside bool field.");
394                }
395                break;
396            case GPBType::STRING:
397                // We don't check UTF-8 here; that will be validated by the
398                // setter later.
399                if (!GPBWire::readString($input, $value)) {
400                    throw new GPBDecodeException(
401                        "Unexpected EOF inside string field.");
402                }
403                break;
404            case GPBType::GROUP:
405                trigger_error("Not implemented.", E_USER_ERROR);
406                break;
407            case GPBType::MESSAGE:
408                if ($field->isMap()) {
409                    $value = new MapEntry($field->getMessageType());
410                } else {
411                    $klass = $field->getMessageType()->getClass();
412                    $value = new $klass;
413                }
414                if (!GPBWire::readMessage($input, $value)) {
415                    throw new GPBDecodeException(
416                        "Unexpected EOF inside message.");
417                }
418                break;
419            case GPBType::BYTES:
420                if (!GPBWire::readString($input, $value)) {
421                    throw new GPBDecodeException(
422                        "Unexpected EOF inside bytes field.");
423                }
424                break;
425            case GPBType::UINT32:
426                if (!GPBWire::readUint32($input, $value)) {
427                    throw new GPBDecodeException(
428                        "Unexpected EOF inside uint32 field.");
429                }
430                break;
431            case GPBType::ENUM:
432                // TODO: Check unknown enum value.
433                if (!GPBWire::readInt32($input, $value)) {
434                    throw new GPBDecodeException(
435                        "Unexpected EOF inside enum field.");
436                }
437                break;
438            case GPBType::SFIXED32:
439                if (!GPBWire::readSfixed32($input, $value)) {
440                    throw new GPBDecodeException(
441                        "Unexpected EOF inside sfixed32 field.");
442                }
443                break;
444            case GPBType::SFIXED64:
445                if (!GPBWire::readSfixed64($input, $value)) {
446                    throw new GPBDecodeException(
447                        "Unexpected EOF inside sfixed64 field.");
448                }
449                break;
450            case GPBType::SINT32:
451                if (!GPBWire::readSint32($input, $value)) {
452                    throw new GPBDecodeException(
453                        "Unexpected EOF inside sint32 field.");
454                }
455                break;
456            case GPBType::SINT64:
457                if (!GPBWire::readSint64($input, $value)) {
458                    throw new GPBDecodeException(
459                        "Unexpected EOF inside sint64 field.");
460                }
461                break;
462            default:
463                user_error("Unsupported type.");
464                return false;
465        }
466        return true;
467    }
468
469    /**
470     * @ignore
471     */
472    private function parseFieldFromStream($tag, $input, $field)
473    {
474        $value = null;
475
476        if (is_null($field)) {
477            $value_format = GPBWire::UNKNOWN;
478        } elseif (GPBWire::getTagWireType($tag) ===
479            GPBWire::getWireType($field->getType())) {
480            $value_format = GPBWire::NORMAL_FORMAT;
481        } elseif ($field->isPackable() &&
482            GPBWire::getTagWireType($tag) ===
483            GPBWire::WIRETYPE_LENGTH_DELIMITED) {
484            $value_format = GPBWire::PACKED_FORMAT;
485        } else {
486            // the wire type doesn't match. Put it in our unknown field set.
487            $value_format = GPBWire::UNKNOWN;
488        }
489
490        if ($value_format === GPBWire::UNKNOWN) {
491            $this->skipField($input, $tag);
492            return;
493        } elseif ($value_format === GPBWire::NORMAL_FORMAT) {
494            self::parseFieldFromStreamNoTag($input, $field, $value);
495        } elseif ($value_format === GPBWire::PACKED_FORMAT) {
496            $length = 0;
497            if (!GPBWire::readInt32($input, $length)) {
498                throw new GPBDecodeException(
499                    "Unexpected EOF inside packed length.");
500            }
501            $limit = $input->pushLimit($length);
502            $getter = $field->getGetter();
503            while ($input->bytesUntilLimit() > 0) {
504                self::parseFieldFromStreamNoTag($input, $field, $value);
505                $this->appendHelper($field, $value);
506            }
507            $input->popLimit($limit);
508            return;
509        } else {
510            return;
511        }
512
513        if ($field->isMap()) {
514            $this->kvUpdateHelper($field, $value->getKey(), $value->getValue());
515        } else if ($field->isRepeated()) {
516            $this->appendHelper($field, $value);
517        } else {
518            $setter = $field->getSetter();
519            $this->$setter($value);
520        }
521    }
522
523    /**
524     * Clear all containing fields.
525     * @return null
526     */
527    public function clear()
528    {
529        $this->unknown = "";
530        foreach ($this->desc->getField() as $field) {
531            $setter = $field->getSetter();
532            if ($field->isMap()) {
533                $message_type = $field->getMessageType();
534                $key_field = $message_type->getFieldByNumber(1);
535                $value_field = $message_type->getFieldByNumber(2);
536                switch ($value_field->getType()) {
537                    case GPBType::MESSAGE:
538                    case GPBType::GROUP:
539                        $map_field = new MapField(
540                            $key_field->getType(),
541                            $value_field->getType(),
542                            $value_field->getMessageType()->getClass());
543                        $this->$setter($map_field);
544                        break;
545                    case GPBType::ENUM:
546                        $map_field = new MapField(
547                            $key_field->getType(),
548                            $value_field->getType(),
549                            $value_field->getEnumType()->getClass());
550                        $this->$setter($map_field);
551                        break;
552                    default:
553                        $map_field = new MapField(
554                            $key_field->getType(),
555                            $value_field->getType());
556                        $this->$setter($map_field);
557                        break;
558                }
559            } else if ($field->getLabel() === GPBLabel::REPEATED) {
560                switch ($field->getType()) {
561                    case GPBType::MESSAGE:
562                    case GPBType::GROUP:
563                        $repeated_field = new RepeatedField(
564                            $field->getType(),
565                            $field->getMessageType()->getClass());
566                        $this->$setter($repeated_field);
567                        break;
568                    case GPBType::ENUM:
569                        $repeated_field = new RepeatedField(
570                            $field->getType(),
571                            $field->getEnumType()->getClass());
572                        $this->$setter($repeated_field);
573                        break;
574                    default:
575                        $repeated_field = new RepeatedField($field->getType());
576                        $this->$setter($repeated_field);
577                        break;
578                }
579            } else if ($field->getOneofIndex() !== -1) {
580                $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
581                $oneof_name = $oneof->getName();
582                $this->$oneof_name = new OneofField($oneof);
583            } else if ($field->getLabel() === GPBLabel::OPTIONAL) {
584                switch ($field->getType()) {
585                    case GPBType::DOUBLE   :
586                    case GPBType::FLOAT    :
587                        $this->$setter(0.0);
588                        break;
589                    case GPBType::INT32    :
590                    case GPBType::FIXED32  :
591                    case GPBType::UINT32   :
592                    case GPBType::SFIXED32 :
593                    case GPBType::SINT32   :
594                    case GPBType::ENUM     :
595                        $this->$setter(0);
596                        break;
597                    case GPBType::BOOL     :
598                        $this->$setter(false);
599                        break;
600                    case GPBType::STRING   :
601                    case GPBType::BYTES    :
602                        $this->$setter("");
603                        break;
604                    case GPBType::GROUP    :
605                    case GPBType::MESSAGE  :
606                        $null = null;
607                        $this->$setter($null);
608                        break;
609                }
610                if (PHP_INT_SIZE == 4) {
611                    switch ($field->getType()) {
612                        case GPBType::INT64:
613                        case GPBType::UINT64:
614                        case GPBType::FIXED64:
615                        case GPBType::SFIXED64:
616                        case GPBType::SINT64:
617                            $this->$setter("0");
618                    }
619                } else {
620                    switch ($field->getType()) {
621                        case GPBType::INT64:
622                        case GPBType::UINT64:
623                        case GPBType::FIXED64:
624                        case GPBType::SFIXED64:
625                        case GPBType::SINT64:
626                            $this->$setter(0);
627                    }
628                }
629            }
630        }
631    }
632
633    /**
634     * Clear all unknown fields previously parsed.
635     * @return null
636     */
637    public function discardUnknownFields()
638    {
639        $this->unknown = "";
640        foreach ($this->desc->getField() as $field) {
641            if ($field->getType() != GPBType::MESSAGE) {
642                continue;
643            }
644            if ($field->isMap()) {
645                $value_field = $field->getMessageType()->getFieldByNumber(2);
646                if ($value_field->getType() != GPBType::MESSAGE) {
647                    continue;
648                }
649                $getter = $field->getGetter();
650                $map = $this->$getter();
651                foreach ($map as $key => $value) {
652                    $value->discardUnknownFields();
653                }
654            } else if ($field->getLabel() === GPBLabel::REPEATED) {
655                $getter = $field->getGetter();
656                $arr = $this->$getter();
657                foreach ($arr as $sub) {
658                    $sub->discardUnknownFields();
659                }
660            } else if ($field->getLabel() === GPBLabel::OPTIONAL) {
661                $getter = $field->getGetter();
662                $sub = $this->$getter();
663                if (!is_null($sub)) {
664                    $sub->discardUnknownFields();
665                }
666            }
667        }
668    }
669
670    /**
671     * Merges the contents of the specified message into current message.
672     *
673     * This method merges the contents of the specified message into the
674     * current message. Singular fields that are set in the specified message
675     * overwrite the corresponding fields in the current message.  Repeated
676     * fields are appended. Map fields key-value pairs are overwritten.
677     * Singular/Oneof sub-messages are recursively merged. All overwritten
678     * sub-messages are deep-copied.
679     *
680     * @param object $msg Protobuf message to be merged from.
681     * @return null
682     */
683    public function mergeFrom($msg)
684    {
685        if (get_class($this) !== get_class($msg)) {
686            user_error("Cannot merge messages with different class.");
687            return;
688        }
689
690        foreach ($this->desc->getField() as $field) {
691            $setter = $field->getSetter();
692            $getter = $field->getGetter();
693            if ($field->isMap()) {
694                if (count($msg->$getter()) != 0) {
695                    $value_field = $field->getMessageType()->getFieldByNumber(2);
696                    foreach ($msg->$getter() as $key => $value) {
697                        if ($value_field->getType() == GPBType::MESSAGE) {
698                            $klass = $value_field->getMessageType()->getClass();
699                            $copy = new $klass;
700                            $copy->mergeFrom($value);
701
702                            $this->kvUpdateHelper($field, $key, $copy);
703                        } else {
704                            $this->kvUpdateHelper($field, $key, $value);
705                        }
706                    }
707                }
708            } else if ($field->getLabel() === GPBLabel::REPEATED) {
709                if (count($msg->$getter()) != 0) {
710                    foreach ($msg->$getter() as $tmp) {
711                        if ($field->getType() == GPBType::MESSAGE) {
712                            $klass = $field->getMessageType()->getClass();
713                            $copy = new $klass;
714                            $copy->mergeFrom($tmp);
715                            $this->appendHelper($field, $copy);
716                        } else {
717                            $this->appendHelper($field, $tmp);
718                        }
719                    }
720                }
721            } else if ($field->getLabel() === GPBLabel::OPTIONAL) {
722                if($msg->$getter() !== $this->defaultValue($field)) {
723                    $tmp = $msg->$getter();
724                    if ($field->getType() == GPBType::MESSAGE) {
725                        if (is_null($this->$getter())) {
726                            $klass = $field->getMessageType()->getClass();
727                            $new_msg = new $klass;
728                            $this->$setter($new_msg);
729                        }
730                        $this->$getter()->mergeFrom($tmp);
731                    } else {
732                        $this->$setter($tmp);
733                    }
734                }
735            }
736        }
737    }
738
739    /**
740     * Parses a protocol buffer contained in a string.
741     *
742     * This function takes a string in the (non-human-readable) binary wire
743     * format, matching the encoding output by serializeToString().
744     * See mergeFrom() for merging behavior, if the field is already set in the
745     * specified message.
746     *
747     * @param string $data Binary protobuf data.
748     * @return null
749     * @throws \Exception Invalid data.
750     */
751    public function mergeFromString($data)
752    {
753        $input = new CodedInputStream($data);
754        $this->parseFromStream($input);
755    }
756
757    /**
758     * Parses a json string to protobuf message.
759     *
760     * This function takes a string in the json wire format, matching the
761     * encoding output by serializeToJsonString().
762     * See mergeFrom() for merging behavior, if the field is already set in the
763     * specified message.
764     *
765     * @param string $data Json protobuf data.
766     * @param bool $ignore_unknown
767     * @return null
768     * @throws \Exception Invalid data.
769     */
770    public function mergeFromJsonString($data, $ignore_unknown = false)
771    {
772        $input = new RawInputStream($data);
773        $this->parseFromJsonStream($input, $ignore_unknown);
774    }
775
776    /**
777     * @ignore
778     */
779    public function parseFromStream($input)
780    {
781        while (true) {
782            $tag = $input->readTag();
783            // End of input.  This is a valid place to end, so return true.
784            if ($tag === 0) {
785                return true;
786            }
787
788            $number = GPBWire::getTagFieldNumber($tag);
789            $field = $this->desc->getFieldByNumber($number);
790
791            $this->parseFieldFromStream($tag, $input, $field);
792        }
793    }
794
795    private function convertJsonValueToProtoValue(
796        $value,
797        $field,
798        $ignore_unknown,
799        $is_map_key = false)
800    {
801        switch ($field->getType()) {
802            case GPBType::MESSAGE:
803                $klass = $field->getMessageType()->getClass();
804                $submsg = new $klass;
805
806                if (is_a($submsg, "Google\Protobuf\Duration")) {
807                    if (is_null($value)) {
808                        return $this->defaultValue($field);
809                    } else if (!is_string($value)) {
810                        throw new GPBDecodeException("Expect string.");
811                    }
812                    return GPBUtil::parseDuration($value);
813                } else if ($field->isTimestamp()) {
814                    if (is_null($value)) {
815                        return $this->defaultValue($field);
816                    } else if (!is_string($value)) {
817                        throw new GPBDecodeException("Expect string.");
818                    }
819                    try {
820                        $timestamp = GPBUtil::parseTimestamp($value);
821                    } catch (\Exception $e) {
822                        throw new GPBDecodeException(
823                            "Invalid RFC 3339 timestamp: ".$e->getMessage());
824                    }
825
826                    $submsg->setSeconds($timestamp->getSeconds());
827                    $submsg->setNanos($timestamp->getNanos());
828                } else if (is_a($submsg, "Google\Protobuf\FieldMask")) {
829                    if (is_null($value)) {
830                        return $this->defaultValue($field);
831                    }
832                    try {
833                        return GPBUtil::parseFieldMask($value);
834                    } catch (\Exception $e) {
835                        throw new GPBDecodeException(
836                            "Invalid FieldMask: ".$e->getMessage());
837                    }
838                } else {
839                    if (is_null($value) &&
840                        !is_a($submsg, "Google\Protobuf\Value")) {
841                        return $this->defaultValue($field);
842                    }
843                    if (GPBUtil::hasSpecialJsonMapping($submsg)) {
844                    } elseif (!is_object($value) && !is_array($value)) {
845                        throw new GPBDecodeException("Expect message.");
846                    }
847                    $submsg->mergeFromJsonArray($value, $ignore_unknown);
848                }
849                return $submsg;
850            case GPBType::ENUM:
851                if (is_null($value)) {
852                    return $this->defaultValue($field);
853                }
854                if (is_integer($value)) {
855                    return $value;
856                }
857                $enum_value = $field->getEnumType()->getValueByName($value);
858                if (!is_null($enum_value)) {
859                    return $enum_value->getNumber();
860                } else if ($ignore_unknown) {
861                    return $this->defaultValue($field);
862                } else {
863                  throw new GPBDecodeException(
864                          "Enum field only accepts integer or enum value name");
865                }
866            case GPBType::STRING:
867                if (is_null($value)) {
868                    return $this->defaultValue($field);
869                }
870                if (is_numeric($value)) {
871                    return strval($value);
872                }
873                if (!is_string($value)) {
874                    throw new GPBDecodeException(
875                        "String field only accepts string value");
876                }
877                return $value;
878            case GPBType::BYTES:
879                if (is_null($value)) {
880                    return $this->defaultValue($field);
881                }
882                if (!is_string($value)) {
883                    throw new GPBDecodeException(
884                        "Byte field only accepts string value");
885                }
886                $proto_value = base64_decode($value, true);
887                if ($proto_value === false) {
888                    throw new GPBDecodeException("Invalid base64 characters");
889                }
890                return $proto_value;
891            case GPBType::BOOL:
892                if (is_null($value)) {
893                    return $this->defaultValue($field);
894                }
895                if ($is_map_key) {
896                    if ($value === "true") {
897                        return true;
898                    }
899                    if ($value === "false") {
900                        return false;
901                    }
902                    throw new GPBDecodeException(
903                        "Bool field only accepts bool value");
904                }
905                if (!is_bool($value)) {
906                    throw new GPBDecodeException(
907                        "Bool field only accepts bool value");
908                }
909                return $value;
910            case GPBType::FLOAT:
911            case GPBType::DOUBLE:
912                if (is_null($value)) {
913                    return $this->defaultValue($field);
914                }
915                if ($value === "Infinity") {
916                    return INF;
917                }
918                if ($value === "-Infinity") {
919                    return -INF;
920                }
921                if ($value === "NaN") {
922                    return NAN;
923                }
924                return $value;
925            case GPBType::INT32:
926            case GPBType::SINT32:
927            case GPBType::SFIXED32:
928                if (is_null($value)) {
929                    return $this->defaultValue($field);
930                }
931                if (!is_numeric($value)) {
932                   throw new GPBDecodeException(
933                       "Invalid data type for int32 field");
934                }
935                if (is_string($value) && trim($value) !== $value) {
936                   throw new GPBDecodeException(
937                       "Invalid data type for int32 field");
938                }
939                if (bccomp($value, "2147483647") > 0) {
940                   throw new GPBDecodeException(
941                       "Int32 too large");
942                }
943                if (bccomp($value, "-2147483648") < 0) {
944                   throw new GPBDecodeException(
945                       "Int32 too small");
946                }
947                return $value;
948            case GPBType::UINT32:
949            case GPBType::FIXED32:
950                if (is_null($value)) {
951                    return $this->defaultValue($field);
952                }
953                if (!is_numeric($value)) {
954                   throw new GPBDecodeException(
955                       "Invalid data type for uint32 field");
956                }
957                if (is_string($value) && trim($value) !== $value) {
958                   throw new GPBDecodeException(
959                       "Invalid data type for int32 field");
960                }
961                if (bccomp($value, 4294967295) > 0) {
962                    throw new GPBDecodeException(
963                        "Uint32 too large");
964                }
965                return $value;
966            case GPBType::INT64:
967            case GPBType::SINT64:
968            case GPBType::SFIXED64:
969                if (is_null($value)) {
970                    return $this->defaultValue($field);
971                }
972                if (!is_numeric($value)) {
973                   throw new GPBDecodeException(
974                       "Invalid data type for int64 field");
975                }
976                if (is_string($value) && trim($value) !== $value) {
977                   throw new GPBDecodeException(
978                       "Invalid data type for int64 field");
979                }
980                if (bccomp($value, "9223372036854775807") > 0) {
981                    throw new GPBDecodeException(
982                        "Int64 too large");
983                }
984                if (bccomp($value, "-9223372036854775808") < 0) {
985                    throw new GPBDecodeException(
986                        "Int64 too small");
987                }
988                return $value;
989            case GPBType::UINT64:
990            case GPBType::FIXED64:
991                if (is_null($value)) {
992                    return $this->defaultValue($field);
993                }
994                if (!is_numeric($value)) {
995                   throw new GPBDecodeException(
996                       "Invalid data type for int64 field");
997                }
998                if (is_string($value) && trim($value) !== $value) {
999                   throw new GPBDecodeException(
1000                       "Invalid data type for int64 field");
1001                }
1002                if (bccomp($value, "18446744073709551615") > 0) {
1003                    throw new GPBDecodeException(
1004                        "Uint64 too large");
1005                }
1006                if (bccomp($value, "9223372036854775807") > 0) {
1007                    $value = bcsub($value, "18446744073709551616");
1008                }
1009                return $value;
1010            default:
1011                return $value;
1012        }
1013    }
1014
1015    /**
1016     * Populates the message from a user-supplied PHP array. Array keys
1017     * correspond to Message properties and nested message properties.
1018     *
1019     * Example:
1020     * ```
1021     * $message->mergeFromArray([
1022     *     'name' => 'This is a message name',
1023     *     'interval' => [
1024     *          'startTime' => time() - 60,
1025     *          'endTime' => time(),
1026     *     ]
1027     * ]);
1028     * ```
1029     *
1030     * This method will trigger an error if it is passed data that cannot
1031     * be converted to the correct type. For example, a StringValue field
1032     * must receive data that is either a string or a StringValue object.
1033     *
1034     * @param array $array An array containing message properties and values.
1035     * @return null
1036     */
1037    protected function mergeFromArray(array $array)
1038    {
1039        // Just call the setters for the field names
1040        foreach ($array as $key => $value) {
1041            $field = $this->desc->getFieldByName($key);
1042            if (is_null($field)) {
1043                throw new \UnexpectedValueException(
1044                    'Invalid message property: ' . $key);
1045            }
1046            $setter = $field->getSetter();
1047            if ($field->isMap()) {
1048                $valueField = $field->getMessageType()->getFieldByName('value');
1049                if (!is_null($valueField) && $valueField->isWrapperType()) {
1050                    self::normalizeArrayElementsToMessageType($value, $valueField->getMessageType()->getClass());
1051                }
1052            } elseif ($field->isWrapperType()) {
1053                $class = $field->getMessageType()->getClass();
1054                if ($field->isRepeated()) {
1055                    self::normalizeArrayElementsToMessageType($value, $class);
1056                } else {
1057                    self::normalizeToMessageType($value, $class);
1058                }
1059            }
1060            $this->$setter($value);
1061        }
1062    }
1063
1064    /**
1065     * Tries to normalize the elements in $value into a provided protobuf
1066     * wrapper type $class. If $value is any type other than array, we do
1067     * not do any conversion, and instead rely on the existing protobuf
1068     * type checking. If $value is an array, we process each element and
1069     * try to convert it to an instance of $class.
1070     *
1071     * @param mixed $value The array of values to normalize.
1072     * @param string $class The expected wrapper class name
1073     */
1074    private static function normalizeArrayElementsToMessageType(&$value, $class)
1075    {
1076        if (!is_array($value)) {
1077            // In the case that $value is not an array, we do not want to
1078            // attempt any conversion. Note that this includes the cases
1079            // when $value is a RepeatedField of MapField. In those cases,
1080            // we do not need to convert the elements, as they should
1081            // already be the correct types.
1082            return;
1083        } else {
1084            // Normalize each element in the array.
1085            foreach ($value as $key => &$elementValue) {
1086              self::normalizeToMessageType($elementValue, $class);
1087            }
1088        }
1089    }
1090
1091    /**
1092     * Tries to normalize $value into a provided protobuf wrapper type $class.
1093     * If $value is any type other than an object, we attempt to construct an
1094     * instance of $class and assign $value to it using the setValue method
1095     * shared by all wrapper types.
1096     *
1097     * This method will raise an error if it receives a type that cannot be
1098     * assigned to the wrapper type via setValue.
1099     *
1100     * @param mixed $value The value to normalize.
1101     * @param string $class The expected wrapper class name
1102     */
1103    private static function normalizeToMessageType(&$value, $class)
1104    {
1105        if (is_null($value) || is_object($value)) {
1106            // This handles the case that $value is an instance of $class. We
1107            // choose not to do any more strict checking here, relying on the
1108            // existing type checking done by GPBUtil.
1109            return;
1110        } else {
1111            // Try to instantiate $class and set the value
1112            try {
1113                $msg = new $class;
1114                $msg->setValue($value);
1115                $value = $msg;
1116                return;
1117            } catch (\Exception $exception) {
1118                trigger_error(
1119                    "Error normalizing value to type '$class': " . $exception->getMessage(),
1120                    E_USER_ERROR
1121                );
1122            }
1123        }
1124    }
1125
1126    protected function mergeFromJsonArray($array, $ignore_unknown)
1127    {
1128        if (is_a($this, "Google\Protobuf\Any")) {
1129            $this->clear();
1130            $this->setTypeUrl($array["@type"]);
1131            $msg = $this->unpack();
1132            if (GPBUtil::hasSpecialJsonMapping($msg)) {
1133                $msg->mergeFromJsonArray($array["value"], $ignore_unknown);
1134            } else {
1135                unset($array["@type"]);
1136                $msg->mergeFromJsonArray($array, $ignore_unknown);
1137            }
1138            $this->setValue($msg->serializeToString());
1139            return;
1140        }
1141        if (is_a($this, "Google\Protobuf\DoubleValue") ||
1142            is_a($this, "Google\Protobuf\FloatValue")  ||
1143            is_a($this, "Google\Protobuf\Int64Value")  ||
1144            is_a($this, "Google\Protobuf\UInt64Value") ||
1145            is_a($this, "Google\Protobuf\Int32Value")  ||
1146            is_a($this, "Google\Protobuf\UInt32Value") ||
1147            is_a($this, "Google\Protobuf\BoolValue")   ||
1148            is_a($this, "Google\Protobuf\StringValue")) {
1149            $this->setValue($array);
1150            return;
1151        }
1152        if (is_a($this, "Google\Protobuf\BytesValue")) {
1153            $this->setValue(base64_decode($array));
1154            return;
1155        }
1156        if (is_a($this, "Google\Protobuf\Duration")) {
1157            $this->mergeFrom(GPBUtil::parseDuration($array));
1158            return;
1159        }
1160        if (is_a($this, "Google\Protobuf\FieldMask")) {
1161            $this->mergeFrom(GPBUtil::parseFieldMask($array));
1162            return;
1163        }
1164        if (is_a($this, "Google\Protobuf\Timestamp")) {
1165            $this->mergeFrom(GPBUtil::parseTimestamp($array));
1166            return;
1167        }
1168        if (is_a($this, "Google\Protobuf\Struct")) {
1169            $fields = $this->getFields();
1170            foreach($array as $key => $value) {
1171                $v = new Value();
1172                $v->mergeFromJsonArray($value, $ignore_unknown);
1173                $fields[$key] = $v;
1174            }
1175            return;
1176        }
1177        if (is_a($this, "Google\Protobuf\Value")) {
1178            if (is_bool($array)) {
1179                $this->setBoolValue($array);
1180            } elseif (is_string($array)) {
1181                $this->setStringValue($array);
1182            } elseif (is_null($array)) {
1183                $this->setNullValue(0);
1184            } elseif (is_double($array) || is_integer($array)) {
1185                $this->setNumberValue($array);
1186            } elseif (is_array($array)) {
1187                if (array_values($array) !== $array) {
1188                    // Associative array
1189                    $struct_value = $this->getStructValue();
1190                    if (is_null($struct_value)) {
1191                        $struct_value = new Struct();
1192                        $this->setStructValue($struct_value);
1193                    }
1194                    foreach ($array as $key => $v) {
1195                        $value = new Value();
1196                        $value->mergeFromJsonArray($v, $ignore_unknown);
1197                        $values = $struct_value->getFields();
1198                        $values[$key]= $value;
1199                    }
1200                } else {
1201                    // Array
1202                    $list_value = $this->getListValue();
1203                    if (is_null($list_value)) {
1204                        $list_value = new ListValue();
1205                        $this->setListValue($list_value);
1206                    }
1207                    foreach ($array as $v) {
1208                        $value = new Value();
1209                        $value->mergeFromJsonArray($v, $ignore_unknown);
1210                        $values = $list_value->getValues();
1211                        $values[]= $value;
1212                    }
1213                }
1214            } else {
1215                throw new GPBDecodeException("Invalid type for Value.");
1216            }
1217            return;
1218        }
1219        $this->mergeFromArrayJsonImpl($array, $ignore_unknown);
1220    }
1221
1222    private function mergeFromArrayJsonImpl($array, $ignore_unknown)
1223    {
1224        foreach ($array as $key => $value) {
1225            $field = $this->desc->getFieldByJsonName($key);
1226            if (is_null($field)) {
1227                $field = $this->desc->getFieldByName($key);
1228                if (is_null($field)) {
1229                    if ($ignore_unknown) {
1230                        continue;
1231                    } else {
1232                        throw new GPBDecodeException(
1233                            $key . ' is unknown.'
1234                        );
1235                    }
1236                }
1237            }
1238            if ($field->isMap()) {
1239                if (is_null($value)) {
1240                    continue;
1241                }
1242                $key_field = $field->getMessageType()->getFieldByNumber(1);
1243                $value_field = $field->getMessageType()->getFieldByNumber(2);
1244                foreach ($value as $tmp_key => $tmp_value) {
1245                    if (is_null($tmp_value)) {
1246                        throw new \Exception(
1247                            "Map value field element cannot be null.");
1248                    }
1249                    $proto_key = $this->convertJsonValueToProtoValue(
1250                        $tmp_key,
1251                        $key_field,
1252                        $ignore_unknown,
1253                        true);
1254                    $proto_value = $this->convertJsonValueToProtoValue(
1255                        $tmp_value,
1256                        $value_field,
1257                        $ignore_unknown);
1258                    self::kvUpdateHelper($field, $proto_key, $proto_value);
1259                }
1260            } else if ($field->isRepeated()) {
1261                if (is_null($value)) {
1262                    continue;
1263                }
1264                foreach ($value as $tmp) {
1265                    if (is_null($tmp)) {
1266                        throw new \Exception(
1267                            "Repeated field elements cannot be null.");
1268                    }
1269                    $proto_value = $this->convertJsonValueToProtoValue(
1270                        $tmp,
1271                        $field,
1272                        $ignore_unknown);
1273                    self::appendHelper($field, $proto_value);
1274                }
1275            } else {
1276                $setter = $field->getSetter();
1277                $proto_value = $this->convertJsonValueToProtoValue(
1278                    $value,
1279                    $field,
1280                    $ignore_unknown);
1281                if ($field->getType() === GPBType::MESSAGE) {
1282                    if (is_null($proto_value)) {
1283                        continue;
1284                    }
1285                    $getter = $field->getGetter();
1286                    $submsg = $this->$getter();
1287                    if (!is_null($submsg)) {
1288                        $submsg->mergeFrom($proto_value);
1289                        continue;
1290                    }
1291                }
1292                $this->$setter($proto_value);
1293            }
1294        }
1295    }
1296
1297    /**
1298     * @ignore
1299     */
1300    public function parseFromJsonStream($input, $ignore_unknown)
1301    {
1302        $array = json_decode($input->getData(), true, 512, JSON_BIGINT_AS_STRING);
1303        if ($this instanceof \Google\Protobuf\ListValue) {
1304            $array = ["values"=>$array];
1305        }
1306        if (is_null($array)) {
1307            if ($this instanceof \Google\Protobuf\Value) {
1308              $this->setNullValue(\Google\Protobuf\NullValue::NULL_VALUE);
1309              return;
1310            } else {
1311              throw new GPBDecodeException(
1312                  "Cannot decode json string: " . $input->getData());
1313            }
1314        }
1315        try {
1316            $this->mergeFromJsonArray($array, $ignore_unknown);
1317        } catch (\Exception $e) {
1318            throw new GPBDecodeException($e->getMessage(), $e->getCode(), $e);
1319        }
1320    }
1321
1322    /**
1323     * @ignore
1324     */
1325    private function serializeSingularFieldToStream($field, &$output)
1326    {
1327        if (!$this->existField($field)) {
1328            return true;
1329        }
1330        $getter = $field->getGetter();
1331        $value = $this->$getter();
1332        if (!GPBWire::serializeFieldToStream($value, $field, true, $output)) {
1333            return false;
1334        }
1335        return true;
1336    }
1337
1338    /**
1339     * @ignore
1340     */
1341    private function serializeRepeatedFieldToStream($field, &$output)
1342    {
1343        $getter = $field->getGetter();
1344        $values = $this->$getter();
1345        $count = count($values);
1346        if ($count === 0) {
1347            return true;
1348        }
1349
1350        $packed = $field->getPacked();
1351        if ($packed) {
1352            if (!GPBWire::writeTag(
1353                $output,
1354                GPBWire::makeTag($field->getNumber(), GPBType::STRING))) {
1355                return false;
1356            }
1357            $size = 0;
1358            foreach ($values as $value) {
1359                $size += $this->fieldDataOnlyByteSize($field, $value);
1360            }
1361            if (!$output->writeVarint32($size, true)) {
1362                return false;
1363            }
1364        }
1365
1366        foreach ($values as $value) {
1367            if (!GPBWire::serializeFieldToStream(
1368                $value,
1369                $field,
1370                !$packed,
1371                $output)) {
1372                return false;
1373            }
1374        }
1375        return true;
1376    }
1377
1378    /**
1379     * @ignore
1380     */
1381    private function serializeMapFieldToStream($field, $output)
1382    {
1383        $getter = $field->getGetter();
1384        $values = $this->$getter();
1385        $count = count($values);
1386        if ($count === 0) {
1387            return true;
1388        }
1389
1390        foreach ($values as $key => $value) {
1391            $map_entry = new MapEntry($field->getMessageType());
1392            $map_entry->setKey($key);
1393            $map_entry->setValue($value);
1394            if (!GPBWire::serializeFieldToStream(
1395                $map_entry,
1396                $field,
1397                true,
1398                $output)) {
1399                return false;
1400            }
1401        }
1402        return true;
1403    }
1404
1405    /**
1406     * @ignore
1407     */
1408    private function serializeFieldToStream(&$output, $field)
1409    {
1410        if ($field->isMap()) {
1411            return $this->serializeMapFieldToStream($field, $output);
1412        } elseif ($field->isRepeated()) {
1413            return $this->serializeRepeatedFieldToStream($field, $output);
1414        } else {
1415            return $this->serializeSingularFieldToStream($field, $output);
1416        }
1417    }
1418
1419    /**
1420     * @ignore
1421     */
1422    private function serializeFieldToJsonStream(&$output, $field)
1423    {
1424        $getter = $field->getGetter();
1425        $values = $this->$getter();
1426        return GPBJsonWire::serializeFieldToStream(
1427            $values, $field, $output, !GPBUtil::hasSpecialJsonMapping($this));
1428    }
1429
1430    /**
1431     * @ignore
1432     */
1433    public function serializeToStream(&$output)
1434    {
1435        $fields = $this->desc->getField();
1436        foreach ($fields as $field) {
1437            if (!$this->serializeFieldToStream($output, $field)) {
1438                return false;
1439            }
1440        }
1441        $output->writeRaw($this->unknown, strlen($this->unknown));
1442        return true;
1443    }
1444
1445    /**
1446     * @ignore
1447     */
1448    public function serializeToJsonStream(&$output)
1449    {
1450        if (is_a($this, 'Google\Protobuf\Any')) {
1451            $output->writeRaw("{", 1);
1452            $type_field = $this->desc->getFieldByNumber(1);
1453            $value_msg = $this->unpack();
1454
1455            // Serialize type url.
1456            $output->writeRaw("\"@type\":", 8);
1457            $output->writeRaw("\"", 1);
1458            $output->writeRaw($this->getTypeUrl(), strlen($this->getTypeUrl()));
1459            $output->writeRaw("\"", 1);
1460
1461            // Serialize value
1462            if (GPBUtil::hasSpecialJsonMapping($value_msg)) {
1463                $output->writeRaw(",\"value\":", 9);
1464                $value_msg->serializeToJsonStream($output);
1465            } else {
1466                $value_fields = $value_msg->desc->getField();
1467                foreach ($value_fields as $field) {
1468                    if ($value_msg->existField($field)) {
1469                        $output->writeRaw(",", 1);
1470                        if (!$value_msg->serializeFieldToJsonStream($output, $field)) {
1471                            return false;
1472                        }
1473                    }
1474                }
1475            }
1476
1477            $output->writeRaw("}", 1);
1478        } elseif (is_a($this, 'Google\Protobuf\FieldMask')) {
1479            $field_mask = GPBUtil::formatFieldMask($this);
1480            $output->writeRaw("\"", 1);
1481            $output->writeRaw($field_mask, strlen($field_mask));
1482            $output->writeRaw("\"", 1);
1483        } elseif (is_a($this, 'Google\Protobuf\Duration')) {
1484            $duration = GPBUtil::formatDuration($this) . "s";
1485            $output->writeRaw("\"", 1);
1486            $output->writeRaw($duration, strlen($duration));
1487            $output->writeRaw("\"", 1);
1488        } elseif (get_class($this) === 'Google\Protobuf\Timestamp') {
1489            $timestamp = GPBUtil::formatTimestamp($this);
1490            $timestamp = json_encode($timestamp);
1491            $output->writeRaw($timestamp, strlen($timestamp));
1492        } elseif (get_class($this) === 'Google\Protobuf\ListValue') {
1493            $field = $this->desc->getField()[1];
1494            if (!$this->existField($field)) {
1495                $output->writeRaw("[]", 2);
1496            } else {
1497                if (!$this->serializeFieldToJsonStream($output, $field)) {
1498                    return false;
1499                }
1500            }
1501        } elseif (get_class($this) === 'Google\Protobuf\Struct') {
1502            $field = $this->desc->getField()[1];
1503            if (!$this->existField($field)) {
1504                $output->writeRaw("{}", 2);
1505            } else {
1506                if (!$this->serializeFieldToJsonStream($output, $field)) {
1507                    return false;
1508                }
1509            }
1510        } else {
1511            if (!GPBUtil::hasSpecialJsonMapping($this)) {
1512                $output->writeRaw("{", 1);
1513            }
1514            $fields = $this->desc->getField();
1515            $first = true;
1516            foreach ($fields as $field) {
1517                if ($this->existField($field) ||
1518                    GPBUtil::hasJsonValue($this)) {
1519                    if ($first) {
1520                        $first = false;
1521                    } else {
1522                        $output->writeRaw(",", 1);
1523                    }
1524                    if (!$this->serializeFieldToJsonStream($output, $field)) {
1525                        return false;
1526                    }
1527                }
1528            }
1529            if (!GPBUtil::hasSpecialJsonMapping($this)) {
1530                $output->writeRaw("}", 1);
1531            }
1532        }
1533        return true;
1534    }
1535
1536    /**
1537     * Serialize the message to string.
1538     * @return string Serialized binary protobuf data.
1539     */
1540    public function serializeToString()
1541    {
1542        $output = new CodedOutputStream($this->byteSize());
1543        $this->serializeToStream($output);
1544        return $output->getData();
1545    }
1546
1547    /**
1548     * Serialize the message to json string.
1549     * @return string Serialized json protobuf data.
1550     */
1551    public function serializeToJsonString()
1552    {
1553        $output = new CodedOutputStream($this->jsonByteSize());
1554        $this->serializeToJsonStream($output);
1555        return $output->getData();
1556    }
1557
1558    /**
1559     * @ignore
1560     */
1561    private function existField($field)
1562    {
1563        $getter = $field->getGetter();
1564        $hazzer = "has" . substr($getter, 3);
1565
1566        if (method_exists($this, $hazzer)) {
1567          return $this->$hazzer();
1568        } else if ($field->getOneofIndex() !== -1) {
1569          // For old generated code, which does not have hazzers for oneof
1570          // fields.
1571          $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
1572          $oneof_name = $oneof->getName();
1573          return $this->$oneof_name->getNumber() === $field->getNumber();
1574        }
1575
1576        $values = $this->$getter();
1577        if ($field->isMap()) {
1578            return count($values) !== 0;
1579        } elseif ($field->isRepeated()) {
1580            return count($values) !== 0;
1581        } else {
1582            return $values !== $this->defaultValue($field);
1583        }
1584    }
1585
1586    /**
1587     * @ignore
1588     */
1589    private function repeatedFieldDataOnlyByteSize($field)
1590    {
1591        $size = 0;
1592
1593        $getter = $field->getGetter();
1594        $values = $this->$getter();
1595        $count = count($values);
1596        if ($count !== 0) {
1597            $size += $count * GPBWire::tagSize($field);
1598            foreach ($values as $value) {
1599                $size += $this->singularFieldDataOnlyByteSize($field);
1600            }
1601        }
1602    }
1603
1604    /**
1605     * @ignore
1606     */
1607    private function fieldDataOnlyByteSize($field, $value)
1608    {
1609        $size = 0;
1610
1611        switch ($field->getType()) {
1612            case GPBType::BOOL:
1613                $size += 1;
1614                break;
1615            case GPBType::FLOAT:
1616            case GPBType::FIXED32:
1617            case GPBType::SFIXED32:
1618                $size += 4;
1619                break;
1620            case GPBType::DOUBLE:
1621            case GPBType::FIXED64:
1622            case GPBType::SFIXED64:
1623                $size += 8;
1624                break;
1625            case GPBType::INT32:
1626            case GPBType::ENUM:
1627                $size += GPBWire::varint32Size($value, true);
1628                break;
1629            case GPBType::UINT32:
1630                $size += GPBWire::varint32Size($value);
1631                break;
1632            case GPBType::UINT64:
1633            case GPBType::INT64:
1634                $size += GPBWire::varint64Size($value);
1635                break;
1636            case GPBType::SINT32:
1637                $size += GPBWire::sint32Size($value);
1638                break;
1639            case GPBType::SINT64:
1640                $size += GPBWire::sint64Size($value);
1641                break;
1642            case GPBType::STRING:
1643            case GPBType::BYTES:
1644                $size += strlen($value);
1645                $size += GPBWire::varint32Size($size);
1646                break;
1647            case GPBType::MESSAGE:
1648                $size += $value->byteSize();
1649                $size += GPBWire::varint32Size($size);
1650                break;
1651            case GPBType::GROUP:
1652                // TODO: Add support.
1653                user_error("Unsupported type.");
1654                break;
1655            default:
1656                user_error("Unsupported type.");
1657                return 0;
1658        }
1659
1660        return $size;
1661    }
1662
1663    /**
1664     * @ignore
1665     */
1666    private function fieldDataOnlyJsonByteSize($field, $value)
1667    {
1668        $size = 0;
1669
1670        switch ($field->getType()) {
1671            case GPBType::SFIXED32:
1672            case GPBType::SINT32:
1673            case GPBType::INT32:
1674                $size += strlen(strval($value));
1675                break;
1676            case GPBType::FIXED32:
1677            case GPBType::UINT32:
1678                if ($value < 0) {
1679                    $value = bcadd($value, "4294967296");
1680                }
1681                $size += strlen(strval($value));
1682                break;
1683            case GPBType::FIXED64:
1684            case GPBType::UINT64:
1685                if ($value < 0) {
1686                    $value = bcadd($value, "18446744073709551616");
1687                }
1688                // Intentional fall through.
1689            case GPBType::SFIXED64:
1690            case GPBType::INT64:
1691            case GPBType::SINT64:
1692                $size += 2;  // size for ""
1693                $size += strlen(strval($value));
1694                break;
1695            case GPBType::FLOAT:
1696                if (is_nan($value)) {
1697                    $size += strlen("NaN") + 2;
1698                } elseif ($value === INF) {
1699                    $size += strlen("Infinity") + 2;
1700                } elseif ($value === -INF) {
1701                    $size += strlen("-Infinity") + 2;
1702                } else {
1703                    $size += strlen(sprintf("%.8g", $value));
1704                }
1705                break;
1706            case GPBType::DOUBLE:
1707                if (is_nan($value)) {
1708                    $size += strlen("NaN") + 2;
1709                } elseif ($value === INF) {
1710                    $size += strlen("Infinity") + 2;
1711                } elseif ($value === -INF) {
1712                    $size += strlen("-Infinity") + 2;
1713                } else {
1714                    $size += strlen(sprintf("%.17g", $value));
1715                }
1716                break;
1717            case GPBType::ENUM:
1718                $enum_desc = $field->getEnumType();
1719                if ($enum_desc->getClass() === "Google\Protobuf\NullValue") {
1720                    $size += 4;
1721                    break;
1722                }
1723                $enum_value_desc = $enum_desc->getValueByNumber($value);
1724                if (!is_null($enum_value_desc)) {
1725                    $size += 2;  // size for ""
1726                    $size += strlen($enum_value_desc->getName());
1727                } else {
1728                    $str_value = strval($value);
1729                    $size += strlen($str_value);
1730                }
1731                break;
1732            case GPBType::BOOL:
1733                if ($value) {
1734                    $size += 4;
1735                } else {
1736                    $size += 5;
1737                }
1738                break;
1739            case GPBType::STRING:
1740                $value = json_encode($value, JSON_UNESCAPED_UNICODE);
1741                $size += strlen($value);
1742                break;
1743            case GPBType::BYTES:
1744                # if (is_a($this, "Google\Protobuf\BytesValue")) {
1745                #     $size += strlen(json_encode($value));
1746                # } else {
1747                #     $size += strlen(base64_encode($value));
1748                #     $size += 2;  // size for \"\"
1749                # }
1750                $size += strlen(base64_encode($value));
1751                $size += 2;  // size for \"\"
1752                break;
1753            case GPBType::MESSAGE:
1754                $size += $value->jsonByteSize();
1755                break;
1756#             case GPBType::GROUP:
1757#                 // TODO: Add support.
1758#                 user_error("Unsupported type.");
1759#                 break;
1760            default:
1761                user_error("Unsupported type " . $field->getType());
1762                return 0;
1763        }
1764
1765        return $size;
1766    }
1767
1768    /**
1769     * @ignore
1770     */
1771    private function fieldByteSize($field)
1772    {
1773        $size = 0;
1774        if ($field->isMap()) {
1775            $getter = $field->getGetter();
1776            $values = $this->$getter();
1777            $count = count($values);
1778            if ($count !== 0) {
1779                $size += $count * GPBWire::tagSize($field);
1780                $message_type = $field->getMessageType();
1781                $key_field = $message_type->getFieldByNumber(1);
1782                $value_field = $message_type->getFieldByNumber(2);
1783                foreach ($values as $key => $value) {
1784                    $data_size = 0;
1785                    if ($key != $this->defaultValue($key_field)) {
1786                        $data_size += $this->fieldDataOnlyByteSize(
1787                            $key_field,
1788                            $key);
1789                        $data_size += GPBWire::tagSize($key_field);
1790                    }
1791                    if ($value != $this->defaultValue($value_field)) {
1792                        $data_size += $this->fieldDataOnlyByteSize(
1793                            $value_field,
1794                            $value);
1795                        $data_size += GPBWire::tagSize($value_field);
1796                    }
1797                    $size += GPBWire::varint32Size($data_size) + $data_size;
1798                }
1799            }
1800        } elseif ($field->isRepeated()) {
1801            $getter = $field->getGetter();
1802            $values = $this->$getter();
1803            $count = count($values);
1804            if ($count !== 0) {
1805                if ($field->getPacked()) {
1806                    $data_size = 0;
1807                    foreach ($values as $value) {
1808                        $data_size += $this->fieldDataOnlyByteSize($field, $value);
1809                    }
1810                    $size += GPBWire::tagSize($field);
1811                    $size += GPBWire::varint32Size($data_size);
1812                    $size += $data_size;
1813                } else {
1814                    $size += $count * GPBWire::tagSize($field);
1815                    foreach ($values as $value) {
1816                        $size += $this->fieldDataOnlyByteSize($field, $value);
1817                    }
1818                }
1819            }
1820        } elseif ($this->existField($field)) {
1821            $size += GPBWire::tagSize($field);
1822            $getter = $field->getGetter();
1823            $value = $this->$getter();
1824            $size += $this->fieldDataOnlyByteSize($field, $value);
1825        }
1826        return $size;
1827    }
1828
1829    /**
1830     * @ignore
1831     */
1832    private function fieldJsonByteSize($field)
1833    {
1834        $size = 0;
1835
1836        if ($field->isMap()) {
1837            $getter = $field->getGetter();
1838            $values = $this->$getter();
1839            $count = count($values);
1840            if ($count !== 0) {
1841                if (!GPBUtil::hasSpecialJsonMapping($this)) {
1842                    $size += 3;                              // size for "\"\":".
1843                    $size += strlen($field->getJsonName());  // size for field name
1844                }
1845                $size += 2;  // size for "{}".
1846                $size += $count - 1;                     // size for commas
1847                $getter = $field->getGetter();
1848                $map_entry = $field->getMessageType();
1849                $key_field = $map_entry->getFieldByNumber(1);
1850                $value_field = $map_entry->getFieldByNumber(2);
1851                switch ($key_field->getType()) {
1852                case GPBType::STRING:
1853                case GPBType::SFIXED64:
1854                case GPBType::INT64:
1855                case GPBType::SINT64:
1856                case GPBType::FIXED64:
1857                case GPBType::UINT64:
1858                    $additional_quote = false;
1859                    break;
1860                default:
1861                    $additional_quote = true;
1862                }
1863                foreach ($values as $key => $value) {
1864                    if ($additional_quote) {
1865                        $size += 2;  // size for ""
1866                    }
1867                    $size += $this->fieldDataOnlyJsonByteSize($key_field, $key);
1868                    $size += $this->fieldDataOnlyJsonByteSize($value_field, $value);
1869                    $size += 1;  // size for :
1870                }
1871            }
1872        } elseif ($field->isRepeated()) {
1873            $getter = $field->getGetter();
1874            $values = $this->$getter();
1875            $count = count($values);
1876            if ($count !== 0) {
1877                if (!GPBUtil::hasSpecialJsonMapping($this)) {
1878                    $size += 3;                              // size for "\"\":".
1879                    $size += strlen($field->getJsonName());  // size for field name
1880                }
1881                $size += 2;  // size for "[]".
1882                $size += $count - 1;                     // size for commas
1883                $getter = $field->getGetter();
1884                foreach ($values as $value) {
1885                    $size += $this->fieldDataOnlyJsonByteSize($field, $value);
1886                }
1887            }
1888        } elseif ($this->existField($field) || GPBUtil::hasJsonValue($this)) {
1889            if (!GPBUtil::hasSpecialJsonMapping($this)) {
1890                $size += 3;                              // size for "\"\":".
1891                $size += strlen($field->getJsonName());  // size for field name
1892            }
1893            $getter = $field->getGetter();
1894            $value = $this->$getter();
1895            $size += $this->fieldDataOnlyJsonByteSize($field, $value);
1896        }
1897        return $size;
1898    }
1899
1900    /**
1901     * @ignore
1902     */
1903    public function byteSize()
1904    {
1905        $size = 0;
1906
1907        $fields = $this->desc->getField();
1908        foreach ($fields as $field) {
1909            $size += $this->fieldByteSize($field);
1910        }
1911        $size += strlen($this->unknown);
1912        return $size;
1913    }
1914
1915    private function appendHelper($field, $append_value)
1916    {
1917        $getter = $field->getGetter();
1918        $setter = $field->getSetter();
1919
1920        $field_arr_value = $this->$getter();
1921        $field_arr_value[] = $append_value;
1922
1923        if (!is_object($field_arr_value)) {
1924            $this->$setter($field_arr_value);
1925        }
1926    }
1927
1928    private function kvUpdateHelper($field, $update_key, $update_value)
1929    {
1930        $getter = $field->getGetter();
1931        $setter = $field->getSetter();
1932
1933        $field_arr_value = $this->$getter();
1934        $field_arr_value[$update_key] = $update_value;
1935
1936        if (!is_object($field_arr_value)) {
1937            $this->$setter($field_arr_value);
1938        }
1939    }
1940
1941    /**
1942     * @ignore
1943     */
1944    public function jsonByteSize()
1945    {
1946        $size = 0;
1947        if (is_a($this, 'Google\Protobuf\Any')) {
1948            // Size for "{}".
1949            $size += 2;
1950
1951            // Size for "\"@type\":".
1952            $size += 8;
1953
1954            // Size for url. +2 for "" /.
1955            $size += strlen($this->getTypeUrl()) + 2;
1956
1957            $value_msg = $this->unpack();
1958            if (GPBUtil::hasSpecialJsonMapping($value_msg)) {
1959                // Size for "\",value\":".
1960                $size += 9;
1961                $size += $value_msg->jsonByteSize();
1962            } else {
1963                $value_size = $value_msg->jsonByteSize();
1964                // size === 2 it's empty message {} which is not serialized inside any
1965                if ($value_size !== 2) {
1966                    // Size for value. +1 for comma, -2 for "{}".
1967                    $size += $value_size -1;
1968                }
1969            }
1970        } elseif (get_class($this) === 'Google\Protobuf\FieldMask') {
1971            $field_mask = GPBUtil::formatFieldMask($this);
1972            $size += strlen($field_mask) + 2;  // 2 for ""
1973        } elseif (get_class($this) === 'Google\Protobuf\Duration') {
1974            $duration = GPBUtil::formatDuration($this) . "s";
1975            $size += strlen($duration) + 2;  // 2 for ""
1976        } elseif (get_class($this) === 'Google\Protobuf\Timestamp') {
1977            $timestamp = GPBUtil::formatTimestamp($this);
1978            $timestamp = json_encode($timestamp);
1979            $size += strlen($timestamp);
1980        } elseif (get_class($this) === 'Google\Protobuf\ListValue') {
1981            $field = $this->desc->getField()[1];
1982            if ($this->existField($field)) {
1983                $field_size = $this->fieldJsonByteSize($field);
1984                $size += $field_size;
1985            } else {
1986                // Size for "[]".
1987                $size += 2;
1988            }
1989        } elseif (get_class($this) === 'Google\Protobuf\Struct') {
1990            $field = $this->desc->getField()[1];
1991            if ($this->existField($field)) {
1992                $field_size = $this->fieldJsonByteSize($field);
1993                $size += $field_size;
1994            } else {
1995                // Size for "{}".
1996                $size += 2;
1997            }
1998        } else {
1999            if (!GPBUtil::hasSpecialJsonMapping($this)) {
2000                // Size for "{}".
2001                $size += 2;
2002            }
2003
2004            $fields = $this->desc->getField();
2005            $count = 0;
2006            foreach ($fields as $field) {
2007                $field_size = $this->fieldJsonByteSize($field);
2008                $size += $field_size;
2009                if ($field_size != 0) {
2010                  $count++;
2011                }
2012            }
2013            // size for comma
2014            $size += $count > 0 ? ($count - 1) : 0;
2015        }
2016        return $size;
2017    }
2018
2019    public function __debugInfo()
2020    {
2021        if (is_a($this, 'Google\Protobuf\FieldMask')) {
2022            return ['paths' => $this->getPaths()->__debugInfo()];
2023        }
2024
2025        if (is_a($this, 'Google\Protobuf\Value')) {
2026            switch ($this->getKind()) {
2027                case 'null_value':
2028                    return ['nullValue' => $this->getNullValue()];
2029                case 'number_value':
2030                    return ['numberValue' => $this->getNumberValue()];
2031                case 'string_value':
2032                    return ['stringValue' => $this->getStringValue()];
2033                case 'bool_value':
2034                    return ['boolValue' => $this->getBoolValue()];
2035                case 'struct_value':
2036                    return ['structValue' => $this->getStructValue()->__debugInfo()];
2037                case 'list_value':
2038                    return ['listValue' => $this->getListValue()->__debugInfo()];
2039            }
2040            return [];
2041        }
2042
2043        if (is_a($this, 'Google\Protobuf\BoolValue')
2044            || is_a($this, 'Google\Protobuf\BytesValue')
2045            || is_a($this, 'Google\Protobuf\DoubleValue')
2046            || is_a($this, 'Google\Protobuf\FloatValue')
2047            || is_a($this, 'Google\Protobuf\StringValue')
2048            || is_a($this, 'Google\Protobuf\Int32Value')
2049            || is_a($this, 'Google\Protobuf\Int64Value')
2050            || is_a($this, 'Google\Protobuf\UInt32Value')
2051            || is_a($this, 'Google\Protobuf\UInt64Value')
2052        ) {
2053            return [
2054                'value' => json_decode($this->serializeToJsonString(), true),
2055            ];
2056        }
2057
2058        if (
2059            is_a($this, 'Google\Protobuf\Duration')
2060            || is_a($this, 'Google\Protobuf\Timestamp')
2061        ) {
2062            return [
2063                'seconds' => $this->getSeconds(),
2064                'nanos' => $this->getNanos(),
2065            ];
2066        }
2067
2068        return json_decode($this->serializeToJsonString(), true);
2069    }
2070}
2071