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