You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@thrift.apache.org by jk...@apache.org on 2017/10/25 12:59:57 UTC

[3/3] thrift git commit: THRIFT-4356: fix segment fault for thrift_protocol NOTE: drops php5 support for PHP extension (thrift_protocol) however library still can support PHP5 as evidenced by CI build Client: php

THRIFT-4356: fix segment fault for thrift_protocol
NOTE: drops php5 support for PHP extension (thrift_protocol)
however library still can support PHP5 as evidenced by CI build
Client: php

This closes #1385
This closes #1391


Project: http://git-wip-us.apache.org/repos/asf/thrift/repo
Commit: http://git-wip-us.apache.org/repos/asf/thrift/commit/9dff0efc
Tree: http://git-wip-us.apache.org/repos/asf/thrift/tree/9dff0efc
Diff: http://git-wip-us.apache.org/repos/asf/thrift/diff/9dff0efc

Branch: refs/heads/master
Commit: 9dff0efc1e099a67e386c95bddc4079daf2b7141
Parents: 1df2d9b
Author: Robert Lu <ro...@gmail.com>
Authored: Wed Oct 4 03:18:38 2017 +0800
Committer: James E. King, III <jk...@apache.org>
Committed: Wed Oct 25 08:57:04 2017 -0400

----------------------------------------------------------------------
 .../cpp/src/thrift/generate/t_php_generator.cc  |   47 +-
 lib/php/Makefile.am                             |    1 -
 lib/php/src/ext/thrift_protocol/config.m4       |   31 +-
 .../ext/thrift_protocol/php_thrift_protocol.cpp | 1113 +++++++++---------
 .../thrift_protocol/php_thrift_protocol7.cpp    | 1102 -----------------
 5 files changed, 596 insertions(+), 1698 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/thrift/blob/9dff0efc/compiler/cpp/src/thrift/generate/t_php_generator.cc
----------------------------------------------------------------------
diff --git a/compiler/cpp/src/thrift/generate/t_php_generator.cc b/compiler/cpp/src/thrift/generate/t_php_generator.cc
index 9b5062c..11771c2 100644
--- a/compiler/cpp/src/thrift/generate/t_php_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_php_generator.cc
@@ -210,6 +210,17 @@ public:
   std::string type_to_enum(t_type* ttype);
   std::string type_to_phpdoc(t_type* ttype);
 
