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