You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@thrift.apache.org by jk...@apache.org on 2018/01/16 14:23:25 UTC

[3/8] thrift git commit: THRIFT-4459: reduce php library directory depth Client: php

http://git-wip-us.apache.org/repos/asf/thrift/blob/bfba370b/lib/php/lib/Transport/TCurlClient.php
----------------------------------------------------------------------
diff --git a/lib/php/lib/Transport/TCurlClient.php b/lib/php/lib/Transport/TCurlClient.php
new file mode 100644
index 0000000..c761cd0
--- /dev/null
+++ b/lib/php/lib/Transport/TCurlClient.php
@@ -0,0 +1,249 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Exception\TTransportException;
+use Thrift\Factory\TStringFuncFactory;
+
+/**
+ * HTTP client for Thrift
+ *
+ * @package thrift.transport
+ */
+class TCurlClient extends TTransport
+{
+  private static $curlHandle;
+
+  /**
+   * The host to connect to
+   *
+   * @var string
+   */
+  protected $host_;
+
+  /**
+   * The port to connect on
+   *
+   * @var int
+   */
+  protected $port_;
+
+  /**
+   * The URI to request
+   *
+   * @var string
+   */
+  protected $uri_;
+
+  /**
+   * The scheme to use for the request, i.e. http, https
+   *
+   * @var string
+   */
+  protected $scheme_;
+
+  /**
+   * Buffer for the HTTP request data
+   *
+   * @var string
+   */
+  protected $request_;
+
+  /**
+   * Buffer for the HTTP response data.
+   *
+   * @var binary string
+   */
+  protected $response_;
+
+  /**
+   * Read timeout
+   *
+   * @var float
+   */
+  protected $timeout_;
+
+  /**
+   * http headers
+   *
+   * @var array
+   */
+  protected $headers_;
+
+  /**
+   * Make a new HTTP client.
+   *
+   * @param string $host
+   * @param int    $port
+   * @param string $uri
+   */
+  public function __construct($host, $port=80, $uri='', $scheme = 'http')
+  {
+    if ((TStringFuncFactory::create()->strlen($uri) > 0) && ($uri{0} != '/')) {
+      $uri = '/'.$uri;
+    }
+    $this->scheme_ = $scheme;
+    $this->host_ = $host;
+    $this->port_ = $port;
+    $this->uri_ = $uri;
+    $this->request_ = '';
+    $this->response_ = null;
+    $this->timeout_ = null;
+    $this->headers_ = array();
+  }
+
+  /**
+   * Set read timeout
+   *
+   * @param float $timeout
+   */
+  public function setTimeoutSecs($timeout)
+  {
+    $this->timeout_ = $timeout;
+  }
+
+  /**
+   * Whether this transport is open.
+   *
+   * @return boolean true if open
+   */
+  public function isOpen()
+  {
+    return true;
+  }
+
+  /**
+   * Open the transport for reading/writing
+   *
+   * @throws TTransportException if cannot open
+   */
+  public function open()
+  {
+  }
+
+  /**
+   * Close the transport.
+   */
+  public function close()
+  {
+    $this->request_ = '';
+    $this->response_ = null;
+  }
+
+  /**
+   * 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 function read($len)
+  {
+    if ($len >= strlen($this->response_)) {
+      return $this->response_;
+    } else {
+      $ret = substr($this->response_, 0, $len);
+      $this->response_ = substr($this->response_, $len);
+
+      return $ret;
+    }
+  }
+
+  /**
+   * Writes some data into the pending buffer
+   *
+   * @param string $buf  The data to write
+   * @throws TTransportException if writing fails
+   */
+  public function write($buf)
+  {
+    $this->request_ .= $buf;
+  }
+
+  /**
+   * Opens and sends the actual request over the HTTP connection
+   *
+   * @throws TTransportException if a writing error occurs
+   */
+  public function flush()
+  {
+    if (!self::$curlHandle) {
+      register_shutdown_function(array('Thrift\\Transport\\TCurlClient', 'closeCurlHandle'));
+      self::$curlHandle = curl_init();
+      curl_setopt(self::$curlHandle, CURLOPT_RETURNTRANSFER, true);
+      curl_setopt(self::$curlHandle, CURLOPT_BINARYTRANSFER, true);
+      curl_setopt(self::$curlHandle, CURLOPT_USERAGENT, 'PHP/TCurlClient');
+      curl_setopt(self::$curlHandle, CURLOPT_CUSTOMREQUEST, 'POST');
+      curl_setopt(self::$curlHandle, CURLOPT_FOLLOWLOCATION, true);
+      curl_setopt(self::$curlHandle, CURLOPT_MAXREDIRS, 1);
+    }
+    // God, PHP really has some esoteric ways of doing simple things.
+    $host = $this->host_.($this->port_ != 80 ? ':'.$this->port_ : '');
+    $fullUrl = $this->scheme_."://".$host.$this->uri_;
+
+    $headers = array();
+    $defaultHeaders = array('Accept' => 'application/x-thrift',
+                     'Content-Type' => 'application/x-thrift',
+                     'Content-Length' => TStringFuncFactory::create()->strlen($this->request_));
+    foreach (array_merge($defaultHeaders, $this->headers_) as $key => $value) {
+      $headers[] = "$key: $value";
+    }
+
+    curl_setopt(self::$curlHandle, CURLOPT_HTTPHEADER, $headers);
+
+    if ($this->timeout_ > 0) {
+      curl_setopt(self::$curlHandle, CURLOPT_TIMEOUT, $this->timeout_);
+    }
+    curl_setopt(self::$curlHandle, CURLOPT_POSTFIELDS, $this->request_);
+    $this->request_ = '';
+
+    curl_setopt(self::$curlHandle, CURLOPT_URL, $fullUrl);
+    $this->response_ = curl_exec(self::$curlHandle);
+
+    // Connect failed?
+    if (!$this->response_) {
+      curl_close(self::$curlHandle);
+      self::$curlHandle = null;
+      $error = 'TCurlClient: Could not connect to '.$fullUrl;
+      throw new TTransportException($error, TTransportException::NOT_OPEN);
+    }
+  }
+
+  public static function closeCurlHandle()
+  {
+    try {
+      if (self::$curlHandle) {
+        curl_close(self::$curlHandle);
+        self::$curlHandle = null;
+      }
+    } catch (\Exception $x) {
+      error_log('There was an error closing the curl handle: ' . $x->getMessage());
+    }
+  }
+
+  public function addHeaders($headers)
+  {
+    $this->headers_ = array_merge($this->headers_, $headers);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/bfba370b/lib/php/lib/Transport/TFramedTransport.php
----------------------------------------------------------------------
diff --git a/lib/php/lib/Transport/TFramedTransport.php b/lib/php/lib/Transport/TFramedTransport.php
new file mode 100644
index 0000000..b8a64a9
--- /dev/null
+++ b/lib/php/lib/Transport/TFramedTransport.php
@@ -0,0 +1,193 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Factory\TStringFuncFactory;
+
+/**
+ * Framed transport. Writes and reads data in chunks that are stamped with
+ * their length.
+ *
+ * @package thrift.transport
+ */
+class TFramedTransport 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_;
+
+  /**
+   * Whether to frame reads
+   *
+   * @var bool
+   */
+  private $read_;
+
+  /**
+   * Whether to frame writes
+   *
+   * @var bool
+   */
+  private $write_;
+
+  /**
+   * Constructor.
+   *
+   * @param TTransport $transport Underlying transport
+   */
+  public function __construct($transport=null, $read=true, $write=true)
+  {
+    $this->transport_ = $transport;
+    $this->read_ = $read;
+    $this->write_ = $write;
+  }
+
+  public function isOpen()
+  {
+    return $this->transport_->isOpen();
+  }
+
+  public function open()
+  {
+    $this->transport_->open();
+  }
+
+  public function close()
+  {
+    $this->transport_->close();
+  }
+
+  /**
+   * 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)
+  {
+    if (!$this->read_) {
+      return $this->transport_->read($len);
+    }
+
+    if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) {
+      $this->readFrame();
+    }
+
+    // Just return full buff
+    if ($len >= TStringFuncFactory::create()->strlen($this->rBuf_)) {
+      $out = $this->rBuf_;
+      $this->rBuf_ = null;
+
+      return $out;
+    }
+
+    // Return TStringFuncFactory::create()->substr
+    $out = TStringFuncFactory::create()->substr($this->rBuf_, 0, $len);
+    $this->rBuf_ = TStringFuncFactory::create()->substr($this->rBuf_, $len);
+
+    return $out;
+  }
+
+  /**
+   * Put previously read data back into the buffer
+   *
+   * @param string $data data to return
+   */
+  public function putBack($data)
+  {
+    if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) {
+      $this->rBuf_ = $data;
+    } else {
+      $this->rBuf_ = ($data . $this->rBuf_);
+    }
+  }
+
+  /**
+   * Reads a chunk of data into the internal read buffer.
+   */
+  private function readFrame()
+  {
+    $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 (!$this->write_) {
+      return $this->transport_->write($buf, $len);
+    }
+
+    if ($len !== null && $len < TStringFuncFactory::create()->strlen($buf)) {
+      $buf = TStringFuncFactory::create()->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()
+  {
+    if (!$this->write_ || TStringFuncFactory::create()->strlen($this->wBuf_) == 0) {
+      return $this->transport_->flush();
+    }
+
+    $out = pack('N', TStringFuncFactory::create()->strlen($this->wBuf_));
+    $out .= $this->wBuf_;
+
+    // Note that we clear the internal wBuf_ prior to the underlying write
+    // to ensure we're in a sane state (i.e. internal buffer cleaned)
+    // if the underlying write throws up an exception
+    $this->wBuf_ = '';
+    $this->transport_->write($out);
+    $this->transport_->flush();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/bfba370b/lib/php/lib/Transport/THttpClient.php
----------------------------------------------------------------------
diff --git a/lib/php/lib/Transport/THttpClient.php b/lib/php/lib/Transport/THttpClient.php
new file mode 100644
index 0000000..b372ab7
--- /dev/null
+++ b/lib/php/lib/Transport/THttpClient.php
@@ -0,0 +1,229 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Exception\TTransportException;
+use Thrift\Factory\TStringFuncFactory;
+
+/**
+ * HTTP client for Thrift
+ *
+ * @package thrift.transport
+ */
+class THttpClient extends TTransport
+{
+  /**
+   * The host to connect to
+   *
+   * @var string
+   */
+  protected $host_;
+
+  /**
+   * The port to connect on
+   *
+   * @var int
+   */
+  protected $port_;
+
+  /**
+   * The URI to request
+   *
+   * @var string
+   */
+  protected $uri_;
+
+  /**
+   * The scheme to use for the request, i.e. http, https
+   *
+   * @var string
+   */
+  protected $scheme_;
+
+  /**
+   * Buffer for the HTTP request data
+   *
+   * @var string
+   */
+  protected $buf_;
+
+  /**
+   * Input socket stream.
+   *
+   * @var resource
+   */
+  protected $handle_;
+
+  /**
+   * Read timeout
+   *
+   * @var float
+   */
+  protected $timeout_;
+
+  /**
+   * http headers
+   *
+   * @var array
+   */
+  protected $headers_;
+
+  /**
+   * Make a new HTTP client.
+   *
+   * @param string $host
+   * @param int    $port
+   * @param string $uri
+   */
+  public function __construct($host, $port=80, $uri='', $scheme = 'http')
+  {
+    if ((TStringFuncFactory::create()->strlen($uri) > 0) && ($uri{0} != '/')) {
+      $uri = '/'.$uri;
+    }
+    $this->scheme_ = $scheme;
+    $this->host_ = $host;
+    $this->port_ = $port;
+    $this->uri_ = $uri;
+    $this->buf_ = '';
+    $this->handle_ = null;
+    $this->timeout_ = null;
+    $this->headers_ = array();
+  }
+
+  /**
+   * Set read timeout
+   *
+   * @param float $timeout
+   */
+  public function setTimeoutSecs($timeout)
+  {
+    $this->timeout_ = $timeout;
+  }
+
+  /**
+   * Whether this transport is open.
+   *
+   * @return boolean true if open
+   */
+  public function isOpen()
+  {
+    return true;
+  }
+
+  /**
+   * Open the transport for reading/writing
+   *
+   * @throws TTransportException if cannot open
+   */
+  public function open() {}
+
+  /**
+   * Close the transport.
+   */
+  public function close()
+  {
+    if ($this->handle_) {
+      @fclose($this->handle_);
+      $this->handle_ = null;
+    }
+  }
+
+  /**
+   * 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 function read($len)
+  {
+    $data = @fread($this->handle_, $len);
+    if ($data === FALSE || $data === '') {
+      $md = stream_get_meta_data($this->handle_);
+      if ($md['timed_out']) {
+        throw new TTransportException('THttpClient: timed out reading '.$len.' bytes from '.$this->host_.':'.$this->port_.$this->uri_, TTransportException::TIMED_OUT);
+      } else {
+        throw new TTransportException('THttpClient: Could not read '.$len.' bytes from '.$this->host_.':'.$this->port_.$this->uri_, TTransportException::UNKNOWN);
+      }
+    }
+
+    return $data;
+  }
+
+  /**
+   * Writes some data into the pending buffer
+   *
+   * @param string $buf  The data to write
+   * @throws TTransportException if writing fails
+   */
+  public function write($buf)
+  {
+    $this->buf_ .= $buf;
+  }
+
+  /**
+   * Opens and sends the actual request over the HTTP connection
+   *
+   * @throws TTransportException if a writing error occurs
+   */
+  public function flush()
+  {
+    // God, PHP really has some esoteric ways of doing simple things.
+    $host = $this->host_.($this->port_ != 80 ? ':'.$this->port_ : '');
+
+    $headers = array();
+    $defaultHeaders = array('Host' => $host,
+                            'Accept' => 'application/x-thrift',
+                            'User-Agent' => 'PHP/THttpClient',
+                            'Content-Type' => 'application/x-thrift',
+                            'Content-Length' => TStringFuncFactory::create()->strlen($this->buf_));
+    foreach (array_merge($defaultHeaders, $this->headers_) as $key => $value) {
+        $headers[] = "$key: $value";
+    }
+
+    $options = array('method' => 'POST',
+                     'header' => implode("\r\n", $headers),
+                     'max_redirects' => 1,
+                     'content' => $this->buf_);
+    if ($this->timeout_ > 0) {
+      $options['timeout'] = $this->timeout_;
+    }
+    $this->buf_ = '';
+
+    $contextid = stream_context_create(array('http' => $options));
+    $this->handle_ = @fopen($this->scheme_.'://'.$host.$this->uri_, 'r', false, $contextid);
+
+    // Connect failed?
+    if ($this->handle_ === FALSE) {
+      $this->handle_ = null;
+      $error = 'THttpClient: Could not connect to '.$host.$this->uri_;
+      throw new TTransportException($error, TTransportException::NOT_OPEN);
+    }
+  }
+
+  public function addHeaders($headers)
+  {
+    $this->headers_ = array_merge($this->headers_, $headers);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/bfba370b/lib/php/lib/Transport/TMemoryBuffer.php
----------------------------------------------------------------------
diff --git a/lib/php/lib/Transport/TMemoryBuffer.php b/lib/php/lib/Transport/TMemoryBuffer.php
new file mode 100644
index 0000000..ca31c57
--- /dev/null
+++ b/lib/php/lib/Transport/TMemoryBuffer.php
@@ -0,0 +1,100 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Exception\TTransportException;
+use Thrift\Factory\TStringFuncFactory;
+
+/**
+ * A memory buffer is a tranpsort that simply reads from and writes to an
+ * in-memory string buffer. Anytime you call write on it, the data is simply
+ * placed into a buffer, and anytime you call read, data is read from that
+ * buffer.
+ *
+ * @package thrift.transport
+ */
+class TMemoryBuffer extends TTransport
+{
+  /**
+   * Constructor. Optionally pass an initial value
+   * for the buffer.
+   */
+  public function __construct($buf = '')
+  {
+    $this->buf_ = $buf;
+  }
+
+  protected $buf_ = '';
+
+  public function isOpen()
+  {
+    return true;
+  }
+
+  public function open() {}
+
+  public function close() {}
+
+  public function write($buf)
+  {
+    $this->buf_ .= $buf;
+  }
+
+  public function read($len)
+  {
+    $bufLength = TStringFuncFactory::create()->strlen($this->buf_);
+
+    if ($bufLength === 0) {
+      throw new TTransportException('TMemoryBuffer: Could not read ' .
+                                    $len . ' bytes from buffer.',
+                                    TTransportException::UNKNOWN);
+    }
+
+    if ($bufLength <= $len) {
+      $ret = $this->buf_;
+      $this->buf_ = '';
+
+      return $ret;
+    }
+
+    $ret = TStringFuncFactory::create()->substr($this->buf_, 0, $len);
+    $this->buf_ = TStringFuncFactory::create()->substr($this->buf_, $len);
+
+    return $ret;
+  }
+
+  public function getBuffer()
+  {
+    return $this->buf_;
+  }
+
+  public function available()
+  {
+    return TStringFuncFactory::create()->strlen($this->buf_);
+  }
+
+  public function putBack($data)
+  {
+    $this->buf_ = $data.$this->buf_;
+  }
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/bfba370b/lib/php/lib/Transport/TNullTransport.php
----------------------------------------------------------------------
diff --git a/lib/php/lib/Transport/TNullTransport.php b/lib/php/lib/Transport/TNullTransport.php
new file mode 100644
index 0000000..feeb7a4
--- /dev/null
+++ b/lib/php/lib/Transport/TNullTransport.php
@@ -0,0 +1,51 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Exception\TTransportException;
+
+/**
+ * Transport that only accepts writes and ignores them.
+ * This is useful for measuring the serialized size of structures.
+ *
+ * @package thrift.transport
+ */
+class TNullTransport extends TTransport
+{
+  public function isOpen()
+  {
+    return true;
+  }
+
+  public function open() {}
+
+  public function close() {}
+
+  public function read($len)
+  {
+    throw new TTransportException("Can't read from TNullTransport.");
+  }
+
+  public function write($buf) {}
+
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/bfba370b/lib/php/lib/Transport/TPhpStream.php
----------------------------------------------------------------------
diff --git a/lib/php/lib/Transport/TPhpStream.php b/lib/php/lib/Transport/TPhpStream.php
new file mode 100644
index 0000000..4c14cdf
--- /dev/null
+++ b/lib/php/lib/Transport/TPhpStream.php
@@ -0,0 +1,123 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Exception\TException;
+use Thrift\Factory\TStringFuncFactory;
+
+/**
+ * Php stream transport. Reads to and writes from the php standard streams
+ * php://input and php://output
+ *
+ * @package thrift.transport
+ */
+class TPhpStream extends TTransport
+{
+  const MODE_R = 1;
+  const MODE_W = 2;
+
+  private $inStream_ = null;
+
+  private $outStream_ = null;
+
+  private $read_ = false;
+
+  private $write_ = false;
+
+  public function __construct($mode)
+  {
+    $this->read_ = $mode & self::MODE_R;
+    $this->write_ = $mode & self::MODE_W;
+  }
+
+  public function open()
+  {
+    if ($this->read_) {
+      $this->inStream_ = @fopen(self::inStreamName(), 'r');
+      if (!is_resource($this->inStream_)) {
+        throw new TException('TPhpStream: Could not open php://input');
+      }
+    }
+    if ($this->write_) {
+      $this->outStream_ = @fopen('php://output', 'w');
+      if (!is_resource($this->outStream_)) {
+        throw new TException('TPhpStream: Could not open php://output');
+      }
+    }
+  }
+
+  public function close()
+  {
+    if ($this->read_) {
+      @fclose($this->inStream_);
+      $this->inStream_ = null;
+    }
+    if ($this->write_) {
+      @fclose($this->outStream_);
+      $this->outStream_ = null;
+    }
+  }
+
+  public function isOpen()
+  {
+    return
+      (!$this->read_ || is_resource($this->inStream_)) &&
+      (!$this->write_ || is_resource($this->outStream_));
+  }
+
+  public function read($len)
+  {
+    $data = @fread($this->inStream_, $len);
+    if ($data === FALSE || $data === '') {
+      throw new TException('TPhpStream: Could not read '.$len.' bytes');
+    }
+
+    return $data;
+  }
+
+  public function write($buf)
+  {
+    while (TStringFuncFactory::create()->strlen($buf) > 0) {
+      $got = @fwrite($this->outStream_, $buf);
+      if ($got === 0 || $got === FALSE) {
+        throw new TException('TPhpStream: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes');
+      }
+      $buf = TStringFuncFactory::create()->substr($buf, $got);
+    }
+  }
+
+  public function flush()
+  {
+    @fflush($this->outStream_);
+  }
+
+  private static function inStreamName()
+  {
+    if (php_sapi_name() == 'cli') {
+      return 'php://stdin';
+    }
+
+    return 'php://input';
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/bfba370b/lib/php/lib/Transport/TSSLSocket.php
----------------------------------------------------------------------
diff --git a/lib/php/lib/Transport/TSSLSocket.php b/lib/php/lib/Transport/TSSLSocket.php
new file mode 100644
index 0000000..533b7bb
--- /dev/null
+++ b/lib/php/lib/Transport/TSSLSocket.php
@@ -0,0 +1,112 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Exception\TException;
+use Thrift\Exception\TTransportException;
+use Thrift\Factory\TStringFuncFactory;
+
+/**
+ * Sockets implementation of the TTransport interface.
+ *
+ * @package thrift.transport
+ */
+class TSSLSocket extends TSocket
+{
+  /**
+   * Remote port
+   *
+   * @var resource
+   */
+  protected $context_ = null;
+
+  /**
+   * Socket constructor
+   *
+   * @param string     $host         Remote hostname
+   * @param int        $port         Remote port
+   * @param resource   $context      Stream context
+   * @param bool       $persist      Whether to use a persistent socket
+   * @param string     $debugHandler Function to call for error logging
+   */
+  public function __construct($host='localhost',
+                              $port=9090,
+                              $context=null,
+                              $debugHandler=null) {
+    $this->host_ = $this->getSSLHost($host);
+    $this->port_ = $port;
+    $this->context_ = $context;
+    $this->debugHandler_ = $debugHandler ? $debugHandler : 'error_log';
+  }
+
+  /**
+   * Creates a host name with SSL transport protocol
+   * if no transport protocol already specified in
+   * the host name.
+   *
+   * @param string $host    Host to listen on
+   * @return string $host   Host name with transport protocol
+   */
+  private function getSSLHost($host)
+  {
+    $transport_protocol_loc = strpos($host, "://");
+    if ($transport_protocol_loc === false) {
+      $host = 'ssl://'.$host;
+    }
+    return $host;
+  }
+
+  /**
+   * Connects the socket.
+   */
+  public function open()
+  {
+    if ($this->isOpen()) {
+      throw new TTransportException('Socket already connected', TTransportException::ALREADY_OPEN);
+    }
+
+    if (empty($this->host_)) {
+      throw new TTransportException('Cannot open null host', TTransportException::NOT_OPEN);
+    }
+
+    if ($this->port_ <= 0) {
+      throw new TTransportException('Cannot open without port', TTransportException::NOT_OPEN);
+    }
+
+    $this->handle_ = @stream_socket_client($this->host_.':'.$this->port_,
+                                          $errno,
+                                          $errstr,
+                                          $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000),
+                                          STREAM_CLIENT_CONNECT,
+                                          $this->context_);
+
+    // Connect failed?
+    if ($this->handle_ === FALSE) {
+      $error = 'TSocket: Could not connect to '.$this->host_.':'.$this->port_.' ('.$errstr.' ['.$errno.'])';
+      if ($this->debug_) {
+        call_user_func($this->debugHandler_, $error);
+      }
+      throw new TException($error);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/bfba370b/lib/php/lib/Transport/TSocket.php
----------------------------------------------------------------------
diff --git a/lib/php/lib/Transport/TSocket.php b/lib/php/lib/Transport/TSocket.php
new file mode 100644
index 0000000..a1872b9
--- /dev/null
+++ b/lib/php/lib/Transport/TSocket.php
@@ -0,0 +1,340 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Exception\TException;
+use Thrift\Exception\TTransportException;
+use Thrift\Factory\TStringFuncFactory;
+
+/**
+ * Sockets implementation of the TTransport interface.
+ *
+ * @package thrift.transport
+ */
+class TSocket extends TTransport
+{
+  /**
+   * Handle to PHP socket
+   *
+   * @var resource
+   */
+  protected $handle_ = null;
+
+  /**
+   * Remote hostname
+   *
+   * @var string
+   */
+  protected $host_ = 'localhost';
+
+  /**
+   * Remote port
+   *
+   * @var int
+   */
+  protected $port_ = '9090';
+
+  /**
+   * Send timeout in seconds.
+   *
+   * Combined with sendTimeoutUsec this is used for send timeouts.
+   *
+   * @var int
+   */
+  protected $sendTimeoutSec_ = 0;
+
+  /**
+   * Send timeout in microseconds.
+   *
+   * Combined with sendTimeoutSec this is used for send timeouts.
+   *
+   * @var int
+   */
+  protected $sendTimeoutUsec_ = 100000;
+
+  /**
+   * Recv timeout in seconds
+   *
+   * Combined with recvTimeoutUsec this is used for recv timeouts.
+   *
+   * @var int
+   */
+  protected $recvTimeoutSec_ = 0;
+
+  /**
+   * Recv timeout in microseconds
+   *
+   * Combined with recvTimeoutSec this is used for recv timeouts.
+   *
+   * @var int
+   */
+  protected $recvTimeoutUsec_ = 750000;
+
+  /**
+   * Persistent socket or plain?
+   *
+   * @var bool
+   */
+  protected $persist_ = false;
+
+  /**
+   * Debugging on?
+   *
+   * @var bool
+   */
+  protected $debug_ = false;
+
+  /**
+   * Debug handler
+   *
+   * @var mixed
+   */
+  protected $debugHandler_ = null;
+
+  /**
+   * Socket constructor
+   *
+   * @param string $host         Remote hostname
+   * @param int    $port         Remote port
+   * @param bool   $persist      Whether to use a persistent socket
+   * @param string $debugHandler Function to call for error logging
+   */
+  public function __construct($host='localhost',
+                              $port=9090,
+                              $persist=false,
+                              $debugHandler=null) {
+    $this->host_ = $host;
+    $this->port_ = $port;
+    $this->persist_ = $persist;
+    $this->debugHandler_ = $debugHandler ? $debugHandler : 'error_log';
+  }
+
+  /**
+   * @param resource $handle
+   * @return void
+   */
+  public function setHandle($handle)
+  {
+    $this->handle_ = $handle;
+  }
+
+  /**
+   * Sets the send timeout.
+   *
+   * @param int $timeout  Timeout in milliseconds.
+   */
+  public function setSendTimeout($timeout)
+  {
+    $this->sendTimeoutSec_ = floor($timeout / 1000);
+    $this->sendTimeoutUsec_ =
+            ($timeout - ($this->sendTimeoutSec_ * 1000)) * 1000;
+  }
+
+  /**
+   * Sets the receive timeout.
+   *
+   * @param int $timeout  Timeout in milliseconds.
+   */
+  public function setRecvTimeout($timeout)
+  {
+    $this->recvTimeoutSec_ = floor($timeout / 1000);
+    $this->recvTimeoutUsec_ =
+            ($timeout - ($this->recvTimeoutSec_ * 1000)) * 1000;
+  }
+
+  /**
+   * Sets debugging output on or off
+   *
+   * @param bool $debug
+   */
+  public function setDebug($debug)
+  {
+    $this->debug_ = $debug;
+  }
+
+  /**
+   * Get the host that this socket is connected to
+   *
+   * @return string host
+   */
+  public function getHost()
+  {
+    return $this->host_;
+  }
+
+  /**
+   * Get the remote port that this socket is connected to
+   *
+   * @return int port
+   */
+  public function getPort()
+  {
+    return $this->port_;
+  }
+
+  /**
+   * 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->isOpen()) {
+      throw new TTransportException('Socket already connected', TTransportException::ALREADY_OPEN);
+    }
+
+    if (empty($this->host_)) {
+      throw new TTransportException('Cannot open null host', TTransportException::NOT_OPEN);
+    }
+
+    if ($this->port_ <= 0) {
+      throw new TTransportException('Cannot open without port', TTransportException::NOT_OPEN);
+    }
+
+    if ($this->persist_) {
+      $this->handle_ = @pfsockopen($this->host_,
+                                   $this->port_,
+                                   $errno,
+                                   $errstr,
+                                   $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000));
+    } else {
+      $this->handle_ = @fsockopen($this->host_,
+                                  $this->port_,
+                                  $errno,
+                                  $errstr,
+                                  $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000));
+    }
+
+    // Connect failed?
+    if ($this->handle_ === FALSE) {
+      $error = 'TSocket: Could not connect to '.$this->host_.':'.$this->port_.' ('.$errstr.' ['.$errno.'])';
+      if ($this->debug_) {
+        call_user_func($this->debugHandler_, $error);
+      }
+      throw new TException($error);
+    }
+
+    if (function_exists('socket_import_stream') && function_exists('socket_set_option')) {
+      $socket = socket_import_stream($this->handle_);
+      socket_set_option($socket, SOL_TCP, TCP_NODELAY, 1);
+    }
+  }
+
+  /**
+   * Closes the socket.
+   */
+  public function close()
+  {
+    @fclose($this->handle_);
+    $this->handle_ = null;
+  }
+
+  /**
+   * Read from the socket at most $len bytes.
+   *
+   * This method will not wait for all the requested data, it will return as
+   * soon as any data is received.
+   *
+   * @param int $len Maximum number of bytes to read.
+   * @return string Binary data
+   */
+  public function read($len)
+  {
+    $null = null;
+    $read = array($this->handle_);
+    $readable = @stream_select($read, $null, $null, $this->recvTimeoutSec_, $this->recvTimeoutUsec_);
+
+    if ($readable > 0) {
+      $data = fread($this->handle_, $len);
+      if ($data === false) {
+          throw new TTransportException('TSocket: Could not read '.$len.' bytes from '.
+                               $this->host_.':'.$this->port_);
+      } elseif ($data == '' && feof($this->handle_)) {
+          throw new TTransportException('TSocket read 0 bytes');
+        }
+
+      return $data;
+    } elseif ($readable === 0) {
+        throw new TTransportException('TSocket: timed out reading '.$len.' bytes from '.
+                             $this->host_.':'.$this->port_);
+      } else {
+        throw new TTransportException('TSocket: Could not read '.$len.' bytes from '.
+                             $this->host_.':'.$this->port_);
+      }
+    }
+
+  /**
+   * Write to the socket.
+   *
+   * @param string $buf The data to write
+   */
+  public function write($buf)
+  {
+    $null = null;
+    $write = array($this->handle_);
+
+    // keep writing until all the data has been written
+    while (TStringFuncFactory::create()->strlen($buf) > 0) {
+      // wait for stream to become available for writing
+      $writable = @stream_select($null, $write, $null, $this->sendTimeoutSec_, $this->sendTimeoutUsec_);
+      if ($writable > 0) {
+        // write buffer to stream
+        $written = fwrite($this->handle_, $buf);
+        if ($written === -1 || $written === false) {
+          throw new TTransportException('TSocket: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes '.
+                                   $this->host_.':'.$this->port_);
+        }
+        // determine how much of the buffer is left to write
+        $buf = TStringFuncFactory::create()->substr($buf, $written);
+      } elseif ($writable === 0) {
+          throw new TTransportException('TSocket: timed out writing '.TStringFuncFactory::create()->strlen($buf).' bytes from '.
+                               $this->host_.':'.$this->port_);
+        } else {
+            throw new TTransportException('TSocket: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes '.
+                                 $this->host_.':'.$this->port_);
+        }
+      }
+    }
+
+  /**
+   * Flush output to the socket.
+   *
+   * Since read(), readAll() and write() operate on the sockets directly,
+   * this is a no-op
+   *
+   * If you wish to have flushable buffering behaviour, wrap this TSocket
+   * in a TBufferedTransport.
+   */
+  public function flush()
+  {
+    // no-op
+    }
+  }

http://git-wip-us.apache.org/repos/asf/thrift/blob/bfba370b/lib/php/lib/Transport/TSocketPool.php
----------------------------------------------------------------------
diff --git a/lib/php/lib/Transport/TSocketPool.php b/lib/php/lib/Transport/TSocketPool.php
new file mode 100644
index 0000000..18ffd8d
--- /dev/null
+++ b/lib/php/lib/Transport/TSocketPool.php
@@ -0,0 +1,300 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Exception\TException;
+
+/**
+ * This library makes use of APC cache to make hosts as down in a web
+ * environment. If you are running from the CLI or on a system without APC
+ * installed, then these null functions will step in and act like cache
+ * misses.
+ */
+if (!function_exists('apc_fetch')) {
+  function apc_fetch($key) { return FALSE; }
+  function apc_store($key, $var, $ttl=0) { return FALSE; }
+}
+
+/**
+ * Sockets implementation of the TTransport interface that allows connection
+ * to a pool of servers.
+ *
+ * @package thrift.transport
+ */
+class TSocketPool extends TSocket
+{
+  /**
+   * Remote servers. Array of associative arrays with 'host' and 'port' keys
+   */
+  private $servers_ = array();
+
+  /**
+   * How many times to retry each host in connect
+   *
+   * @var int
+   */
+  private $numRetries_ = 1;
+
+  /**
+   * Retry interval in seconds, how long to not try a host if it has been
+   * marked as down.
+   *
+   * @var int
+   */
+  private $retryInterval_ = 60;
+
+  /**
+   * Max consecutive failures before marking a host down.
+   *
+   * @var int
+   */
+  private $maxConsecutiveFailures_ = 1;
+
+  /**
+   * Try hosts in order? or Randomized?
+   *
+   * @var bool
+   */
+  private $randomize_ = true;
+
+  /**
+   * Always try last host, even if marked down?
+   *
+   * @var bool
+   */
+  private $alwaysTryLast_ = true;
+
+  /**
+   * Socket pool constructor
+   *
+   * @param array  $hosts        List of remote hostnames
+   * @param mixed  $ports        Array of remote ports, or a single common port
+   * @param bool   $persist      Whether to use a persistent socket
+   * @param mixed  $debugHandler Function for error logging
+   */
+  public function __construct($hosts=array('localhost'),
+                              $ports=array(9090),
+                              $persist=false,
+                              $debugHandler=null) {
+    parent::__construct(null, 0, $persist, $debugHandler);
+
+    if (!is_array($ports)) {
+      $port = $ports;
+      $ports = array();
+      foreach ($hosts as $key => $val) {
+        $ports[$key] = $port;
+      }
+    }
+
+    foreach ($hosts as $key => $host) {
+      $this->servers_ []= array('host' => $host,
+                                'port' => $ports[$key]);
+    }
+  }
+
+  /**
+   * Add a server to the pool
+   *
+   * This function does not prevent you from adding a duplicate server entry.
+   *
+   * @param string $host hostname or IP
+   * @param int $port port
+   */
+  public function addServer($host, $port)
+  {
+    $this->servers_[] = array('host' => $host, 'port' => $port);
+  }
+
+  /**
+   * Sets how many time to keep retrying a host in the connect function.
+   *
+   * @param int $numRetries
+   */
+  public function setNumRetries($numRetries)
+  {
+    $this->numRetries_ = $numRetries;
+  }
+
+  /**
+   * Sets how long to wait until retrying a host if it was marked down
+   *
+   * @param int $numRetries
+   */
+  public function setRetryInterval($retryInterval)
+  {
+    $this->retryInterval_ = $retryInterval;
+  }
+
+  /**
+   * Sets how many time to keep retrying a host before marking it as down.
+   *
+   * @param int $numRetries
+   */
+  public function setMaxConsecutiveFailures($maxConsecutiveFailures)
+  {
+    $this->maxConsecutiveFailures_ = $maxConsecutiveFailures;
+  }
+
+  /**
+   * Turns randomization in connect order on or off.
+   *
+   * @param bool $randomize
+   */
+  public function setRandomize($randomize)
+  {
+    $this->randomize_ = $randomize;
+  }
+
+  /**
+   * Whether to always try the last server.
+   *
+   * @param bool $alwaysTryLast
+   */
+  public function setAlwaysTryLast($alwaysTryLast)
+  {
+    $this->alwaysTryLast_ = $alwaysTryLast;
+  }
+
+  /**
+   * Connects the socket by iterating through all the servers in the pool
+   * and trying to find one that works.
+   */
+  public function open()
+  {
+    // Check if we want order randomization
+    if ($this->randomize_) {
+      shuffle($this->servers_);
+    }
+
+    // Count servers to identify the "last" one
+    $numServers = count($this->servers_);
+
+    for ($i = 0; $i < $numServers; ++$i) {
+
+      // This extracts the $host and $port variables
+      extract($this->servers_[$i]);
+
+      // Check APC cache for a record of this server being down
+      $failtimeKey = 'thrift_failtime:'.$host.':'.$port.'~';
+
+      // Cache miss? Assume it's OK
+      $lastFailtime = apc_fetch($failtimeKey);
+      if ($lastFailtime === FALSE) {
+        $lastFailtime = 0;
+      }
+
+      $retryIntervalPassed = false;
+
+      // Cache hit...make sure enough the retry interval has elapsed
+      if ($lastFailtime > 0) {
+        $elapsed = time() - $lastFailtime;
+        if ($elapsed > $this->retryInterval_) {
+          $retryIntervalPassed = true;
+          if ($this->debug_) {
+            call_user_func($this->debugHandler_,
+                           'TSocketPool: retryInterval '.
+                           '('.$this->retryInterval_.') '.
+                           'has passed for host '.$host.':'.$port);
+          }
+        }
+      }
+
+      // Only connect if not in the middle of a fail interval, OR if this
+      // is the LAST server we are trying, just hammer away on it
+      $isLastServer = false;
+      if ($this->alwaysTryLast_) {
+        $isLastServer = ($i == ($numServers - 1));
+      }
+
+      if (($lastFailtime === 0) ||
+          ($isLastServer) ||
+          ($lastFailtime > 0 && $retryIntervalPassed)) {
+
+        // Set underlying TSocket params to this one
+        $this->host_ = $host;
+        $this->port_ = $port;
+
+        // Try up to numRetries_ connections per server
+        for ($attempt = 0; $attempt < $this->numRetries_; $attempt++) {
+          try {
+            // Use the underlying TSocket open function
+            parent::open();
+
+            // Only clear the failure counts if required to do so
+            if ($lastFailtime > 0) {
+              apc_store($failtimeKey, 0);
+            }
+
+            // Successful connection, return now
+            return;
+
+          } catch (TException $tx) {
+            // Connection failed
+          }
+        }
+
+        // Mark failure of this host in the cache
+        $consecfailsKey = 'thrift_consecfails:'.$host.':'.$port.'~';
+
+        // Ignore cache misses
+        $consecfails = apc_fetch($consecfailsKey);
+        if ($consecfails === FALSE) {
+          $consecfails = 0;
+        }
+
+        // Increment by one
+        $consecfails++;
+
+        // Log and cache this failure
+        if ($consecfails >= $this->maxConsecutiveFailures_) {
+          if ($this->debug_) {
+            call_user_func($this->debugHandler_,
+                           'TSocketPool: marking '.$host.':'.$port.
+                           ' as down for '.$this->retryInterval_.' secs '.
+                           'after '.$consecfails.' failed attempts.');
+          }
+          // Store the failure time
+          apc_store($failtimeKey, time());
+
+          // Clear the count of consecutive failures
+          apc_store($consecfailsKey, 0);
+        } else {
+          apc_store($consecfailsKey, $consecfails);
+        }
+      }
+    }
+
+    // Oh no; we failed them all. The system is totally ill!
+    $error = 'TSocketPool: All hosts in pool are down. ';
+    $hosts = array();
+    foreach ($this->servers_ as $server) {
+      $hosts []= $server['host'].':'.$server['port'];
+    }
+    $hostlist = implode(',', $hosts);
+    $error .= '('.$hostlist.')';
+    if ($this->debug_) {
+      call_user_func($this->debugHandler_, $error);
+    }
+    throw new TException($error);
+  }
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/bfba370b/lib/php/lib/Transport/TTransport.php
----------------------------------------------------------------------
diff --git a/lib/php/lib/Transport/TTransport.php b/lib/php/lib/Transport/TTransport.php
new file mode 100644
index 0000000..99c39ff
--- /dev/null
+++ b/lib/php/lib/Transport/TTransport.php
@@ -0,0 +1,95 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Factory\TStringFuncFactory;
+
+/**
+ * Base interface for a transport agent.
+ *
+ * @package thrift.transport
+ */
+abstract class TTransport
+{
+  /**
+   * Whether this transport is open.
+   *
+   * @return boolean true if open
+   */
+  abstract public function isOpen();
+
+  /**
+   * Open the transport for reading/writing
+   *
+   * @throws TTransportException if cannot open
+   */
+  abstract public function open();
+
+  /**
+   * Close the transport.
+   */
+  abstract public 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
+   */
+  abstract public 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 = TStringFuncFactory::create()->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
+   */
+  abstract public function write($buf);
+
+  /**
+   * Flushes any pending data out of a buffer
+   *
+   * @throws TTransportException if a writing error occurs
+   */
+  public function flush() {}
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/bfba370b/lib/php/lib/Type/TConstant.php
----------------------------------------------------------------------
diff --git a/lib/php/lib/Type/TConstant.php b/lib/php/lib/Type/TConstant.php
new file mode 100644
index 0000000..7c8eceb
--- /dev/null
+++ b/lib/php/lib/Type/TConstant.php
@@ -0,0 +1,50 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift
+ */
+
+namespace Thrift\Type;
+
+/**
+ * Base class for constant Management
+ */
+abstract class TConstant
+{
+    /**
+     * Don't instanciate this class
+     */
+    protected function __construct() {}
+
+    /**
+     * Get a constant value
+     * @param  string $constant
+     * @return mixed
+     */
+    public static function get($constant)
+    {
+        if (is_null(static::$$constant)) {
+            static::$$constant = call_user_func(
+                    sprintf('static::init_%s', $constant)
+                );
+        }
+
+        return static::$$constant;
+    }
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/bfba370b/lib/php/lib/Type/TMessageType.php
----------------------------------------------------------------------
diff --git a/lib/php/lib/Type/TMessageType.php b/lib/php/lib/Type/TMessageType.php
new file mode 100644
index 0000000..bff224f
--- /dev/null
+++ b/lib/php/lib/Type/TMessageType.php
@@ -0,0 +1,34 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift
+ */
+
+namespace Thrift\Type;
+
+/**
+ * Message types for RPC
+ */
+class TMessageType
+{
+  const CALL  = 1;
+  const REPLY = 2;
+  const EXCEPTION = 3;
+  const ONEWAY = 4;
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/bfba370b/lib/php/lib/Type/TType.php
----------------------------------------------------------------------
diff --git a/lib/php/lib/Type/TType.php b/lib/php/lib/Type/TType.php
new file mode 100644
index 0000000..71219c2
--- /dev/null
+++ b/lib/php/lib/Type/TType.php
@@ -0,0 +1,47 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift
+ */
+
+namespace Thrift\Type;
+
+/**
+ * Data types that can be sent via Thrift
+ */
+class TType
+{
+  const STOP   = 0;
+  const VOID   = 1;
+  const BOOL   = 2;
+  const BYTE   = 3;
+  const I08    = 3;
+  const DOUBLE = 4;
+  const I16    = 6;
+  const I32    = 8;
+  const I64    = 10;
+  const STRING = 11;
+  const UTF7   = 11;
+  const STRUCT = 12;
+  const MAP    = 13;
+  const SET    = 14;
+  const LST    = 15;    // N.B. cannot use LIST keyword in PHP!
+  const UTF8   = 16;
+  const UTF16  = 17;
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/bfba370b/lib/php/test/Fixtures.php
----------------------------------------------------------------------
diff --git a/lib/php/test/Fixtures.php b/lib/php/test/Fixtures.php
new file mode 100644
index 0000000..2c60a08
--- /dev/null
+++ b/lib/php/test/Fixtures.php
@@ -0,0 +1,194 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.test
+ */
+
+namespace Test\Thrift;
+
+use ThriftTest\Xtruct;
+use ThriftTest\Xtruct2;
+use ThriftTest\Numberz;
+use ThriftTest\Insanity;
+
+class Fixtures
+{
+  public static $testArgs = array();
+
+  public static function populateTestArgs()
+  {
+    self::$testArgs['testString1'] = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška, Беларуская, Беларуская (тарашкевіца), Български, Bamanankan, বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Нохчийн, Cebuano, ᏣᎳᎩ, Česky, Словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ, Чӑвашла, Cymraeg, Dansk, Zazaki, ދިވެހިބަސް, Ελληνικά, Emiliàn e rumagnòl, English, Esperanto, Español, Eesti, Euskara, فارسی, Suomi, Võro, Føroyskt, Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, Avañe'ẽ, ગુજરાતી, Gaelg, עברית, हिन्दी, Fiji Hindi, Hrvatski, Kreyòl ayisyen, Magyar, Հայերեն, Interlingua, Bahasa Indonesia, Ilokano, Ido, Íslenska, Italiano, 日本語, Lojban, Basa Jawa, ქართული, Kongo, Kalaallisut, ಕನ್ನಡ, �
 ��국어, Къарачай-Малкъар, Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, Latviešu, Basa Banyumasan, Malagasy, Македонски, മലയാളം, मराठी, Bahasa Melayu, مازِرونی, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪Norsk (nynorsk)‬, ‪Norsk (bokmål)‬, Nouormand, Diné bizaad, Occitan, Иронау, Papiamentu, Deitsch, Norfuk / Pitkern, Polski, پنجابی, پښتو, Português, Runa Simi, Rumantsch, Romani, Română, Русский, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple English, Slovenčina, Slovenščina, Српски / Srpski, Seeltersk, Svenska, Kiswahili, தமிழ், తెలుగు, Тоҷикӣ, ไทย, Türkmençe, Tagalog, Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük, Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá
 , Zeêuws, 中文, Bân-lâm-gú, 粵語";
+
+    self::$testArgs['testString2'] =
+      "quote: \\\" backslash:" .
+      " forwardslash-escaped: \\/ " .
+      " backspace: \b formfeed: \f newline: \n return: \r tab: " .
+      " now-all-of-them-together: \"\\\/\b\n\r\t" .
+      " now-a-bunch-of-junk: !@#\$%&()(&%$#{}{}<><><";
+
+    self::$testArgs['testString3'] =
+      "string that ends in double-backslash \\\\";
+
+    self::$testArgs['testUnicodeStringWithNonBMP'] =
+      "สวัสดี/𝒯";
+
+    self::$testArgs['testDouble'] = 3.1415926535898;
+
+	// TODO: add testBinary() call
+	
+    self::$testArgs['testByte'] = 0x01;
+
+    self::$testArgs['testI32'] = pow( 2, 30 );
+
+    if (PHP_INT_SIZE == 8) {
+      self::$testArgs['testI64'] = pow( 2, 60 );
+    } else {
+      self::$testArgs['testI64'] = "1152921504606847000";
+    }
+
+    self::$testArgs['testStruct'] =
+      new Xtruct(
+            array(
+                    'string_thing' => 'worked',
+                    'byte_thing' => 0x01,
+                    'i32_thing' => pow( 2, 30 ),
+                    'i64_thing' => self::$testArgs['testI64']
+                    )
+            );
+
+    self::$testArgs['testNestNested'] =
+      new Xtruct(
+            array(
+                    'string_thing' => 'worked',
+                    'byte_thing' => 0x01,
+                    'i32_thing' => pow( 2, 30 ),
+                    'i64_thing' => self::$testArgs['testI64']
+                    )
+            );
+
+    self::$testArgs['testNest'] =
+      new Xtruct2(
+            array(
+                'byte_thing' => 0x01,
+                'struct_thing' => self::$testArgs['testNestNested'],
+                'i32_thing' => pow( 2, 15 )
+                )
+            );
+
+    self::$testArgs['testMap'] =
+      array(
+            7 => 77,
+            8 => 88,
+            9 => 99
+            );
+
+    self::$testArgs['testStringMap'] =
+      array(
+            "a" => "123",
+            "a b" => "with spaces ",
+            "same" => "same",
+            "0" => "numeric key",
+            "longValue" => self::$testArgs['testString1'],
+            self::$testArgs['testString1'] => "long key"
+            );
+
+    self::$testArgs['testSet'] = array( 1 => true, 5 => true, 6 => true );
+
+    self::$testArgs['testList'] = array( 1, 2, 3 );
+
+    self::$testArgs['testEnum'] = Numberz::ONE;
+
+    self::$testArgs['testTypedef'] = 69;
+
+    self::$testArgs['testMapMapExpectedResult'] =
+      array(
+            4 => array(
+                       1 => 1,
+                       2 => 2,
+                       3 => 3,
+                       4 => 4,
+                       ),
+            -4 => array(
+                        -4 => -4,
+                        -3 => -3,
+                        -2 => -2,
+                        -1 => -1
+                        )
+            );
+
+    // testInsanity ... takes a few steps to set up!
+
+    $xtruct1 =
+      new Xtruct(
+            array(
+                'string_thing' => 'Goodbye4',
+                'byte_thing' => 4,
+                'i32_thing' => 4,
+                'i64_thing' => 4
+                )
+            );
+
+    $xtruct2 =
+      new Xtruct(
+            array(
+                'string_thing' => 'Hello2',
+                'byte_thing' =>2,
+                'i32_thing' => 2,
+                'i64_thing' => 2
+                )
+            );
+
+    $userMap =
+      array(
+            Numberz::FIVE => 5,
+            Numberz::EIGHT => 8
+            );
+
+    $insanity2 =
+      new Insanity(
+            array(
+                'userMap' => $userMap,
+                'xtructs' => array($xtruct1,$xtruct2)
+                )
+            );
+
+    $insanity3 = $insanity2;
+
+    $insanity6 =
+      new Insanity(
+            array(
+                'userMap' => null,
+                'xtructs' => null
+                )
+            );
+
+    self::$testArgs['testInsanityExpectedResult'] =
+      array(
+            "1" => array(
+                         Numberz::TWO => $insanity2,
+                         Numberz::THREE => $insanity3
+                    ),
+            "2" => array(
+                         Numberz::SIX => $insanity6
+                    )
+            );
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/bfba370b/lib/php/test/JsonSerialize/JsonSerializeTest.php
----------------------------------------------------------------------
diff --git a/lib/php/test/JsonSerialize/JsonSerializeTest.php b/lib/php/test/JsonSerialize/JsonSerializeTest.php
new file mode 100644
index 0000000..dfb34c3
--- /dev/null
+++ b/lib/php/test/JsonSerialize/JsonSerializeTest.php
@@ -0,0 +1,114 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Test\Thrift\JsonSerialize;
+
+use stdClass;
+use Thrift\ClassLoader\ThriftClassLoader;
+
+require_once __DIR__.'/../../../../vendor/autoload.php';
+
+$loader = new ThriftClassLoader();
+$loader->registerDefinition('ThriftTest', __DIR__ . '/../packages/phpjs');
+$loader->register();
+
+class JsonSerializeTest extends \PHPUnit_Framework_TestCase
+{
+  protected function setUp() {
+    if (version_compare(phpversion(), '5.4', '<')) {
+      $this->markTestSkipped('Requires PHP 5.4 or newer!');
+    }
+  }
+
+  public function testEmptyStruct()
+  {
+    $empty = new \ThriftTest\EmptyStruct(array('non_existing_key' => 'bar'));
+    $this->assertEquals(new stdClass(), json_decode(json_encode($empty)));
+  }
+
+  public function testStringsAndInts()
+  {
+    $input = array(
+      'string_thing' => 'foo',
+      'i64_thing' => 1234567890,
+    );
+    $xtruct = new \ThriftTest\Xtruct($input);
+
+    // Xtruct's 'i32_thing' and 'byte_thing' fields should not be present here!
+    $expected = new stdClass();
+    $expected->string_thing = $input['string_thing'];
+    $expected->i64_thing = $input['i64_thing'];
+    $this->assertEquals($expected, json_decode(json_encode($xtruct)));
+  }
+
+  public function testNestedStructs()
+  {
+    $xtruct2 = new \ThriftTest\Xtruct2(array(
+      'byte_thing' => 42,
+      'struct_thing' => new \ThriftTest\Xtruct(array(
+        'i32_thing' => 123456,
+      )),
+    ));
+
+    $expected = new stdClass();
+    $expected->byte_thing = $xtruct2->byte_thing;
+    $expected->struct_thing = new stdClass();
+    $expected->struct_thing->i32_thing = $xtruct2->struct_thing->i32_thing;
+    $this->assertEquals($expected, json_decode(json_encode($xtruct2)));
+  }
+
+  public function testInsanity()
+  {
+    $xinput = array('string_thing' => 'foo');
+    $xtruct = new \ThriftTest\Xtruct($xinput);
+    $insanity = new \ThriftTest\Insanity(array(
+      'xtructs' => array($xtruct, $xtruct, $xtruct)
+    ));
+    $expected = new stdClass();
+    $expected->xtructs = array((object) $xinput, (object) $xinput, (object) $xinput);
+    $this->assertEquals($expected, json_decode(json_encode($insanity)));
+  }
+
+  public function testNestedLists()
+  {
+    $bonk = new \ThriftTest\Bonk(array('message' => 'foo'));
+    $nested = new \ThriftTest\NestedListsBonk(array('bonk' => array(array(array($bonk)))));
+    $expected = new stdClass();
+    $expected->bonk = array(array(array((object) array('message' => 'foo'))));
+    $this->assertEquals($expected, json_decode(json_encode($nested)));
+  }
+
+  public function testMaps()
+  {
+    $intmap = new \ThriftTest\ThriftTest_testMap_args(['thing' => [0 => 'zero']]);
+    $emptymap = new \ThriftTest\ThriftTest_testMap_args([]);
+    $this->assertEquals('{"thing":{"0":"zero"}}', json_encode($intmap));
+    $this->assertEquals('{}', json_encode($emptymap));
+  }
+
+  public function testScalarTypes()
+  {
+    $b = new \ThriftTest\Bools(['im_true' => '1', 'im_false' => '0']);
+    $this->assertEquals('{"im_true":true,"im_false":false}', json_encode($b));
+    $s = new \ThriftTest\StructA(['s' => 42]);
+    $this->assertEquals('{"s":"42"}', json_encode($s));
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/bfba370b/lib/php/test/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/php/test/Makefile.am b/lib/php/test/Makefile.am
index c4dcde3..5658afa 100755
--- a/lib/php/test/Makefile.am
+++ b/lib/php/test/Makefile.am
@@ -35,16 +35,16 @@ deps: $(top_srcdir)/composer.json
 all-local: deps
 
 check-json-serializer: deps stubs
-	$(PHPUNIT) --log-junit=TEST-json-serializer.xml Test/Thrift/JsonSerialize/
+	$(PHPUNIT) --log-junit=TEST-json-serializer.xml JsonSerialize/
 
 check-validator: deps stubs
-	php Test/Thrift/TestValidators.php
-	php Test/Thrift/TestValidators.php -oop
+	php TestValidators.php
+	php TestValidators.php -oop
 
 check-protocol:	deps stubs
-	$(PHPUNIT) --log-junit=TEST-log-json-protocol.xml Test/Thrift/Protocol/TestTJSONProtocol.php
-	$(PHPUNIT) --log-junit=TEST-binary-serializer.xml Test/Thrift/Protocol/TestBinarySerializer.php
-	$(PHPUNIT) --log-junit=TEST-log-simple-json-protocol.xml Test/Thrift/Protocol/TestTSimpleJSONProtocol.php
+	$(PHPUNIT) --log-junit=TEST-log-json-protocol.xml Protocol/TestTJSONProtocol.php
+	$(PHPUNIT) --log-junit=TEST-binary-serializer.xml Protocol/TestBinarySerializer.php
+	$(PHPUNIT) --log-junit=TEST-log-simple-json-protocol.xml Protocol/TestTSimpleJSONProtocol.php
 
 check: deps stubs \
   check-protocol \
@@ -54,7 +54,3 @@ check: deps stubs \
 clean-local:
 	$(RM) -r ./packages
 	$(RM) TEST-*.xml
-
-EXTRA_DIST = \
-	Test \
-	TestValidators.thrift

http://git-wip-us.apache.org/repos/asf/thrift/blob/bfba370b/lib/php/test/Protocol/TestBinarySerializer.php
----------------------------------------------------------------------
diff --git a/lib/php/test/Protocol/TestBinarySerializer.php b/lib/php/test/Protocol/TestBinarySerializer.php
new file mode 100644
index 0000000..e08057b
--- /dev/null
+++ b/lib/php/test/Protocol/TestBinarySerializer.php
@@ -0,0 +1,62 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.test
+ */
+
+namespace Test\Thrift\Protocol;
+
+use Thrift\ClassLoader\ThriftClassLoader;
+use Thrift\Serializer\TBinarySerializer;
+
+require_once __DIR__.'/../../../../vendor/autoload.php';
+
+$loader = new ThriftClassLoader();
+$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);
+  }
+
+}