THRIFT-4353: support php ext read data after message begin
Client: php

This closes #1383
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 4c9062e..5374286 100644
--- a/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp
+++ b/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp
@@ -98,6 +98,7 @@
 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}
 } ;
 
@@ -1123,4 +1124,61 @@
   }
 }
 
+
+// 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);
+
+  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();
+  }
+
+  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;
+
+    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();
+  } catch (const std::exception& ex) {
+    throw_zend_exception_from_std_exception(ex TSRMLS_CC);
+    RETURN_NULL();
+  }
+}
+
+
+
 #endif /* PHP_VERSION_ID < 70000 && PHP_VERSION_ID > 50000 */
diff --git a/lib/php/src/ext/thrift_protocol/php_thrift_protocol.h b/lib/php/src/ext/thrift_protocol/php_thrift_protocol.h
index 44d03cc..0420997 100644
--- a/lib/php/src/ext/thrift_protocol/php_thrift_protocol.h
+++ b/lib/php/src/ext/thrift_protocol/php_thrift_protocol.h
@@ -21,6 +21,7 @@
 
 PHP_FUNCTION(thrift_protocol_write_binary);
 PHP_FUNCTION(thrift_protocol_read_binary);
+PHP_FUNCTION(thrift_protocol_read_binary_after_message_begin);
 
 extern zend_module_entry thrift_protocol_module_entry;
 #define phpext_thrift_protocol_ptr &thrift_protocol_module_entry
diff --git a/lib/php/src/ext/thrift_protocol/php_thrift_protocol7.cpp b/lib/php/src/ext/thrift_protocol/php_thrift_protocol7.cpp
index 3c6c3db..dafc185 100644
--- a/lib/php/src/ext/thrift_protocol/php_thrift_protocol7.cpp
+++ b/lib/php/src/ext/thrift_protocol/php_thrift_protocol7.cpp
@@ -88,6 +88,7 @@
 static zend_function_entry thrift_protocol_functions[] = {
   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}
 };
 
@@ -959,6 +960,10 @@
 
     zval rv;
     zval* prop = zend_read_property(Z_OBJCE_P(zthis), zthis, varname, strlen(varname), false, &rv);
+
+    if (Z_TYPE_P(prop) == IS_REFERENCE){
+      ZVAL_UNREF(prop);   
+    }
     if (Z_TYPE_P(prop) != IS_NULL) {
       transport.writeI8(ttype);
       transport.writeI16(fieldno);
@@ -1068,4 +1073,30 @@
   }
 }
 
+// 4 params: $transport $response_Typename $strict_read $buffer_size
+PHP_FUNCTION(thrift_protocol_read_binary_after_message_begin) {
+  zval *protocol;
+  zend_string *obj_typename;
+  zend_bool strict_read;
+  size_t buffer_size = 8192;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "oSb|l", &protocol, &obj_typename, &strict_read, &buffer_size) == FAILURE) {
+    return;
+  }
+
+  try {
+    PHPInputTransport transport(protocol, buffer_size);
+
+    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);
+    RETURN_NULL();
+  } catch (const std::exception& ex) {
+    throw_zend_exception_from_std_exception(ex);
+    RETURN_NULL();
+  }
+}
+
 #endif /* PHP_VERSION_ID >= 70000 */