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