Thrift now a TLP - INFRA-3116

git-svn-id: https://svn.apache.org/repos/asf/thrift/branches/0.1.x@1028168 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/php/README b/lib/php/README
new file mode 100644
index 0000000..bb566f4
--- /dev/null
+++ b/lib/php/README
@@ -0,0 +1,63 @@
+Thrift PHP Software Library
+
+License
+=======
+
+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.
+
+Using Thrift with PHP
+=====================
+
+Thrift requires PHP 5. Thrift makes as few assumptions about your PHP
+environment as possible while trying to make some more advanced PHP
+features (i.e. APC cacheing using asbolute path URLs) as simple as possible.
+
+To use Thrift in your PHP codebase, take the following steps:
+
+#1) Copy all of thrift/lib/php/src into your PHP codebase
+#2) Set $GLOBALS['THRIFT_ROOT'] to the path you installed Thrift
+#3) include_once $GLOBALS['THRIFT_ROOT'].'/Thrift.php';
+
+Note that #3 must be done before including any other Thrift files.
+If you do not do #2, Thrift.php will set this global for you, but it will be
+done using dirname(__FILE__), which is less efficient than providing the static
+string yourself.
+
+When you generate a Thrift package using the compiler, it makes an assumption
+about where your generated code will live. If your file is "MyPackage.thrift",
+the generated files must be installed into:
+
+$GLOBALS['THRIFT_ROOT'].'/packages/MyPackage/';
+
+This allows the code generator to compile your code without any extra flags
+for the target directory names while still allowing your include paths to
+be absolute (if you have an absolute THRIFT_ROOT).
+
+Dependencies
+============
+
+PHP_INT_SIZE
+
+  This built-in signals whether your architecture is 32 or 64 bit and is
+  used by the TBinaryProtocol to properly use pack() and unpack() to
+  serialize data.
+
+apc_fetch(), apc_store()
+
+  APC cache is used by the TSocketPool class. If you do not have APC installed,
+  Thrift will fill in null stub function definitions.
diff --git a/lib/php/README.apache b/lib/php/README.apache
new file mode 100644
index 0000000..8c41833
--- /dev/null
+++ b/lib/php/README.apache
@@ -0,0 +1,62 @@
+Thrift PHP/Apache Integration
+
+License
+=======
+
+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.
+
+Building PHP Thrift Services with Apache
+========================================
+
+Thrift can be embedded in the Apache webserver with PHP installed. Sample
+code is provided below. Note that to make requests to this type of server
+you must use a THttpClient transport.
+
+Sample Code
+===========
+
+<?php
+
+/**
+ * Example of how to build a Thrift server in Apache/PHP
+ *
+ */
+
+$GLOBALS['THRIFT_ROOT'] = '/your/thrift/root';
+
+include_once $GLOBALS['THRIFT_ROOT'].'/Thrift.php';
+include_once $GLOBALS['THRIFT_ROOT'].'/packages/Service/Service.php';
+include_once $GLOBALS['THRIFT_ROOT'].'/transport/TPhpStream.php';
+include_once $GLOBALS['THRIFT_ROOT'].'/protocol/TBinaryProtocol.php';
+
+class ServiceHandler implements ServiceIf {
+  // Implement your interface and methods here
+}
+
+header('Content-Type: application/x-thrift');
+
+$handler = new ServiceHandler();
+$processor = new ServiceProcessor($handler);
+
+// Use the TPhpStream transport to read/write directly from HTTP
+$transport = new TPhpStream(TPhpStream::MODE_R | TPhpStream::MODE_W);
+$protocol = new TBinaryProtocol($transport);
+
+$transport->open();
+$processor->process($protocol, $protocol);
+$transport->close();
diff --git a/lib/php/src/Thrift.php b/lib/php/src/Thrift.php
new file mode 100644
index 0000000..ef6ab8a
--- /dev/null
+++ b/lib/php/src/Thrift.php
@@ -0,0 +1,787 @@
+<?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
+ */
+
+
+/**
+ * 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;
+}
+
+/**
+ * Message types for RPC
+ */
+class TMessageType {
+  const CALL  = 1;
+  const REPLY = 2;
+  const EXCEPTION = 3;
+  const ONEWAY = 4;
+}
+
+/**
+ * NOTE(mcslee): This currently contains a ton of duplicated code from TBase
+ * because we need to save CPU cycles and this is not yet in an extension.
+ * Ideally we'd multiply-inherit TException from both Exception and Base, but
+ * that's not possible in PHP and there are no modules either, so for now we
+ * apologetically take a trip to HackTown.
+ *
+ * Can be called with standard Exception constructor (message, code) or with
+ * Thrift Base object constructor (spec, vals).
+ *
+ * @param mixed $p1 Message (string) or type-spec (array)
+ * @param mixed $p2 Code (integer) or values (array)
+ */
+class TException extends Exception {
+  function __construct($p1=null, $p2=0) {
+    if (is_array($p1) && is_array($p2)) {
+      $spec = $p1;
+      $vals = $p2;
+      foreach ($spec as $fid => $fspec) {
+        $var = $fspec['var'];
+        if (isset($vals[$var])) {
+          $this->$var = $vals[$var];
+        }
+      }
+    } else {
+      parent::__construct($p1, $p2);
+    }
+  }
+
+  static $tmethod = array(TType::BOOL   => 'Bool',
+                          TType::BYTE   => 'Byte',
+                          TType::I16    => 'I16',
+                          TType::I32    => 'I32',
+                          TType::I64    => 'I64',
+                          TType::DOUBLE => 'Double',
+                          TType::STRING => 'String');
+
+  private function _readMap(&$var, $spec, $input) {
+    $xfer = 0;
+    $ktype = $spec['ktype'];
+    $vtype = $spec['vtype'];
+    $kread = $vread = null;
+    if (isset(TBase::$tmethod[$ktype])) {
+      $kread = 'read'.TBase::$tmethod[$ktype];
+    } else {
+      $kspec = $spec['key'];
+    }
+    if (isset(TBase::$tmethod[$vtype])) {
+      $vread = 'read'.TBase::$tmethod[$vtype];
+    } else {
+      $vspec = $spec['val'];
+    }
+    $var = array();
+    $_ktype = $_vtype = $size = 0;
+    $xfer += $input->readMapBegin($_ktype, $_vtype, $size);
+    for ($i = 0; $i < $size; ++$i) {
+      $key = $val = null;
+      if ($kread !== null) {
+        $xfer += $input->$kread($key);
+      } else {
+        switch ($ktype) {
+        case TType::STRUCT:
+          $class = $kspec['class'];
+          $key = new $class();
+          $xfer += $key->read($input);
+          break;
+        case TType::MAP:
+          $xfer += $this->_readMap($key, $kspec, $input);
+          break;
+        case TType::LST:
+          $xfer += $this->_readList($key, $kspec, $input, false);
+          break;
+        case TType::SET:
+          $xfer += $this->_readList($key, $kspec, $input, true);
+          break;
+        }
+      }
+      if ($vread !== null) {
+        $xfer += $input->$vread($val);
+      } else {
+        switch ($vtype) {
+        case TType::STRUCT:
+          $class = $vspec['class'];
+          $val = new $class();
+          $xfer += $val->read($input);
+          break;
+        case TType::MAP:
+          $xfer += $this->_readMap($val, $vspec, $input);
+          break;
+        case TType::LST:
+          $xfer += $this->_readList($val, $vspec, $input, false);
+          break;
+        case TType::SET:
+          $xfer += $this->_readList($val, $vspec, $input, true);
+          break;
+        }
+      }
+      $var[$key] = $val;
+    }
+    $xfer += $input->readMapEnd();
+    return $xfer;
+  }
+
+  private function _readList(&$var, $spec, $input, $set=false) {
+    $xfer = 0;
+    $etype = $spec['etype'];
+    $eread = $vread = null;
+    if (isset(TBase::$tmethod[$etype])) {
+      $eread = 'read'.TBase::$tmethod[$etype];
+    } else {
+      $espec = $spec['elem'];
+    }
+    $var = array();
+    $_etype = $size = 0;
+    if ($set) {
+      $xfer += $input->readSetBegin($_etype, $size);
+    } else {
+      $xfer += $input->readListBegin($_etype, $size);
+    }
+    for ($i = 0; $i < $size; ++$i) {
+      $elem = null;
+      if ($eread !== null) {
+        $xfer += $input->$eread($elem);
+      } else {
+        $espec = $spec['elem'];
+        switch ($etype) {
+        case TType::STRUCT:
+          $class = $espec['class'];
+          $elem = new $class();
+          $xfer += $elem->read($input);
+          break;
+        case TType::MAP:
+          $xfer += $this->_readMap($elem, $espec, $input);
+          break;
+        case TType::LST:
+          $xfer += $this->_readList($elem, $espec, $input, false);
+          break;
+        case TType::SET:
+          $xfer += $this->_readList($elem, $espec, $input, true);
+          break;
+        }
+      }
+      if ($set) {
+        $var[$elem] = true;
+      } else {
+        $var []= $elem;
+      }
+    }
+    if ($set) {
+      $xfer += $input->readSetEnd();
+    } else {
+      $xfer += $input->readListEnd();
+    }
+    return $xfer;
+  }
+
+  protected function _read($class, $spec, $input) {
+    $xfer = 0;
+    $fname = null;
+    $ftype = 0;
+    $fid = 0;
+    $xfer += $input->readStructBegin($fname);
+    while (true) {
+      $xfer += $input->readFieldBegin($fname, $ftype, $fid);
+      if ($ftype == TType::STOP) {
+        break;
+      }
+      if (isset($spec[$fid])) {
+        $fspec = $spec[$fid];
+        $var = $fspec['var'];
+        if ($ftype == $fspec['type']) {
+          $xfer = 0;
+          if (isset(TBase::$tmethod[$ftype])) {
+            $func = 'read'.TBase::$tmethod[$ftype];
+            $xfer += $input->$func($this->$var);
+          } else {
+            switch ($ftype) {
+            case TType::STRUCT:
+              $class = $fspec['class'];
+              $this->$var = new $class();
+              $xfer += $this->$var->read($input);
+              break;
+            case TType::MAP:
+              $xfer += $this->_readMap($this->$var, $fspec, $input);
+              break;
+            case TType::LST:
+              $xfer += $this->_readList($this->$var, $fspec, $input, false);
+              break;
+            case TType::SET:
+              $xfer += $this->_readList($this->$var, $fspec, $input, true);
+              break;
+            }
+          }
+        } else {
+          $xfer += $input->skip($ftype);
+        }
+      } else {
+        $xfer += $input->skip($ftype);
+      }
+      $xfer += $input->readFieldEnd();
+    }
+    $xfer += $input->readStructEnd();
+    return $xfer;
+  }
+
+  private function _writeMap($var, $spec, $output) {
+    $xfer = 0;
+    $ktype = $spec['ktype'];
+    $vtype = $spec['vtype'];
+    $kwrite = $vwrite = null;
+    if (isset(TBase::$tmethod[$ktype])) {
+      $kwrite = 'write'.TBase::$tmethod[$ktype];
+    } else {
+      $kspec = $spec['key'];
+    }
+    if (isset(TBase::$tmethod[$vtype])) {
+      $vwrite = 'write'.TBase::$tmethod[$vtype];
+    } else {
+      $vspec = $spec['val'];
+    }
+    $xfer += $output->writeMapBegin($ktype, $vtype, count($var));
+    foreach ($var as $key => $val) {
+      if (isset($kwrite)) {
+        $xfer += $output->$kwrite($key);
+      } else {
+        switch ($ktype) {
+        case TType::STRUCT:
+          $xfer += $key->write($output);
+          break;
+        case TType::MAP:
+          $xfer += $this->_writeMap($key, $kspec, $output);
+          break;
+        case TType::LST:
+          $xfer += $this->_writeList($key, $kspec, $output, false);
+          break;
+        case TType::SET:
+          $xfer += $this->_writeList($key, $kspec, $output, true);
+          break;
+        }
+      }
+      if (isset($vwrite)) {
+        $xfer += $output->$vwrite($val);
+      } else {
+        switch ($vtype) {
+        case TType::STRUCT:
+          $xfer += $val->write($output);
+          break;
+        case TType::MAP:
+          $xfer += $this->_writeMap($val, $vspec, $output);
+          break;
+        case TType::LST:
+          $xfer += $this->_writeList($val, $vspec, $output, false);
+          break;
+        case TType::SET:
+          $xfer += $this->_writeList($val, $vspec, $output, true);
+          break;
+        }
+      }
+    }
+    $xfer += $output->writeMapEnd();
+    return $xfer;
+  }
+
+  private function _writeList($var, $spec, $output, $set=false) {
+    $xfer = 0;
+    $etype = $spec['etype'];
+    $ewrite = null;
+    if (isset(TBase::$tmethod[$etype])) {
+      $ewrite = 'write'.TBase::$tmethod[$etype];
+    } else {
+      $espec = $spec['elem'];
+    }
+    if ($set) {
+      $xfer += $output->writeSetBegin($etype, count($var));
+    } else {
+      $xfer += $output->writeListBegin($etype, count($var));
+    }
+    foreach ($var as $key => $val) {
+      $elem = $set ? $key : $val;
+      if (isset($ewrite)) {
+        $xfer += $output->$ewrite($elem);
+      } else {
+        switch ($etype) {
+        case TType::STRUCT:
+          $xfer += $elem->write($output);
+          break;
+        case TType::MAP:
+          $xfer += $this->_writeMap($elem, $espec, $output);
+          break;
+        case TType::LST:
+          $xfer += $this->_writeList($elem, $espec, $output, false);
+          break;
+        case TType::SET:
+          $xfer += $this->_writeList($elem, $espec, $output, true);
+          break;
+        }
+      }
+    }
+    if ($set) {
+      $xfer += $output->writeSetEnd();
+    } else {
+      $xfer += $output->writeListEnd();
+    }
+    return $xfer;
+  }
+
+  protected function _write($class, $spec, $output) {
+    $xfer = 0;
+    $xfer += $output->writeStructBegin($class);
+    foreach ($spec as $fid => $fspec) {
+      $var = $fspec['var'];
+      if ($this->$var !== null) {
+        $ftype = $fspec['type'];
+        $xfer += $output->writeFieldBegin($var, $ftype, $fid);
+        if (isset(TBase::$tmethod[$ftype])) {
+          $func = 'write'.TBase::$tmethod[$ftype];
+          $xfer += $output->$func($this->$var);
+        } else {
+          switch ($ftype) {
+          case TType::STRUCT:
+            $xfer += $this->$var->write($output);
+            break;
+          case TType::MAP:
+            $xfer += $this->_writeMap($this->$var, $fspec, $output);
+            break;
+          case TType::LST:
+            $xfer += $this->_writeList($this->$var, $fspec, $output, false);
+            break;
+          case TType::SET:
+            $xfer += $this->_writeList($this->$var, $fspec, $output, true);
+            break;
+          }
+        }
+        $xfer += $output->writeFieldEnd();
+      }
+    }
+    $xfer += $output->writeFieldStop();
+    $xfer += $output->writeStructEnd();
+    return $xfer;
+  }
+
+}
+
+/**
+ * Base class from which other Thrift structs extend. This is so that we can
+ * cut back on the size of the generated code which is turning out to have a
+ * nontrivial cost just to load thanks to the wondrously abysmal implementation
+ * of PHP. Note that code is intentionally duplicated in here to avoid making
+ * function calls for every field or member of a container..
+ */
+abstract class TBase {
+
+  static $tmethod = array(TType::BOOL   => 'Bool',
+                          TType::BYTE   => 'Byte',
+                          TType::I16    => 'I16',
+                          TType::I32    => 'I32',
+                          TType::I64    => 'I64',
+                          TType::DOUBLE => 'Double',
+                          TType::STRING => 'String');
+
+  abstract function read($input);
+
+  abstract function write($output);
+
+  public function __construct($spec=null, $vals=null) {
+    if (is_array($spec) && is_array($vals)) {
+      foreach ($spec as $fid => $fspec) {
+        $var = $fspec['var'];
+        if (isset($vals[$var])) {
+          $this->$var = $vals[$var];
+        }
+      }
+    }
+  }
+
+  private function _readMap(&$var, $spec, $input) {
+    $xfer = 0;
+    $ktype = $spec['ktype'];
+    $vtype = $spec['vtype'];
+    $kread = $vread = null;
+    if (isset(TBase::$tmethod[$ktype])) {
+      $kread = 'read'.TBase::$tmethod[$ktype];
+    } else {
+      $kspec = $spec['key'];
+    }
+    if (isset(TBase::$tmethod[$vtype])) {
+      $vread = 'read'.TBase::$tmethod[$vtype];
+    } else {
+      $vspec = $spec['val'];
+    }
+    $var = array();
+    $_ktype = $_vtype = $size = 0;
+    $xfer += $input->readMapBegin($_ktype, $_vtype, $size);
+    for ($i = 0; $i < $size; ++$i) {
+      $key = $val = null;
+      if ($kread !== null) {
+        $xfer += $input->$kread($key);
+      } else {
+        switch ($ktype) {
+        case TType::STRUCT:
+          $class = $kspec['class'];
+          $key = new $class();
+          $xfer += $key->read($input);
+          break;
+        case TType::MAP:
+          $xfer += $this->_readMap($key, $kspec, $input);
+          break;
+        case TType::LST:
+          $xfer += $this->_readList($key, $kspec, $input, false);
+          break;
+        case TType::SET:
+          $xfer += $this->_readList($key, $kspec, $input, true);
+          break;
+        }
+      }
+      if ($vread !== null) {
+        $xfer += $input->$vread($val);
+      } else {
+        switch ($vtype) {
+        case TType::STRUCT:
+          $class = $vspec['class'];
+          $val = new $class();
+          $xfer += $val->read($input);
+          break;
+        case TType::MAP:
+          $xfer += $this->_readMap($val, $vspec, $input);
+          break;
+        case TType::LST:
+          $xfer += $this->_readList($val, $vspec, $input, false);
+          break;
+        case TType::SET:
+          $xfer += $this->_readList($val, $vspec, $input, true);
+          break;
+        }
+      }
+      $var[$key] = $val;
+    }
+    $xfer += $input->readMapEnd();
+    return $xfer;
+  }
+
+  private function _readList(&$var, $spec, $input, $set=false) {
+    $xfer = 0;
+    $etype = $spec['etype'];
+    $eread = $vread = null;
+    if (isset(TBase::$tmethod[$etype])) {
+      $eread = 'read'.TBase::$tmethod[$etype];
+    } else {
+      $espec = $spec['elem'];
+    }
+    $var = array();
+    $_etype = $size = 0;
+    if ($set) {
+      $xfer += $input->readSetBegin($_etype, $size);
+    } else {
+      $xfer += $input->readListBegin($_etype, $size);
+    }
+    for ($i = 0; $i < $size; ++$i) {
+      $elem = null;
+      if ($eread !== null) {
+        $xfer += $input->$eread($elem);
+      } else {
+        $espec = $spec['elem'];
+        switch ($etype) {
+        case TType::STRUCT:
+          $class = $espec['class'];
+          $elem = new $class();
+          $xfer += $elem->read($input);
+          break;
+        case TType::MAP:
+          $xfer += $this->_readMap($elem, $espec, $input);
+          break;
+        case TType::LST:
+          $xfer += $this->_readList($elem, $espec, $input, false);
+          break;
+        case TType::SET:
+          $xfer += $this->_readList($elem, $espec, $input, true);
+          break;
+        }
+      }
+      if ($set) {
+        $var[$elem] = true;
+      } else {
+        $var []= $elem;
+      }
+    }
+    if ($set) {
+      $xfer += $input->readSetEnd();
+    } else {
+      $xfer += $input->readListEnd();
+    }
+    return $xfer;
+  }
+
+  protected function _read($class, $spec, $input) {
+    $xfer = 0;
+    $fname = null;
+    $ftype = 0;
+    $fid = 0;
+    $xfer += $input->readStructBegin($fname);
+    while (true) {
+      $xfer += $input->readFieldBegin($fname, $ftype, $fid);
+      if ($ftype == TType::STOP) {
+        break;
+      }
+      if (isset($spec[$fid])) {
+        $fspec = $spec[$fid];
+        $var = $fspec['var'];
+        if ($ftype == $fspec['type']) {
+          $xfer = 0;
+          if (isset(TBase::$tmethod[$ftype])) {
+            $func = 'read'.TBase::$tmethod[$ftype];
+            $xfer += $input->$func($this->$var);
+          } else {
+            switch ($ftype) {
+            case TType::STRUCT:
+              $class = $fspec['class'];
+              $this->$var = new $class();
+              $xfer += $this->$var->read($input);
+              break;
+            case TType::MAP:
+              $xfer += $this->_readMap($this->$var, $fspec, $input);
+              break;
+            case TType::LST:
+              $xfer += $this->_readList($this->$var, $fspec, $input, false);
+              break;
+            case TType::SET:
+              $xfer += $this->_readList($this->$var, $fspec, $input, true);
+              break;
+            }
+          }
+        } else {
+          $xfer += $input->skip($ftype);
+        }
+      } else {
+        $xfer += $input->skip($ftype);
+      }
+      $xfer += $input->readFieldEnd();
+    }
+    $xfer += $input->readStructEnd();
+    return $xfer;
+  }
+
+  private function _writeMap($var, $spec, $output) {
+    $xfer = 0;
+    $ktype = $spec['ktype'];
+    $vtype = $spec['vtype'];
+    $kwrite = $vwrite = null;
+    if (isset(TBase::$tmethod[$ktype])) {
+      $kwrite = 'write'.TBase::$tmethod[$ktype];
+    } else {
+      $kspec = $spec['key'];
+    }
+    if (isset(TBase::$tmethod[$vtype])) {
+      $vwrite = 'write'.TBase::$tmethod[$vtype];
+    } else {
+      $vspec = $spec['val'];
+    }
+    $xfer += $output->writeMapBegin($ktype, $vtype, count($var));
+    foreach ($var as $key => $val) {
+      if (isset($kwrite)) {
+        $xfer += $output->$kwrite($key);
+      } else {
+        switch ($ktype) {
+        case TType::STRUCT:
+          $xfer += $key->write($output);
+          break;
+        case TType::MAP:
+          $xfer += $this->_writeMap($key, $kspec, $output);
+          break;
+        case TType::LST:
+          $xfer += $this->_writeList($key, $kspec, $output, false);
+          break;
+        case TType::SET:
+          $xfer += $this->_writeList($key, $kspec, $output, true);
+          break;
+        }
+      }
+      if (isset($vwrite)) {
+        $xfer += $output->$vwrite($val);
+      } else {
+        switch ($vtype) {
+        case TType::STRUCT:
+          $xfer += $val->write($output);
+          break;
+        case TType::MAP:
+          $xfer += $this->_writeMap($val, $vspec, $output);
+          break;
+        case TType::LST:
+          $xfer += $this->_writeList($val, $vspec, $output, false);
+          break;
+        case TType::SET:
+          $xfer += $this->_writeList($val, $vspec, $output, true);
+          break;
+        }
+      }
+    }
+    $xfer += $output->writeMapEnd();
+    return $xfer;
+  }
+
+  private function _writeList($var, $spec, $output, $set=false) {
+    $xfer = 0;
+    $etype = $spec['etype'];
+    $ewrite = null;
+    if (isset(TBase::$tmethod[$etype])) {
+      $ewrite = 'write'.TBase::$tmethod[$etype];
+    } else {
+      $espec = $spec['elem'];
+    }
+    if ($set) {
+      $xfer += $output->writeSetBegin($etype, count($var));
+    } else {
+      $xfer += $output->writeListBegin($etype, count($var));
+    }
+    foreach ($var as $key => $val) {
+      $elem = $set ? $key : $val;
+      if (isset($ewrite)) {
+        $xfer += $output->$ewrite($elem);
+      } else {
+        switch ($etype) {
+        case TType::STRUCT:
+          $xfer += $elem->write($output);
+          break;
+        case TType::MAP:
+          $xfer += $this->_writeMap($elem, $espec, $output);
+          break;
+        case TType::LST:
+          $xfer += $this->_writeList($elem, $espec, $output, false);
+          break;
+        case TType::SET:
+          $xfer += $this->_writeList($elem, $espec, $output, true);
+          break;
+        }
+      }
+    }
+    if ($set) {
+      $xfer += $output->writeSetEnd();
+    } else {
+      $xfer += $output->writeListEnd();
+    }
+    return $xfer;
+  }
+
+  protected function _write($class, $spec, $output) {
+    $xfer = 0;
+    $xfer += $output->writeStructBegin($class);
+    foreach ($spec as $fid => $fspec) {
+      $var = $fspec['var'];
+      if ($this->$var !== null) {
+        $ftype = $fspec['type'];
+        $xfer += $output->writeFieldBegin($var, $ftype, $fid);
+        if (isset(TBase::$tmethod[$ftype])) {
+          $func = 'write'.TBase::$tmethod[$ftype];
+          $xfer += $output->$func($this->$var);
+        } else {
+          switch ($ftype) {
+          case TType::STRUCT:
+            $xfer += $this->$var->write($output);
+            break;
+          case TType::MAP:
+            $xfer += $this->_writeMap($this->$var, $fspec, $output);
+            break;
+          case TType::LST:
+            $xfer += $this->_writeList($this->$var, $fspec, $output, false);
+            break;
+          case TType::SET:
+            $xfer += $this->_writeList($this->$var, $fspec, $output, true);
+            break;
+          }
+        }
+        $xfer += $output->writeFieldEnd();
+      }
+    }
+    $xfer += $output->writeFieldStop();
+    $xfer += $output->writeStructEnd();
+    return $xfer;
+  }
+}
+
+class TApplicationException extends TException {
+  static $_TSPEC =
+    array(1 => array('var' => 'message',
+                     'type' => TType::STRING),
+          2 => array('var' => 'code',
+                     'type' => TType::I32));
+
+  const UNKNOWN = 0;
+  const UNKNOWN_METHOD = 1;
+  const INVALID_MESSAGE_TYPE = 2;
+  const WRONG_METHOD_NAME = 3;
+  const BAD_SEQUENCE_ID = 4;
+  const MISSING_RESULT = 5;
+
+  function __construct($message=null, $code=0) {
+    parent::__construct($message, $code);
+  }
+
+  public function read($output) {
+    return $this->_read('TApplicationException', self::$_TSPEC, $output);
+  }
+
+  public function write($output) {
+    $xfer = 0;
+    $xfer += $output->writeStructBegin('TApplicationException');
+    if ($message = $this->getMessage()) {
+      $xfer += $output->writeFieldBegin('message', TType::STRING, 1);
+      $xfer += $output->writeString($message);
+      $xfer += $output->writeFieldEnd();
+    }
+    if ($code = $this->getCode()) {
+      $xfer += $output->writeFieldBegin('type', TType::I32, 2);
+      $xfer += $output->writeI32($code);
+      $xfer += $output->writeFieldEnd();
+    }
+    $xfer += $output->writeFieldStop();
+    $xfer += $output->writeStructEnd();
+    return $xfer;
+  }
+}
+
+/**
+ * Set global THRIFT ROOT automatically via inclusion here
+ */
+if (!isset($GLOBALS['THRIFT_ROOT'])) {
+  $GLOBALS['THRIFT_ROOT'] = dirname(__FILE__);
+}
+include_once $GLOBALS['THRIFT_ROOT'].'/protocol/TProtocol.php';
+include_once $GLOBALS['THRIFT_ROOT'].'/transport/TTransport.php';
+
+?>
diff --git a/lib/php/src/autoload.php b/lib/php/src/autoload.php
new file mode 100644
index 0000000..3a35545
--- /dev/null
+++ b/lib/php/src/autoload.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
+ */
+
+
+/**
+ * Include this file if you wish to use autoload with your PHP generated Thrift
+ * code. The generated code will *not* include any defined Thrift classes by
+ * default, except for the service interfaces. The generated code will populate
+ * values into $GLOBALS['THRIFT_AUTOLOAD'] which can be used by the autoload
+ * method below. If you have your own autoload system already in place, rename your
+ * __autoload function to something else and then do:
+ * $GLOBALS['AUTOLOAD_HOOKS'][] = 'my_autoload_func';
+ *
+ * Generate this code using the --gen php:autoload Thrift generator flag.
+ */
+
+$GLOBALS['THRIFT_AUTOLOAD'] = array();
+$GLOBALS['AUTOLOAD_HOOKS'] = array();
+
+if (!function_exists('__autoload')) {
+  function __autoload($class) {
+    global $THRIFT_AUTOLOAD;
+    $classl = strtolower($class);
+    if (isset($THRIFT_AUTOLOAD[$classl])) {
+      include_once $GLOBALS['THRIFT_ROOT'].'/packages/'.$THRIFT_AUTOLOAD[$classl];
+    } else if (!empty($GLOBALS['AUTOLOAD_HOOKS'])) {
+      foreach ($GLOBALS['AUTOLOAD_HOOKS'] as $hook) {
+        $hook($class);
+      }
+    }
+  }
+}
diff --git a/lib/php/src/ext/thrift_protocol/config.m4 b/lib/php/src/ext/thrift_protocol/config.m4
new file mode 100644
index 0000000..8cfb37d
--- /dev/null
+++ b/lib/php/src/ext/thrift_protocol/config.m4
@@ -0,0 +1,13 @@
+dnl Copyright (C) 2009 Facebook
+dnl Copying and distribution of this file, with or without modification,
+dnl are permitted in any medium without royalty provided the copyright
+dnl notice and this notice are preserved.
+
+PHP_ARG_ENABLE(thrift_protocol, whether to enable the thrift_protocol extension,
+[  --enable-thrift_protocol	Enable the fbthrift_protocol extension])
+
+if test "$PHP_THRIFT_PROTOCOL" != "no"; then
+  PHP_REQUIRE_CXX()
+  PHP_NEW_EXTENSION(thrift_protocol, php_thrift_protocol.cpp, $ext_shared)
+fi
+
diff --git a/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp b/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp
new file mode 100644
index 0000000..399cbe6
--- /dev/null
+++ b/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp
@@ -0,0 +1,999 @@
+/*
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <endian.h>
+#include <byteswap.h>
+#include <stdexcept>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htonll(x) bswap_64(x)
+#define ntohll(x) bswap_64(x)
+#else
+#define htonll(x) x
+#define ntohll(x) x
+#endif
+
+enum TType {
+  T_STOP       = 0,
+  T_VOID       = 1,
+  T_BOOL       = 2,
+  T_BYTE       = 3,
+  T_I08        = 3,
+  T_I16        = 6,
+  T_I32        = 8,
+  T_U64        = 9,
+  T_I64        = 10,
+  T_DOUBLE     = 4,
+  T_STRING     = 11,
+  T_UTF7       = 11,
+  T_STRUCT     = 12,
+  T_MAP        = 13,
+  T_SET        = 14,
+  T_LIST       = 15,
+  T_UTF8       = 16,
+  T_UTF16      = 17
+};
+
+const int32_t VERSION_MASK = 0xffff0000;
+const int32_t VERSION_1 = 0x80010000;
+const int8_t T_CALL = 1;
+const int8_t T_REPLY = 2;
+const int8_t T_EXCEPTION = 3;
+// tprotocolexception
+const int INVALID_DATA = 1;
+const int BAD_VERSION = 4;
+
+#include "php.h"
+#include "zend_interfaces.h"
+#include "zend_exceptions.h"
+#include "php_thrift_protocol.h"
+
+static function_entry thrift_protocol_functions[] = {
+  PHP_FE(thrift_protocol_write_binary, NULL)
+  PHP_FE(thrift_protocol_read_binary, NULL)
+  {NULL, NULL, NULL}
+} ;
+
+zend_module_entry thrift_protocol_module_entry = {
+  STANDARD_MODULE_HEADER,
+  "thrift_protocol",
+  thrift_protocol_functions,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  "1.0",
+  STANDARD_MODULE_PROPERTIES
+};
+
+#ifdef COMPILE_DL_THRIFT_PROTOCOL
+ZEND_GET_MODULE(thrift_protocol)
+#endif
+
+class PHPExceptionWrapper : public std::exception {
+public:
+  PHPExceptionWrapper(zval* _ex) throw() : ex(_ex) {
+    snprintf(_what, 40, "PHP exception zval=%p", ex);
+  }
+  const char* what() const throw() { return _what; }
+  ~PHPExceptionWrapper() throw() {}
+  operator zval*() const throw() { return const_cast<zval*>(ex); } // Zend API doesn't do 'const'...
+protected:
+  zval* ex;
+  char _what[40];
+} ;
+
+class PHPTransport {
+public:
+  zval* protocol() { return p; }
+  zval* transport() { return t; }
+protected:
+  PHPTransport() {}
+
+  void construct_with_zval(zval* _p, size_t _buffer_size) {
+    buffer = reinterpret_cast<char*>(emalloc(_buffer_size));
+    buffer_ptr = buffer;
+    buffer_used = 0;
+    buffer_size = _buffer_size;
+    p = _p;
+
+    // Get the transport for the passed protocol
+    zval gettransport;
+    ZVAL_STRING(&gettransport, "getTransport", 0);
+    MAKE_STD_ZVAL(t);
+    ZVAL_NULL(t);
+    TSRMLS_FETCH();
+    call_user_function(EG(function_table), &p, &gettransport, t, 0, NULL TSRMLS_CC);
+  }
+  ~PHPTransport() {
+    efree(buffer);
+    zval_ptr_dtor(&t);
+  }
+
+  char* buffer;
+  char* buffer_ptr;
+  size_t buffer_used;
+  size_t buffer_size;
+
+  zval* p;
+  zval* t;
+};
+
+
+class PHPOutputTransport : public PHPTransport {
+public:
+  PHPOutputTransport(zval* _p, size_t _buffer_size = 8192) {
+    construct_with_zval(_p, _buffer_size);
+  }
+
+  ~PHPOutputTransport() {
+    flush();
+    directFlush();
+  }
+
+  void write(const char* data, size_t len) {
+    if ((len + buffer_used) > buffer_size) {
+      flush();
+    }
+    if (len > buffer_size) {
+      directWrite(data, len);
+    } else {
+      memcpy(buffer_ptr, data, len);
+      buffer_used += len;
+      buffer_ptr += len;
+    }
+  }
+
+  void writeI64(int64_t i) {
+    i = htonll(i);
+    write((const char*)&i, 8);
+  }
+
+  void writeU32(uint32_t i) {
+    i = htonl(i);
+    write((const char*)&i, 4);
+  }
+
+  void writeI32(int32_t i) {
+    i = htonl(i);
+    write((const char*)&i, 4);
+  }
+
+  void writeI16(int16_t i) {
+    i = htons(i);
+    write((const char*)&i, 2);
+  }
+
+  void writeI8(int8_t i) {
+    write((const char*)&i, 1);
+  }
+
+  void writeString(const char* str, size_t len) {
+    writeU32(len);
+    write(str, len);
+  }
+
+  void flush() {
+    if (buffer_used) {
+      directWrite(buffer, buffer_used);
+      buffer_ptr = buffer;
+      buffer_used = 0;
+    }
+  }
+
+protected:
+  void directFlush() {
+    zval ret;
+    ZVAL_NULL(&ret);
+    zval flushfn;
+    ZVAL_STRING(&flushfn, "flush", 0);
+    TSRMLS_FETCH();
+    call_user_function(EG(function_table), &t, &flushfn, &ret, 0, NULL TSRMLS_CC);
+    zval_dtor(&ret);
+  }
+  void directWrite(const char* data, size_t len) {
+    zval writefn;
+    ZVAL_STRING(&writefn, "write", 0);
+    char* newbuf = (char*)emalloc(buffer_used + 1);
+    memcpy(newbuf, buffer, buffer_used);
+    newbuf[buffer_used] = '\0';
+    zval *args[1];
+    MAKE_STD_ZVAL(args[0]);
+    ZVAL_STRINGL(args[0], newbuf, buffer_used, 0);
+    TSRMLS_FETCH();
+    zval ret;
+    ZVAL_NULL(&ret);
+    call_user_function(EG(function_table), &t, &writefn, &ret, 1, args TSRMLS_CC);
+    zval_ptr_dtor(args);
+    zval_dtor(&ret);
+    if (EG(exception)) {
+      zval* ex = EG(exception);
+      EG(exception) = NULL;
+      throw PHPExceptionWrapper(ex);
+    }
+  }
+};
+
+class PHPInputTransport : public PHPTransport {
+public:
+  PHPInputTransport(zval* _p, size_t _buffer_size = 8192) {
+    construct_with_zval(_p, _buffer_size);
+  }
+
+  ~PHPInputTransport() {
+    put_back();
+  }
+
+  void put_back() {
+    if (buffer_used) {
+      zval putbackfn;
+      ZVAL_STRING(&putbackfn, "putBack", 0);
+
+      char* newbuf = (char*)emalloc(buffer_used + 1);
+      memcpy(newbuf, buffer_ptr, buffer_used);
+      newbuf[buffer_used] = '\0';
+
+      zval *args[1];
+      MAKE_STD_ZVAL(args[0]);
+      ZVAL_STRINGL(args[0], newbuf, buffer_used, 0);
+
+      TSRMLS_FETCH();
+
+      zval ret;
+      ZVAL_NULL(&ret);
+      call_user_function(EG(function_table), &t, &putbackfn, &ret, 1, args TSRMLS_CC);
+      zval_ptr_dtor(args);
+      zval_dtor(&ret);
+    }
+    buffer_used = 0;
+    buffer_ptr = buffer;
+  }
+
+  void skip(size_t len) {
+    while (len) {
+      size_t chunk_size = MIN(len, buffer_used);
+      if (chunk_size) {
+        buffer_ptr = reinterpret_cast<char*>(buffer_ptr) + chunk_size;
+        buffer_used -= chunk_size;
+        len -= chunk_size;
+      }
+      if (! len) break;
+      refill();
+    }
+  }
+
+  void readBytes(void* buf, size_t len) {
+    while (len) {
+      size_t chunk_size = MIN(len, buffer_used);
+      if (chunk_size) {
+        memcpy(buf, buffer_ptr, chunk_size);
+        buffer_ptr = reinterpret_cast<char*>(buffer_ptr) + chunk_size;
+        buffer_used -= chunk_size;
+        buf = reinterpret_cast<char*>(buf) + chunk_size;
+        len -= chunk_size;
+      }
+      if (! len) break;
+      refill();
+    }
+  }
+
+  int8_t readI8() {
+    int8_t c;
+    readBytes(&c, 1);
+    return c;
+  }
+
+  int16_t readI16() {
+    int16_t c;
+    readBytes(&c, 2);
+    return (int16_t)ntohs(c);
+  }
+
+  uint32_t readU32() {
+    uint32_t c;
+    readBytes(&c, 4);
+    return (uint32_t)ntohl(c);
+  }
+
+  int32_t readI32() {
+    int32_t c;
+    readBytes(&c, 4);
+    return (int32_t)ntohl(c);
+  }
+
+protected:
+  void refill() {
+    assert(buffer_used == 0);
+    zval retval;
+    ZVAL_NULL(&retval);
+
+    zval *args[1];
+    MAKE_STD_ZVAL(args[0]);
+    ZVAL_LONG(args[0], buffer_size);
+
+    TSRMLS_FETCH();
+
+    zval funcname;
+    ZVAL_STRING(&funcname, "read", 0);
+
+    call_user_function(EG(function_table), &t, &funcname, &retval, 1, args TSRMLS_CC);
+    zval_ptr_dtor(args);
+
+    if (EG(exception)) {
+      zval_dtor(&retval);
+      zval* ex = EG(exception);
+      EG(exception) = NULL;
+      throw PHPExceptionWrapper(ex);
+    }
+
+    buffer_used = Z_STRLEN(retval);
+    memcpy(buffer, Z_STRVAL(retval), buffer_used);
+    zval_dtor(&retval);
+
+    buffer_ptr = buffer;
+  }
+
+};
+
+void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec);
+void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec);
+void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval** value, HashTable* fieldspec);
+void skip_element(long thrift_typeID, PHPInputTransport& transport);
+
+// Create a PHP object given a typename and call the ctor, optionally passing up to 2 arguments
+void createObject(char* obj_typename, zval* return_value, int nargs = 0, zval* arg1 = NULL, zval* arg2 = NULL) {
+  TSRMLS_FETCH();
+  size_t obj_typename_len = strlen(obj_typename);
+  zend_class_entry* ce = zend_fetch_class(obj_typename, obj_typename_len, ZEND_FETCH_CLASS_DEFAULT TSRMLS_CC);
+  if (! ce) {
+    php_error_docref(NULL TSRMLS_CC, E_ERROR, "Class %s does not exist", obj_typename);
+    RETURN_NULL();
+  }
+
+  object_and_properties_init(return_value, ce, NULL);
+  zend_function* constructor = zend_std_get_constructor(return_value TSRMLS_CC);
+  zval* ctor_rv = NULL;
+  zend_call_method(&return_value, ce, &constructor, NULL, 0, &ctor_rv, nargs, arg1, arg2 TSRMLS_CC);
+  zval_ptr_dtor(&ctor_rv);
+}
+
+void throw_tprotocolexception(char* what, long errorcode) {
+  TSRMLS_FETCH();
+
+  zval *zwhat, *zerrorcode;
+  MAKE_STD_ZVAL(zwhat);
+  MAKE_STD_ZVAL(zerrorcode);
+
+  ZVAL_STRING(zwhat, what, 1);
+  ZVAL_LONG(zerrorcode, errorcode);
+
+  zval* ex;
+  MAKE_STD_ZVAL(ex);
+  createObject("TProtocolException", ex, 2, zwhat, zerrorcode);
+  zval_ptr_dtor(&zwhat);
+  zval_ptr_dtor(&zerrorcode);
+  throw PHPExceptionWrapper(ex);
+}
+
+void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval* return_value, HashTable* fieldspec) {
+  zval** val_ptr;
+  Z_TYPE_P(return_value) = IS_NULL; // just in case
+
+  switch (thrift_typeID) {
+    case T_STOP:
+    case T_VOID:
+      RETURN_NULL();
+      return;
+    case T_STRUCT: {
+      if (zend_hash_find(fieldspec, "class", 6, (void**)&val_ptr) != SUCCESS) {
+        throw_tprotocolexception("no class type in spec", INVALID_DATA);
+        skip_element(T_STRUCT, transport);
+        RETURN_NULL();
+      }
+      char* structType = Z_STRVAL_PP(val_ptr);
+      createObject(structType, return_value);
+      if (Z_TYPE_P(return_value) == IS_NULL) {
+        // unable to create class entry
+        skip_element(T_STRUCT, transport);
+        RETURN_NULL();
+      }
+      TSRMLS_FETCH();
+      zval* spec = zend_read_static_property(zend_get_class_entry(return_value TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC);
+      if (Z_TYPE_P(spec) != IS_ARRAY) {
+        char errbuf[128];
+        snprintf(errbuf, 128, "spec for %s is wrong type: %d\n", structType, Z_TYPE_P(spec));
+        throw_tprotocolexception(errbuf, INVALID_DATA);
+        RETURN_NULL();
+      }
+      binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec));
+      return;
+    } break;
+    case T_BOOL: {
+      uint8_t c;
+      transport.readBytes(&c, 1);
+      RETURN_BOOL(c != 0);
+    }
+  //case T_I08: // same numeric value as T_BYTE
+    case T_BYTE: {
+      uint8_t c;
+      transport.readBytes(&c, 1);
+      RETURN_LONG(c);
+    }
+    case T_I16: {
+      uint16_t c;
+      transport.readBytes(&c, 2);
+      RETURN_LONG(ntohs(c));
+    }
+    case T_I32: {
+      uint32_t c;
+      transport.readBytes(&c, 4);
+      RETURN_LONG(ntohl(c));
+    }
+    case T_U64:
+    case T_I64: {
+      uint64_t c;
+      transport.readBytes(&c, 8);
+      RETURN_LONG(ntohll(c));
+    }
+    case T_DOUBLE: {
+      union {
+        uint64_t c;
+        double d;
+      } a;
+      transport.readBytes(&(a.c), 8);
+      a.c = ntohll(a.c);
+      RETURN_DOUBLE(a.d);
+    }
+    //case T_UTF7: // aliases T_STRING
+    case T_UTF8:
+    case T_UTF16:
+    case T_STRING: {
+      uint32_t size = transport.readU32();
+      if (size) {
+        char* strbuf = (char*) emalloc(size + 1);
+        transport.readBytes(strbuf, size);
+        strbuf[size] = '\0';
+        ZVAL_STRINGL(return_value, strbuf, size, 0);
+      } else {
+        ZVAL_EMPTY_STRING(return_value);
+      }
+      return;
+    }
+    case T_MAP: { // array of key -> value
+      uint8_t types[2];
+      transport.readBytes(types, 2);
+      uint32_t size = transport.readU32();
+      array_init(return_value);
+
+      zend_hash_find(fieldspec, "key", 4, (void**)&val_ptr);
+      HashTable* keyspec = Z_ARRVAL_PP(val_ptr);
+      zend_hash_find(fieldspec, "val", 4, (void**)&val_ptr);
+      HashTable* valspec = Z_ARRVAL_PP(val_ptr);
+
+      for (uint32_t s = 0; s < size; ++s) {
+        zval *value;
+        MAKE_STD_ZVAL(value);
+
+        zval* key;
+        MAKE_STD_ZVAL(key);
+
+        binary_deserialize(types[0], transport, key, keyspec);
+        binary_deserialize(types[1], transport, value, valspec);
+        if (Z_TYPE_P(key) == IS_LONG) {
+          zend_hash_index_update(return_value->value.ht, Z_LVAL_P(key), &value, sizeof(zval *), NULL);
+        }
+        else {
+          if (Z_TYPE_P(key) != IS_STRING) convert_to_string(key);
+          zend_hash_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL);
+        }
+        zval_ptr_dtor(&key);
+      }
+      return; // return_value already populated
+    }
+    case T_LIST: { // array with autogenerated numeric keys
+      int8_t type = transport.readI8();
+      uint32_t size = transport.readU32();
+      zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr);
+      HashTable* elemspec = Z_ARRVAL_PP(val_ptr);
+
+      array_init(return_value);
+      for (uint32_t s = 0; s < size; ++s) {
+        zval *value;
+        MAKE_STD_ZVAL(value);
+        binary_deserialize(type, transport, value, elemspec);
+        zend_hash_next_index_insert(return_value->value.ht, &value, sizeof(zval *), NULL);
+      }
+      return;
+    }
+    case T_SET: { // array of key -> TRUE
+      uint8_t type;
+      uint32_t size;
+      transport.readBytes(&type, 1);
+      transport.readBytes(&size, 4);
+      size = ntohl(size);
+      zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr);
+      HashTable* elemspec = Z_ARRVAL_PP(val_ptr);
+
+      array_init(return_value);
+
+      for (uint32_t s = 0; s < size; ++s) {
+        zval* key;
+        zval* value;
+        MAKE_STD_ZVAL(key);
+        MAKE_STD_ZVAL(value);
+        ZVAL_TRUE(value);
+
+        binary_deserialize(type, transport, key, elemspec);
+
+        if (Z_TYPE_P(key) == IS_LONG) {
+          zend_hash_index_update(return_value->value.ht, Z_LVAL_P(key), &value, sizeof(zval *), NULL);
+        }
+        else {
+          if (Z_TYPE_P(key) != IS_STRING) convert_to_string(key);
+          zend_hash_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL);
+        }
+        zval_ptr_dtor(&key);
+      }
+      return;
+    }
+  };
+
+  char errbuf[128];
+  sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID);
+  throw_tprotocolexception(errbuf, INVALID_DATA);
+}
+
+void skip_element(long thrift_typeID, PHPInputTransport& transport) {
+  switch (thrift_typeID) {
+    case T_STOP:
+    case T_VOID:
+      return;
+    case T_STRUCT:
+      while (true) {
+        int8_t ttype = transport.readI8(); // get field type
+        if (ttype == T_STOP) break;
+        transport.skip(2); // skip field number, I16
+        skip_element(ttype, transport); // skip field payload
+      }
+      return;
+    case T_BOOL:
+    case T_BYTE:
+      transport.skip(1);
+      return;
+    case T_I16:
+      transport.skip(2);
+      return;
+    case T_I32:
+      transport.skip(4);
+      return;
+    case T_U64:
+    case T_I64:
+    case T_DOUBLE:
+      transport.skip(8);
+      return;
+    //case T_UTF7: // aliases T_STRING
+    case T_UTF8:
+    case T_UTF16:
+    case T_STRING: {
+      uint32_t len = transport.readU32();
+      transport.skip(len);
+      } return;
+    case T_MAP: {
+      int8_t keytype = transport.readI8();
+      int8_t valtype = transport.readI8();
+      uint32_t size = transport.readU32();
+      for (uint32_t i = 0; i < size; ++i) {
+        skip_element(keytype, transport);
+        skip_element(valtype, transport);
+      }
+    } return;
+    case T_LIST:
+    case T_SET: {
+      int8_t valtype = transport.readI8();
+      uint32_t size = transport.readU32();
+      for (uint32_t i = 0; i < size; ++i) {
+        skip_element(valtype, transport);
+      }
+    } return;
+  };
+
+  char errbuf[128];
+  sprintf(errbuf, "Unknown thrift typeID %ld", thrift_typeID);
+  throw_tprotocolexception(errbuf, INVALID_DATA);
+}
+
+void binary_serialize_hashtable_key(int8_t keytype, PHPOutputTransport& transport, HashTable* ht, HashPosition& ht_pos) {
+  bool keytype_is_numeric = (!((keytype == T_STRING) || (keytype == T_UTF8) || (keytype == T_UTF16)));
+
+  char* key;
+  uint key_len;
+  long index = 0;
+
+  zval* z;
+  MAKE_STD_ZVAL(z);
+
+  int res = zend_hash_get_current_key_ex(ht, &key, &key_len, (ulong*)&index, 0, &ht_pos);
+  if (keytype_is_numeric) {
+    if (res == HASH_KEY_IS_STRING) {
+      index = strtol(key, NULL, 10);
+    }
+    ZVAL_LONG(z, index);
+  } else {
+    char buf[64];
+    if (res == HASH_KEY_IS_STRING) {
+      key_len -= 1; // skip the null terminator
+    } else {
+      sprintf(buf, "%ld", index);
+      key = buf; key_len = strlen(buf);
+    }
+    ZVAL_STRINGL(z, key, key_len, 1);
+  }
+  binary_serialize(keytype, transport, &z, NULL);
+  zval_ptr_dtor(&z);
+}
+
+inline bool ttype_is_int(int8_t t) {
+  return ((t == T_BYTE) || ((t >= T_I16)  && (t <= T_I64)));
+}
+
+inline bool ttypes_are_compatible(int8_t t1, int8_t t2) {
+  // Integer types of different widths are considered compatible;
+  // otherwise the typeID must match.
+  return ((t1 == t2) || (ttype_is_int(t1) && ttype_is_int(t2)));
+}
+
+void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec) {
+  // SET and LIST have 'elem' => array('type', [optional] 'class')
+  // MAP has 'val' => array('type', [optiona] 'class')
+  TSRMLS_FETCH();
+  zend_class_entry* ce = zend_get_class_entry(zthis TSRMLS_CC);
+  while (true) {
+    zval** val_ptr = NULL;
+
+    int8_t ttype = transport.readI8();
+    if (ttype == T_STOP) return;
+    int16_t fieldno = transport.readI16();
+    if (zend_hash_index_find(spec, fieldno, (void**)&val_ptr) == SUCCESS) {
+      HashTable* fieldspec = Z_ARRVAL_PP(val_ptr);
+      // pull the field name
+      // zend hash tables use the null at the end in the length... so strlen(hash key) + 1.
+      zend_hash_find(fieldspec, "var", 4, (void**)&val_ptr);
+      char* varname = Z_STRVAL_PP(val_ptr);
+
+      // and the type
+      zend_hash_find(fieldspec, "type", 5, (void**)&val_ptr);
+      if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr);
+      int8_t expected_ttype = Z_LVAL_PP(val_ptr);
+
+      if (ttypes_are_compatible(ttype, expected_ttype)) {
+        zval* rv = NULL;
+        MAKE_STD_ZVAL(rv);
+        binary_deserialize(ttype, transport, rv, fieldspec);
+        zend_update_property(ce, zthis, varname, strlen(varname), rv TSRMLS_CC);
+        zval_ptr_dtor(&rv);
+      } else {
+        skip_element(ttype, transport);
+      }
+    } else {
+      skip_element(ttype, transport);
+    }
+  }
+}
+
+void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval** value, HashTable* fieldspec) {
+  // At this point the typeID (and field num, if applicable) should've already been written to the output so all we need to do is write the payload.
+  switch (thrift_typeID) {
+    case T_STOP:
+    case T_VOID:
+      return;
+    case T_STRUCT: {
+      TSRMLS_FETCH();
+      if (Z_TYPE_PP(value) != IS_OBJECT) {
+        throw_tprotocolexception("Attempt to send non-object type as a T_STRUCT", INVALID_DATA);
+      }
+      zval* spec = zend_read_static_property(zend_get_class_entry(*value TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC);
+      binary_serialize_spec(*value, transport, Z_ARRVAL_P(spec));
+    } return;
+    case T_BOOL:
+      if (Z_TYPE_PP(value) != IS_BOOL) convert_to_boolean(*value);
+      transport.writeI8(Z_BVAL_PP(value) ? 1 : 0);
+      return;
+    case T_BYTE:
+      if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value);
+      transport.writeI8(Z_LVAL_PP(value));
+      return;
+    case T_I16:
+      if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value);
+      transport.writeI16(Z_LVAL_PP(value));
+      return;
+    case T_I32:
+      if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value);
+      transport.writeI32(Z_LVAL_PP(value));
+      return;
+    case T_I64:
+    case T_U64:
+      if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value);
+      transport.writeI64(Z_LVAL_PP(value));
+      return;
+    case T_DOUBLE: {
+      union {
+        int64_t c;
+        double d;
+      } a;
+      if (Z_TYPE_PP(value) != IS_DOUBLE) convert_to_double(*value);
+      a.d = Z_DVAL_PP(value);
+      transport.writeI64(a.c);
+    } return;
+    //case T_UTF7:
+    case T_UTF8:
+    case T_UTF16:
+    case T_STRING:
+      if (Z_TYPE_PP(value) != IS_STRING) convert_to_string(*value);
+      transport.writeString(Z_STRVAL_PP(value), Z_STRLEN_PP(value));
+      return;
+    case T_MAP: {
+      if (Z_TYPE_PP(value) != IS_ARRAY) convert_to_array(*value);
+      if (Z_TYPE_PP(value) != IS_ARRAY) {
+        throw_tprotocolexception("Attempt to send an incompatible type as an array (T_MAP)", INVALID_DATA);
+      }
+      HashTable* ht = Z_ARRVAL_PP(value);
+      zval** val_ptr;
+
+      zend_hash_find(fieldspec, "ktype", 6, (void**)&val_ptr);
+      if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr);
+      uint8_t keytype = Z_LVAL_PP(val_ptr);
+      transport.writeI8(keytype);
+      zend_hash_find(fieldspec, "vtype", 6, (void**)&val_ptr);
+      if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr);
+      uint8_t valtype = Z_LVAL_PP(val_ptr);
+      transport.writeI8(valtype);
+
+      zend_hash_find(fieldspec, "val", 4, (void**)&val_ptr);
+      HashTable* valspec = Z_ARRVAL_PP(val_ptr);
+
+      transport.writeI32(zend_hash_num_elements(ht));
+      HashPosition key_ptr;
+      for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); zend_hash_get_current_data_ex(ht, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(ht, &key_ptr)) {
+        binary_serialize_hashtable_key(keytype, transport, ht, key_ptr);
+        binary_serialize(valtype, transport, val_ptr, valspec);
+      }
+    } return;
+    case T_LIST: {
+      if (Z_TYPE_PP(value) != IS_ARRAY) convert_to_array(*value);
+      if (Z_TYPE_PP(value) != IS_ARRAY) {
+        throw_tprotocolexception("Attempt to send an incompatible type as an array (T_LIST)", INVALID_DATA);
+      }
+      HashTable* ht = Z_ARRVAL_PP(value);
+      zval** val_ptr;
+
+      zend_hash_find(fieldspec, "etype", 6, (void**)&val_ptr);
+      if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr);
+      uint8_t valtype = Z_LVAL_PP(val_ptr);
+      transport.writeI8(valtype);
+
+      zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr);
+      HashTable* valspec = Z_ARRVAL_PP(val_ptr);
+
+      transport.writeI32(zend_hash_num_elements(ht));
+      HashPosition key_ptr;
+      for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); zend_hash_get_current_data_ex(ht, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(ht, &key_ptr)) {
+        binary_serialize(valtype, transport, val_ptr, valspec);
+      }
+    } return;
+    case T_SET: {
+      if (Z_TYPE_PP(value) != IS_ARRAY) convert_to_array(*value);
+      if (Z_TYPE_PP(value) != IS_ARRAY) {
+        throw_tprotocolexception("Attempt to send an incompatible type as an array (T_SET)", INVALID_DATA);
+      }
+      HashTable* ht = Z_ARRVAL_PP(value);
+      zval** val_ptr;
+
+      zend_hash_find(fieldspec, "etype", 6, (void**)&val_ptr);
+      if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr);
+      uint8_t keytype = Z_LVAL_PP(val_ptr);
+      transport.writeI8(keytype);
+
+      transport.writeI32(zend_hash_num_elements(ht));
+      HashPosition key_ptr;
+      for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); zend_hash_get_current_data_ex(ht, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(ht, &key_ptr)) {
+        binary_serialize_hashtable_key(keytype, transport, ht, key_ptr);
+      }
+    } return;
+  };
+  char errbuf[128];
+  sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID);
+  throw_tprotocolexception(errbuf, INVALID_DATA);
+}
+
+
+void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec) {
+  HashPosition key_ptr;
+  zval** val_ptr;
+
+  TSRMLS_FETCH();
+  zend_class_entry* ce = zend_get_class_entry(zthis TSRMLS_CC);
+
+  for (zend_hash_internal_pointer_reset_ex(spec, &key_ptr); zend_hash_get_current_data_ex(spec, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(spec, &key_ptr)) {
+    ulong fieldno;
+    if (zend_hash_get_current_key_ex(spec, NULL, NULL, &fieldno, 0, &key_ptr) != HASH_KEY_IS_LONG) {
+      throw_tprotocolexception("Bad keytype in TSPEC (expected 'long')", INVALID_DATA);
+      return;
+    }
+    HashTable* fieldspec = Z_ARRVAL_PP(val_ptr);
+
+    // field name
+    zend_hash_find(fieldspec, "var", 4, (void**)&val_ptr);
+    char* varname = Z_STRVAL_PP(val_ptr);
+
+    // thrift type
+    zend_hash_find(fieldspec, "type", 5, (void**)&val_ptr);
+    if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr);
+    int8_t ttype = Z_LVAL_PP(val_ptr);
+
+    zval* prop = zend_read_property(ce, zthis, varname, strlen(varname), false TSRMLS_CC);
+    if (Z_TYPE_P(prop) != IS_NULL) {
+      transport.writeI8(ttype);
+      transport.writeI16(fieldno);
+      binary_serialize(ttype, transport, &prop, fieldspec);
+    }
+  }
+  transport.writeI8(T_STOP); // struct end
+}
+
+// 6 params: $transport $method_name $ttype $request_struct $seqID $strict_write
+PHP_FUNCTION(thrift_protocol_write_binary) {
+  int argc = ZEND_NUM_ARGS();
+  if (argc < 6) {
+    WRONG_PARAM_COUNT;
+  }
+
+  zval ***args = (zval***) emalloc(argc * sizeof(zval**));
+  zend_get_parameters_array_ex(argc, args);
+
+  if (Z_TYPE_PP(args[0]) != IS_OBJECT) {
+    php_error_docref(NULL TSRMLS_CC, E_ERROR, "1st parameter is not an object (transport)");
+    efree(args);
+    RETURN_NULL();
+  }
+
+  if (Z_TYPE_PP(args[1]) != IS_STRING) {
+    php_error_docref(NULL TSRMLS_CC, E_ERROR, "2nd parameter is not a string (method name)");
+    efree(args);
+    RETURN_NULL();
+  }
+
+  if (Z_TYPE_PP(args[3]) != IS_OBJECT) {
+    php_error_docref(NULL TSRMLS_CC, E_ERROR, "4th parameter is not an object (request struct)");
+    efree(args);
+    RETURN_NULL();
+  }
+
+  PHPOutputTransport transport(*args[0]);
+  const char* method_name = Z_STRVAL_PP(args[1]);
+  convert_to_long(*args[2]);
+  int32_t msgtype = Z_LVAL_PP(args[2]);
+  zval* request_struct = *args[3];
+  convert_to_long(*args[4]);
+  int32_t seqID = Z_LVAL_PP(args[4]);
+  convert_to_boolean(*args[5]);
+  bool strictWrite = Z_BVAL_PP(args[5]);
+  efree(args);
+  args = NULL;
+
+  try {
+    if (strictWrite) {
+      int32_t version = VERSION_1 | msgtype;
+      transport.writeI32(version);
+      transport.writeString(method_name, strlen(method_name));
+      transport.writeI32(seqID);
+    } else {
+      transport.writeString(method_name, strlen(method_name));
+      transport.writeI8(msgtype);
+      transport.writeI32(seqID);
+    }
+
+    zval* spec = zend_read_static_property(zend_get_class_entry(request_struct TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC);
+    binary_serialize_spec(request_struct, transport, Z_ARRVAL_P(spec));
+  } catch (const PHPExceptionWrapper& ex) {
+    zend_throw_exception_object(ex TSRMLS_CC);
+    RETURN_NULL();
+  }
+}
+
+// 3 params: $transport $response_Typename $strict_read
+PHP_FUNCTION(thrift_protocol_read_binary) {
+  int argc = ZEND_NUM_ARGS();
+
+  if (argc < 3) {
+    WRONG_PARAM_COUNT;
+  }
+
+  zval ***args = (zval***) emalloc(argc * sizeof(zval**));
+  zend_get_parameters_array_ex(argc, args);
+
+  if (Z_TYPE_PP(args[0]) != IS_OBJECT) {
+    php_error_docref(NULL TSRMLS_CC, E_ERROR, "1st parameter is not an object (transport)");
+    efree(args);
+    RETURN_NULL();
+  }
+
+  if (Z_TYPE_PP(args[1]) != IS_STRING) {
+    php_error_docref(NULL TSRMLS_CC, E_ERROR, "2nd parameter is not a string (typename of expected response struct)");
+    efree(args);
+    RETURN_NULL();
+  }
+
+  PHPInputTransport transport(*args[0]);
+  char* obj_typename = Z_STRVAL_PP(args[1]);
+  convert_to_boolean(*args[2]);
+  bool strict_read = Z_BVAL_PP(args[2]);
+  efree(args);
+  args = NULL;
+
+  try {
+    int8_t messageType = 0;
+    int32_t sz = transport.readI32();
+
+    if (sz < 0) {
+      // Check for correct version number
+      int32_t version = sz & VERSION_MASK;
+      if (version != VERSION_1) {
+        throw_tprotocolexception("Bad version identifier", BAD_VERSION);
+      }
+      messageType = (sz & 0x000000ff);
+      int32_t namelen = transport.readI32();
+      // skip the name string and the sequence ID, we don't care about those
+      transport.skip(namelen + 4);
+    } else {
+      if (strict_read) {
+        throw_tprotocolexception("No version identifier... old protocol client in strict mode?", BAD_VERSION);
+      } else {
+        // Handle pre-versioned input
+        transport.skip(sz); // skip string body
+        messageType = transport.readI8();
+        transport.skip(4); // skip sequence number
+      }
+    }
+
+    if (messageType == T_EXCEPTION) {
+      zval* ex;
+      MAKE_STD_ZVAL(ex);
+      createObject("TApplicationException", ex);
+      zval* spec = zend_read_static_property(zend_get_class_entry(ex TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC);
+      binary_deserialize_spec(ex, transport, Z_ARRVAL_P(spec));
+      throw PHPExceptionWrapper(ex);
+    }
+
+    createObject(obj_typename, return_value);
+    zval* spec = zend_read_static_property(zend_get_class_entry(return_value TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC);
+    binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec));
+  } catch (const PHPExceptionWrapper& ex) {
+    zend_throw_exception_object(ex TSRMLS_CC);
+    RETURN_NULL();
+  }
+}
+
diff --git a/lib/php/src/ext/thrift_protocol/php_thrift_protocol.h b/lib/php/src/ext/thrift_protocol/php_thrift_protocol.h
new file mode 100644
index 0000000..c9a3e00
--- /dev/null
+++ b/lib/php/src/ext/thrift_protocol/php_thrift_protocol.h
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+PHP_FUNCTION(thrift_protocol_write_binary);
+PHP_FUNCTION(thrift_protocol_read_binary);
+
+extern zend_module_entry thrift_protocole_module_entry;
+
diff --git a/lib/php/src/protocol/TBinaryProtocol.php b/lib/php/src/protocol/TBinaryProtocol.php
new file mode 100644
index 0000000..31bbbf9
--- /dev/null
+++ b/lib/php/src/protocol/TBinaryProtocol.php
@@ -0,0 +1,431 @@
+<?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.protocol
+ */
+
+include_once $GLOBALS['THRIFT_ROOT'].'/transport/TBufferedTransport.php';
+
+/**
+ * Binary implementation of the Thrift protocol.
+ *
+ */
+class TBinaryProtocol extends TProtocol {
+
+  const VERSION_MASK = 0xffff0000;
+  const VERSION_1 = 0x80010000;
+
+  protected $strictRead_ = false;
+  protected $strictWrite_ = true;
+
+  public function __construct($trans, $strictRead=false, $strictWrite=true) {
+    parent::__construct($trans);
+    $this->strictRead_ = $strictRead;
+    $this->strictWrite_ = $strictWrite;
+  }
+
+  public function writeMessageBegin($name, $type, $seqid) {
+    if ($this->strictWrite_) {
+      $version = self::VERSION_1 | $type;
+      return
+        $this->writeI32($version) +
+        $this->writeString($name) +
+        $this->writeI32($seqid);
+    } else {
+      return
+        $this->writeString($name) +
+        $this->writeByte($type) +
+        $this->writeI32($seqid);
+    }
+  }
+
+  public function writeMessageEnd() {
+    return 0;
+  }
+
+  public function writeStructBegin($name) {
+    return 0;
+  }
+
+  public function writeStructEnd() {
+    return 0;
+  }
+
+  public function writeFieldBegin($fieldName, $fieldType, $fieldId) {
+    return
+      $this->writeByte($fieldType) +
+      $this->writeI16($fieldId);
+  }
+
+  public function writeFieldEnd() {
+    return 0;
+  }
+
+  public function writeFieldStop() {
+    return
+      $this->writeByte(TType::STOP);
+  }
+
+  public function writeMapBegin($keyType, $valType, $size) {
+    return
+      $this->writeByte($keyType) +
+      $this->writeByte($valType) +
+      $this->writeI32($size);
+  }
+
+  public function writeMapEnd() {
+    return 0;
+  }
+
+  public function writeListBegin($elemType, $size) {
+    return
+      $this->writeByte($elemType) +
+      $this->writeI32($size);
+  }
+
+  public function writeListEnd() {
+    return 0;
+  }
+
+  public function writeSetBegin($elemType, $size) {
+    return
+      $this->writeByte($elemType) +
+      $this->writeI32($size);
+  }
+
+  public function writeSetEnd() {
+    return 0;
+  }
+
+  public function writeBool($value) {
+    $data = pack('c', $value ? 1 : 0);
+    $this->trans_->write($data, 1);
+    return 1;
+  }
+
+  public function writeByte($value) {
+    $data = pack('c', $value);
+    $this->trans_->write($data, 1);
+    return 1;
+  }
+
+  public function writeI16($value) {
+    $data = pack('n', $value);
+    $this->trans_->write($data, 2);
+    return 2;
+  }
+
+  public function writeI32($value) {
+    $data = pack('N', $value);
+    $this->trans_->write($data, 4);
+    return 4;
+  }
+
+  public function writeI64($value) {
+    // If we are on a 32bit architecture we have to explicitly deal with
+    // 64-bit twos-complement arithmetic since PHP wants to treat all ints
+    // as signed and any int over 2^31 - 1 as a float
+    if (PHP_INT_SIZE == 4) {
+      $neg = $value < 0;
+
+      if ($neg) {
+        $value *= -1;
+      }
+
+      $hi = (int)($value / 4294967296);
+      $lo = (int)$value;
+
+      if ($neg) {
+        $hi = ~$hi;
+        $lo = ~$lo;
+        if (($lo & (int)0xffffffff) == (int)0xffffffff) {
+          $lo = 0;
+          $hi++;
+        } else {
+          $lo++;
+        }
+      }
+      $data = pack('N2', $hi, $lo);
+
+    } else {
+      $hi = $value >> 32;
+      $lo = $value & 0xFFFFFFFF;
+      $data = pack('N2', $hi, $lo);
+    }
+
+    $this->trans_->write($data, 8);
+    return 8;
+  }
+
+  public function writeDouble($value) {
+    $data = pack('d', $value);
+    $this->trans_->write(strrev($data), 8);
+    return 8;
+  }
+
+  public function writeString($value) {
+    $len = strlen($value);
+    $result = $this->writeI32($len);
+    if ($len) {
+      $this->trans_->write($value, $len);
+    }
+    return $result + $len;
+  }
+
+  public function readMessageBegin(&$name, &$type, &$seqid) {
+    $result = $this->readI32($sz);
+    if ($sz < 0) {
+      $version = (int) ($sz & self::VERSION_MASK);
+      if ($version != (int) self::VERSION_1) {
+        throw new TProtocolException('Bad version identifier: '.$sz, TProtocolException::BAD_VERSION);
+      }
+      $type = $sz & 0x000000ff;
+      $result +=
+        $this->readString($name) +
+        $this->readI32($seqid);
+    } else {
+      if ($this->strictRead_) {
+        throw new TProtocolException('No version identifier, old protocol client?', TProtocolException::BAD_VERSION);
+      } else {
+        // Handle pre-versioned input
+        $name = $this->trans_->readAll($sz);
+        $result +=
+          $sz +
+          $this->readByte($type) +
+          $this->readI32($seqid);
+      }
+    }
+    return $result;
+  }
+
+  public function readMessageEnd() {
+    return 0;
+  }
+
+  public function readStructBegin(&$name) {
+    $name = '';
+    return 0;
+  }
+
+  public function readStructEnd() {
+    return 0;
+  }
+
+  public function readFieldBegin(&$name, &$fieldType, &$fieldId) {
+    $result = $this->readByte($fieldType);
+    if ($fieldType == TType::STOP) {
+      $fieldId = 0;
+      return $result;
+    }
+    $result += $this->readI16($fieldId);
+    return $result;
+  }
+
+  public function readFieldEnd() {
+    return 0;
+  }
+
+  public function readMapBegin(&$keyType, &$valType, &$size) {
+    return
+      $this->readByte($keyType) +
+      $this->readByte($valType) +
+      $this->readI32($size);
+  }
+
+  public function readMapEnd() {
+    return 0;
+  }
+
+  public function readListBegin(&$elemType, &$size) {
+    return
+      $this->readByte($elemType) +
+      $this->readI32($size);
+  }
+
+  public function readListEnd() {
+    return 0;
+  }
+
+  public function readSetBegin(&$elemType, &$size) {
+    return
+      $this->readByte($elemType) +
+      $this->readI32($size);
+  }
+
+  public function readSetEnd() {
+    return 0;
+  }
+
+  public function readBool(&$value) {
+    $data = $this->trans_->readAll(1);
+    $arr = unpack('c', $data);
+    $value = $arr[1] == 1;
+    return 1;
+  }
+
+  public function readByte(&$value) {
+    $data = $this->trans_->readAll(1);
+    $arr = unpack('c', $data);
+    $value = $arr[1];
+    return 1;
+  }
+
+  public function readI16(&$value) {
+    $data = $this->trans_->readAll(2);
+    $arr = unpack('n', $data);
+    $value = $arr[1];
+    if ($value > 0x7fff) {
+      $value = 0 - (($value - 1) ^ 0xffff);
+    }
+    return 2;
+  }
+
+  public function readI32(&$value) {
+    $data = $this->trans_->readAll(4);
+    $arr = unpack('N', $data);
+    $value = $arr[1];
+    if ($value > 0x7fffffff) {
+      $value = 0 - (($value - 1) ^ 0xffffffff);
+    }
+    return 4;
+  }
+
+  public function readI64(&$value) {
+    $data = $this->trans_->readAll(8);
+
+    $arr = unpack('N2', $data);
+
+    // If we are on a 32bit architecture we have to explicitly deal with
+    // 64-bit twos-complement arithmetic since PHP wants to treat all ints
+    // as signed and any int over 2^31 - 1 as a float
+    if (PHP_INT_SIZE == 4) {
+
+      $hi = $arr[1];
+      $lo = $arr[2];
+      $isNeg = $hi  < 0;
+
+      // Check for a negative
+      if ($isNeg) {
+        $hi = ~$hi & (int)0xffffffff;
+        $lo = ~$lo & (int)0xffffffff;
+
+        if ($lo == (int)0xffffffff) {
+          $hi++;
+          $lo = 0;
+        } else {
+          $lo++;
+        }
+      }
+
+      // Force 32bit words in excess of 2G to pe positive - we deal wigh sign
+      // explicitly below
+
+      if ($hi & (int)0x80000000) {
+        $hi &= (int)0x7fffffff;
+        $hi += 0x80000000;
+      }
+
+      if ($lo & (int)0x80000000) {
+        $lo &= (int)0x7fffffff;
+        $lo += 0x80000000;
+      }
+
+      $value = $hi * 4294967296 + $lo;
+
+      if ($isNeg) {
+        $value = 0 - $value;
+      }
+    } else {
+
+      // Upcast negatives in LSB bit
+      if ($arr[2] & 0x80000000) {
+        $arr[2] = $arr[2] & 0xffffffff;
+      }
+
+      // Check for a negative
+      if ($arr[1] & 0x80000000) {
+        $arr[1] = $arr[1] & 0xffffffff;
+        $arr[1] = $arr[1] ^ 0xffffffff;
+        $arr[2] = $arr[2] ^ 0xffffffff;
+        $value = 0 - $arr[1]*4294967296 - $arr[2] - 1;
+      } else {
+        $value = $arr[1]*4294967296 + $arr[2];
+      }
+    }
+
+    return 8;
+  }
+
+  public function readDouble(&$value) {
+    $data = strrev($this->trans_->readAll(8));
+    $arr = unpack('d', $data);
+    $value = $arr[1];
+    return 8;
+  }
+
+  public function readString(&$value) {
+    $result = $this->readI32($len);
+    if ($len) {
+      $value = $this->trans_->readAll($len);
+    } else {
+      $value = '';
+    }
+    return $result + $len;
+  }
+}
+
+/**
+ * Binary Protocol Factory
+ */
+class TBinaryProtocolFactory implements TProtocolFactory {
+  private $strictRead_ = false;
+  private $strictWrite_ = false;
+
+  public function __construct($strictRead=false, $strictWrite=false) {
+    $this->strictRead_ = $strictRead;
+    $this->strictWrite_ = $strictWrite;
+  }
+
+  public function getProtocol($trans) {
+    return new TBinaryProtocol($trans, $this->strictRead, $this->strictWrite);
+  }
+}
+
+/**
+ * Accelerated binary protocol: used in conjunction with the thrift_protocol
+ * extension for faster deserialization
+ */
+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)
+    if (!method_exists($trans, 'putBack')) {
+      $trans = new TBufferedTransport($trans);
+    }
+    parent::__construct($trans, $strictRead, $strictWrite);
+  }
+  public function isStrictRead() {
+    return $this->strictRead_;
+  }
+  public function isStrictWrite() {
+    return $this->strictWrite_;
+  }
+}
+
+?>
diff --git a/lib/php/src/protocol/TProtocol.php b/lib/php/src/protocol/TProtocol.php
new file mode 100644
index 0000000..e9ff41a
--- /dev/null
+++ b/lib/php/src/protocol/TProtocol.php
@@ -0,0 +1,377 @@
+<?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.protocol
+ */
+
+
+/**
+ * Protocol module. Contains all the types and definitions needed to implement
+ * a protocol encoder/decoder.
+ *
+ * @package thrift.protocol
+ */
+
+/**
+ * Protocol exceptions
+ */
+class TProtocolException extends TException {
+  const UNKNOWN = 0;
+  const INVALID_DATA = 1;
+  const NEGATIVE_SIZE = 2;
+  const SIZE_LIMIT = 3;
+  const BAD_VERSION = 4;
+
+  function __construct($message=null, $code=0) {
+    parent::__construct($message, $code);
+  }
+}
+
+/**
+ * Protocol base class module.
+ */
+abstract class TProtocol {
+  // The below may seem silly, but it is to get around the problem that the
+  // "instanceof" operator can only take in a T_VARIABLE and not a T_STRING
+  // or T_CONSTANT_ENCAPSED_STRING. Using "is_a()" instead of "instanceof" is
+  // a workaround but is deprecated in PHP5. This is used in the generated
+  // deserialization code.
+  static $TBINARYPROTOCOLACCELERATED = 'TBinaryProtocolAccelerated';
+
+  /**
+   * Underlying transport
+   *
+   * @var TTransport
+   */
+  protected $trans_;
+
+  /**
+   * Constructor
+   */
+  protected function __construct($trans) {
+    $this->trans_ = $trans;
+  }
+
+  /**
+   * Accessor for transport
+   *
+   * @return TTransport
+   */
+  public function getTransport() {
+    return $this->trans_;
+  }
+
+  /**
+   * Writes the message header
+   *
+   * @param string $name Function name
+   * @param int $type message type TMessageType::CALL or TMessageType::REPLY
+   * @param int $seqid The sequence id of this message
+   */
+  public abstract function writeMessageBegin($name, $type, $seqid);
+
+  /**
+   * Close the message
+   */
+  public abstract function writeMessageEnd();
+
+  /**
+   * Writes a struct header.
+   *
+   * @param string     $name Struct name
+   * @throws TException on write error
+   * @return int How many bytes written
+   */
+  public abstract function writeStructBegin($name);
+
+  /**
+   * Close a struct.
+   *
+   * @throws TException on write error
+   * @return int How many bytes written
+   */
+  public abstract function writeStructEnd();
+
+  /*
+   * Starts a field.
+   *
+   * @param string     $name Field name
+   * @param int        $type Field type
+   * @param int        $fid  Field id
+   * @throws TException on write error
+   * @return int How many bytes written
+   */
+  public abstract function writeFieldBegin($fieldName, $fieldType, $fieldId);
+
+  public abstract function writeFieldEnd();
+
+  public abstract function writeFieldStop();
+
+  public abstract function writeMapBegin($keyType, $valType, $size);
+
+  public abstract function writeMapEnd();
+
+  public abstract function writeListBegin($elemType, $size);
+
+  public abstract function writeListEnd();
+
+  public abstract function writeSetBegin($elemType, $size);
+
+  public abstract function writeSetEnd();
+
+  public abstract function writeBool($bool);
+
+  public abstract function writeByte($byte);
+
+  public abstract function writeI16($i16);
+
+  public abstract function writeI32($i32);
+
+  public abstract function writeI64($i64);
+
+  public abstract function writeDouble($dub);
+
+  public abstract function writeString($str);
+
+  /**
+   * Reads the message header
+   *
+   * @param string $name Function name
+   * @param int $type message type TMessageType::CALL or TMessageType::REPLY
+   * @parem int $seqid The sequence id of this message
+   */
+  public abstract function readMessageBegin(&$name, &$type, &$seqid);
+
+  /**
+   * Read the close of message
+   */
+  public abstract function readMessageEnd();
+
+  public abstract function readStructBegin(&$name);
+
+  public abstract function readStructEnd();
+
+  public abstract function readFieldBegin(&$name, &$fieldType, &$fieldId);
+
+  public abstract function readFieldEnd();
+
+  public abstract function readMapBegin(&$keyType, &$valType, &$size);
+
+  public abstract function readMapEnd();
+
+  public abstract function readListBegin(&$elemType, &$size);
+
+  public abstract function readListEnd();
+
+  public abstract function readSetBegin(&$elemType, &$size);
+
+  public abstract function readSetEnd();
+
+  public abstract function readBool(&$bool);
+
+  public abstract function readByte(&$byte);
+
+  public abstract function readI16(&$i16);
+
+  public abstract function readI32(&$i32);
+
+  public abstract function readI64(&$i64);
+
+  public abstract function readDouble(&$dub);
+
+  public abstract function readString(&$str);
+
+  /**
+   * The skip function is a utility to parse over unrecognized date without
+   * causing corruption.
+   *
+   * @param TType $type What type is it
+   */
+  public function skip($type) {
+    switch ($type) {
+    case TType::BOOL:
+      return $this->readBool($bool);
+    case TType::BYTE:
+      return $this->readByte($byte);
+    case TType::I16:
+      return $this->readI16($i16);
+    case TType::I32:
+      return $this->readI32($i32);
+    case TType::I64:
+      return $this->readI64($i64);
+    case TType::DOUBLE:
+      return $this->readDouble($dub);
+    case TType::STRING:
+      return $this->readString($str);
+    case TType::STRUCT:
+      {
+        $result = $this->readStructBegin($name);
+        while (true) {
+          $result += $this->readFieldBegin($name, $ftype, $fid);
+          if ($ftype == TType::STOP) {
+            break;
+          }
+          $result += $this->skip($ftype);
+          $result += $this->readFieldEnd();
+        }
+        $result += $this->readStructEnd();
+        return $result;
+      }
+    case TType::MAP:
+      {
+        $result = $this->readMapBegin($keyType, $valType, $size);
+        for ($i = 0; $i < $size; $i++) {
+          $result += $this->skip($keyType);
+          $result += $this->skip($valType);
+        }
+        $result += $this->readMapEnd();
+        return $result;
+      }
+    case TType::SET:
+      {
+        $result = $this->readSetBegin($elemType, $size);
+        for ($i = 0; $i < $size; $i++) {
+          $result += $this->skip($elemType);
+        }
+        $result += $this->readSetEnd();
+        return $result;
+      }
+    case TType::LST:
+      {
+        $result = $this->readListBegin($elemType, $size);
+        for ($i = 0; $i < $size; $i++) {
+          $result += $this->skip($elemType);
+        }
+        $result += $this->readListEnd();
+        return $result;
+      }
+    default:
+      return 0;
+    }
+  }
+
+  /**
+   * Utility for skipping binary data
+   *
+   * @param TTransport $itrans TTransport object
+   * @param int        $type   Field type
+   */
+  public static function skipBinary($itrans, $type) {
+    switch ($type) {
+    case TType::BOOL:
+      return $itrans->readAll(1);
+    case TType::BYTE:
+      return $itrans->readAll(1);
+    case TType::I16:
+      return $itrans->readAll(2);
+    case TType::I32:
+      return $itrans->readAll(4);
+    case TType::I64:
+      return $itrans->readAll(8);
+    case TType::DOUBLE:
+      return $itrans->readAll(8);
+    case TType::STRING:
+      $len = unpack('N', $itrans->readAll(4));
+      $len = $len[1];
+      if ($len > 0x7fffffff) {
+        $len = 0 - (($len - 1) ^ 0xffffffff);
+      }
+      return 4 + $itrans->readAll($len);
+    case TType::STRUCT:
+      {
+        $result = 0;
+        while (true) {
+          $ftype = 0;
+          $fid = 0;
+          $data = $itrans->readAll(1);
+          $arr = unpack('c', $data);
+          $ftype = $arr[1];
+          if ($ftype == TType::STOP) {
+            break;
+          }
+          // I16 field id
+          $result += $itrans->readAll(2);
+          $result += self::skipBinary($itrans, $ftype);
+        }
+        return $result;
+      }
+    case TType::MAP:
+      {
+        // Ktype
+        $data = $itrans->readAll(1);
+        $arr = unpack('c', $data);
+        $ktype = $arr[1];
+        // Vtype
+        $data = $itrans->readAll(1);
+        $arr = unpack('c', $data);
+        $vtype = $arr[1];
+        // Size
+        $data = $itrans->readAll(4);
+        $arr = unpack('N', $data);
+        $size = $arr[1];
+        if ($size > 0x7fffffff) {
+          $size = 0 - (($size - 1) ^ 0xffffffff);
+        }
+        $result = 6;
+        for ($i = 0; $i < $size; $i++) {
+          $result += self::skipBinary($itrans, $ktype);
+          $result += self::skipBinary($itrans, $vtype);
+        }
+        return $result;
+      }
+    case TType::SET:
+    case TType::LST:
+      {
+        // Vtype
+        $data = $itrans->readAll(1);
+        $arr = unpack('c', $data);
+        $vtype = $arr[1];
+        // Size
+        $data = $itrans->readAll(4);
+        $arr = unpack('N', $data);
+        $size = $arr[1];
+        if ($size > 0x7fffffff) {
+          $size = 0 - (($size - 1) ^ 0xffffffff);
+        }
+        $result = 5;
+        for ($i = 0; $i < $size; $i++) {
+          $result += self::skipBinary($itrans, $vtype);
+        }
+        return $result;
+      }
+    default:
+      return 0;
+    }
+  }
+}
+
+/**
+ * Protocol factory creates protocol objects from transports
+ */
+interface TProtocolFactory {
+  /**
+   * Build a protocol from the base transport
+   *
+   * @return TProtcol protocol
+   */
+  public function getProtocol($trans);
+}
+
+
+?>
diff --git a/lib/php/src/transport/TBufferedTransport.php b/lib/php/src/transport/TBufferedTransport.php
new file mode 100644
index 0000000..cfae767
--- /dev/null
+++ b/lib/php/src/transport/TBufferedTransport.php
@@ -0,0 +1,163 @@
+<?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
+ */
+
+
+/**
+ * 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
+ */
+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 putBack($data) {
+    if (strlen($this->rBuf_) === 0) {
+      $this->rBuf_ = $data;
+    } else {
+      $this->rBuf_ = ($data . $this->rBuf_);
+    }
+  }
+
+  /**
+   * The reason that we customize readAll here is that the majority of PHP
+   * streams are already internally buffered by PHP. The socket stream, for
+   * example, buffers internally and blocks if you call read with $len greater
+   * than the amount of data available, unlike recv() in C.
+   *
+   * Therefore, use the readAll method of the wrapped transport inside
+   * the buffered readAll.
+   */
+  public function readAll($len) {
+    $have = strlen($this->rBuf_);
+    if ($have == 0) {
+      $data = $this->transport_->readAll($len);
+    } else if ($have < $len) {
+      $data = $this->rBuf_;
+      $this->rBuf_ = '';
+      $data .= $this->transport_->readAll($len - $have);
+    } else if ($have == $len) {
+      $data = $this->rBuf_;
+      $this->rBuf_ = '';
+    } else if ($have > $len) {
+      $data = substr($this->rBuf_, 0, $len);
+      $this->rBuf_ = substr($this->rBuf_, $len);
+    }
+    return $data;
+  }
+
+  public function read($len) {
+    if (strlen($this->rBuf_) === 0) {
+      $this->rBuf_ = $this->transport_->read($this->rBufSize_);
+    }
+
+    if (strlen($this->rBuf_) <= $len) {
+      $ret = $this->rBuf_;
+      $this->rBuf_ = '';
+      return $ret;
+    }
+
+    $ret = substr($this->rBuf_, 0, $len);
+    $this->rBuf_ = substr($this->rBuf_, $len);
+    return $ret;
+  }
+
+  public function write($buf) {
+    $this->wBuf_ .= $buf;
+    if (strlen($this->wBuf_) >= $this->wBufSize_) {
+      $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);
+    }
+  }
+
+  public function flush() {
+    if (strlen($this->wBuf_) > 0) {
+      $this->transport_->write($this->wBuf_);
+      $this->wBuf_ = '';
+    }
+    $this->transport_->flush();
+  }
+
+}
+
+?>
diff --git a/lib/php/src/transport/TFramedTransport.php b/lib/php/src/transport/TFramedTransport.php
new file mode 100644
index 0000000..dc57392
--- /dev/null
+++ b/lib/php/src/transport/TFramedTransport.php
@@ -0,0 +1,179 @@
+<?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
+ */
+
+
+/**
+ * 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 (strlen($this->rBuf_) === 0) {
+      $this->readFrame();
+    }
+
+    // Just return full buff
+    if ($len >= strlen($this->rBuf_)) {
+      $out = $this->rBuf_;
+      $this->rBuf_ = null;
+      return $out;
+    }
+
+    // Return substr
+    $out = substr($this->rBuf_, 0, $len);
+    $this->rBuf_ = 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 (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 < 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() {
+    if (!$this->write_) {
+      return $this->transport_->flush();
+    }
+
+    $out = pack('N', 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();
+  }
+
+}
diff --git a/lib/php/src/transport/THttpClient.php b/lib/php/src/transport/THttpClient.php
new file mode 100644
index 0000000..224d403
--- /dev/null
+++ b/lib/php/src/transport/THttpClient.php
@@ -0,0 +1,202 @@
+<?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
+ */
+
+
+/**
+ * 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_;
+
+  /**
+   * Make a new HTTP client.
+   *
+   * @param string $host
+   * @param int    $port
+   * @param string $uri
+   */
+  public function __construct($host, $port=80, $uri='', $scheme = 'http') {
+    if ((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;
+  }
+
+  /**
+   * 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('Host: '.$host,
+                     'Accept: application/x-thrift',
+                     'User-Agent: PHP/THttpClient',
+                     'Content-Type: application/x-thrift',
+                     'Content-Length: '.strlen($this->buf_));
+
+    $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);
+    }
+  }
+
+}
+
+?>
diff --git a/lib/php/src/transport/TMemoryBuffer.php b/lib/php/src/transport/TMemoryBuffer.php
new file mode 100644
index 0000000..01eb0f5
--- /dev/null
+++ b/lib/php/src/transport/TMemoryBuffer.php
@@ -0,0 +1,84 @@
+<?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
+ */
+
+
+/**
+ * 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) {
+    if (strlen($this->buf_) === 0) {
+      throw new TTransportException('TMemoryBuffer: Could not read ' .
+                                    $len . ' bytes from buffer.',
+                                    TTransportException::UNKNOWN);
+    }
+
+    if (strlen($this->buf_) <= $len) {
+      $ret = $this->buf_;
+      $this->buf_ = '';
+      return $ret;
+    }
+
+    $ret = substr($this->buf_, 0, $len);
+    $this->buf_ = substr($this->buf_, $len);
+
+    return $ret;
+  }
+
+  function getBuffer() {
+    return $this->buf_;
+  }
+
+  public function available() {
+    return strlen($this->buf_);
+  }
+}
+
+?>
diff --git a/lib/php/src/transport/TNullTransport.php b/lib/php/src/transport/TNullTransport.php
new file mode 100644
index 0000000..bada5df
--- /dev/null
+++ b/lib/php/src/transport/TNullTransport.php
@@ -0,0 +1,48 @@
+<?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
+ */
+
+
+/**
+ * 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) {}
+
+}
+
+?>
diff --git a/lib/php/src/transport/TPhpStream.php b/lib/php/src/transport/TPhpStream.php
new file mode 100644
index 0000000..3a1c80b
--- /dev/null
+++ b/lib/php/src/transport/TPhpStream.php
@@ -0,0 +1,111 @@
+<?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
+ */
+
+
+/**
+ * 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 (strlen($buf) > 0) {
+      $got = @fwrite($this->outStream_, $buf);
+      if ($got === 0 || $got === FALSE) {
+        throw new TException('TPhpStream: Could not write '.strlen($buf).' bytes');
+      }
+      $buf = substr($buf, $got);
+    }
+  }
+
+  public function flush() {
+    @fflush($this->outStream_);
+  }
+
+  private static function inStreamName() {
+    if (php_sapi_name() == 'cli') {
+      return 'php://stdin';
+    }
+    return 'php://input';
+  }
+
+}
+
+?>
diff --git a/lib/php/src/transport/TSocket.php b/lib/php/src/transport/TSocket.php
new file mode 100644
index 0000000..ba3a631
--- /dev/null
+++ b/lib/php/src/transport/TSocket.php
@@ -0,0 +1,312 @@
+<?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
+ */
+
+
+/**
+ * Sockets implementation of the TTransport interface.
+ *
+ * @package thrift.transport
+ */
+class TSocket extends TTransport {
+
+  /**
+   * Handle to PHP socket
+   *
+   * @var resource
+   */
+  private $handle_ = null;
+
+  /**
+   * Remote hostname
+   *
+   * @var string
+   */
+  protected $host_ = 'localhost';
+
+  /**
+   * Remote port
+   *
+   * @var int
+   */
+  protected $port_ = '9090';
+
+  /**
+   * Send timeout in milliseconds
+   *
+   * @var int
+   */
+  private $sendTimeout_ = 100;
+
+  /**
+   * Recv timeout in milliseconds
+   *
+   * @var int
+   */
+  private $recvTimeout_ = 750;
+
+  /**
+   * Is send timeout set?
+   *
+   * @var bool
+   */
+  private $sendTimeoutSet_ = FALSE;
+
+  /**
+   * Persistent socket or plain?
+   *
+   * @var bool
+   */
+  private $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';
+  }
+
+  /**
+   * Sets the send timeout.
+   *
+   * @param int $timeout  Timeout in milliseconds.
+   */
+  public function setSendTimeout($timeout) {
+    $this->sendTimeout_ = $timeout;
+  }
+
+  /**
+   * Sets the receive timeout.
+   *
+   * @param int $timeout  Timeout in milliseconds.
+   */
+  public function setRecvTimeout($timeout) {
+    $this->recvTimeout_ = $timeout;
+  }
+
+  /**
+   * 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->persist_) {
+      $this->handle_ = @pfsockopen($this->host_,
+                                   $this->port_,
+                                   $errno,
+                                   $errstr,
+                                   $this->sendTimeout_/1000.0);
+    } else {
+      $this->handle_ = @fsockopen($this->host_,
+                                  $this->port_,
+                                  $errno,
+                                  $errstr,
+                                  $this->sendTimeout_/1000.0);
+    }
+
+    // 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);
+    }
+
+    stream_set_timeout($this->handle_, 0, $this->sendTimeout_*1000);
+    $this->sendTimeoutSet_ = TRUE;
+  }
+
+  /**
+   * Closes the socket.
+   */
+  public function close() {
+    if (!$this->persist_) {
+      @fclose($this->handle_);
+      $this->handle_ = null;
+    }
+  }
+
+  /**
+   * Uses stream get contents to do the reading
+   *
+   * @param int $len How many bytes
+   * @return string Binary data
+   */
+  public function readAll($len) {
+    if ($this->sendTimeoutSet_) {
+      stream_set_timeout($this->handle_, 0, $this->recvTimeout_*1000);
+      $this->sendTimeoutSet_ = FALSE;
+    }
+    // This call does not obey stream_set_timeout values!
+    // $buf = @stream_get_contents($this->handle_, $len);
+
+    $pre = null;
+    while (TRUE) {
+      $buf = @fread($this->handle_, $len);
+      if ($buf === FALSE || $buf === '') {
+        $md = stream_get_meta_data($this->handle_);
+        if ($md['timed_out']) {
+          throw new TException('TSocket: timed out reading '.$len.' bytes from '.
+                               $this->host_.':'.$this->port_);
+        } else {
+          throw new TException('TSocket: Could not read '.$len.' bytes from '.
+                               $this->host_.':'.$this->port_);
+        }
+      } else if (($sz = strlen($buf)) < $len) {
+        $md = stream_get_meta_data($this->handle_);
+        if ($md['timed_out']) {
+          throw new TException('TSocket: timed out reading '.$len.' bytes from '.
+                               $this->host_.':'.$this->port_);
+        } else {
+          $pre .= $buf;
+          $len -= $sz;
+        }
+      } else {
+        return $pre.$buf;
+      }
+    }
+  }
+
+  /**
+   * Read from the socket
+   *
+   * @param int $len How many bytes
+   * @return string Binary data
+   */
+  public function read($len) {
+    if ($this->sendTimeoutSet_) {
+      stream_set_timeout($this->handle_, 0, $this->recvTimeout_*1000);
+      $this->sendTimeoutSet_ = FALSE;
+    }
+    $data = @fread($this->handle_, $len);
+    if ($data === FALSE || $data === '') {
+      $md = stream_get_meta_data($this->handle_);
+      if ($md['timed_out']) {
+        throw new TException('TSocket: timed out reading '.$len.' bytes from '.
+                             $this->host_.':'.$this->port_);
+      } else {
+        throw new TException('TSocket: Could not read '.$len.' bytes from '.
+                             $this->host_.':'.$this->port_);
+      }
+    }
+    return $data;
+  }
+
+  /**
+   * Write to the socket.
+   *
+   * @param string $buf The data to write
+   */
+  public function write($buf) {
+    if (!$this->sendTimeoutSet_) {
+      stream_set_timeout($this->handle_, 0, $this->sendTimeout_*1000);
+      $this->sendTimeoutSet_ = TRUE;
+    }
+    while (strlen($buf) > 0) {
+      $got = @fwrite($this->handle_, $buf);
+      if ($got === 0 || $got === FALSE) {
+        $md = stream_get_meta_data($this->handle_);
+        if ($md['timed_out']) {
+          throw new TException('TSocket: timed out writing '.strlen($buf).' bytes from '.
+                               $this->host_.':'.$this->port_);
+        } else {
+            throw new TException('TSocket: Could not write '.strlen($buf).' bytes '.
+                                 $this->host_.':'.$this->port_);
+        }
+      }
+      $buf = substr($buf, $got);
+    }
+  }
+
+  /**
+   * Flush output to the socket.
+   */
+  public function flush() {
+    $ret = fflush($this->handle_);
+    if ($ret === FALSE) {
+      throw new TException('TSocket: Could not flush: '.
+                           $this->host_.':'.$this->port_);
+    }
+  }
+}
+
+?>
diff --git a/lib/php/src/transport/TSocketPool.php b/lib/php/src/transport/TSocketPool.php
new file mode 100644
index 0000000..7f1157c
--- /dev/null
+++ b/lib/php/src/transport/TSocketPool.php
@@ -0,0 +1,296 @@
+<?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
+ */
+
+
+/** Inherits from Socket */
+include_once $GLOBALS['THRIFT_ROOT'].'/transport/TSocket.php';
+
+/**
+ * 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);
+        }
+      }
+    }
+
+    // Holy shit 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);
+  }
+}
+
+?>
diff --git a/lib/php/src/transport/TTransport.php b/lib/php/src/transport/TTransport.php
new file mode 100644
index 0000000..e244525
--- /dev/null
+++ b/lib/php/src/transport/TTransport.php
@@ -0,0 +1,108 @@
+<?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
+ */
+
+
+/**
+ * Transport exceptions
+ */
+class TTransportException extends TException {
+
+  const UNKNOWN = 0;
+  const NOT_OPEN = 1;
+  const ALREADY_OPEN = 2;
+  const TIMED_OUT = 3;
+  const END_OF_FILE = 4;
+
+  function __construct($message=null, $code=0) {
+    parent::__construct($message, $code);
+  }
+}
+
+/**
+ * Base interface for a transport agent.
+ *
+ * @package thrift.transport
+ */
+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() {}
+}
+
+?>