Thrift now works in PHP, hot stuff
Summary: End to end communication working in Thrift with PHP
Problem: It's a bit slower than pillar still. Need to find out why.
Reviewed By: aditya
Test Plan: Unit tests are in the test directory. Get lucas on the PHP case...
git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@664720 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/php/src/transport/TBufferedTransport.php b/lib/php/src/transport/TBufferedTransport.php
new file mode 100644
index 0000000..dad96ff
--- /dev/null
+++ b/lib/php/src/transport/TBufferedTransport.php
@@ -0,0 +1,108 @@
+<?php
+
+/**
+ * Buffered transport. Stores data to an internal buffer that it doesn't
+ * actually write out until flush is called. For reading, we do a greedy
+ * read and then serve data out of the internal buffer.
+ *
+ * @package thrift.transport
+ * @author Mark Slee <mcslee@facebook.com>
+ */
+class TBufferedTransport extends TTransport {
+
+ /**
+ * Constructor. Creates a buffered transport around an underlying transport
+ */
+ public function __construct($transport=null, $rBufSize=512, $wBufSize=512) {
+ $this->transport_ = $transport;
+ $this->rBufSize_ = $rBufSize;
+ $this->wBufSize_ = $wBufSize;
+ }
+
+ /**
+ * The underlying transport
+ *
+ * @var TTransport
+ */
+ protected $transport_ = null;
+
+ /**
+ * The receive buffer size
+ *
+ * @var int
+ */
+ protected $rBufSize_ = 512;
+
+ /**
+ * The write buffer size
+ *
+ * @var int
+ */
+ protected $wBufSize_ = 512;
+
+ /**
+ * The write buffer.
+ *
+ * @var string
+ */
+ protected $wBuf_ = '';
+
+ /**
+ * The read buffer.
+ *
+ * @var string
+ */
+ protected $rBuf_ = '';
+
+ public function isOpen() {
+ return $this->transport_->isOpen();
+ }
+
+ public function open() {
+ $this->transport_->open();
+ }
+
+ public function close() {
+ $this->transport_->close();
+ }
+
+ public function readAll($len) {
+ return $this->transport_->readAll($len);
+ }
+
+ public function read($len) {
+ // Methinks PHP is already buffering these for us
+ return $this->transport_->read($len);
+
+ if (strlen($this->rBuf_) >= $len) {
+ $ret = substr($this->rBuf_, 0, $len);
+ $this->rBuf_ = substr($this->rBuf_, $len);
+ return $ret;
+ }
+
+ $this->rBuf_ .= $this->transport_->read($this->rBufSize_);
+ $give = min(strlen($this->rBuf_), $len);
+ $ret = substr($this->rBuf_, 0, $give);
+ $this->rBuf_ = substr($this->rBuf_, $give);
+ return $ret;
+ }
+
+ public function write($buf) {
+ $this->wBuf_ .= $buf;
+ if (strlen($this->wBuf_) >= $this->wBufSize_) {
+ $this->transport_->write($this->wBuf_);
+ $this->wBuf_ = '';
+ }
+ }
+
+ public function flush() {
+ if (!empty($this->wBuf_)) {
+ $this->transport_->write($this->wBuf_);
+ $this->wBuf_ = '';
+ }
+ }
+
+}
+
+?>
+
diff --git a/lib/php/src/transport/TChunkedTransport.php b/lib/php/src/transport/TChunkedTransport.php
new file mode 100644
index 0000000..04fba1f
--- /dev/null
+++ b/lib/php/src/transport/TChunkedTransport.php
@@ -0,0 +1,106 @@
+<?php
+
+/**
+ * Chunked transport. Writes and reads data in chunks that are stamped with
+ * their length.
+ *
+ * @package thrift.transport
+ * @author Mark Slee <mcslee@facebook.com>
+ */
+class TChunkedTransport extends TTransport {
+
+ /**
+ * Underlying transport object.
+ *
+ * @var TTransport
+ */
+ private $transport_;
+
+ /**
+ * Buffer for read data.
+ *
+ * @var string
+ */
+ private $rBuf_;
+
+ /**
+ * Buffer for queued output data
+ *
+ * @var string
+ */
+ private $wBuf_;
+
+ /**
+ * Constructor.
+ *
+ * @param TTransport $transport Underlying transport
+ */
+ public __construct($transport=null) {
+ $this->transport_ = $transport;
+ }
+
+ /**
+ * Reads from the buffer. When more data is required reads another entire
+ * chunk and serves future reads out of that.
+ *
+ * @param int $len How much data
+ */
+ public function read($len) {
+ $out = '';
+ $need = $len;
+ $have = strlen($this->rBuf_);
+ if ($need > $have) {
+ $out = $this->rBuf_;
+ $need -= $have;
+ $this->readChunk();
+ }
+
+ $give = $need;
+ if (strlen($this->rBuf_) < $give) {
+ $out .= $this->rBuf_;
+ $this->rBuf_ = '';
+ } else {
+ $out .= substr($this->rBuf_, 0, $give);
+ $this->rBuf_ = substr($this->rBuf_, $give);
+ }
+
+ return $out;
+ }
+
+ /**
+ * Reads a chunk of data into the internal read buffer.
+ */
+ private function readChunk() {
+ $buf = $this->transport_->readAll(4);
+ $val = unpack('N', $buf);
+ $sz = $val[1];
+
+ $this->rBuf_ = $this->transport_->readAll($sz);
+ }
+
+ /**
+ * Writes some data to the pending output buffer.
+ *
+ * @param string $buf The data
+ * @param int $len Limit of bytes to write
+ */
+ public function write($buf, $len=null) {
+ if ($len !== null && $len < strlen($buf)) {
+ $buf = substr($buf, 0, $len);
+ }
+ $this->wBuf_ .= $buf;
+ }
+
+ /**
+ * Writes the output buffer to the stream in the format of a 4-byte length
+ * followed by the actual data.
+ */
+ public function flush() {
+ $out = pack('N', strlen($this->wBuf_));
+ $out .= $this->wBuf_;
+ $this->transport_->write($out);
+ $this->transport_->flush();
+ $this->wBuf_ = '';
+ }
+
+}
\ No newline at end of file
diff --git a/lib/php/src/transport/TSocket.php b/lib/php/src/transport/TSocket.php
new file mode 100644
index 0000000..0a3b090
--- /dev/null
+++ b/lib/php/src/transport/TSocket.php
@@ -0,0 +1,126 @@
+<?php
+
+/**
+ * Sockets implementation of the TTransport interface.
+ *
+ * @package thrift.transport
+ * @author Mark Slee <mcslee@facebook.com>
+ */
+class TSocket extends TTransport {
+
+ /**
+ * Handle to PHP socket
+ *
+ * @var resource
+ */
+ private $handle_ = null;
+
+ /**
+ * Remote hostname
+ *
+ * @var string
+ */
+ private $host_ = 'localhost';
+
+ /**
+ * Remote port
+ *
+ * @var int
+ */
+ private $port_ = '9090';
+
+ /**
+ * Persistent socket or plain?
+ *
+ * @var bool
+ */
+ private $persist_ = false;
+
+ /**
+ * Socket constructor
+ *
+ * @param string $host Remote hostname
+ * @param int $port Remote port
+ * @param bool $persist Whether to use a persistent socket
+ */
+ public function __construct($host='localhost', $port=9090, $persist=false) {
+ $this->host_ = $host;
+ $this->port_ = $port;
+ $this->persist_ = $persist;
+ }
+
+ /**
+ * Tests whether this is open
+ *
+ * @return bool true if the socket is open
+ */
+ public function isOpen() {
+ return is_resource($this->handle_);
+ }
+
+ /**
+ * Connects the socket.
+ */
+ public function open() {
+ if ($this->persist_) {
+ $this->handle_ = pfsockopen($this->host_, $this->port_);
+ } else {
+ $this->handle_ = fsockopen($this->host_, $this->port_);
+ }
+ if ($this->handle_ === FALSE) {
+ throw new Exception('TSocket: Could not connect to '.
+ $this->host_.':'.$this->port_);
+ }
+ }
+
+ /**
+ * Closes the socket
+ */
+ public function close() {
+ if (!$this->persist_) {
+ fclose($this->handle_);
+ }
+ }
+
+ /**
+ * Uses stream get contents to do the reading
+ */
+ public function readAll($len) {
+ return stream_get_contents($this->handle_, $len);
+ }
+
+ /**
+ * Read from the socket
+ */
+ public function read($len) {
+ $data = fread($this->handle_, 1);
+ if ($data === FALSE) {
+ throw new Exception('TSocket: Could not read '.$len.' bytes from '.
+ $this->host_.':'.$this->port_);
+ }
+ return $data;
+ }
+
+ /**
+ * Write to the socket.
+ */
+ public function write($buf) {
+ while (!empty($buf)) {
+ $got = fwrite($this->handle_, $buf);
+ if ($got == false) {
+ throw new Exception('TSocket: Could not write '.strlen($buf).' bytes '.
+ $this->host_.':'.$this->port_);
+ }
+ $buf = substr($buf, $got);
+ }
+ }
+
+ /**
+ * Flush output to the socket.
+ */
+ public function flush() {
+ fflush($this->handle_);
+ }
+}
+
+?>
diff --git a/lib/php/src/transport/TTransport.php b/lib/php/src/transport/TTransport.php
new file mode 100644
index 0000000..8d57d2a
--- /dev/null
+++ b/lib/php/src/transport/TTransport.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * Base interface for a transport agent.
+ *
+ * @package thrift.transport
+ * @author Mark Slee <mcslee@facebook.com>
+ */
+abstract class TTransport {
+
+ /**
+ * Whether this transport is open.
+ *
+ * @return boolean true if open
+ */
+ public abstract function isOpen();
+
+ /**
+ * Open the transport for reading/writing
+ *
+ * @throws TTransportException if cannot open
+ */
+ public abstract function open();
+
+ /**
+ * Close the transport.
+ */
+ public abstract function close();
+
+ /**
+ * Read some data into the array.
+ *
+ * @param int $len How much to read
+ * @return string The data that has been read
+ * @throws TTransportException if cannot read any more data
+ */
+ public abstract function read($len);
+
+ /**
+ * Guarantees that the full amount of data is read.
+ *
+ * @return string The data, of exact length
+ * @throws TTransportException if cannot read data
+ */
+ public function readAll($len) {
+ // return $this->read($len);
+
+ $data = '';
+ $got = 0;
+ while (($got = strlen($data)) < $len) {
+ $data .= $this->read($len - $got);
+ }
+ return $data;
+ }
+
+ /**
+ * Writes the given data out.
+ *
+ * @param string $buf The data to write
+ * @throws TTransportException if writing fails
+ */
+ public abstract function write($buf);
+
+ /**
+ * Flushes any pending data out of a buffer
+ *
+ * @throws TTransportException if a writing error occurs
+ */
+ public function flush() {}
+}
+
+?>