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