+  bool php_is_scalar(t_type *ttype) {
+    ttype = ttype->get_true_type();
+    if(ttype->is_base_type()) {
+      return true;
+    } else if(ttype->is_enum()) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+
   std::string php_namespace_base(const t_program* p) {
     std::string ns = p->get_namespace("php");
     const char* delimiter = "\\";
@@ -1332,11 +1343,24 @@ void t_php_generator::generate_process_function(std::ofstream& out, t_service* t
   string resultname = php_namespace(tservice->get_program()) + service_name_ + "_"
                       + tfunction->get_name() + "_result";
 
+  out << indent() << "$bin_accel = ($input instanceof "
+             << "TBinaryProtocolAccelerated) && function_exists('thrift_protocol_read_binary_after_message_begin');"
+             << endl;
+  out << indent() << "if ($bin_accel)" << endl;
+  scope_up(out);
+
+  out << indent() << "$args = thrift_protocol_read_binary_after_message_begin($input, '" << argsname
+             << "', $input->isStrictRead());" << endl;
+
+  scope_down(out);
+  out << indent() << "else" << endl;
+  scope_up(out);
   out << indent() << "$args = new " << argsname << "();" << endl << indent()
              << "$args->read($input);" << endl;
   if (!binary_inline_) {
     out << indent() << "$input->readMessageEnd();" << endl;
   }
+  scope_down(out);
 
   t_struct* xs = tfunction->get_xceptions();
   const std::vector<t_field*>& xceptions = xs->get_members();
@@ -2093,11 +2117,12 @@ void t_php_generator::generate_deserialize_set_element(ofstream& out, t_set* tse
 
   generate_deserialize_field(out, &felem);
 
-  indent(out) << "if (is_scalar($" << elem << ")) {" << endl;
-  indent(out) << "  $" << prefix << "[$" << elem << "] = true;" << endl;
-  indent(out) << "} else {" << endl;
-  indent(out) << "  $" << prefix << " []= $" << elem << ";" << endl;
-  indent(out) << "}" << endl;
+  t_type* elem_type = tset->get_elem_type();
+  if(php_is_scalar(elem_type)) {
+    indent(out) << "$" << prefix << "[$" << elem << "] = true;" << endl;
+  } else {
+    indent(out) << "$" << prefix << "[] = $" << elem << ";" << endl;
+  }
 }
 
 void t_php_generator::generate_deserialize_list_element(ofstream& out,
@@ -2285,11 +2310,13 @@ void t_php_generator::generate_serialize_container(ofstream& out, t_type* ttype,
     string iter_val = tmp("iter");
     indent(out) << "foreach ($" << prefix << " as $" << iter << " => $" << iter_val << ")" << endl;
     scope_up(out);
-    indent(out) << "if (is_scalar($" << iter_val << ")) {" << endl;
-    generate_serialize_set_element(out, (t_set*)ttype, iter);
-    indent(out) << "} else {" << endl;
-    generate_serialize_set_element(out, (t_set*)ttype, iter_val);
-    indent(out) << "}" << endl;
+
+    t_type* elem_type = ((t_set*)ttype)->get_elem_type();
+    if(php_is_scalar(elem_type)) {
+      generate_serialize_set_element(out, (t_set*)ttype, iter);
+    } else {
+      generate_serialize_set_element(out, (t_set*)ttype, iter_val);
+    }
     scope_down(out);
   } else if (ttype->is_list()) {
     string iter = tmp("iter");

http://git-wip-us.apache.org/repos/asf/thrift/blob/9dff0efc/lib/php/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/php/Makefile.am b/lib/php/Makefile.am
index 8e62960..5aa3be4 100755
--- a/lib/php/Makefile.am
+++ b/lib/php/Makefile.am
@@ -133,7 +133,6 @@ EXTRA_DIST = \
   src/autoload.php \
   src/ext/thrift_protocol/config.m4 \
   src/ext/thrift_protocol/config.w32 \
-  src/ext/thrift_protocol/php_thrift_protocol7.cpp \
   src/ext/thrift_protocol/php_thrift_protocol.cpp \
   src/ext/thrift_protocol/php_thrift_protocol.h \
   src/ext/thrift_protocol/run-tests.php \

http://git-wip-us.apache.org/repos/asf/thrift/blob/9dff0efc/lib/php/src/ext/thrift_protocol/config.m4
----------------------------------------------------------------------
diff --git a/lib/php/src/ext/thrift_protocol/config.m4 b/lib/php/src/ext/thrift_protocol/config.m4
index 0fe3ef4..c54be3e 100644
--- a/lib/php/src/ext/thrift_protocol/config.m4
+++ b/lib/php/src/ext/thrift_protocol/config.m4
@@ -2,24 +2,31 @@ 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.
+dnl
+dnl Licensed to the Apache Software Foundation (ASF) under one
+dnl or more contributor license agreements. See the NOTICE file
+dnl distributed with this work for additional information
+dnl regarding copyright ownership. The ASF licenses this file
+dnl to you under the Apache License, Version 2.0 (the
+dnl "License"); you may not use this file except in compliance
+dnl with the License. You may obtain a copy of the License at
+dnl
+dnl  http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing,
+dnl software distributed under the License is distributed on an
+dnl "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+dnl KIND, either express or implied. See the License for the
+dnl specific language governing permissions and limitations
+dnl under the License.
 
 PHP_ARG_ENABLE(thrift_protocol, whether to enable the thrift_protocol extension,
-[  --enable-thrift_protocol	Enable the fbthrift_protocol extension])
+[  --enable-thrift_protocol	Enable the thrift_protocol extension])
 
 if test "$PHP_THRIFT_PROTOCOL" != "no"; then
   PHP_REQUIRE_CXX()
-  PHP_ADD_LIBRARY_WITH_PATH(stdc++, "", THRIFT_PROTOCOL_SHARED_LIBADD)
   CXXFLAGS="$CXXFLAGS -std=c++11"
 
-  AC_MSG_CHECKING([check for supported PHP versions])
-  PHP_THRIFT_FOUND_VERSION=`${PHP_CONFIG} --version`
-  PHP_THRIFT_FOUND_VERNUM=`echo "${PHP_THRIFT_FOUND_VERSION}" | $AWK 'BEGIN { FS = "."; } { printf "%d", ([$]1 * 100 + [$]2) * 100 + [$]3;}'`
-  if test "$PHP_THRIFT_FOUND_VERNUM" -ge "50000"; then
-    PHP_SUBST(THRIFT_PROTOCOL_SHARED_LIBADD)
-    PHP_NEW_EXTENSION(thrift_protocol, php_thrift_protocol.cpp php_thrift_protocol7.cpp, $ext_shared)
-    AC_MSG_RESULT([supported ($PHP_THRIFT_FOUND_VERSION)])
-  else
-    AC_MSG_ERROR([unsupported PHP version ($PHP_THRIFT_FOUND_VERSION)])
-  fi
+  PHP_NEW_EXTENSION(thrift_protocol, php_thrift_protocol.cpp, $ext_shared)
 fi
 

http://git-wip-us.apache.org/repos/asf/thrift/blob/9dff0efc/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp
----------------------------------------------------------------------
diff --git a/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp b/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp
index 5374286..75ef2ec 100644
--- a/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp
+++ b/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
@@ -26,23 +25,14 @@
 #include "zend_exceptions.h"
 #include "php_thrift_protocol.h"
 
-/* GUARD FOR PHP 5 */
-#if PHP_VERSION_ID < 70000 && PHP_VERSION_ID > 50000
+#if PHP_VERSION_ID >= 70000
 
 #include <sys/types.h>
-#if defined( WIN32 ) || defined( _WIN64 )
-typedef int  int32_t; 
-typedef signed char int8_t;
-typedef unsigned char   uint8_t;
-typedef unsigned short  uint16_t;
-typedef long long  int64_t;
-typedef unsigned   uint32_t; 
-typedef short  int16_t; 
-typedef unsigned long long   uint64_t;
-#else
-#include <arpa/inet.h> 
-#endif
+#include <arpa/inet.h>
+
+#include <cstdint>
 #include <stdexcept>
+#include <algorithm>
 
 #ifndef bswap_64
 #define	bswap_64(x)     (((uint64_t)(x) << 56) | \
@@ -96,21 +86,21 @@ const int INVALID_DATA = 1;
 const int BAD_VERSION = 4;
 
 static zend_function_entry thrift_protocol_functions[] = {
-  PHP_FE(thrift_protocol_write_binary, NULL)
-  PHP_FE(thrift_protocol_read_binary, NULL)
-  PHP_FE(thrift_protocol_read_binary_after_message_begin, NULL)
-  {NULL, NULL, NULL}
-} ;
+  PHP_FE(thrift_protocol_write_binary, nullptr)
+  PHP_FE(thrift_protocol_read_binary, nullptr)
+  PHP_FE(thrift_protocol_read_binary_after_message_begin, nullptr)
+  {nullptr, nullptr, nullptr}
+};
 
 zend_module_entry thrift_protocol_module_entry = {
   STANDARD_MODULE_HEADER,
   "thrift_protocol",
   thrift_protocol_functions,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
+  nullptr,
+  nullptr,
+  nullptr,
+  nullptr,
+  nullptr,
   "1.0",
   STANDARD_MODULE_PROPERTIES
 };
@@ -121,42 +111,61 @@ ZEND_GET_MODULE(thrift_protocol)
 
 class PHPExceptionWrapper : public std::exception {
 public:
-  PHPExceptionWrapper(zval* _ex) throw() : ex(_ex) {
-    snprintf(_what, 40, "PHP exception zval=%p", ex);
+  PHPExceptionWrapper(zval* _ex) throw() {
+    ZVAL_COPY(&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'...
+
+  PHPExceptionWrapper(zend_object* _exobj) throw() {
+    ZVAL_OBJ(&ex, _exobj);
+    snprintf(_what, 40, "PHP exception zval=%p", _exobj);
+  }
+  ~PHPExceptionWrapper() throw() {
+    zval_dtor(&ex);
+  }
+
+  const char* what() const throw() {
+    return _what;
+  }
+  operator zval*() const throw() {
+    return const_cast<zval*>(&ex);
+  } // Zend API doesn't do 'const'...
 protected:
-  zval* ex;
+  zval ex;
   char _what[40];
 } ;
 
 class PHPTransport {
-public:
-  zval* protocol() { return p; }
-  zval* transport() { return t; }
 protected:
-  PHPTransport() {}
+  PHPTransport(zval* _p, size_t _buffer_size) {
+    assert(Z_TYPE_P(_p) == IS_OBJECT);
+
+    ZVAL_UNDEF(&t);
 
-  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);
+    ZVAL_STRING(&gettransport, "getTransport");
+    call_user_function(nullptr, _p, &gettransport, &t, 0, nullptr);
+
+    zval_dtor(&gettransport);
+
+    if (EG(exception)) {
+      zend_object *ex = EG(exception);
+      EG(exception) = nullptr;
+      throw PHPExceptionWrapper(ex);
+    }
+
+    assert(Z_TYPE(t) == IS_OBJECT);
   }
+
   ~PHPTransport() {
     efree(buffer);
-    zval_ptr_dtor(&t);
+    zval_dtor(&t);
   }
 
   char* buffer;
@@ -164,20 +173,14 @@ protected:
   size_t buffer_used;
   size_t buffer_size;
 
-  zval* p;
-  zval* t;
+  zval t;
 };
 
 
 class PHPOutputTransport : public PHPTransport {
 public:
-  PHPOutputTransport(zval* _p, size_t _buffer_size = 8192) {
-    construct_with_zval(_p, _buffer_size);
-  }
-
-  ~PHPOutputTransport() {
-    //flush();
-  }
+  PHPOutputTransport(zval* _p, size_t _buffer_size = 8192) : PHPTransport(_p, _buffer_size) { }
+  ~PHPOutputTransport() { }
 
   void write(const char* data, size_t len) {
     if ((len + buffer_used) > buffer_size) {
@@ -235,32 +238,35 @@ protected:
     }
   }
   void directFlush() {
-    zval ret;
+    zval ret, flushfn;
     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_STRING(&flushfn, "flush");
+
+    call_user_function(EG(function_table), &(this->t), &flushfn, &ret, 0, nullptr);
+    zval_dtor(&flushfn);
     zval_dtor(&ret);
+    if (EG(exception)) {
+      zend_object *ex = EG(exception);
+      EG(exception) = nullptr;
+      throw PHPExceptionWrapper(ex);
+    }
   }
   void directWrite(const char* data, size_t len) {
-    zval writefn;
-    ZVAL_STRING(&writefn, "write", 0);
-    char* newbuf = (char*)emalloc(len + 1);
-    memcpy(newbuf, data, len);
-    newbuf[len] = '\0';
-    zval *args[1];
-    MAKE_STD_ZVAL(args[0]);
-    ZVAL_STRINGL(args[0], newbuf, len, 0);
-    TSRMLS_FETCH();
-    zval ret;
+    zval args[1], ret, writefn;
+
+    ZVAL_STRING(&writefn, "write");
+    ZVAL_STRINGL(&args[0], data, len);
+
     ZVAL_NULL(&ret);
-    call_user_function(EG(function_table), &t, &writefn, &ret, 1, args TSRMLS_CC);
-    zval_ptr_dtor(args);
+    call_user_function(EG(function_table), &(this->t), &writefn, &ret, 1, args);
+
+    zval_dtor(&writefn);
     zval_dtor(&ret);
+    zval_dtor(&args[0]);
+
     if (EG(exception)) {
-      zval* ex = EG(exception);
-      EG(exception) = NULL;
+      zend_object *ex = EG(exception);
+      EG(exception) = nullptr;
       throw PHPExceptionWrapper(ex);
     }
   }
@@ -268,8 +274,7 @@ protected:
 
 class PHPInputTransport : public PHPTransport {
 public:
-  PHPInputTransport(zval* _p, size_t _buffer_size = 8192) {
-    construct_with_zval(_p, _buffer_size);
+  PHPInputTransport(zval* _p, size_t _buffer_size = 8192) : PHPTransport(_p, _buffer_size) {
   }
 
   ~PHPInputTransport() {
@@ -278,24 +283,21 @@ public:
 
   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);
+      zval args[1], ret, putbackfn;
+      ZVAL_STRINGL(&args[0], buffer_ptr, buffer_used);
+      ZVAL_STRING(&putbackfn, "putBack");
+      ZVAL_NULL(&ret);
 
-      TSRMLS_FETCH();
+      call_user_function(EG(function_table), &(this->t), &putbackfn, &ret, 1, args);
 
-      zval ret;
-      ZVAL_NULL(&ret);
-      call_user_function(EG(function_table), &t, &putbackfn, &ret, 1, args TSRMLS_CC);
-      zval_ptr_dtor(args);
+      zval_dtor(&putbackfn);
       zval_dtor(&ret);
+      zval_dtor(&args[0]);
+      if (EG(exception)) {
+        zend_object *ex = EG(exception);
+        EG(exception) = nullptr;
+        throw PHPExceptionWrapper(ex);
+      }
     }
     buffer_used = 0;
     buffer_ptr = buffer;
@@ -303,7 +305,7 @@ public:
 
   void skip(size_t len) {
     while (len) {
-      size_t chunk_size = MIN(len, buffer_used);
+      size_t chunk_size = std::min(len, buffer_used);
       if (chunk_size) {
         buffer_ptr = reinterpret_cast<char*>(buffer_ptr) + chunk_size;
         buffer_used -= chunk_size;
@@ -316,7 +318,7 @@ public:
 
   void readBytes(void* buf, size_t len) {
     while (len) {
-      size_t chunk_size = MIN(len, buffer_used);
+      size_t chunk_size = std::min(len, buffer_used);
       if (chunk_size) {
         memcpy(buf, buffer_ptr, chunk_size);
         buffer_ptr = reinterpret_cast<char*>(buffer_ptr) + chunk_size;
@@ -357,29 +359,29 @@ 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);
+    zval args[1];
+    zval funcname;
 
-    TSRMLS_FETCH();
+    ZVAL_NULL(&retval);
+    ZVAL_LONG(&args[0], buffer_size);
 
-    zval funcname;
-    ZVAL_STRING(&funcname, "read", 0);
+    ZVAL_STRING(&funcname, "read");
 
-    call_user_function(EG(function_table), &t, &funcname, &retval, 1, args TSRMLS_CC);
-    zval_ptr_dtor(args);
+    call_user_function(EG(function_table), &(this->t), &funcname, &retval, 1, args);
+    zval_dtor(&args[0]);
+    zval_dtor(&funcname);
 
     if (EG(exception)) {
       zval_dtor(&retval);
-      zval* ex = EG(exception);
-      EG(exception) = NULL;
+
+      zend_object *ex = EG(exception);
+      EG(exception) = nullptr;
       throw PHPExceptionWrapper(ex);
     }
 
     buffer_used = Z_STRLEN(retval);
     memcpy(buffer, Z_STRVAL(retval), buffer_used);
+
     zval_dtor(&retval);
 
     buffer_ptr = buffer;
@@ -387,57 +389,130 @@ protected:
 
 };
 
+static
 void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec);
+static
 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);
-void protocol_writeMessageBegin(zval *transport, const char* method_name, int32_t msgtype, int32_t seqID);
-
+static
+void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval* value, HashTable* fieldspec);
+static inline
+bool ttype_is_scalar(int8_t t);
 
 // Create a PHP object given a typename and call the ctor, optionally passing up to 2 arguments
-void createObject(const 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);
+static
+void createObject(const char* obj_typename, zval* return_value, int nargs = 0, zval* arg1 = nullptr, zval* arg2 = nullptr) {
+  /* is there a better way to do that on the stack ? */
+  zend_string *obj_name = zend_string_init(obj_typename, strlen(obj_typename), 0);
+  zend_class_entry* ce = zend_fetch_class(obj_name, ZEND_FETCH_CLASS_DEFAULT);
+  zend_string_release(obj_name);
+
   if (! ce) {
-    php_error_docref(NULL TSRMLS_CC, E_ERROR, "Class %s does not exist", obj_typename);
+    php_error_docref(nullptr, 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);
+  object_and_properties_init(return_value, ce, nullptr);
+  zend_function* constructor = zend_std_get_constructor(Z_OBJ_P(return_value));
+  zval ctor_rv;
+  zend_call_method(return_value, ce, &constructor, NULL, 0, &ctor_rv, nargs, arg1, arg2);
+  zval_dtor(&ctor_rv);
+  if (EG(exception)) {
+    zend_object *ex = EG(exception);
+    EG(exception) = nullptr;
+    throw PHPExceptionWrapper(ex);
+  }
 }
 
+static
 void throw_tprotocolexception(const char* what, long errorcode) {
-  TSRMLS_FETCH();
+  zval zwhat, zerrorcode;
+
+  ZVAL_STRING(&zwhat, what);
+  ZVAL_LONG(&zerrorcode, errorcode);
 
-  zval *zwhat, *zerrorcode;
-  MAKE_STD_ZVAL(zwhat);
-  MAKE_STD_ZVAL(zerrorcode);
+  zval ex;
+  createObject("\\Thrift\\Exception\\TProtocolException", &ex, 2, &zwhat, &zerrorcode);
 
-  ZVAL_STRING(zwhat, what, 1);
-  ZVAL_LONG(zerrorcode, errorcode);
+  zval_dtor(&zwhat);
+  zval_dtor(&zerrorcode);
 
-  zval* ex;
-  MAKE_STD_ZVAL(ex);
-  createObject("\\Thrift\\Exception\\TProtocolException", ex, 2, zwhat, zerrorcode);
-  zval_ptr_dtor(&zwhat);
-  zval_ptr_dtor(&zerrorcode);
-  throw PHPExceptionWrapper(ex);
+  throw PHPExceptionWrapper(&ex);
 }
 
 // Sets EG(exception), call this and then RETURN_NULL();
-void throw_zend_exception_from_std_exception(const std::exception& ex TSRMLS_DC) {
-  zend_throw_exception(zend_exception_get_default(TSRMLS_C), const_cast<char*>(ex.what()), 0 TSRMLS_CC);
+static
+void throw_zend_exception_from_std_exception(const std::exception& ex) {
+  zend_throw_exception(zend_exception_get_default(), const_cast<char*>(ex.what()), 0);
+}
+
+static
+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);
 }
 
+static inline
+bool zval_is_bool(zval* v) {
+  return Z_TYPE_P(v) == IS_TRUE || Z_TYPE_P(v) == IS_FALSE;
+}
 
+static
 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
+  ZVAL_NULL(return_value);
 
   switch (thrift_typeID) {
     case T_STOP:
@@ -445,20 +520,28 @@ void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval
       RETURN_NULL();
       return;
     case T_STRUCT: {
-      if (zend_hash_find(fieldspec, "class", 6, (void**)&val_ptr) != SUCCESS) {
+      zval* val_ptr = zend_hash_str_find(fieldspec, "class", sizeof("class")-1);
+      if (val_ptr == nullptr) {
         throw_tprotocolexception("no class type in spec", INVALID_DATA);
         skip_element(T_STRUCT, transport);
         RETURN_NULL();
       }
-      char* structType = Z_STRVAL_PP(val_ptr);
+
+      char* structType = Z_STRVAL_P(val_ptr);
+      // Create an object in PHP userland based on our spec
       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);
+
+      zval* spec = zend_read_static_property(Z_OBJCE_P(return_value), "_TSPEC", sizeof("_TSPEC")-1, false);
+      if (EG(exception)) {
+        zend_object *ex = EG(exception);
+        EG(exception) = nullptr;
+        throw PHPExceptionWrapper(ex);
+      }
       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));
@@ -510,10 +593,10 @@ void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval
     case T_STRING: {
       uint32_t size = transport.readU32();
       if (size) {
-        char* strbuf = (char*) emalloc(size + 1);
+        char strbuf[size+1];
         transport.readBytes(strbuf, size);
         strbuf[size] = '\0';
-        ZVAL_STRINGL(return_value, strbuf, size, 0);
+        ZVAL_STRINGL(return_value, strbuf, size);
       } else {
         ZVAL_EMPTY_STRING(return_value);
       }
@@ -525,43 +608,38 @@ void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval
       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);
+      zval *val_ptr;
+      val_ptr = zend_hash_str_find(fieldspec, "key", sizeof("key")-1);
+      HashTable* keyspec = Z_ARRVAL_P(val_ptr);
+      val_ptr = zend_hash_str_find(fieldspec, "val", sizeof("val")-1);
+      HashTable* valspec = Z_ARRVAL_P(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);
+        zval key, value;
+
+        binary_deserialize(types[0], transport, &key, keyspec);
+        binary_deserialize(types[1], transport, &value, valspec);
+        if (Z_TYPE(key) == IS_LONG) {
+          zend_hash_index_update(Z_ARR_P(return_value), Z_LVAL(key), &value);
+        } else {
+          if (Z_TYPE(key) != IS_STRING) convert_to_string(&key);
+          zend_symtable_update(Z_ARR_P(return_value), Z_STR(key), &value);
         }
-        else {
-          if (Z_TYPE_P(key) != IS_STRING) convert_to_string(key);
-          zend_symtable_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL);
-        }
-        zval_ptr_dtor(&key);
+        zval_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);
+      zval *val_ptr = zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1);
+      HashTable* elemspec = Z_ARRVAL_P(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);
+        zval value;
+        binary_deserialize(type, transport, &value, elemspec);
+        zend_hash_next_index_insert(Z_ARR_P(return_value), &value);
       }
       return;
     }
@@ -571,28 +649,24 @@ void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval
       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);
+      zval *val_ptr = zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1);
+      HashTable* elemspec = Z_ARRVAL_P(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);
+        zval key, value;
+        ZVAL_TRUE(&value);
 
-        binary_deserialize(type, transport, key, elemspec);
+        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_symtable_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL);
+        if (Z_TYPE(key) == IS_LONG) {
+          zend_hash_index_update(Z_ARR_P(return_value), Z_LVAL(key), &value);
+        } else {
+          if (Z_TYPE(key) != IS_STRING) convert_to_string(&key);
+          zend_symtable_update(Z_ARR_P(return_value), Z_STR(key), &value);
         }
