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