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
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 @@
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 @@
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 @@
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 @@
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");
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 @@
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 \
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 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
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 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 @@
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);
- void construct_with_zval(zval* _p, size_t _buffer_size) {
+ ZVAL_UNDEF(&t);
+
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 @@
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 @@
}
}
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_dtor(&ret);
- }
- 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_NULL(&ret);
- call_user_function(EG(function_table), &t, &writefn, &ret, 1, args TSRMLS_CC);
- zval_ptr_dtor(args);
+ 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)) {
- zval* ex = EG(exception);
- EG(exception) = NULL;
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
+ throw PHPExceptionWrapper(ex);
+ }
+ }
+ void directWrite(const char* data, size_t len) {
+ zval args[1], ret, writefn;
+
+ ZVAL_STRING(&writefn, "write");
+ ZVAL_STRINGL(&args[0], data, len);
+
+ ZVAL_NULL(&ret);
+ 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)) {
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
throw PHPExceptionWrapper(ex);
}
}
@@ -268,8 +274,7 @@
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 @@
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 args[1], ret, putbackfn;
+ ZVAL_STRINGL(&args[0], buffer_ptr, buffer_used);
+ ZVAL_STRING(&putbackfn, "putBack");
ZVAL_NULL(&ret);
- call_user_function(EG(function_table), &t, &putbackfn, &ret, 1, args TSRMLS_CC);
- zval_ptr_dtor(args);
+
+ call_user_function(EG(function_table), &(this->t), &putbackfn, &ret, 1, 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 @@
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 @@
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 @@
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 args[1];
zval funcname;
- ZVAL_STRING(&funcname, "read", 0);
- call_user_function(EG(function_table), &t, &funcname, &retval, 1, args TSRMLS_CC);
- zval_ptr_dtor(args);
+ ZVAL_NULL(&retval);
+ ZVAL_LONG(&args[0], buffer_size);
+
+ ZVAL_STRING(&funcname, "read");
+
+ 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,222 +389,63 @@
};
+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 *zwhat, *zerrorcode;
- MAKE_STD_ZVAL(zwhat);
- MAKE_STD_ZVAL(zerrorcode);
+ ZVAL_STRING(&zwhat, what);
+ ZVAL_LONG(&zerrorcode, errorcode);
- ZVAL_STRING(zwhat, what, 1);
- ZVAL_LONG(zerrorcode, errorcode);
+ zval ex;
+ createObject("\\Thrift\\Exception\\TProtocolException", &ex, 2, &zwhat, &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);
+ zval_dtor(&zwhat);
+ zval_dtor(&zerrorcode);
+
+ 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);
}
-
-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((int8_t)c);
- }
- case T_I16: {
- uint16_t c;
- transport.readBytes(&c, 2);
- RETURN_LONG((int16_t)ntohs(c));
- }
- case T_I32: {
- uint32_t c;
- transport.readBytes(&c, 4);
- RETURN_LONG((int32_t)ntohl(c));
- }
- case T_U64:
- case T_I64: {
- uint64_t c;
- transport.readBytes(&c, 8);
- RETURN_LONG((int64_t)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_symtable_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_symtable_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);
-}
-
+static
void skip_element(long thrift_typeID, PHPInputTransport& transport) {
switch (thrift_typeID) {
case T_STOP:
@@ -662,68 +505,384 @@
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);
+static inline
+bool zval_is_bool(zval* v) {
+ return Z_TYPE_P(v) == IS_TRUE || Z_TYPE_P(v) == IS_FALSE;
}
-void binary_serialize_hashtable_key(int8_t keytype, PHPOutputTransport& transport, HashTable* ht, HashPosition& ht_pos) {
+static
+void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval* return_value, HashTable* fieldspec) {
+ ZVAL_NULL(return_value);
+
+ switch (thrift_typeID) {
+ case T_STOP:
+ case T_VOID:
+ RETURN_NULL();
+ return;
+ case T_STRUCT: {
+ 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_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();
+ }
+
+ 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));
+ 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((int8_t)c);
+ }
+ case T_I16: {
+ uint16_t c;
+ transport.readBytes(&c, 2);
+ RETURN_LONG((int16_t)ntohs(c));
+ }
+ case T_I32: {
+ uint32_t c;
+ transport.readBytes(&c, 4);
+ RETURN_LONG((int32_t)ntohl(c));
+ }
+ case T_U64:
+ case T_I64: {
+ uint64_t c;
+ transport.readBytes(&c, 8);
+ RETURN_LONG((int64_t)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[size+1];
+ transport.readBytes(strbuf, size);
+ strbuf[size] = '\0';
+ ZVAL_STRINGL(return_value, strbuf, size);
+ } 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);
+
+ 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 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);
+ }
+ 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();
+ 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;
+ binary_deserialize(type, transport, &value, elemspec);
+ zend_hash_next_index_insert(Z_ARR_P(return_value), &value);
+ }
+ 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);
+ 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, value;
+ ZVAL_TRUE(&value);
+
+ binary_deserialize(type, transport, &key, elemspec);
+
+ 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_dtor(&key);
+ }
+ return;
+ }
+ };
+
+ char errbuf[128];
+ sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID);
+ throw_tprotocolexception(errbuf, INVALID_DATA);
+}
+
+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);
+ binary_serialize(keytype, transport, &z, spec);
+ zval_dtor(&z);
}
-inline bool ttype_is_int(int8_t t) {
+static
+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: {
+ 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(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));
+ } return;
+ case T_BOOL:
+ 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_P(value) != IS_LONG) convert_to_long(value);
+ transport.writeI8(Z_LVAL_P(value));
+ return;
+ case T_I16:
+ if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
+ transport.writeI16(Z_LVAL_P(value));
+ return;
+ case T_I32:
+ 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_P(value) != IS_LONG) convert_to_long(value);
+ l_data = Z_LVAL_P(value);
+#else
+ 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;
+ case T_DOUBLE: {
+ union {
+ int64_t c;
+ double d;
+ } a;
+ if (Z_TYPE_P(value) != IS_DOUBLE) convert_to_double(value);
+ a.d = Z_DVAL_P(value);
+ transport.writeI64(a.c);
+ } return;
+ case T_UTF8:
+ case T_UTF16:
+ case T_STRING:
+ 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_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_P(value);
+ zval* 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);
+ 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);
+
+ 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);
+ (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_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_P(value);
+ zval* 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);
+
+ 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);
+ (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_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_P(value);
+ zval* 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;
+ 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];
+ 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));
+}
-inline bool ttypes_are_compatible(int8_t t1, int8_t t2) {
+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)));
@@ -732,70 +891,73 @@
//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;
- 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)) {
- 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));
+ 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);
- 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;
+ // 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);
+ }
+ }
}
- 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);
- }
- }
}
+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')
- TSRMLS_FETCH();
- zend_class_entry* ce = zend_get_class_entry(zthis TSRMLS_CC);
+ zend_class_entry* ce = Z_OBJCE_P(zthis);
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);
+ zval* val_ptr = zend_hash_index_find(spec, fieldno);
+ if (val_ptr != nullptr) {
+ HashTable* fieldspec = Z_ARRVAL_P(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);
+ val_ptr = zend_hash_str_find(fieldspec, "var", sizeof("var")-1);
+ char* varname = Z_STRVAL_P(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);
+ 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 = NULL;
- MAKE_STD_ZVAL(rv);
- binary_deserialize(ttype, transport, rv, fieldspec);
- zend_update_property(ce, zthis, varname, strlen(varname), rv TSRMLS_CC);
+ 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);
@@ -806,174 +968,44 @@
}
}
-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);
- if (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));
- } 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: {
- 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);
-#else
- if (Z_TYPE_PP(value) != IS_DOUBLE) convert_to_double(*value);
- l_data = (int64_t)Z_DVAL_PP(value);
-#endif
- transport.writeI64(l_data);
- } 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);
-}
-
-
+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* prop = zend_read_property(ce, zthis, varname, strlen(varname), false TSRMLS_CC);
+ 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);
- binary_serialize(ttype, transport, &prop, fieldspec);
+ binary_serialize(ttype, transport, prop, fieldspec);
}
}
transport.writeI8(T_STOP); // struct end
@@ -981,104 +1013,56 @@
// 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 *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;
}
- 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();
- }
-
-
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();
+ zval *protocol;
+ zend_string *obj_typename;
+ zend_bool strict_read;
+ size_t buffer_size = 8192;
- 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();
+ 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 @@
}
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();
+ zval *protocol;
+ zend_string *obj_typename;
+ zend_bool strict_read;
+ size_t buffer_size = 8192;
- 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();
+ 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(protocol, buffer_size);
- 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);
+ 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 */
diff --git a/lib/php/src/ext/thrift_protocol/php_thrift_protocol7.cpp b/lib/php/src/ext/thrift_protocol/php_thrift_protocol7.cpp
deleted file mode 100644
index dafc185..0000000
--- a/lib/php/src/ext/thrift_protocol/php_thrift_protocol7.cpp
+++ /dev/null
@@ -1,1102 +0,0 @@
-/*
- * 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 "php.h"
-#include "zend_interfaces.h"
-#include "zend_exceptions.h"
-#include "php_thrift_protocol.h"
-
-#if PHP_VERSION_ID >= 70000
-
-#include <sys/types.h>
-#include <arpa/inet.h>
-
-#include <cstdint>
-#include <stdexcept>
-#include <algorithm>
-
-#ifndef bswap_64
-#define bswap_64(x) (((uint64_t)(x) << 56) | \
- (((uint64_t)(x) << 40) & 0xff000000000000ULL) | \
- (((uint64_t)(x) << 24) & 0xff0000000000ULL) | \
- (((uint64_t)(x) << 8) & 0xff00000000ULL) | \
- (((uint64_t)(x) >> 8) & 0xff000000ULL) | \
- (((uint64_t)(x) >> 24) & 0xff0000ULL) | \
- (((uint64_t)(x) >> 40) & 0xff00ULL) | \
- ((uint64_t)(x) >> 56))
-#endif
-
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-#define htonll(x) bswap_64(x)
-#define ntohll(x) bswap_64(x)
-#elif __BYTE_ORDER == __BIG_ENDIAN
-#define htonll(x) x
-#define ntohll(x) x
-#else
-#error Unknown __BYTE_ORDER
-#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;
-
-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}
-};
-
-zend_module_entry thrift_protocol_module_entry = {
- STANDARD_MODULE_HEADER,
- "thrift_protocol",
- thrift_protocol_functions,
- nullptr,
- nullptr,
- nullptr,
- nullptr,
- nullptr,
- "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() {
- ZVAL_COPY(&ex, _ex);
- snprintf(_what, 40, "PHP exception zval=%p", _ex);
- }
-
- 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;
- char _what[40];
-} ;
-
-class PHPTransport {
-protected:
- PHPTransport(zval* _p, size_t _buffer_size) {
- assert(Z_TYPE_P(_p) == IS_OBJECT);
-
- ZVAL_UNDEF(&t);
-
- buffer = reinterpret_cast<char*>(emalloc(_buffer_size));
- buffer_ptr = buffer;
- buffer_used = 0;
- buffer_size = _buffer_size;
-
- // Get the transport for the passed protocol
- zval gettransport;
- ZVAL_STRING(&gettransport, "getTransport");
- call_user_function(nullptr, _p, &gettransport, &t, 0, nullptr);
-
- zval_dtor(&gettransport);
-
- assert(Z_TYPE(t) == IS_OBJECT);
- }
-
- ~PHPTransport() {
- efree(buffer);
- zval_dtor(&t);
- }
-
- char* buffer;
- char* buffer_ptr;
- size_t buffer_used;
- size_t buffer_size;
-
- zval t;
-};
-
-
-class PHPOutputTransport : public PHPTransport {
-public:
- 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) {
- internalFlush();
- }
- 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() {
- internalFlush();
- directFlush();
- }
-
-protected:
- void internalFlush() {
- if (buffer_used) {
- directWrite(buffer, buffer_used);
- buffer_ptr = buffer;
- buffer_used = 0;
- }
- }
- void directFlush() {
- zval ret, flushfn;
- ZVAL_NULL(&ret);
- ZVAL_STRING(&flushfn, "flush");
-
- call_user_function(EG(function_table), &(this->t), &flushfn, &ret, 0, nullptr);
- zval_dtor(&flushfn);
- zval_dtor(&ret);
- }
- void directWrite(const char* data, size_t len) {
- zval args[1], ret, writefn;
-
- ZVAL_STRING(&writefn, "write");
- ZVAL_STRINGL(&args[0], data, len);
-
- ZVAL_NULL(&ret);
- 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)) {
- zend_object *ex = EG(exception);
- EG(exception) = nullptr;
- throw PHPExceptionWrapper(ex);
- }
- }
-};
-
-class PHPInputTransport : public PHPTransport {
-public:
- PHPInputTransport(zval* _p, size_t _buffer_size = 8192) : PHPTransport(_p, _buffer_size) {
- }
-
- ~PHPInputTransport() {
- put_back();
- }
-
- void put_back() {
- if (buffer_used) {
- zval args[1], ret, putbackfn;
- ZVAL_STRINGL(&args[0], buffer_ptr, buffer_used);
- ZVAL_STRING(&putbackfn, "putBack");
- ZVAL_NULL(&ret);
-
- call_user_function(EG(function_table), &(this->t), &putbackfn, &ret, 1, args);
-
- zval_dtor(&putbackfn);
- zval_dtor(&ret);
- zval_dtor(&args[0]);
- }
- buffer_used = 0;
- buffer_ptr = buffer;
- }
-
- void skip(size_t len) {
- while (len) {
- 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;
- len -= chunk_size;
- }
- if (! len) break;
- refill();
- }
- }
-
- void readBytes(void* buf, size_t len) {
- while (len) {
- 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;
- 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 args[1];
- zval funcname;
-
- ZVAL_NULL(&retval);
- ZVAL_LONG(&args[0], buffer_size);
-
- ZVAL_STRING(&funcname, "read");
-
- 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);
-
- 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;
- }
-
-};
-
-static
-void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec);
-static
-void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec);
-static
-void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval* value, HashTable* fieldspec);
-
-// Create a PHP object given a typename and call the ctor, optionally passing up to 2 arguments
-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(nullptr, E_ERROR, "Class %s does not exist", obj_typename);
- RETURN_NULL();
- }
-
- 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);
-}
-
-static
-void throw_tprotocolexception(const char* what, long errorcode) {
- zval zwhat, zerrorcode;
-
- ZVAL_STRING(&zwhat, what);
- ZVAL_LONG(&zerrorcode, errorcode);
-
- zval ex;
- createObject("\\Thrift\\Exception\\TProtocolException", &ex, 2, &zwhat, &zerrorcode);
-
- zval_dtor(&zwhat);
- zval_dtor(&zerrorcode);
-
- throw PHPExceptionWrapper(&ex);
-}
-
-// Sets EG(exception), call this and then RETURN_NULL();
-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_NULL(return_value);
-
- switch (thrift_typeID) {
- case T_STOP:
- case T_VOID:
- RETURN_NULL();
- return;
- case T_STRUCT: {
- 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_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();
- }
-
- zval* spec = zend_read_static_property(Z_OBJCE_P(return_value), "_TSPEC", sizeof("_TSPEC")-1, false);
- 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((int8_t)c);
- }
- case T_I16: {
- uint16_t c;
- transport.readBytes(&c, 2);
- RETURN_LONG((int16_t)ntohs(c));
- }
- case T_I32: {
- uint32_t c;
- transport.readBytes(&c, 4);
- RETURN_LONG((int32_t)ntohl(c));
- }
- case T_U64:
- case T_I64: {
- uint64_t c;
- transport.readBytes(&c, 8);
- RETURN_LONG((int64_t)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[size+1];
- transport.readBytes(strbuf, size);
- strbuf[size] = '\0';
- ZVAL_STRINGL(return_value, strbuf, size);
- } 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);
-
- 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 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);
- }
- 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();
- 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;
- binary_deserialize(type, transport, &value, elemspec);
- zend_hash_next_index_insert(Z_ARR_P(return_value), &value);
- }
- 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);
- 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, value;
- ZVAL_TRUE(&value);
-
- binary_deserialize(type, transport, &key, elemspec);
-
- 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_dtor(&key);
- }
- return;
- }
- };
-
- char errbuf[128];
- sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID);
- throw_tprotocolexception(errbuf, INVALID_DATA);
-}
-
-static
-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)));
-
- zend_string* key;
- uint key_len;
- long index = 0;
-
- zval z;
-
- int res = zend_hash_get_current_key_ex(ht, &key, (zend_ulong*)&index, &ht_pos);
- if (keytype_is_numeric) {
- if (res == HASH_KEY_IS_STRING) {
- index = strtol(ZSTR_VAL(key), nullptr, 10);
- }
- ZVAL_LONG(&z, index);
- } else {
- char buf[64];
- if (res == HASH_KEY_IS_STRING) {
- ZVAL_STR_COPY(&z, key);
- } else {
- snprintf(buf, 64, "%ld", index);
- ZVAL_STRING(&z, buf);
- }
- }
- binary_serialize(keytype, transport, &z, nullptr);
- zval_dtor(&z);
-}
-
-static
-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: {
- 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(Z_OBJCE_P(value), "_TSPEC", sizeof("_TSPEC")-1, false);
- if (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));
- } return;
- case T_BOOL:
- 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_P(value) != IS_LONG) convert_to_long(value);
- transport.writeI8(Z_LVAL_P(value));
- return;
- case T_I16:
- if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
- transport.writeI16(Z_LVAL_P(value));
- return;
- case T_I32:
- 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_P(value) != IS_LONG) convert_to_long(value);
- l_data = Z_LVAL_P(value);
-#else
- 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;
- case T_DOUBLE: {
- union {
- int64_t c;
- double d;
- } a;
- if (Z_TYPE_P(value) != IS_DOUBLE) convert_to_double(value);
- a.d = Z_DVAL_P(value);
- transport.writeI64(a.c);
- } return;
- case T_UTF8:
- case T_UTF16:
- case T_STRING:
- 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_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_P(value);
- zval* 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);
- 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);
-
- val_ptr = zend_hash_str_find(fieldspec, "val", sizeof("val")-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);
- (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);
- binary_serialize(valtype, transport, val_ptr, valspec);
- }
- } return;
- case T_LIST: {
- 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_P(value);
- zval* 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);
-
- 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);
- (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_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_P(value);
- zval* 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 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);
- (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);
- }
- } return;
- };
-
- char errbuf[128];
- 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);
-}
-
-static inline
-bool ttype_is_int(int8_t t) {
- return ((t == T_BYTE) || ((t >= T_I16) && (t <= T_I64)));
-}
-
-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, false);
- zval* spec = zend_read_static_property(object_class_entry, "_TSPEC", sizeof("_TSPEC")-1, false);
- HashPosition key_ptr;
- zval* val_ptr;
-
- if (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;
-
- 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)) {
-
- 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_P(val_ptr);
-
- // field name
- val_ptr = zend_hash_str_find(fieldspec, "var", sizeof("var")-1);
- char* varname = Z_STRVAL_P(val_ptr);
-
- // thrift 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 ttype = Z_LVAL_P(val_ptr);
-
- 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);
- 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) {
- 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 {
- zval* spec = zend_read_static_property(Z_OBJCE_P(request_struct), "_TSPEC", sizeof("_TSPEC")-1, false);
-
- if (Z_TYPE_P(spec) != IS_ARRAY) {
- throw_tprotocolexception("Attempt to send 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) {
- // 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);
- RETURN_NULL();
- }
-}
-
-
-// 4 params: $transport $response_Typename $strict_read $buffer_size
-PHP_FUNCTION(thrift_protocol_read_binary) {
- 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);
- 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;
- createObject("\\Thrift\\Exception\\TApplicationException", &ex);
- zval* spec = zend_read_static_property(Z_OBJCE(ex), "_TSPEC", sizeof("_TPSEC")-1, false);
- binary_deserialize_spec(&ex, transport, Z_ARRVAL_P(spec));
- throw PHPExceptionWrapper(&ex);
- }
-
- 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) {
- // 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);
- RETURN_NULL();
- }
-}
-
-// 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 */