-        zval_ptr_dtor(&key);
+        zval_dtor(&key);
       }
       return;
     }
@@ -603,251 +677,68 @@ void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval
   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 protocol_writeMessageBegin(zval* transport, const char* method_name, int32_t msgtype, int32_t seqID) {
-  TSRMLS_FETCH();
-  zval *args[3];
-
-  MAKE_STD_ZVAL(args[0]);
-  ZVAL_STRINGL(args[0], (char*)method_name, strlen(method_name), 1);
-
-  MAKE_STD_ZVAL(args[1]);
-  ZVAL_LONG(args[1], msgtype);
-
-  MAKE_STD_ZVAL(args[2]);
-  ZVAL_LONG(args[2], seqID);
-
-  zval ret;
-  ZVAL_NULL(&ret);
-
-  zval writeMessagefn;
-  ZVAL_STRING(&writeMessagefn, "writeMessageBegin", 0);
-
-  call_user_function(EG(function_table), &transport, &writeMessagefn, &ret, 3, args TSRMLS_CC);
-
-  zval_ptr_dtor(&args[0]);
-  zval_ptr_dtor(&args[1]);
-  zval_ptr_dtor(&args[2]);
-  zval_dtor(&ret);
-}
-
-void binary_serialize_hashtable_key(int8_t keytype, PHPOutputTransport& transport, HashTable* ht, HashPosition& ht_pos) {
+static
+void binary_serialize_hashtable_key(int8_t keytype, PHPOutputTransport& transport, HashTable* ht, HashPosition& ht_pos, HashTable* spec) {
   bool keytype_is_numeric = (!((keytype == T_STRING) || (keytype == T_UTF8) || (keytype == T_UTF16)));
 
-  char* key;
+  zend_string* key;
   uint key_len;
   long index = 0;
 
-  zval* z;
-  MAKE_STD_ZVAL(z);
+  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);
+  int res = zend_hash_get_current_key_ex(ht, &key, (zend_ulong*)&index, &ht_pos);
+  if (res == HASH_KEY_IS_STRING) {
+    ZVAL_STR_COPY(&z, key);
   } 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);
