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