From 199833807f8dabd0d6d1707a594b7d6cac82641e Mon Sep 17 00:00:00 2001 From: Jens Geyer Date: Sat, 22 Feb 2014 17:34:29 +0100 Subject: [PATCH] THRIFT-1579 PHP Extension - function thrift_protocol_read_binary not working from TBinarySerializer::deserialize Patch: Tobias Heintz --- .../Protocol/TBinaryProtocolAccelerated.php | 17 ++++- .../Thrift/Serializer/TBinarySerializer.php | 8 ++- lib/php/test/Makefile.am | 1 + .../Thrift/Protocol/TestBinarySerializer.php | 65 +++++++++++++++++++ 4 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 lib/php/test/Test/Thrift/Protocol/TestBinarySerializer.php diff --git a/lib/php/lib/Thrift/Protocol/TBinaryProtocolAccelerated.php b/lib/php/lib/Thrift/Protocol/TBinaryProtocolAccelerated.php index 392aa21f..7a40ce9c 100644 --- a/lib/php/lib/Thrift/Protocol/TBinaryProtocolAccelerated.php +++ b/lib/php/lib/Thrift/Protocol/TBinaryProtocolAccelerated.php @@ -33,6 +33,21 @@ class TBinaryProtocolAccelerated extends TBinaryProtocol { public function __construct($trans, $strictRead=false, $strictWrite=true) { // If the transport doesn't implement putBack, wrap it in a // TBufferedTransport (which does) + + // NOTE (t.heintz): This is very evil to do, because the TBufferedTransport may swallow bytes, which + // are then never written to the underlying transport. This happens precisely when a number of bytes + // less than the max buffer size (512 by default) is written to the transport and then flush() is NOT + // called. In that case the data stays in the writeBuffer of the transport, from where it can never be + // accessed again (for example through read()). + // + // Since the caller of this method does not know about the wrapping transport, this creates bugs which + // are very difficult to find. Hence the wrapping of a transport in a buffer should be left to the + // calling code. An interface could used to mandate the presence of the putBack() method in the transport. + // + // I am leaving this code in nonetheless, because there may be applications depending on this behavior. + // + // @see THRIFT-1579 + if (!method_exists($trans, 'putBack')) { $trans = new TBufferedTransport($trans); } @@ -44,4 +59,4 @@ class TBinaryProtocolAccelerated extends TBinaryProtocol { public function isStrictWrite() { return $this->strictWrite_; } -} \ No newline at end of file +} diff --git a/lib/php/lib/Thrift/Serializer/TBinarySerializer.php b/lib/php/lib/Thrift/Serializer/TBinarySerializer.php index 2a7cc3e0..4e0af872 100644 --- a/lib/php/lib/Thrift/Serializer/TBinarySerializer.php +++ b/lib/php/lib/Thrift/Serializer/TBinarySerializer.php @@ -59,8 +59,14 @@ class TBinarySerializer { $transport = new TMemoryBuffer(); $protocol = new TBinaryProtocolAccelerated($transport); if (function_exists('thrift_protocol_read_binary')) { + // NOTE (t.heintz) TBinaryProtocolAccelerated internally wraps our TMemoryBuffer in a + // TBufferedTransport, so we have to retrieve it again or risk losing data when writing + // less than 512 bytes to the transport (see the comment there as well). + // @see THRIFT-1579 $protocol->writeMessageBegin('', TMessageType::REPLY, 0); - $transport->write($string_object); + $protocolTransport = $protocol->getTransport(); + $protocolTransport->write($string_object); + $protocolTransport->flush(); return thrift_protocol_read_binary($protocol, $class_name, $protocol->isStrictRead()); } else { diff --git a/lib/php/test/Makefile.am b/lib/php/test/Makefile.am index 2fd7f81e..1292b818 100755 --- a/lib/php/test/Makefile.am +++ b/lib/php/test/Makefile.am @@ -26,6 +26,7 @@ stubs: ../../../test/ThriftTest.thrift if HAVE_PHPUNIT check: stubs $(PHPUNIT) --log-junit=phpunit.xml Test/Thrift/Protocol/TestTJSONProtocol.php + $(PHPUNIT) --log-junit=phpunit.xml Test/Thrift/Protocol/TestBinarySerializer.php endif clean-local: diff --git a/lib/php/test/Test/Thrift/Protocol/TestBinarySerializer.php b/lib/php/test/Test/Thrift/Protocol/TestBinarySerializer.php new file mode 100644 index 00000000..65feadde --- /dev/null +++ b/lib/php/test/Test/Thrift/Protocol/TestBinarySerializer.php @@ -0,0 +1,65 @@ +registerNamespace('Thrift', __DIR__ . '/../../../../lib'); +$loader->registerNamespace('Test', __DIR__ . '/../../..'); +$loader->registerDefinition('ThriftTest', __DIR__ . '/../../../packages'); +$loader->register(); + +/*** + * This test suite depends on running the compiler against the + * standard ThriftTest.thrift file: + * + * lib/php/test$ ../../../compiler/cpp/thrift --gen php -r \ + * --out ./packages ../../../test/ThriftTest.thrift + */ + +class TestBinarySerializer extends \PHPUnit_Framework_TestCase +{ + + public function setUp() + { + } + + /** + * We try to serialize and deserialize a random object to make sure no exceptions are thrown. + * @see THRIFT-1579 + */ + public function testBinarySerializer() + { + $struct = new \ThriftTest\Xtruct(array('string_thing' => 'abc')); + $serialized = TBinarySerializer::serialize($struct, 'ThriftTest\\Xtruct'); + $deserialized = TBinarySerializer::deserialize($serialized, 'ThriftTest\\Xtruct'); + $this->assertEquals($struct, $deserialized); + } + +} + -- 2.17.1