+    ZVAL_LONG(&z, index);
   }
-  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)));
+  binary_serialize(keytype, transport, &z, spec);
+  zval_dtor(&z);
 }
 
-//is used to validate objects before serialization and after deserialization. For now, only required fields are validated.
 static
-void validate_thrift_object(zval* object) {
-
-  HashPosition key_ptr;
-  zval** val_ptr;
-
-  TSRMLS_FETCH();
-  zend_class_entry* object_class_entry = zend_get_class_entry(object TSRMLS_CC);
-  HashTable* spec = Z_ARRVAL_P(zend_read_static_property(object_class_entry, "_TSPEC", 6, false 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);
-
-    zend_hash_find(fieldspec, "isRequired", 11, (void**)&val_ptr);
-    bool is_required = Z_BVAL_PP(val_ptr);
-
-    zval* prop = zend_read_property(object_class_entry, object, varname, strlen(varname), false TSRMLS_CC);
-
-    if (is_required && Z_TYPE_P(prop) == IS_NULL) {
-        char errbuf[128];
-        snprintf(errbuf, 128, "Required field %s.%s is unset!", object_class_entry->name, varname);
-        throw_tprotocolexception(errbuf, INVALID_DATA);
-    }
-  }
-}
-
-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) {
-      validate_thrift_object(zthis);
-      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) {
+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) {
+      if (Z_TYPE_P(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);
-      if (Z_TYPE_P(spec) != IS_ARRAY) {
+      zval* spec = zend_read_static_property(Z_OBJCE_P(value), "_TSPEC", sizeof("_TSPEC")-1, true);
+      if (!spec || Z_TYPE_P(spec) != IS_ARRAY) {
         throw_tprotocolexception("Attempt to send non-Thrift object as a T_STRUCT", INVALID_DATA);
       }
-      binary_serialize_spec(*value, transport, Z_ARRVAL_P(spec));
+      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);
+      if (!zval_is_bool(value)) convert_to_boolean(value);
+      transport.writeI8(Z_TYPE_INFO_P(value) == IS_TRUE ? 1 : 0);
       return;
     case T_BYTE:
-      if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value);
-      transport.writeI8(Z_LVAL_PP(value));
+      if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
+      transport.writeI8(Z_LVAL_P(value));
       return;
     case T_I16:
-      if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value);
-      transport.writeI16(Z_LVAL_PP(value));
+      if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
+      transport.writeI16(Z_LVAL_P(value));
       return;
     case T_I32:
