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