1<?php 2 3require_once('test_base.php'); 4require_once('test_util.php'); 5 6use Foo\TestEnum; 7use Foo\TestMessage; 8use Foo\TestMessage\Sub; 9use Foo\TestPackedMessage; 10use Google\Protobuf\Internal\CodedInputStream; 11use Google\Protobuf\Internal\FileDescriptorSet; 12use Google\Protobuf\Internal\GPBLabel; 13use Google\Protobuf\Internal\GPBType; 14use Google\Protobuf\Internal\GPBWire; 15use Google\Protobuf\Internal\CodedOutputStream; 16 17/** 18 * Please note, this test is only intended to be run without the protobuf C 19 * extension. 20 */ 21class PhpImplementationTest extends TestBase 22{ 23 /** 24 * Avoid calling setUp, which has void return type (not avalialbe in php7.0). 25 * @before 26 */ 27 public function skipTestsForExtension() 28 { 29 if (extension_loaded('protobuf')) { 30 $this->markTestSkipped(); 31 } 32 } 33 34 public function testReadInt32() 35 { 36 $value = null; 37 38 // Positive number. 39 $input = new CodedInputStream(hex2bin("01")); 40 GPBWire::readInt32($input, $value); 41 $this->assertSame(1, $value); 42 43 // Negative number. 44 $input = new CodedInputStream(hex2bin("ffffffff0f")); 45 GPBWire::readInt32($input, $value); 46 $this->assertSame(-1, $value); 47 48 // Discard overflow bits. 49 $input = new CodedInputStream(hex2bin("ffffffff7f")); 50 GPBWire::readInt32($input, $value); 51 $this->assertSame(-1, $value); 52 } 53 54 public function testReadUint32() 55 { 56 $value = null; 57 58 // Positive number. 59 $input = new CodedInputStream(hex2bin("01")); 60 GPBWire::readUint32($input, $value); 61 $this->assertSame(1, $value); 62 63 // Max uint32. 64 $input = new CodedInputStream(hex2bin("ffffffff0f")); 65 GPBWire::readUint32($input, $value); 66 $this->assertSame(-1, $value); 67 68 // Discard overflow bits. 69 $input = new CodedInputStream(hex2bin("ffffffff7f")); 70 GPBWire::readUint32($input, $value); 71 $this->assertSame(-1, $value); 72 } 73 74 public function testReadInt64() 75 { 76 $value = null; 77 78 // Positive number. 79 $input = new CodedInputStream(hex2bin("01")); 80 GPBWire::readInt64($input, $value); 81 $this->assertEquals(1, $value); 82 83 // Negative number. 84 $input = new CodedInputStream(hex2bin("ffffffffffffffffff01")); 85 GPBWire::readInt64($input, $value); 86 $this->assertEquals(-1, $value); 87 88 // Discard overflow bits. 89 $input = new CodedInputStream(hex2bin("ffffffffffffffffff0f")); 90 GPBWire::readInt64($input, $value); 91 $this->assertEquals(-1, $value); 92 } 93 94 public function testReadUint64() 95 { 96 $value = null; 97 98 // Positive number. 99 $input = new CodedInputStream(hex2bin("01")); 100 GPBWire::readUint64($input, $value); 101 $this->assertEquals(1, $value); 102 103 // Negative number. 104 $input = new CodedInputStream(hex2bin("FFFFFFFFFFFFFFFFFF01")); 105 GPBWire::readUint64($input, $value); 106 $this->assertEquals(-1, $value); 107 108 // Discard overflow bits. 109 $input = new CodedInputStream(hex2bin("FFFFFFFFFFFFFFFFFF0F")); 110 GPBWire::readUint64($input, $value); 111 $this->assertEquals(-1, $value); 112 } 113 114 public function testReadSint32() 115 { 116 $value = null; 117 118 $input = new CodedInputStream(hex2bin("00")); 119 GPBWire::readSint32($input, $value); 120 $this->assertSame(0, $value); 121 122 $input = new CodedInputStream(hex2bin("01")); 123 GPBWire::readSint32($input, $value); 124 $this->assertSame(-1, $value); 125 126 $input = new CodedInputStream(hex2bin("02")); 127 GPBWire::readSint32($input, $value); 128 $this->assertSame(1, $value); 129 } 130 131 public function testReadSint64() 132 { 133 $value = null; 134 135 $input = new CodedInputStream(hex2bin("00")); 136 GPBWire::readSint64($input, $value); 137 $this->assertEquals(0, $value); 138 139 $input = new CodedInputStream(hex2bin("01")); 140 GPBWire::readSint64($input, $value); 141 $this->assertEquals(-1, $value); 142 143 $input = new CodedInputStream(hex2bin("02")); 144 GPBWire::readSint64($input, $value); 145 $this->assertEquals(1, $value); 146 } 147 148 public function testReadFixed32() 149 { 150 $value = null; 151 $input = new CodedInputStream(hex2bin("12345678")); 152 GPBWire::readFixed32($input, $value); 153 $this->assertSame(0x78563412, $value); 154 } 155 156 public function testReadFixed64() 157 { 158 $value = null; 159 $input = new CodedInputStream(hex2bin("1234567812345678")); 160 GPBWire::readFixed64($input, $value); 161 if (PHP_INT_SIZE == 4) { 162 $this->assertSame("8671175386481439762", $value); 163 } else { 164 $this->assertSame(0x7856341278563412, $value); 165 } 166 } 167 168 public function testReadSfixed32() 169 { 170 $value = null; 171 $input = new CodedInputStream(hex2bin("12345678")); 172 GPBWire::readSfixed32($input, $value); 173 $this->assertSame(0x78563412, $value); 174 } 175 176 public function testReadFloat() 177 { 178 $value = null; 179 $input = new CodedInputStream(hex2bin("0000803F")); 180 GPBWire::readFloat($input, $value); 181 $this->assertSame(1.0, $value); 182 } 183 184 public function testReadBool() 185 { 186 $value = null; 187 188 $input = new CodedInputStream(hex2bin("00")); 189 GPBWire::readBool($input, $value); 190 $this->assertSame(false, $value); 191 192 $input = new CodedInputStream(hex2bin("01")); 193 GPBWire::readBool($input, $value); 194 $this->assertSame(true, $value); 195 } 196 197 public function testReadDouble() 198 { 199 $value = null; 200 $input = new CodedInputStream(hex2bin("000000000000F03F")); 201 GPBWire::readDouble($input, $value); 202 $this->assertSame(1.0, $value); 203 } 204 205 public function testReadSfixed64() 206 { 207 $value = null; 208 $input = new CodedInputStream(hex2bin("1234567812345678")); 209 GPBWire::readSfixed64($input, $value); 210 if (PHP_INT_SIZE == 4) { 211 $this->assertSame("8671175386481439762", $value); 212 } else { 213 $this->assertSame(0x7856341278563412, $value); 214 } 215 } 216 217 public function testZigZagEncodeDecode() 218 { 219 $this->assertSame(0, GPBWire::zigZagEncode32(0)); 220 $this->assertSame(1, GPBWire::zigZagEncode32(-1)); 221 $this->assertSame(2, GPBWire::zigZagEncode32(1)); 222 $this->assertSame(3, GPBWire::zigZagEncode32(-2)); 223 $this->assertSame(0x7FFFFFFE, GPBWire::zigZagEncode32(0x3FFFFFFF)); 224 $this->assertSame(0x7FFFFFFF, GPBWire::zigZagEncode32(0xC0000000)); 225 $this->assertSame(0x7FFFFFFF, GPBWire::zigZagEncode32(-1073741824)); 226 227 $this->assertSame(0, GPBWire::zigZagDecode32(0)); 228 $this->assertSame(-1, GPBWire::zigZagDecode32(1)); 229 $this->assertSame(1, GPBWire::zigZagDecode32(2)); 230 $this->assertSame(-2, GPBWire::zigZagDecode32(3)); 231 $this->assertSame(0x3FFFFFFF, GPBWire::zigZagDecode32(0x7FFFFFFE)); 232 $this->assertSame(-1073741824, GPBWire::zigZagDecode32(0x7FFFFFFF)); 233 $this->assertSame(0x7FFFFFFF, GPBWire::zigZagDecode32(0xFFFFFFFE)); 234 $this->assertSame((int)-2147483648,GPBWire::zigZagDecode32(0xFFFFFFFF)); 235 236 if (PHP_INT_SIZE == 4) { 237 $this->assertSame(-2, GPBWire::zigZagEncode32(0x7FFFFFFF)); 238 $this->assertSame(-1, GPBWire::zigZagEncode32(0x80000000)); 239 $this->assertSame('0', GPBWire::zigZagEncode64(0)); 240 $this->assertSame('1', GPBWire::zigZagEncode64(-1)); 241 $this->assertSame('2', GPBWire::zigZagEncode64(1)); 242 $this->assertSame('3', GPBWire::zigZagEncode64(-2)); 243 $this->assertSame( 244 '2147483646', // 0x7FFFFFE 245 GPBWire::zigZagEncode64(0x3FFFFFFF)); 246 $this->assertSame( 247 '2147483647', // 0x7FFFFFF 248 GPBWire::zigZagEncode64(-1073741824)); // 0xFFFFFFFFC0000000 249 $this->assertSame( 250 '4294967294', // 0xFFFFFFFE 251 GPBWire::zigZagEncode64(2147483647)); // 0x7FFFFFFF 252 $this->assertSame( 253 '4294967295', // 0xFFFFFFFF 254 GPBWire::zigZagEncode64(-2147483648)); // 0xFFFFFFFF80000000 255 $this->assertSame( 256 '18446744073709551614', // 0xFFFFFFFFFFFFFFFE 257 // 0x7FFFFFFFFFFFFFFF 258 GPBWire::zigZagEncode64("9223372036854775807")); 259 $this->assertSame( 260 '18446744073709551615', // 0xFFFFFFFFFFFFFFFF 261 // 0x8000000000000000 262 GPBWire::zigZagEncode64("-9223372036854775808")); 263 264 $this->assertSame('0', GPBWire::zigZagDecode64(0)); 265 $this->assertSame('-1', GPBWire::zigZagDecode64(1)); 266 $this->assertSame('1', GPBWire::zigZagDecode64(2)); 267 $this->assertSame('-2', GPBWire::zigZagDecode64(3)); 268 } else { 269 $this->assertSame(4294967294, GPBWire::zigZagEncode32(0x7FFFFFFF)); 270 $this->assertSame(4294967295, GPBWire::zigZagEncode32(0x80000000)); 271 $this->assertSame(0, GPBWire::zigZagEncode64(0)); 272 $this->assertSame(1, GPBWire::zigZagEncode64(-1)); 273 $this->assertSame(2, GPBWire::zigZagEncode64(1)); 274 $this->assertSame(3, GPBWire::zigZagEncode64(-2)); 275 $this->assertSame(0x7FFFFFFE, GPBWire::zigZagEncode64(0x3FFFFFFF)); 276 $this->assertSame( 277 0x7FFFFFFF, 278 GPBWire::zigZagEncode64(0xFFFFFFFFC0000000)); 279 $this->assertSame( 280 0xFFFFFFFE, 281 GPBWire::zigZagEncode64(0x7FFFFFFF)); 282 $this->assertSame( 283 0xFFFFFFFF, 284 GPBWire::zigZagEncode64(0xFFFFFFFF80000000)); 285 $this->assertSame( 286 -2, // 0xFFFFFFFFFFFFFFFE 287 GPBWire::zigZagEncode64(0x7FFFFFFFFFFFFFFF)); 288 $this->assertSame( 289 -1, // 0xFFFFFFFFFFFFFFFF 290 GPBWire::zigZagEncode64(0x8000000000000000)); 291 292 $this->assertSame(0, GPBWire::zigZagDecode64(0)); 293 $this->assertSame(-1, GPBWire::zigZagDecode64(1)); 294 $this->assertSame(1, GPBWire::zigZagDecode64(2)); 295 $this->assertSame(-2, GPBWire::zigZagDecode64(3)); 296 } 297 298 // Round trip 299 $this->assertSame(0, GPBWire::zigZagDecode32(GPBWire::zigZagEncode32(0))); 300 $this->assertSame(1, GPBWire::zigZagDecode32(GPBWire::zigZagEncode32(1))); 301 $this->assertSame(-1, GPBWire::zigZagDecode32(GPBWire::zigZagEncode32(-1))); 302 $this->assertSame(14927, 303 GPBWire::zigZagDecode32(GPBWire::zigZagEncode32(14927))); 304 $this->assertSame(-3612, 305 GPBWire::zigZagDecode32(GPBWire::zigZagEncode32(-3612))); 306 } 307 308 public function testDecode() 309 { 310 $m = new TestMessage(); 311 $m->mergeFromString(TestUtil::getGoldenTestMessage()); 312 TestUtil::assertTestMessage($m); 313 314 $this->assertTrue(true); 315 } 316 317 public function testDescriptorDecode() 318 { 319 $file_desc_set = new FileDescriptorSet(); 320 $file_desc_set->mergeFromString(hex2bin( 321 "0a3b0a12746573745f696e636c7564652e70726f746f120362617222180a" . 322 "0b54657374496e636c75646512090a0161180120012805620670726f746f33")); 323 324 $this->assertSame(1, sizeof($file_desc_set->getFile())); 325 326 $file_desc = $file_desc_set->getFile()[0]; 327 $this->assertSame("test_include.proto", $file_desc->getName()); 328 $this->assertSame("bar", $file_desc->getPackage()); 329 $this->assertSame(0, sizeof($file_desc->getDependency())); 330 $this->assertSame(1, sizeof($file_desc->getMessageType())); 331 $this->assertSame(0, sizeof($file_desc->getEnumType())); 332 $this->assertSame("proto3", $file_desc->getSyntax()); 333 334 $desc = $file_desc->getMessageType()[0]; 335 $this->assertSame("TestInclude", $desc->getName()); 336 $this->assertSame(1, sizeof($desc->getField())); 337 $this->assertSame(0, sizeof($desc->getNestedType())); 338 $this->assertSame(0, sizeof($desc->getEnumType())); 339 $this->assertSame(0, sizeof($desc->getOneofDecl())); 340 341 $field = $desc->getField()[0]; 342 $this->assertSame("a", $field->getName()); 343 $this->assertSame(1, $field->getNumber()); 344 $this->assertSame(GPBLabel::OPTIONAL, $field->getLabel()); 345 $this->assertSame(GPBType::INT32, $field->getType()); 346 } 347 348 public function testReadVarint64() 349 { 350 $var = 0; 351 352 // Empty buffer. 353 $input = new CodedInputStream(hex2bin('')); 354 $this->assertFalse($input->readVarint64($var)); 355 356 // The largest varint is 10 bytes long. 357 $input = new CodedInputStream(hex2bin('8080808080808080808001')); 358 $this->assertFalse($input->readVarint64($var)); 359 360 // Corrupted varint. 361 $input = new CodedInputStream(hex2bin('808080')); 362 $this->assertFalse($input->readVarint64($var)); 363 364 // Normal case. 365 $input = new CodedInputStream(hex2bin('808001')); 366 $this->assertTrue($input->readVarint64($var)); 367 if (PHP_INT_SIZE == 4) { 368 $this->assertSame('16384', $var); 369 } else { 370 $this->assertSame(16384, $var); 371 } 372 $this->assertFalse($input->readVarint64($var)); 373 374 // Read two varint. 375 $input = new CodedInputStream(hex2bin('808001808002')); 376 $this->assertTrue($input->readVarint64($var)); 377 if (PHP_INT_SIZE == 4) { 378 $this->assertSame('16384', $var); 379 } else { 380 $this->assertSame(16384, $var); 381 } 382 $this->assertTrue($input->readVarint64($var)); 383 if (PHP_INT_SIZE == 4) { 384 $this->assertSame('32768', $var); 385 } else { 386 $this->assertSame(32768, $var); 387 } 388 $this->assertFalse($input->readVarint64($var)); 389 390 // Read 64 testing 391 $testVals = array( 392 '10' => '0a000000000000000000', 393 '100' => '64000000000000000000', 394 '800' => 'a0060000000000000000', 395 '6400' => '80320000000000000000', 396 '70400' => '80a60400000000000000', 397 '774400' => '80a22f00000000000000', 398 '9292800' => '8098b704000000000000', 399 '74342400' => '80c0b923000000000000', 400 '743424000' => '8080bfe2020000000000', 401 '8177664000' => '8080b5bb1e0000000000', 402 '65421312000' => '8080a8dbf30100000000', 403 '785055744000' => '8080e0c7ec1600000000', 404 '9420668928000' => '808080dd969202000000', 405 '103627358208000' => '808080fff9c717000000', 406 '1139900940288000' => '808080f5bd9783020000', 407 '13678811283456000' => '808080fce699a6180000', 408 '109430490267648000' => '808080e0b7ceb1c20100', 409 '984874412408832000' => '808080e0f5c1bed50d00', 410 ); 411 412 foreach ($testVals as $original => $encoded) { 413 $input = new CodedInputStream(hex2bin($encoded)); 414 $this->assertTrue($input->readVarint64($var)); 415 $this->assertEquals($original, $var); 416 } 417 } 418 419 public function testReadVarint32() 420 { 421 $var = 0; 422 423 // Empty buffer. 424 $input = new CodedInputStream(hex2bin('')); 425 $this->assertFalse($input->readVarint32($var)); 426 427 // The largest varint is 10 bytes long. 428 $input = new CodedInputStream(hex2bin('8080808080808080808001')); 429 $this->assertFalse($input->readVarint32($var)); 430 431 // Corrupted varint. 432 $input = new CodedInputStream(hex2bin('808080')); 433 $this->assertFalse($input->readVarint32($var)); 434 435 // Normal case. 436 $input = new CodedInputStream(hex2bin('808001')); 437 $this->assertTrue($input->readVarint32($var)); 438 $this->assertSame(16384, $var); 439 $this->assertFalse($input->readVarint32($var)); 440 441 // Read two varint. 442 $input = new CodedInputStream(hex2bin('808001808002')); 443 $this->assertTrue($input->readVarint32($var)); 444 $this->assertSame(16384, $var); 445 $this->assertTrue($input->readVarint32($var)); 446 $this->assertSame(32768, $var); 447 $this->assertFalse($input->readVarint32($var)); 448 449 // Read a 64-bit integer. High-order bits should be discarded. 450 $input = new CodedInputStream(hex2bin('808081808001')); 451 $this->assertTrue($input->readVarint32($var)); 452 $this->assertSame(16384, $var); 453 $this->assertFalse($input->readVarint32($var)); 454 } 455 456 public function testReadTag() 457 { 458 $input = new CodedInputStream(hex2bin('808001')); 459 $tag = $input->readTag(); 460 $this->assertSame(16384, $tag); 461 $tag = $input->readTag(); 462 $this->assertSame(0, $tag); 463 } 464 465 public function testPushPopLimit() 466 { 467 $input = new CodedInputStream(hex2bin('808001')); 468 $old_limit = $input->pushLimit(0); 469 $tag = $input->readTag(); 470 $this->assertSame(0, $tag); 471 $input->popLimit($old_limit); 472 $tag = $input->readTag(); 473 $this->assertSame(16384, $tag); 474 } 475 476 public function testReadRaw() 477 { 478 $input = new CodedInputStream(hex2bin('808001')); 479 $buffer = null; 480 481 $this->assertTrue($input->readRaw(3, $buffer)); 482 $this->assertSame(hex2bin('808001'), $buffer); 483 484 $this->assertFalse($input->readRaw(1, $buffer)); 485 } 486 487 public function testWriteVarint32() 488 { 489 $output = new CodedOutputStream(3); 490 $output->writeVarint32(16384, true); 491 $this->assertSame(hex2bin('808001'), $output->getData()); 492 493 // Negative numbers are padded to be compatible with int64. 494 $output = new CodedOutputStream(10); 495 $output->writeVarint32(-43, false); 496 $this->assertSame(hex2bin('D5FFFFFFFFFFFFFFFF01'), $output->getData()); 497 } 498 499 public function testWriteVarint64() 500 { 501 $output = new CodedOutputStream(10); 502 $output->writeVarint64(-43); 503 $this->assertSame(hex2bin('D5FFFFFFFFFFFFFFFF01'), $output->getData()); 504 } 505 506 public function testWriteLittleEndian32() 507 { 508 $output = new CodedOutputStream(4); 509 $output->writeLittleEndian32(46); 510 $this->assertSame(hex2bin('2E000000'), $output->getData()); 511 } 512 513 public function testWriteLittleEndian64() 514 { 515 $output = new CodedOutputStream(8); 516 $output->writeLittleEndian64(47); 517 $this->assertSame(hex2bin('2F00000000000000'), $output->getData()); 518 } 519 520 public function testByteSize() 521 { 522 $m = new TestMessage(); 523 TestUtil::setTestMessage($m); 524 $this->assertSame(504, $m->byteSize()); 525 } 526 527 public function testPackedByteSize() 528 { 529 $m = new TestPackedMessage(); 530 TestUtil::setTestPackedMessage($m); 531 $this->assertSame(166, $m->byteSize()); 532 } 533 534 public function testArrayConstructorJsonCaseThrowsException() 535 { 536 $this->expectException(UnexpectedValueException::class); 537 $this->expectExceptionMessage( 538 'Invalid message property: optionalInt32'); 539 540 $m = new TestMessage([ 541 'optionalInt32' => -42, 542 ]); 543 } 544 545 public function testArraysForMessagesThrowsException() 546 { 547 $this->expectException(Exception::class); 548 $this->expectExceptionMessage( 549 'Expect Foo\TestMessage\Sub.'); 550 551 $m = new TestMessage([ 552 'optional_message' => [ 553 'a' => 33 554 ] 555 ]); 556 } 557 558 public function testArrayConstructorWithNullValues() 559 { 560 $requestData = [ 561 'optional_bool' => null, 562 'optional_string' => null, 563 'optional_bytes' => null, 564 'optional_message' => null, 565 ]; 566 567 $m = new TestMessage($requestData); 568 569 $this->assertSame(false, $m->getOptionalBool()); 570 $this->assertSame('', $m->getOptionalString()); 571 $this->assertSame('', $m->getOptionalBytes()); 572 $this->assertSame(null, $m->getOptionalMessage()); 573 } 574 575 /** 576 * @dataProvider provideArrayConstructorWithNullValuesThrowsException 577 */ 578 public function testArrayConstructorWithNullValuesThrowsException($requestData) 579 { 580 $this->expectException(Exception::class); 581 582 $m = new TestMessage($requestData); 583 } 584 585 public function provideArrayConstructorWithNullValuesThrowsException() 586 { 587 return [ 588 [['optional_int32' => null]], 589 [['optional_int64' => null]], 590 [['optional_uint32' => null]], 591 [['optional_uint64' => null]], 592 [['optional_sint32' => null]], 593 [['optional_sint64' => null]], 594 [['optional_fixed32' => null]], 595 [['optional_fixed64' => null]], 596 [['optional_sfixed32' => null]], 597 [['optional_sfixed64' => null]], 598 [['optional_float' => null]], 599 [['optional_double' => null]], 600 [['optional_enum' => null]], 601 [['repeated_int32' => null]], 602 [['map_int32_int32' => null]], 603 ]; 604 } 605} 606