-      if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value);
-      transport.writeI32(Z_LVAL_PP(value));
+      if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
+      transport.writeI32(Z_LVAL_P(value));
       return;
     case T_I64:
     case T_U64: {
       int64_t l_data;
 #if defined(_LP64) || defined(_WIN64)
-      if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value);
-      l_data = Z_LVAL_PP(value);
+      if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
+      l_data = Z_LVAL_P(value);
 #else
-      if (Z_TYPE_PP(value) != IS_DOUBLE) convert_to_double(*value);
-      l_data = (int64_t)Z_DVAL_PP(value);
+      if (Z_TYPE_P(value) != IS_DOUBLE) convert_to_double(value);
+      l_data = (int64_t)Z_DVAL_P(value);
 #endif
       transport.writeI64(l_data);
     } return;
@@ -856,124 +747,265 @@ void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval*
         int64_t c;
         double d;
       } a;
-      if (Z_TYPE_PP(value) != IS_DOUBLE) convert_to_double(*value);
-      a.d = Z_DVAL_PP(value);
+      if (Z_TYPE_P(value) != IS_DOUBLE) convert_to_double(value);
+      a.d = Z_DVAL_P(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));
+      if (Z_TYPE_P(value) != IS_STRING) convert_to_string(value);
+      transport.writeString(Z_STRVAL_P(value), Z_STRLEN_P(value));
       return;
     case T_MAP: {
-      if (Z_TYPE_PP(value) != IS_ARRAY) convert_to_array(*value);
-      if (Z_TYPE_PP(value) != IS_ARRAY) {
+      if (Z_TYPE_P(value) != IS_ARRAY) convert_to_array(value);
+      if (Z_TYPE_P(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;
+      HashTable* ht = Z_ARRVAL_P(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);
+      val_ptr = zend_hash_str_find(fieldspec, "ktype", sizeof("ktype")-1);
+      if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
+      uint8_t keytype = Z_LVAL_P(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);
+      val_ptr = zend_hash_str_find(fieldspec, "vtype", sizeof("vtype")-1);
+      if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
+      uint8_t valtype = Z_LVAL_P(val_ptr);
       transport.writeI8(valtype);
 
-      zend_hash_find(fieldspec, "val", 4, (void**)&val_ptr);
-      HashTable* valspec = Z_ARRVAL_PP(val_ptr);
+      val_ptr = zend_hash_str_find(fieldspec, "val", sizeof("val")-1);
+      HashTable* valspec = Z_ARRVAL_P(val_ptr);
+      HashTable* keyspec = Z_ARRVAL_P(zend_hash_str_find(fieldspec, "key", sizeof("key")-1));
 
       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);
+      for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr);
+           (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr;
+           zend_hash_move_forward_ex(ht, &key_ptr)) {
+        binary_serialize_hashtable_key(keytype, transport, ht, key_ptr, keyspec);
         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) {
+      if (Z_TYPE_P(value) != IS_ARRAY) convert_to_array(value);
+      if (Z_TYPE_P(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;
+      HashTable* ht = Z_ARRVAL_P(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);
+      val_ptr = zend_hash_str_find(fieldspec, "etype", sizeof("etype")-1);
+      if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
+      uint8_t valtype = Z_LVAL_P(val_ptr);
       transport.writeI8(valtype);
 
-      zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr);
-      HashTable* valspec = Z_ARRVAL_PP(val_ptr);
+      val_ptr = zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1);
+      HashTable* valspec = Z_ARRVAL_P(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)) {
+      for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr);
+           (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr;
+           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) {
+      if (Z_TYPE_P(value) != IS_ARRAY) convert_to_array(value);
+      if (Z_TYPE_P(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;
+      HashTable* ht = Z_ARRVAL_P(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);
+      val_ptr = zend_hash_str_find(fieldspec, "etype", sizeof("etype")-1);
+      HashTable* spec = Z_ARRVAL_P(zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1));
+      if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
+      uint8_t keytype = Z_LVAL_P(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);
+      if(ttype_is_scalar(keytype)){
+        for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr);
+             (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr;
+             zend_hash_move_forward_ex(ht, &key_ptr)) {
+          binary_serialize_hashtable_key(keytype, transport, ht, key_ptr, spec);
+        }
+      } else {
+        for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr);
+             (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr;
+             zend_hash_move_forward_ex(ht, &key_ptr)) {
+          binary_serialize(keytype, transport, val_ptr, spec);
+        }
       }
     } return;
   };
+
   char errbuf[128];
-  sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID);
+  snprintf(errbuf, 128, "Unknown thrift typeID %d", thrift_typeID);
   throw_tprotocolexception(errbuf, INVALID_DATA);
 }
 
+static
+void protocol_writeMessageBegin(zval* transport, zend_string* method_name, int32_t msgtype, int32_t seqID) {
+  zval args[3];
+  zval ret;
+  zval writeMessagefn;
+
+  ZVAL_STR_COPY(&args[0], method_name);
+  ZVAL_LONG(&args[1], msgtype);
+  ZVAL_LONG(&args[2], seqID);
+  ZVAL_NULL(&ret);
+  ZVAL_STRING(&writeMessagefn, "writeMessageBegin");
+
+  call_user_function(EG(function_table), transport, &writeMessagefn, &ret, 3, args);
+
+  zval_dtor(&writeMessagefn);
+  zval_dtor(&args[2]); zval_dtor(&args[1]); zval_dtor(&args[0]);
+  zval_dtor(&ret);
+  if (EG(exception)) {
+    zend_object *ex = EG(exception);
+    EG(exception) = nullptr;
+    throw PHPExceptionWrapper(ex);
+  }
+}
+
+static inline
+bool ttype_is_int(int8_t t) {
+  return ((t == T_BYTE) || ((t >= T_I16)  && (t <= T_I64)));
+}
+static inline
+bool ttype_is_scalar(int8_t t) {
+  return !((t == T_STRUCT) || ( t== T_MAP)  || (t == T_SET) || (t == T_LIST));
+}
 
+static 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)));
+}
+
+//is used to validate objects before serialization and after deserialization. For now, only required fields are validated.
+static
+void validate_thrift_object(zval* object) {
+    zend_class_entry* object_class_entry = Z_OBJCE_P(object);
+    zval* is_validate = zend_read_static_property(object_class_entry, "isValidate", sizeof("isValidate")-1, true);
+    zval* spec = zend_read_static_property(object_class_entry, "_TSPEC", sizeof("_TSPEC")-1, true);
+    HashPosition key_ptr;
+    zval* val_ptr;
+
+    if (is_validate && Z_TYPE_INFO_P(is_validate) == IS_TRUE) {
+        for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(spec), &key_ptr);
+           (val_ptr = zend_hash_get_current_data_ex(Z_ARRVAL_P(spec), &key_ptr)) != nullptr;
+           zend_hash_move_forward_ex(Z_ARRVAL_P(spec), &key_ptr)) {
+
+            zend_ulong fieldno;
+            if (zend_hash_get_current_key_ex(Z_ARRVAL_P(spec), nullptr, &fieldno, &key_ptr) != HASH_KEY_IS_LONG) {
+              throw_tprotocolexception("Bad keytype in TSPEC (expected 'long')", INVALID_DATA);
+              return;
+            }
+            HashTable* fieldspec = Z_ARRVAL_P(val_ptr);
+
+            // field name
+            zval* zvarname = zend_hash_str_find(fieldspec, "var", sizeof("var")-1);
+            char* varname = Z_STRVAL_P(zvarname);
+
+            zval* is_required = zend_hash_str_find(fieldspec, "isRequired", sizeof("isRequired")-1);
+            zval rv;
+            zval* prop = zend_read_property(object_class_entry, object, varname, strlen(varname), false, &rv);
+
+            if (Z_TYPE_INFO_P(is_required) == IS_TRUE && Z_TYPE_P(prop) == IS_NULL) {
+                char errbuf[128];
+                snprintf(errbuf, 128, "Required field %s.%s is unset!", ZSTR_VAL(object_class_entry->name), varname);
+                throw_tprotocolexception(errbuf, INVALID_DATA);
+            }
+        }
+    }
+}
+
+static
+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')
+  zend_class_entry* ce = Z_OBJCE_P(zthis);
+  while (true) {
+    int8_t ttype = transport.readI8();
+    if (ttype == T_STOP) {
+      validate_thrift_object(zthis);
+      return;
+    }
+
+    int16_t fieldno = transport.readI16();
+    zval* val_ptr = zend_hash_index_find(spec, fieldno);
+    if (val_ptr != nullptr) {
+      HashTable* fieldspec = Z_ARRVAL_P(val_ptr);
+      // pull the field name
+      val_ptr = zend_hash_str_find(fieldspec, "var", sizeof("var")-1);
+      char* varname = Z_STRVAL_P(val_ptr);
+
+      // and the type
+      val_ptr = zend_hash_str_find(fieldspec, "type", sizeof("type")-1);
+      if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
+      int8_t expected_ttype = Z_LVAL_P(val_ptr);
+
+      if (ttypes_are_compatible(ttype, expected_ttype)) {
+        zval rv;
+        ZVAL_UNDEF(&rv);
+
+        binary_deserialize(ttype, transport, &rv, fieldspec);
+        zend_update_property(ce, zthis, varname, strlen(varname), &rv);
+
+        zval_ptr_dtor(&rv);
+      } else {
+        skip_element(ttype, transport);
+      }
+    } else {
+      skip_element(ttype, transport);
+    }
+  }
+}
+
+static
 void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec) {
 
   validate_thrift_object(zthis);
 
   HashPosition key_ptr;
-  zval** val_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);
+       (val_ptr = zend_hash_get_current_data_ex(spec, &key_ptr)) != nullptr;
+       zend_hash_move_forward_ex(spec, &key_ptr)) {
 
-  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) {
+    zend_ulong fieldno;
+    if (zend_hash_get_current_key_ex(spec, nullptr, &fieldno, &key_ptr) != HASH_KEY_IS_LONG) {
       throw_tprotocolexception("Bad keytype in TSPEC (expected 'long')", INVALID_DATA);
       return;
     }
-    HashTable* fieldspec = Z_ARRVAL_PP(val_ptr);
+    HashTable* fieldspec = Z_ARRVAL_P(val_ptr);
 
     // field name
-    zend_hash_find(fieldspec, "var", 4, (void**)&val_ptr);
-    char* varname = Z_STRVAL_PP(val_ptr);
+    val_ptr = zend_hash_str_find(fieldspec, "var", sizeof("var")-1);
+    char* varname = Z_STRVAL_P(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);
+    val_ptr = zend_hash_str_find(fieldspec, "type", sizeof("type")-1);
+    if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
+    int8_t ttype = Z_LVAL_P(val_ptr);
+
+    zval rv;
+    zval* prop = zend_read_property(Z_OBJCE_P(zthis), zthis, varname, strlen(varname), false, &rv);
 
-    zval* prop = zend_read_property(ce, zthis, varname, strlen(varname), false TSRMLS_CC);
+    if (Z_TYPE_P(prop) == IS_REFERENCE){
+      ZVAL_UNREF(prop);
+    }
     if (Z_TYPE_P(prop) != IS_NULL) {
       transport.writeI8(ttype);
       transport.writeI16(fieldno);
-      binary_serialize(ttype, transport, &prop, fieldspec);
+      binary_serialize(ttype, transport, prop, fieldspec);
     }
   }
   transport.writeI8(T_STOP); // struct end
@@ -981,104 +1013,56 @@ void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable
 
 // 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();
+  zval *protocol;
+  zval *request_struct;
+  zend_string *method_name;
+  long msgtype, seqID;
+  zend_bool strict_write;
+
+  if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "oSlolb",
+    &protocol, &method_name, &msgtype,
+    &request_struct, &seqID, &strict_write) == FAILURE) {
+      return;
   }
 
-
   try {
-    PHPOutputTransport transport(*args[0]);
-    zval *protocol = *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;
-    protocol_writeMessageBegin(protocol, method_name, msgtype, seqID);
-    zval* spec = zend_read_static_property(zend_get_class_entry(request_struct TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC);
-    if (Z_TYPE_P(spec) != IS_ARRAY) {
-        throw_tprotocolexception("Attempt to send non-Thrift object", INVALID_DATA);
+    zval* spec = zend_read_static_property(Z_OBJCE_P(request_struct), "_TSPEC", sizeof("_TSPEC")-1, true);
+
+    if (!spec || Z_TYPE_P(spec) != IS_ARRAY) {
+      throw_tprotocolexception("Attempt serialize from non-Thrift object", INVALID_DATA);
     }
+
+    PHPOutputTransport transport(protocol);
+    protocol_writeMessageBegin(protocol, method_name, (int32_t) msgtype, (int32_t) seqID);
     binary_serialize_spec(request_struct, transport, Z_ARRVAL_P(spec));
     transport.flush();
+
   } catch (const PHPExceptionWrapper& ex) {
-    zend_throw_exception_object(ex TSRMLS_CC);
+    // ex will be destructed, so copy to a zval that zend_throw_exception_object can take ownership of
+    zval myex;
+    ZVAL_COPY(&myex, ex);
+    zend_throw_exception_object(&myex);
     RETURN_NULL();
   } catch (const std::exception& ex) {
-    throw_zend_exception_from_std_exception(ex TSRMLS_CC);
+    throw_zend_exception_from_std_exception(ex);
     RETURN_NULL();
   }
 }
 
+
 // 4 params: $transport $response_Typename $strict_read $buffer_size
 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);
+  zval *protocol;
+  zend_string *obj_typename;
+  zend_bool strict_read;
+  size_t buffer_size = 8192;
 
-  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();
-  }
-
-  if (argc == 4 && Z_TYPE_PP(args[3]) != IS_LONG) {
-    php_error_docref(NULL TSRMLS_CC, E_ERROR, "4nd parameter is not an integer (typename of expected buffer size)");
-    efree(args);
-    RETURN_NULL();
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "oSb|l", &protocol, &obj_typename, &strict_read, &buffer_size) == FAILURE) {
+    return;
   }
 
   try {
-    size_t buffer_size = 8192;
-    if (argc == 4) {
-      buffer_size = Z_LVAL_PP(args[3]);
-    }
-
-    PHPInputTransport transport(*args[0], buffer_size);
-    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;
-
+    PHPInputTransport transport(protocol, buffer_size);
     int8_t messageType = 0;
     int32_t sz = transport.readI32();
 
@@ -1104,81 +1088,64 @@ PHP_FUNCTION(thrift_protocol_read_binary) {
     }
 
     if (messageType == T_EXCEPTION) {
-      zval* ex;
-      MAKE_STD_ZVAL(ex);
-      createObject("\\Thrift\\Exception\\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);
+      zval ex;
+      createObject("\\Thrift\\Exception\\TApplicationException", &ex);
+      zval* spec = zend_read_static_property(Z_OBJCE(ex), "_TSPEC", sizeof("_TPSEC")-1, false);
+      if (EG(exception)) {
+        zend_object *ex = EG(exception);
+        EG(exception) = nullptr;
+        throw PHPExceptionWrapper(ex);
+      }
+      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);
+    createObject(ZSTR_VAL(obj_typename), return_value);
+    zval* spec = zend_read_static_property(Z_OBJCE_P(return_value), "_TSPEC", sizeof("_TSPEC")-1, true);
+    if (!spec || Z_TYPE_P(spec) != IS_ARRAY) {
+      throw_tprotocolexception("Attempt deserialize to non-Thrift object", INVALID_DATA);
+    }
     binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec));
   } catch (const PHPExceptionWrapper& ex) {
-    zend_throw_exception_object(ex TSRMLS_CC);
+    // ex will be destructed, so copy to a zval that zend_throw_exception_object can ownership of
+    zval myex;
+    ZVAL_COPY(&myex, ex);
+    zval_dtor(return_value);
+    zend_throw_exception_object(&myex);
     RETURN_NULL();
   } catch (const std::exception& ex) {
-    throw_zend_exception_from_std_exception(ex TSRMLS_CC);
+    throw_zend_exception_from_std_exception(ex);
     RETURN_NULL();
   }
 }
 
-
 // 4 params: $transport $response_Typename $strict_read $buffer_size
 PHP_FUNCTION(thrift_protocol_read_binary_after_message_begin) {
-  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);
+  zval *protocol;
+  zend_string *obj_typename;
+  zend_bool strict_read;
+  size_t buffer_size = 8192;
 
-  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();
-  }
-
-  if (argc == 4 && Z_TYPE_PP(args[3]) != IS_LONG) {
-    php_error_docref(NULL TSRMLS_CC, E_ERROR, "4nd parameter is not an integer (typename of expected buffer size)");
-    efree(args);
-    RETURN_NULL();
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "oSb|l", &protocol, &obj_typename, &strict_read, &buffer_size) == FAILURE) {
+    return;
   }
 
   try {
-    size_t buffer_size = 8192;
-    if (argc == 4) {
-      buffer_size = Z_LVAL_PP(args[3]);
-    }
-
-    PHPInputTransport transport(*args[0], buffer_size);
-    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;
+    PHPInputTransport transport(protocol, buffer_size);
 
-    createObject(obj_typename, return_value);
-    zval* spec = zend_read_static_property(zend_get_class_entry(return_value TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC);
+    createObject(ZSTR_VAL(obj_typename), return_value);
+    zval* spec = zend_read_static_property(Z_OBJCE_P(return_value), "_TSPEC", sizeof("_TSPEC")-1, false);
     binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec));
   } catch (const PHPExceptionWrapper& ex) {
-    zend_throw_exception_object(ex TSRMLS_CC);
+    // ex will be destructed, so copy to a zval that zend_throw_exception_object can take ownership of
+    zval myex;
+    ZVAL_COPY(&myex, ex);
+    zend_throw_exception_object(&myex);
     RETURN_NULL();
   } catch (const std::exception& ex) {
-    throw_zend_exception_from_std_exception(ex TSRMLS_CC);
+    throw_zend_exception_from_std_exception(ex);
     RETURN_NULL();
   }
 }
 
-
-
-#endif /* PHP_VERSION_ID < 70000 && PHP_VERSION_ID > 50000 */
+#endif /* PHP_VERSION_ID >= 70000 */