summaryrefslogtreecommitdiff
path: root/lib/php
diff options
context:
space:
mode:
authorRobert Lu <robberphex@gmail.com>2017-10-04 03:18:38 +0800
committerJames E. King, III <jking@apache.org>2017-10-25 08:57:04 -0400
commit9dff0efc1e099a67e386c95bddc4079daf2b7141 (patch)
tree0cd5e7f0fe7679c0fd2e1682c31d026c667b3acc /lib/php
parent1df2d9b801553a1addc1df112bedde09527dbfdd (diff)
downloadthrift-9dff0efc1e099a67e386c95bddc4079daf2b7141.tar.gz
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
Diffstat (limited to 'lib/php')
-rwxr-xr-xlib/php/Makefile.am1
-rw-r--r--lib/php/src/ext/thrift_protocol/config.m431
-rw-r--r--lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp1113
-rw-r--r--lib/php/src/ext/thrift_protocol/php_thrift_protocol7.cpp1102
4 files changed, 559 insertions, 1688 deletions
diff --git a/lib/php/Makefile.am b/lib/php/Makefile.am
index 8e6296000..5aa3be49b 100755
--- a/lib/php/Makefile.am
+++ b/lib/php/Makefile.am
@@ -133,7 +133,6 @@ EXTRA_DIST = \
src/autoload.php \
src/ext/thrift_protocol/config.m4 \
src/ext/thrift_protocol/config.w32 \
- src/ext/thrift_protocol/php_thrift_protocol7.cpp \
src/ext/thrift_protocol/php_thrift_protocol.cpp \
src/ext/thrift_protocol/php_thrift_protocol.h \
src/ext/thrift_protocol/run-tests.php \
diff --git a/lib/php/src/ext/thrift_protocol/config.m4 b/lib/php/src/ext/thrift_protocol/config.m4
index 0fe3ef4de..c54be3eb1 100644
--- a/lib/php/src/ext/thrift_protocol/config.m4
+++ b/lib/php/src/ext/thrift_protocol/config.m4
@@ -2,24 +2,31 @@ dnl Copyright (C) 2009 Facebook
dnl Copying and distribution of this file, with or without modification,
dnl are permitted in any medium without royalty provided the copyright
dnl notice and this notice are preserved.
+dnl
+dnl Licensed to the Apache Software Foundation (ASF) under one
+dnl or more contributor license agreements. See the NOTICE file
+dnl distributed with this work for additional information
+dnl regarding copyright ownership. The ASF licenses this file
+dnl to you under the Apache License, Version 2.0 (the
+dnl "License"); you may not use this file except in compliance
+dnl with the License. You may obtain a copy of the License at
+dnl
+dnl http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing,
+dnl software distributed under the License is distributed on an
+dnl "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+dnl KIND, either express or implied. See the License for the
+dnl specific language governing permissions and limitations
+dnl under the License.
PHP_ARG_ENABLE(thrift_protocol, whether to enable the thrift_protocol extension,
-[ --enable-thrift_protocol Enable the fbthrift_protocol extension])
+[ --enable-thrift_protocol Enable the thrift_protocol extension])
if test "$PHP_THRIFT_PROTOCOL" != "no"; then
PHP_REQUIRE_CXX()
- PHP_ADD_LIBRARY_WITH_PATH(stdc++, "", THRIFT_PROTOCOL_SHARED_LIBADD)
CXXFLAGS="$CXXFLAGS -std=c++11"
- AC_MSG_CHECKING([check for supported PHP versions])
- PHP_THRIFT_FOUND_VERSION=`${PHP_CONFIG} --version`
- PHP_THRIFT_FOUND_VERNUM=`echo "${PHP_THRIFT_FOUND_VERSION}" | $AWK 'BEGIN { FS = "."; } { printf "%d", ([$]1 * 100 + [$]2) * 100 + [$]3;}'`
- if test "$PHP_THRIFT_FOUND_VERNUM" -ge "50000"; then
- PHP_SUBST(THRIFT_PROTOCOL_SHARED_LIBADD)
- PHP_NEW_EXTENSION(thrift_protocol, php_thrift_protocol.cpp php_thrift_protocol7.cpp, $ext_shared)
- AC_MSG_RESULT([supported ($PHP_THRIFT_FOUND_VERSION)])
- else
- AC_MSG_ERROR([unsupported PHP version ($PHP_THRIFT_FOUND_VERSION)])
- fi
+ PHP_NEW_EXTENSION(thrift_protocol, php_thrift_protocol.cpp, $ext_shared)
fi
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 5374286f4..75ef2ec8f 100644
--- a/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp
+++ b/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp
@@ -16,7 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
-
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@@ -26,23 +25,14 @@
#include "zend_exceptions.h"
#include "php_thrift_protocol.h"
-/* GUARD FOR PHP 5 */
-#if PHP_VERSION_ID < 70000 && PHP_VERSION_ID > 50000
+#if PHP_VERSION_ID >= 70000
#include <sys/types.h>
-#if defined( WIN32 ) || defined( _WIN64 )
-typedef int int32_t;
-typedef signed char int8_t;
-typedef unsigned char uint8_t;
-typedef unsigned short uint16_t;
-typedef long long int64_t;
-typedef unsigned uint32_t;
-typedef short int16_t;
-typedef unsigned long long uint64_t;
-#else
-#include <arpa/inet.h>
-#endif
+#include <arpa/inet.h>
+
+#include <cstdint>
#include <stdexcept>
+#include <algorithm>
#ifndef bswap_64
#define bswap_64(x) (((uint64_t)(x) << 56) | \
@@ -96,21 +86,21 @@ const int INVALID_DATA = 1;
const int BAD_VERSION = 4;
static zend_function_entry thrift_protocol_functions[] = {
- PHP_FE(thrift_protocol_write_binary, NULL)
- PHP_FE(thrift_protocol_read_binary, NULL)
- PHP_FE(thrift_protocol_read_binary_after_message_begin, NULL)
- {NULL, NULL, NULL}
-} ;
+ PHP_FE(thrift_protocol_write_binary, nullptr)
+ PHP_FE(thrift_protocol_read_binary, nullptr)
+ PHP_FE(thrift_protocol_read_binary_after_message_begin, nullptr)
+ {nullptr, nullptr, nullptr}
+};
zend_module_entry thrift_protocol_module_entry = {
STANDARD_MODULE_HEADER,
"thrift_protocol",
thrift_protocol_functions,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
"1.0",
STANDARD_MODULE_PROPERTIES
};
@@ -121,42 +111,61 @@ ZEND_GET_MODULE(thrift_protocol)
class PHPExceptionWrapper : public std::exception {
public:
- PHPExceptionWrapper(zval* _ex) throw() : ex(_ex) {
- snprintf(_what, 40, "PHP exception zval=%p", ex);
+ PHPExceptionWrapper(zval* _ex) throw() {
+ ZVAL_COPY(&ex, _ex);
+ snprintf(_what, 40, "PHP exception zval=%p", _ex);
}
- const char* what() const throw() { return _what; }
- ~PHPExceptionWrapper() throw() {}
- operator zval*() const throw() { return const_cast<zval*>(ex); } // Zend API doesn't do 'const'...
+
+ PHPExceptionWrapper(zend_object* _exobj) throw() {
+ ZVAL_OBJ(&ex, _exobj);
+ snprintf(_what, 40, "PHP exception zval=%p", _exobj);
+ }
+ ~PHPExceptionWrapper() throw() {
+ zval_dtor(&ex);
+ }
+
+ const char* what() const throw() {
+ return _what;
+ }
+ operator zval*() const throw() {
+ return const_cast<zval*>(&ex);
+ } // Zend API doesn't do 'const'...
protected:
- zval* ex;
+ zval ex;
char _what[40];
} ;
class PHPTransport {
-public:
- zval* protocol() { return p; }
- zval* transport() { return t; }
protected:
- PHPTransport() {}
+ PHPTransport(zval* _p, size_t _buffer_size) {
+ assert(Z_TYPE_P(_p) == IS_OBJECT);
+
+ ZVAL_UNDEF(&t);
- void construct_with_zval(zval* _p, size_t _buffer_size) {
buffer = reinterpret_cast<char*>(emalloc(_buffer_size));
buffer_ptr = buffer;
buffer_used = 0;
buffer_size = _buffer_size;
- p = _p;
// Get the transport for the passed protocol
zval gettransport;
- ZVAL_STRING(&gettransport, "getTransport", 0);
- MAKE_STD_ZVAL(t);
- ZVAL_NULL(t);
- TSRMLS_FETCH();
- call_user_function(EG(function_table), &p, &gettransport, t, 0, NULL TSRMLS_CC);
+ ZVAL_STRING(&gettransport, "getTransport");
+ call_user_function(nullptr, _p, &gettransport, &t, 0, nullptr);
+
+ zval_dtor(&gettransport);
+
+ if (EG(exception)) {
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
+ throw PHPExceptionWrapper(ex);
+ }
+
+ assert(Z_TYPE(t) == IS_OBJECT);
}
+
~PHPTransport() {
efree(buffer);
- zval_ptr_dtor(&t);
+ zval_dtor(&t);
}
char* buffer;
@@ -164,20 +173,14 @@ protected:
size_t buffer_used;
size_t buffer_size;
- zval* p;
- zval* t;
+ zval t;
};
class PHPOutputTransport : public PHPTransport {
public:
- PHPOutputTransport(zval* _p, size_t _buffer_size = 8192) {
- construct_with_zval(_p, _buffer_size);
- }
-
- ~PHPOutputTransport() {
- //flush();
- }
+ PHPOutputTransport(zval* _p, size_t _buffer_size = 8192) : PHPTransport(_p, _buffer_size) { }
+ ~PHPOutputTransport() { }
void write(const char* data, size_t len) {
if ((len + buffer_used) > buffer_size) {
@@ -235,32 +238,35 @@ protected:
}
}
void directFlush() {
- zval ret;
+ zval ret, flushfn;
ZVAL_NULL(&ret);
- zval flushfn;
- ZVAL_STRING(&flushfn, "flush", 0);
- TSRMLS_FETCH();
- call_user_function(EG(function_table), &t, &flushfn, &ret, 0, NULL TSRMLS_CC);
+ ZVAL_STRING(&flushfn, "flush");
+
+ call_user_function(EG(function_table), &(this->t), &flushfn, &ret, 0, nullptr);
+ zval_dtor(&flushfn);
zval_dtor(&ret);
+ if (EG(exception)) {
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
+ throw PHPExceptionWrapper(ex);
+ }
}
void directWrite(const char* data, size_t len) {
- zval writefn;
- ZVAL_STRING(&writefn, "write", 0);
- char* newbuf = (char*)emalloc(len + 1);
- memcpy(newbuf, data, len);
- newbuf[len] = '\0';
- zval *args[1];
- MAKE_STD_ZVAL(args[0]);
- ZVAL_STRINGL(args[0], newbuf, len, 0);
- TSRMLS_FETCH();
- zval ret;
+ zval args[1], ret, writefn;
+
+ ZVAL_STRING(&writefn, "write");
+ ZVAL_STRINGL(&args[0], data, len);
+
ZVAL_NULL(&ret);
- call_user_function(EG(function_table), &t, &writefn, &ret, 1, args TSRMLS_CC);
- zval_ptr_dtor(args);
+ call_user_function(EG(function_table), &(this->t), &writefn, &ret, 1, args);
+
+ zval_dtor(&writefn);
zval_dtor(&ret);
+ zval_dtor(&args[0]);
+
if (EG(exception)) {
- zval* ex = EG(exception);
- EG(exception) = NULL;
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
throw PHPExceptionWrapper(ex);
}
}
@@ -268,8 +274,7 @@ protected:
class PHPInputTransport : public PHPTransport {
public:
- PHPInputTransport(zval* _p, size_t _buffer_size = 8192) {
- construct_with_zval(_p, _buffer_size);
+ PHPInputTransport(zval* _p, size_t _buffer_size = 8192) : PHPTransport(_p, _buffer_size) {
}
~PHPInputTransport() {
@@ -278,24 +283,21 @@ public:
void put_back() {
if (buffer_used) {
- zval putbackfn;
- ZVAL_STRING(&putbackfn, "putBack", 0);
-
- char* newbuf = (char*)emalloc(buffer_used + 1);
- memcpy(newbuf, buffer_ptr, buffer_used);
- newbuf[buffer_used] = '\0';
-
- zval *args[1];
- MAKE_STD_ZVAL(args[0]);
- ZVAL_STRINGL(args[0], newbuf, buffer_used, 0);
+ zval args[1], ret, putbackfn;
+ ZVAL_STRINGL(&args[0], buffer_ptr, buffer_used);
+ ZVAL_STRING(&putbackfn, "putBack");
+ ZVAL_NULL(&ret);
- TSRMLS_FETCH();
+ call_user_function(EG(function_table), &(this->t), &putbackfn, &ret, 1, args);
- zval ret;
- ZVAL_NULL(&ret);
- call_user_function(EG(function_table), &t, &putbackfn, &ret, 1, args TSRMLS_CC);
- zval_ptr_dtor(args);
+ zval_dtor(&putbackfn);
zval_dtor(&ret);
+ zval_dtor(&args[0]);
+ if (EG(exception)) {
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
+ throw PHPExceptionWrapper(ex);
+ }
}
buffer_used = 0;
buffer_ptr = buffer;
@@ -303,7 +305,7 @@ public:
void skip(size_t len) {
while (len) {
- size_t chunk_size = MIN(len, buffer_used);
+ size_t chunk_size = std::min(len, buffer_used);
if (chunk_size) {
buffer_ptr = reinterpret_cast<char*>(buffer_ptr) + chunk_size;
buffer_used -= chunk_size;
@@ -316,7 +318,7 @@ public:
void readBytes(void* buf, size_t len) {
while (len) {
- size_t chunk_size = MIN(len, buffer_used);
+ size_t chunk_size = std::min(len, buffer_used);
if (chunk_size) {
memcpy(buf, buffer_ptr, chunk_size);
buffer_ptr = reinterpret_cast<char*>(buffer_ptr) + chunk_size;
@@ -357,29 +359,29 @@ protected:
void refill() {
assert(buffer_used == 0);
zval retval;
- ZVAL_NULL(&retval);
-
- zval *args[1];
- MAKE_STD_ZVAL(args[0]);
- ZVAL_LONG(args[0], buffer_size);
+ zval args[1];
+ zval funcname;
- TSRMLS_FETCH();
+ ZVAL_NULL(&retval);
+ ZVAL_LONG(&args[0], buffer_size);
- zval funcname;
- ZVAL_STRING(&funcname, "read", 0);
+ ZVAL_STRING(&funcname, "read");
- call_user_function(EG(function_table), &t, &funcname, &retval, 1, args TSRMLS_CC);
- zval_ptr_dtor(args);
+ call_user_function(EG(function_table), &(this->t), &funcname, &retval, 1, args);
+ zval_dtor(&args[0]);
+ zval_dtor(&funcname);
if (EG(exception)) {
zval_dtor(&retval);
- zval* ex = EG(exception);
- EG(exception) = NULL;
+
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
throw PHPExceptionWrapper(ex);
}
buffer_used = Z_STRLEN(retval);
memcpy(buffer, Z_STRVAL(retval), buffer_used);
+
zval_dtor(&retval);
buffer_ptr = buffer;
@@ -387,57 +389,130 @@ protected:
};
+static
void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec);
+static
void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec);
-void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval** value, HashTable* fieldspec);
-void skip_element(long thrift_typeID, PHPInputTransport& transport);
-void protocol_writeMessageBegin(zval *transport, const char* method_name, int32_t msgtype, int32_t seqID);
-
+static
+void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval* value, HashTable* fieldspec);
+static inline
+bool ttype_is_scalar(int8_t t);
// Create a PHP object given a typename and call the ctor, optionally passing up to 2 arguments
-void createObject(const char* obj_typename, zval* return_value, int nargs = 0, zval* arg1 = NULL, zval* arg2 = NULL) {
- TSRMLS_FETCH();
- size_t obj_typename_len = strlen(obj_typename);
- zend_class_entry* ce = zend_fetch_class(obj_typename, obj_typename_len, ZEND_FETCH_CLASS_DEFAULT TSRMLS_CC);
+static
+void createObject(const char* obj_typename, zval* return_value, int nargs = 0, zval* arg1 = nullptr, zval* arg2 = nullptr) {
+ /* is there a better way to do that on the stack ? */
+ zend_string *obj_name = zend_string_init(obj_typename, strlen(obj_typename), 0);
+ zend_class_entry* ce = zend_fetch_class(obj_name, ZEND_FETCH_CLASS_DEFAULT);
+ zend_string_release(obj_name);
+
if (! ce) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "Class %s does not exist", obj_typename);
+ php_error_docref(nullptr, E_ERROR, "Class %s does not exist", obj_typename);
RETURN_NULL();
}
- object_and_properties_init(return_value, ce, NULL);
- zend_function* constructor = zend_std_get_constructor(return_value TSRMLS_CC);
- zval* ctor_rv = NULL;
- zend_call_method(&return_value, ce, &constructor, NULL, 0, &ctor_rv, nargs, arg1, arg2 TSRMLS_CC);
- zval_ptr_dtor(&ctor_rv);
+ object_and_properties_init(return_value, ce, nullptr);
+ zend_function* constructor = zend_std_get_constructor(Z_OBJ_P(return_value));
+ zval ctor_rv;
+ zend_call_method(return_value, ce, &constructor, NULL, 0, &ctor_rv, nargs, arg1, arg2);
+ zval_dtor(&ctor_rv);
+ if (EG(exception)) {
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
+ throw PHPExceptionWrapper(ex);
+ }
}
+static
void throw_tprotocolexception(const char* what, long errorcode) {
- TSRMLS_FETCH();
+ zval zwhat, zerrorcode;
+
+ ZVAL_STRING(&zwhat, what);
+ ZVAL_LONG(&zerrorcode, errorcode);
- zval *zwhat, *zerrorcode;
- MAKE_STD_ZVAL(zwhat);
- MAKE_STD_ZVAL(zerrorcode);
+ zval ex;
+ createObject("\\Thrift\\Exception\\TProtocolException", &ex, 2, &zwhat, &zerrorcode);
- ZVAL_STRING(zwhat, what, 1);
- ZVAL_LONG(zerrorcode, errorcode);
+ zval_dtor(&zwhat);
+ zval_dtor(&zerrorcode);
- zval* ex;
- MAKE_STD_ZVAL(ex);
- createObject("\\Thrift\\Exception\\TProtocolException", ex, 2, zwhat, zerrorcode);
- zval_ptr_dtor(&zwhat);
- zval_ptr_dtor(&zerrorcode);
- throw PHPExceptionWrapper(ex);
+ throw PHPExceptionWrapper(&ex);
}
// Sets EG(exception), call this and then RETURN_NULL();
-void throw_zend_exception_from_std_exception(const std::exception& ex TSRMLS_DC) {
- zend_throw_exception(zend_exception_get_default(TSRMLS_C), const_cast<char*>(ex.what()), 0 TSRMLS_CC);
+static
+void throw_zend_exception_from_std_exception(const std::exception& ex) {
+ zend_throw_exception(zend_exception_get_default(), const_cast<char*>(ex.what()), 0);
+}
+
+static
+void skip_element(long thrift_typeID, PHPInputTransport& transport) {
+ switch (thrift_typeID) {
+ case T_STOP:
+ case T_VOID:
+ return;
+ case T_STRUCT:
+ while (true) {
+ int8_t ttype = transport.readI8(); // get field type
+ if (ttype == T_STOP) break;
+ transport.skip(2); // skip field number, I16
+ skip_element(ttype, transport); // skip field payload
+ }
+ return;
+ case T_BOOL:
+ case T_BYTE:
+ transport.skip(1);
+ return;
+ case T_I16:
+ transport.skip(2);
+ return;
+ case T_I32:
+ transport.skip(4);
+ return;
+ case T_U64:
+ case T_I64:
+ case T_DOUBLE:
+ transport.skip(8);
+ return;
+ //case T_UTF7: // aliases T_STRING
+ case T_UTF8:
+ case T_UTF16:
+ case T_STRING: {
+ uint32_t len = transport.readU32();
+ transport.skip(len);
+ } return;
+ case T_MAP: {
+ int8_t keytype = transport.readI8();
+ int8_t valtype = transport.readI8();
+ uint32_t size = transport.readU32();
+ for (uint32_t i = 0; i < size; ++i) {
+ skip_element(keytype, transport);
+ skip_element(valtype, transport);
+ }
+ } return;
+ case T_LIST:
+ case T_SET: {
+ int8_t valtype = transport.readI8();
+ uint32_t size = transport.readU32();
+ for (uint32_t i = 0; i < size; ++i) {
+ skip_element(valtype, transport);
+ }
+ } return;
+ };
+
+ char errbuf[128];
+ sprintf(errbuf, "Unknown thrift typeID %ld", thrift_typeID);
+ throw_tprotocolexception(errbuf, INVALID_DATA);
}
+static inline
+bool zval_is_bool(zval* v) {
+ return Z_TYPE_P(v) == IS_TRUE || Z_TYPE_P(v) == IS_FALSE;
+}
+static
void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval* return_value, HashTable* fieldspec) {
- zval** val_ptr;
- Z_TYPE_P(return_value) = IS_NULL; // just in case
+ ZVAL_NULL(return_value);
switch (thrift_typeID) {
case T_STOP:
@@ -445,20 +520,28 @@ void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval
RETURN_NULL();
return;
case T_STRUCT: {
- if (zend_hash_find(fieldspec, "class", 6, (void**)&val_ptr) != SUCCESS) {
+ zval* val_ptr = zend_hash_str_find(fieldspec, "class", sizeof("class")-1);
+ if (val_ptr == nullptr) {
throw_tprotocolexception("no class type in spec", INVALID_DATA);
skip_element(T_STRUCT, transport);
RETURN_NULL();
}
- char* structType = Z_STRVAL_PP(val_ptr);
+
+ char* structType = Z_STRVAL_P(val_ptr);
+ // Create an object in PHP userland based on our spec
createObject(structType, return_value);
if (Z_TYPE_P(return_value) == IS_NULL) {
// unable to create class entry
skip_element(T_STRUCT, transport);
RETURN_NULL();
}
- TSRMLS_FETCH();
- zval* spec = zend_read_static_property(zend_get_class_entry(return_value TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC);
+
+ zval* spec = zend_read_static_property(Z_OBJCE_P(return_value), "_TSPEC", sizeof("_TSPEC")-1, false);
+ if (EG(exception)) {
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
+ throw PHPExceptionWrapper(ex);
+ }
if (Z_TYPE_P(spec) != IS_ARRAY) {
char errbuf[128];
snprintf(errbuf, 128, "spec for %s is wrong type: %d\n", structType, Z_TYPE_P(spec));
@@ -510,10 +593,10 @@ void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval
case T_STRING: {
uint32_t size = transport.readU32();
if (size) {
- char* strbuf = (char*) emalloc(size + 1);
+ char strbuf[size+1];
transport.readBytes(strbuf, size);
strbuf[size] = '\0';
- ZVAL_STRINGL(return_value, strbuf, size, 0);
+ ZVAL_STRINGL(return_value, strbuf, size);
} else {
ZVAL_EMPTY_STRING(return_value);
}
@@ -525,43 +608,38 @@ void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval
uint32_t size = transport.readU32();
array_init(return_value);
- zend_hash_find(fieldspec, "key", 4, (void**)&val_ptr);
- HashTable* keyspec = Z_ARRVAL_PP(val_ptr);
- zend_hash_find(fieldspec, "val", 4, (void**)&val_ptr);
- HashTable* valspec = Z_ARRVAL_PP(val_ptr);
+ zval *val_ptr;
+ val_ptr = zend_hash_str_find(fieldspec, "key", sizeof("key")-1);
+ HashTable* keyspec = Z_ARRVAL_P(val_ptr);
+ val_ptr = zend_hash_str_find(fieldspec, "val", sizeof("val")-1);
+ HashTable* valspec = Z_ARRVAL_P(val_ptr);
for (uint32_t s = 0; s < size; ++s) {
- zval *value;
- MAKE_STD_ZVAL(value);
-
- zval* key;
- MAKE_STD_ZVAL(key);
-
- binary_deserialize(types[0], transport, key, keyspec);
- binary_deserialize(types[1], transport, value, valspec);
- if (Z_TYPE_P(key) == IS_LONG) {
- zend_hash_index_update(return_value->value.ht, Z_LVAL_P(key), &value, sizeof(zval *), NULL);
+ zval key, value;
+
+ binary_deserialize(types[0], transport, &key, keyspec);
+ binary_deserialize(types[1], transport, &value, valspec);
+ if (Z_TYPE(key) == IS_LONG) {
+ zend_hash_index_update(Z_ARR_P(return_value), Z_LVAL(key), &value);
+ } else {
+ if (Z_TYPE(key) != IS_STRING) convert_to_string(&key);
+ zend_symtable_update(Z_ARR_P(return_value), Z_STR(key), &value);
}
- else {
- if (Z_TYPE_P(key) != IS_STRING) convert_to_string(key);
- zend_symtable_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL);
- }
- zval_ptr_dtor(&key);
+ zval_dtor(&key);
}
return; // return_value already populated
}
case T_LIST: { // array with autogenerated numeric keys
int8_t type = transport.readI8();
uint32_t size = transport.readU32();
- zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr);
- HashTable* elemspec = Z_ARRVAL_PP(val_ptr);
+ zval *val_ptr = zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1);
+ HashTable* elemspec = Z_ARRVAL_P(val_ptr);
array_init(return_value);
for (uint32_t s = 0; s < size; ++s) {
- zval *value;
- MAKE_STD_ZVAL(value);
- binary_deserialize(type, transport, value, elemspec);
- zend_hash_next_index_insert(return_value->value.ht, &value, sizeof(zval *), NULL);
+ zval value;
+ binary_deserialize(type, transport, &value, elemspec);
+ zend_hash_next_index_insert(Z_ARR_P(return_value), &value);
}
return;
}
@@ -571,28 +649,24 @@ void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval
transport.readBytes(&type, 1);
transport.readBytes(&size, 4);
size = ntohl(size);
- zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr);
- HashTable* elemspec = Z_ARRVAL_PP(val_ptr);
+ zval *val_ptr = zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1);
+ HashTable* elemspec = Z_ARRVAL_P(val_ptr);
array_init(return_value);
for (uint32_t s = 0; s < size; ++s) {
- zval* key;
- zval* value;
- MAKE_STD_ZVAL(key);
- MAKE_STD_ZVAL(value);
- ZVAL_TRUE(value);
+ zval key, value;
+ ZVAL_TRUE(&value);
- binary_deserialize(type, transport, key, elemspec);
+ binary_deserialize(type, transport, &key, elemspec);
- if (Z_TYPE_P(key) == IS_LONG) {
- zend_hash_index_update(return_value->value.ht, Z_LVAL_P(key), &value, sizeof(zval *), NULL);
- }
- else {
- if (Z_TYPE_P(key) != IS_STRING) convert_to_string(key);
- zend_symtable_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL);
+ if (Z_TYPE(key) == IS_LONG) {
+ zend_hash_index_update(Z_ARR_P(return_value), Z_LVAL(key), &value);
+ } else {
+ if (Z_TYPE(key) != IS_STRING) convert_to_string(&key);
+ zend_symtable_update(Z_ARR_P(return_value), Z_STR(key), &value);
}
- zval_ptr_dtor(&key);
+ zval_dtor(&key);
}
return;
}
@@ -603,251 +677,68 @@ void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval
throw_tprotocolexception(errbuf, INVALID_DATA);
}
-void skip_element(long thrift_typeID, PHPInputTransport& transport) {
- switch (thrift_typeID) {
- case T_STOP:
- case T_VOID:
- return;
- case T_STRUCT:
- while (true) {
- int8_t ttype = transport.readI8(); // get field type
- if (ttype == T_STOP) break;
- transport.skip(2); // skip field number, I16
- skip_element(ttype, transport); // skip field payload
- }
- return;
- case T_BOOL:
- case T_BYTE:
- transport.skip(1);
- return;
- case T_I16:
- transport.skip(2);
- return;
- case T_I32:
- transport.skip(4);
- return;
- case T_U64:
- case T_I64:
- case T_DOUBLE:
- transport.skip(8);
- return;
- //case T_UTF7: // aliases T_STRING
- case T_UTF8:
- case T_UTF16:
- case T_STRING: {
- uint32_t len = transport.readU32();
- transport.skip(len);
- } return;
- case T_MAP: {
- int8_t keytype = transport.readI8();
- int8_t valtype = transport.readI8();
- uint32_t size = transport.readU32();
- for (uint32_t i = 0; i < size; ++i) {
- skip_element(keytype, transport);
- skip_element(valtype, transport);
- }
- } return;
- case T_LIST:
- case T_SET: {
- int8_t valtype = transport.readI8();
- uint32_t size = transport.readU32();
- for (uint32_t i = 0; i < size; ++i) {
- skip_element(valtype, transport);
- }
- } return;
- };
-
- char errbuf[128];
- sprintf(errbuf, "Unknown thrift typeID %ld", thrift_typeID);
- throw_tprotocolexception(errbuf, INVALID_DATA);
-}
-
-void protocol_writeMessageBegin(zval* transport, const char* method_name, int32_t msgtype, int32_t seqID) {
- TSRMLS_FETCH();
- zval *args[3];
-
- MAKE_STD_ZVAL(args[0]);
- ZVAL_STRINGL(args[0], (char*)method_name, strlen(method_name), 1);
-
- MAKE_STD_ZVAL(args[1]);
- ZVAL_LONG(args[1], msgtype);
-
- MAKE_STD_ZVAL(args[2]);
- ZVAL_LONG(args[2], seqID);
-
- zval ret;
- ZVAL_NULL(&ret);
-
- zval writeMessagefn;
- ZVAL_STRING(&writeMessagefn, "writeMessageBegin", 0);
-
- call_user_function(EG(function_table), &transport, &writeMessagefn, &ret, 3, args TSRMLS_CC);
-
- zval_ptr_dtor(&args[0]);
- zval_ptr_dtor(&args[1]);
- zval_ptr_dtor(&args[2]);
- zval_dtor(&ret);
-}
-
-void binary_serialize_hashtable_key(int8_t keytype, PHPOutputTransport& transport, HashTable* ht, HashPosition& ht_pos) {
+static
+void binary_serialize_hashtable_key(int8_t keytype, PHPOutputTransport& transport, HashTable* ht, HashPosition& ht_pos, HashTable* spec) {
bool keytype_is_numeric = (!((keytype == T_STRING) || (keytype == T_UTF8) || (keytype == T_UTF16)));
- char* key;
+ zend_string* key;
uint key_len;
long index = 0;
- zval* z;
- MAKE_STD_ZVAL(z);
+ zval z;
- int res = zend_hash_get_current_key_ex(ht, &key, &key_len, (ulong*)&index, 0, &ht_pos);
- if (keytype_is_numeric) {
- if (res == HASH_KEY_IS_STRING) {
- index = strtol(key, NULL, 10);
- }
- ZVAL_LONG(z, index);
+ int res = zend_hash_get_current_key_ex(ht, &key, (zend_ulong*)&index, &ht_pos);
+ if (res == HASH_KEY_IS_STRING) {
+ ZVAL_STR_COPY(&z, key);
} else {
- char buf[64];
- if (res == HASH_KEY_IS_STRING) {
- key_len -= 1; // skip the null terminator
- } else {
- sprintf(buf, "%ld", index);
- key = buf; key_len = strlen(buf);
- }
- ZVAL_STRINGL(z, key, key_len, 1);
+ ZVAL_LONG(&z, index);
}
- binary_serialize(keytype, transport, &z, NULL);
- zval_ptr_dtor(&z);
-}
-
-inline bool ttype_is_int(int8_t t) {
- return ((t == T_BYTE) || ((t >= T_I16) && (t <= T_I64)));
-}
-
-inline bool ttypes_are_compatible(int8_t t1, int8_t t2) {
- // Integer types of different widths are considered compatible;
- // otherwise the typeID must match.
- return ((t1 == t2) || (ttype_is_int(t1) && ttype_is_int(t2)));
+ binary_serialize(keytype, transport, &z, spec);
+ zval_dtor(&z);
}
-//is used to validate objects before serialization and after deserialization. For now, only required fields are validated.
static
-void validate_thrift_object(zval* object) {
-
- HashPosition key_ptr;
- zval** val_ptr;
-
- TSRMLS_FETCH();
- zend_class_entry* object_class_entry = zend_get_class_entry(object TSRMLS_CC);
- HashTable* spec = Z_ARRVAL_P(zend_read_static_property(object_class_entry, "_TSPEC", 6, false TSRMLS_CC));
-
- for (zend_hash_internal_pointer_reset_ex(spec, &key_ptr); zend_hash_get_current_data_ex(spec, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(spec, &key_ptr)) {
- ulong fieldno;
- if (zend_hash_get_current_key_ex(spec, NULL, NULL, &fieldno, 0, &key_ptr) != HASH_KEY_IS_LONG) {
- throw_tprotocolexception("Bad keytype in TSPEC (expected 'long')", INVALID_DATA);
- return;
- }
- HashTable* fieldspec = Z_ARRVAL_PP(val_ptr);
-
- // field name
- zend_hash_find(fieldspec, "var", 4, (void**)&val_ptr);
- char* varname = Z_STRVAL_PP(val_ptr);
-
- zend_hash_find(fieldspec, "isRequired", 11, (void**)&val_ptr);
- bool is_required = Z_BVAL_PP(val_ptr);
-
- zval* prop = zend_read_property(object_class_entry, object, varname, strlen(varname), false TSRMLS_CC);
-
- if (is_required && Z_TYPE_P(prop) == IS_NULL) {
- char errbuf[128];
- snprintf(errbuf, 128, "Required field %s.%s is unset!", object_class_entry->name, varname);
- throw_tprotocolexception(errbuf, INVALID_DATA);
- }
- }
-}
-
-void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec) {
- // SET and LIST have 'elem' => array('type', [optional] 'class')
- // MAP has 'val' => array('type', [optiona] 'class')
- TSRMLS_FETCH();
- zend_class_entry* ce = zend_get_class_entry(zthis TSRMLS_CC);
- while (true) {
- zval** val_ptr = NULL;
-
- int8_t ttype = transport.readI8();
- if (ttype == T_STOP) {
- validate_thrift_object(zthis);
- return;
- }
- int16_t fieldno = transport.readI16();
- if (zend_hash_index_find(spec, fieldno, (void**)&val_ptr) == SUCCESS) {
- HashTable* fieldspec = Z_ARRVAL_PP(val_ptr);
- // pull the field name
- // zend hash tables use the null at the end in the length... so strlen(hash key) + 1.
- zend_hash_find(fieldspec, "var", 4, (void**)&val_ptr);
- char* varname = Z_STRVAL_PP(val_ptr);
-
- // and the type
- zend_hash_find(fieldspec, "type", 5, (void**)&val_ptr);
- if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr);
- int8_t expected_ttype = Z_LVAL_PP(val_ptr);
-
- if (ttypes_are_compatible(ttype, expected_ttype)) {
- zval* rv = NULL;
- MAKE_STD_ZVAL(rv);
- binary_deserialize(ttype, transport, rv, fieldspec);
- zend_update_property(ce, zthis, varname, strlen(varname), rv TSRMLS_CC);
- zval_ptr_dtor(&rv);
- } else {
- skip_element(ttype, transport);
- }
- } else {
- skip_element(ttype, transport);
- }
- }
-}
-
-void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval** value, HashTable* fieldspec) {
+void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval* value, HashTable* fieldspec) {
// At this point the typeID (and field num, if applicable) should've already been written to the output so all we need to do is write the payload.
switch (thrift_typeID) {
case T_STOP:
case T_VOID:
return;
case T_STRUCT: {
- TSRMLS_FETCH();
- if (Z_TYPE_PP(value) != IS_OBJECT) {
+ if (Z_TYPE_P(value) != IS_OBJECT) {
throw_tprotocolexception("Attempt to send non-object type as a T_STRUCT", INVALID_DATA);
}
- zval* spec = zend_read_static_property(zend_get_class_entry(*value TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC);
- if (Z_TYPE_P(spec) != IS_ARRAY) {
+ zval* spec = zend_read_static_property(Z_OBJCE_P(value), "_TSPEC", sizeof("_TSPEC")-1, true);
+ if (!spec || Z_TYPE_P(spec) != IS_ARRAY) {
throw_tprotocolexception("Attempt to send non-Thrift object as a T_STRUCT", INVALID_DATA);
}
- binary_serialize_spec(*value, transport, Z_ARRVAL_P(spec));
+ binary_serialize_spec(value, transport, Z_ARRVAL_P(spec));
} return;
case T_BOOL:
- if (Z_TYPE_PP(value) != IS_BOOL) convert_to_boolean(*value);
- transport.writeI8(Z_BVAL_PP(value) ? 1 : 0);
+ if (!zval_is_bool(value)) convert_to_boolean(value);
+ transport.writeI8(Z_TYPE_INFO_P(value) == IS_TRUE ? 1 : 0);
return;
case T_BYTE:
- if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value);
- transport.writeI8(Z_LVAL_PP(value));
+ if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
+ transport.writeI8(Z_LVAL_P(value));
return;
case T_I16:
- if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value);
- transport.writeI16(Z_LVAL_PP(value));
+ if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
+ transport.writeI16(Z_LVAL_P(value));
return;
case T_I32:
- if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value);
- transport.writeI32(Z_LVAL_PP(value));
+ if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
+ transport.writeI32(Z_LVAL_P(value));
return;
case T_I64:
case T_U64: {
int64_t l_data;
#if defined(_LP64) || defined(_WIN64)
- if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value);
- l_data = Z_LVAL_PP(value);
+ if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
+ l_data = Z_LVAL_P(value);
#else
- if (Z_TYPE_PP(value) != IS_DOUBLE) convert_to_double(*value);
- l_data = (int64_t)Z_DVAL_PP(value);
+ if (Z_TYPE_P(value) != IS_DOUBLE) convert_to_double(value);
+ l_data = (int64_t)Z_DVAL_P(value);
#endif
transport.writeI64(l_data);
} return;
@@ -856,124 +747,265 @@ void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval*
int64_t c;
double d;
} a;
- if (Z_TYPE_PP(value) != IS_DOUBLE) convert_to_double(*value);
- a.d = Z_DVAL_PP(value);
+ if (Z_TYPE_P(value) != IS_DOUBLE) convert_to_double(value);
+ a.d = Z_DVAL_P(value);
transport.writeI64(a.c);
} return;
- //case T_UTF7:
case T_UTF8:
case T_UTF16:
case T_STRING:
- if (Z_TYPE_PP(value) != IS_STRING) convert_to_string(*value);
- transport.writeString(Z_STRVAL_PP(value), Z_STRLEN_PP(value));
+ if (Z_TYPE_P(value) != IS_STRING) convert_to_string(value);
+ transport.writeString(Z_STRVAL_P(value), Z_STRLEN_P(value));
return;
case T_MAP: {
- if (Z_TYPE_PP(value) != IS_ARRAY) convert_to_array(*value);
- if (Z_TYPE_PP(value) != IS_ARRAY) {
+ if (Z_TYPE_P(value) != IS_ARRAY) convert_to_array(value);
+ if (Z_TYPE_P(value) != IS_ARRAY) {
throw_tprotocolexception("Attempt to send an incompatible type as an array (T_MAP)", INVALID_DATA);
}
- HashTable* ht = Z_ARRVAL_PP(value);
- zval** val_ptr;
+ HashTable* ht = Z_ARRVAL_P(value);
+ zval* val_ptr;
- zend_hash_find(fieldspec, "ktype", 6, (void**)&val_ptr);
- if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr);
- uint8_t keytype = Z_LVAL_PP(val_ptr);
+ val_ptr = zend_hash_str_find(fieldspec, "ktype", sizeof("ktype")-1);
+ if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
+ uint8_t keytype = Z_LVAL_P(val_ptr);
transport.writeI8(keytype);
- zend_hash_find(fieldspec, "vtype", 6, (void**)&val_ptr);
- if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr);
- uint8_t valtype = Z_LVAL_PP(val_ptr);
+ val_ptr = zend_hash_str_find(fieldspec, "vtype", sizeof("vtype")-1);
+ if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
+ uint8_t valtype = Z_LVAL_P(val_ptr);
transport.writeI8(valtype);
- zend_hash_find(fieldspec, "val", 4, (void**)&val_ptr);
- HashTable* valspec = Z_ARRVAL_PP(val_ptr);
+ val_ptr = zend_hash_str_find(fieldspec, "val", sizeof("val")-1);
+ HashTable* valspec = Z_ARRVAL_P(val_ptr);
+ HashTable* keyspec = Z_ARRVAL_P(zend_hash_str_find(fieldspec, "key", sizeof("key")-1));
transport.writeI32(zend_hash_num_elements(ht));
HashPosition key_ptr;
- for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); zend_hash_get_current_data_ex(ht, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(ht, &key_ptr)) {
- binary_serialize_hashtable_key(keytype, transport, ht, key_ptr);
+ for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr);
+ (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr;
+ zend_hash_move_forward_ex(ht, &key_ptr)) {
+ binary_serialize_hashtable_key(keytype, transport, ht, key_ptr, keyspec);
binary_serialize(valtype, transport, val_ptr, valspec);
}
} return;
case T_LIST: {
- if (Z_TYPE_PP(value) != IS_ARRAY) convert_to_array(*value);
- if (Z_TYPE_PP(value) != IS_ARRAY) {
+ if (Z_TYPE_P(value) != IS_ARRAY) convert_to_array(value);
+ if (Z_TYPE_P(value) != IS_ARRAY) {
throw_tprotocolexception("Attempt to send an incompatible type as an array (T_LIST)", INVALID_DATA);
}
- HashTable* ht = Z_ARRVAL_PP(value);
- zval** val_ptr;
+ HashTable* ht = Z_ARRVAL_P(value);
+ zval* val_ptr;
- zend_hash_find(fieldspec, "etype", 6, (void**)&val_ptr);
- if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr);
- uint8_t valtype = Z_LVAL_PP(val_ptr);
+ val_ptr = zend_hash_str_find(fieldspec, "etype", sizeof("etype")-1);
+ if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
+ uint8_t valtype = Z_LVAL_P(val_ptr);
transport.writeI8(valtype);
- zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr);
- HashTable* valspec = Z_ARRVAL_PP(val_ptr);
+ val_ptr = zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1);
+ HashTable* valspec = Z_ARRVAL_P(val_ptr);
transport.writeI32(zend_hash_num_elements(ht));
HashPosition key_ptr;
- for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); zend_hash_get_current_data_ex(ht, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(ht, &key_ptr)) {
+ for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr);
+ (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr;
+ zend_hash_move_forward_ex(ht, &key_ptr)) {
binary_serialize(valtype, transport, val_ptr, valspec);
}
} return;
case T_SET: {
- if (Z_TYPE_PP(value) != IS_ARRAY) convert_to_array(*value);
- if (Z_TYPE_PP(value) != IS_ARRAY) {
+ if (Z_TYPE_P(value) != IS_ARRAY) convert_to_array(value);
+ if (Z_TYPE_P(value) != IS_ARRAY) {
throw_tprotocolexception("Attempt to send an incompatible type as an array (T_SET)", INVALID_DATA);
}
- HashTable* ht = Z_ARRVAL_PP(value);
- zval** val_ptr;
+ HashTable* ht = Z_ARRVAL_P(value);
+ zval* val_ptr;
- zend_hash_find(fieldspec, "etype", 6, (void**)&val_ptr);
- if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr);
- uint8_t keytype = Z_LVAL_PP(val_ptr);
+ val_ptr = zend_hash_str_find(fieldspec, "etype", sizeof("etype")-1);
+ HashTable* spec = Z_ARRVAL_P(zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1));
+ if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
+ uint8_t keytype = Z_LVAL_P(val_ptr);
transport.writeI8(keytype);
transport.writeI32(zend_hash_num_elements(ht));
HashPosition key_ptr;
- for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); zend_hash_get_current_data_ex(ht, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(ht, &key_ptr)) {
- binary_serialize_hashtable_key(keytype, transport, ht, key_ptr);
+ if(ttype_is_scalar(keytype)){
+ for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr);
+ (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr;
+ zend_hash_move_forward_ex(ht, &key_ptr)) {
+ binary_serialize_hashtable_key(keytype, transport, ht, key_ptr, spec);
+ }
+ } else {
+ for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr);
+ (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr;
+ zend_hash_move_forward_ex(ht, &key_ptr)) {
+ binary_serialize(keytype, transport, val_ptr, spec);
+ }
}
} return;
};
+
char errbuf[128];
- sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID);
+ snprintf(errbuf, 128, "Unknown thrift typeID %d", thrift_typeID);
throw_tprotocolexception(errbuf, INVALID_DATA);
}
+static
+void protocol_writeMessageBegin(zval* transport, zend_string* method_name, int32_t msgtype, int32_t seqID) {
+ zval args[3];
+ zval ret;
+ zval writeMessagefn;
+
+ ZVAL_STR_COPY(&args[0], method_name);
+ ZVAL_LONG(&args[1], msgtype);
+ ZVAL_LONG(&args[2], seqID);
+ ZVAL_NULL(&ret);
+ ZVAL_STRING(&writeMessagefn, "writeMessageBegin");
+
+ call_user_function(EG(function_table), transport, &writeMessagefn, &ret, 3, args);
+
+ zval_dtor(&writeMessagefn);
+ zval_dtor(&args[2]); zval_dtor(&args[1]); zval_dtor(&args[0]);
+ zval_dtor(&ret);
+ if (EG(exception)) {
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
+ throw PHPExceptionWrapper(ex);
+ }
+}
+
+static inline
+bool ttype_is_int(int8_t t) {
+ return ((t == T_BYTE) || ((t >= T_I16) && (t <= T_I64)));
+}
+static inline
+bool ttype_is_scalar(int8_t t) {
+ return !((t == T_STRUCT) || ( t== T_MAP) || (t == T_SET) || (t == T_LIST));
+}
+static inline
+bool ttypes_are_compatible(int8_t t1, int8_t t2) {
+ // Integer types of different widths are considered compatible;
+ // otherwise the typeID must match.
+ return ((t1 == t2) || (ttype_is_int(t1) && ttype_is_int(t2)));
+}
+
+//is used to validate objects before serialization and after deserialization. For now, only required fields are validated.
+static
+void validate_thrift_object(zval* object) {
+ zend_class_entry* object_class_entry = Z_OBJCE_P(object);
+ zval* is_validate = zend_read_static_property(object_class_entry, "isValidate", sizeof("isValidate")-1, true);
+ zval* spec = zend_read_static_property(object_class_entry, "_TSPEC", sizeof("_TSPEC")-1, true);
+ HashPosition key_ptr;
+ zval* val_ptr;
+
+ if (is_validate && Z_TYPE_INFO_P(is_validate) == IS_TRUE) {
+ for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(spec), &key_ptr);
+ (val_ptr = zend_hash_get_current_data_ex(Z_ARRVAL_P(spec), &key_ptr)) != nullptr;
+ zend_hash_move_forward_ex(Z_ARRVAL_P(spec), &key_ptr)) {
+
+ zend_ulong fieldno;
+ if (zend_hash_get_current_key_ex(Z_ARRVAL_P(spec), nullptr, &fieldno, &key_ptr) != HASH_KEY_IS_LONG) {
+ throw_tprotocolexception("Bad keytype in TSPEC (expected 'long')", INVALID_DATA);
+ return;
+ }
+ HashTable* fieldspec = Z_ARRVAL_P(val_ptr);
+
+ // field name
+ zval* zvarname = zend_hash_str_find(fieldspec, "var", sizeof("var")-1);
+ char* varname = Z_STRVAL_P(zvarname);
+
+ zval* is_required = zend_hash_str_find(fieldspec, "isRequired", sizeof("isRequired")-1);
+ zval rv;
+ zval* prop = zend_read_property(object_class_entry, object, varname, strlen(varname), false, &rv);
+
+ if (Z_TYPE_INFO_P(is_required) == IS_TRUE && Z_TYPE_P(prop) == IS_NULL) {
+ char errbuf[128];
+ snprintf(errbuf, 128, "Required field %s.%s is unset!", ZSTR_VAL(object_class_entry->name), varname);
+ throw_tprotocolexception(errbuf, INVALID_DATA);
+ }
+ }
+ }
+}
+
+static
+void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec) {
+ // SET and LIST have 'elem' => array('type', [optional] 'class')
+ // MAP has 'val' => array('type', [optiona] 'class')
+ zend_class_entry* ce = Z_OBJCE_P(zthis);
+ while (true) {
+ int8_t ttype = transport.readI8();
+ if (ttype == T_STOP) {
+ validate_thrift_object(zthis);
+ return;
+ }
+
+ int16_t fieldno = transport.readI16();
+ zval* val_ptr = zend_hash_index_find(spec, fieldno);
+ if (val_ptr != nullptr) {
+ HashTable* fieldspec = Z_ARRVAL_P(val_ptr);
+ // pull the field name
+ val_ptr = zend_hash_str_find(fieldspec, "var", sizeof("var")-1);
+ char* varname = Z_STRVAL_P(val_ptr);
+
+ // and the type
+ val_ptr = zend_hash_str_find(fieldspec, "type", sizeof("type")-1);
+ if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
+ int8_t expected_ttype = Z_LVAL_P(val_ptr);
+
+ if (ttypes_are_compatible(ttype, expected_ttype)) {
+ zval rv;
+ ZVAL_UNDEF(&rv);
+
+ binary_deserialize(ttype, transport, &rv, fieldspec);
+ zend_update_property(ce, zthis, varname, strlen(varname), &rv);
+
+ zval_ptr_dtor(&rv);
+ } else {
+ skip_element(ttype, transport);
+ }
+ } else {
+ skip_element(ttype, transport);
+ }
+ }
+}
+
+static
void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec) {
validate_thrift_object(zthis);
HashPosition key_ptr;
- zval** val_ptr;
+ zval* val_ptr;
- TSRMLS_FETCH();
- zend_class_entry* ce = zend_get_class_entry(zthis TSRMLS_CC);
+ for (zend_hash_internal_pointer_reset_ex(spec, &key_ptr);
+ (val_ptr = zend_hash_get_current_data_ex(spec, &key_ptr)) != nullptr;
+ zend_hash_move_forward_ex(spec, &key_ptr)) {
- for (zend_hash_internal_pointer_reset_ex(spec, &key_ptr); zend_hash_get_current_data_ex(spec, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(spec, &key_ptr)) {
- ulong fieldno;
- if (zend_hash_get_current_key_ex(spec, NULL, NULL, &fieldno, 0, &key_ptr) != HASH_KEY_IS_LONG) {
+ zend_ulong fieldno;
+ if (zend_hash_get_current_key_ex(spec, nullptr, &fieldno, &key_ptr) != HASH_KEY_IS_LONG) {
throw_tprotocolexception("Bad keytype in TSPEC (expected 'long')", INVALID_DATA);
return;
}
- HashTable* fieldspec = Z_ARRVAL_PP(val_ptr);
+ HashTable* fieldspec = Z_ARRVAL_P(val_ptr);
// field name
- zend_hash_find(fieldspec, "var", 4, (void**)&val_ptr);
- char* varname = Z_STRVAL_PP(val_ptr);
+ val_ptr = zend_hash_str_find(fieldspec, "var", sizeof("var")-1);
+ char* varname = Z_STRVAL_P(val_ptr);
// thrift type
- zend_hash_find(fieldspec, "type", 5, (void**)&val_ptr);
- if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr);
- int8_t ttype = Z_LVAL_PP(val_ptr);
+ val_ptr = zend_hash_str_find(fieldspec, "type", sizeof("type")-1);
+ if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
+ int8_t ttype = Z_LVAL_P(val_ptr);
+
+ zval rv;
+ zval* prop = zend_read_property(Z_OBJCE_P(zthis), zthis, varname, strlen(varname), false, &rv);
- zval* prop = zend_read_property(ce, zthis, varname, strlen(varname), false TSRMLS_CC);
+ if (Z_TYPE_P(prop) == IS_REFERENCE){
+ ZVAL_UNREF(prop);
+ }
if (Z_TYPE_P(prop) != IS_NULL) {
transport.writeI8(ttype);
transport.writeI16(fieldno);
- binary_serialize(ttype, transport, &prop, fieldspec);
+ binary_serialize(ttype, transport, prop, fieldspec);
}
}
transport.writeI8(T_STOP); // struct end
@@ -981,104 +1013,56 @@ void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable
// 6 params: $transport $method_name $ttype $request_struct $seqID $strict_write
PHP_FUNCTION(thrift_protocol_write_binary) {
- int argc = ZEND_NUM_ARGS();
- if (argc < 6) {
- WRONG_PARAM_COUNT;
- }
-
- zval ***args = (zval***) emalloc(argc * sizeof(zval**));
- zend_get_parameters_array_ex(argc, args);
-
- if (Z_TYPE_PP(args[0]) != IS_OBJECT) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "1st parameter is not an object (transport)");
- efree(args);
- RETURN_NULL();
- }
-
- if (Z_TYPE_PP(args[1]) != IS_STRING) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "2nd parameter is not a string (method name)");
- efree(args);
- RETURN_NULL();
- }
-
- if (Z_TYPE_PP(args[3]) != IS_OBJECT) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "4th parameter is not an object (request struct)");
- efree(args);
- RETURN_NULL();
+ zval *protocol;
+ zval *request_struct;
+ zend_string *method_name;
+ long msgtype, seqID;
+ zend_bool strict_write;
+
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "oSlolb",
+ &protocol, &method_name, &msgtype,
+ &request_struct, &seqID, &strict_write) == FAILURE) {
+ return;
}
-
try {
- PHPOutputTransport transport(*args[0]);
- zval *protocol = *args[0];
- const char* method_name = Z_STRVAL_PP(args[1]);
- convert_to_long(*args[2]);
- int32_t msgtype = Z_LVAL_PP(args[2]);
- zval* request_struct = *args[3];
- convert_to_long(*args[4]);
- int32_t seqID = Z_LVAL_PP(args[4]);
- convert_to_boolean(*args[5]);
- bool strictWrite = Z_BVAL_PP(args[5]);
- efree(args);
- args = NULL;
- protocol_writeMessageBegin(protocol, method_name, msgtype, seqID);
- zval* spec = zend_read_static_property(zend_get_class_entry(request_struct TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC);
- if (Z_TYPE_P(spec) != IS_ARRAY) {
- throw_tprotocolexception("Attempt to send non-Thrift object", INVALID_DATA);
+ zval* spec = zend_read_static_property(Z_OBJCE_P(request_struct), "_TSPEC", sizeof("_TSPEC")-1, true);
+
+ if (!spec || Z_TYPE_P(spec) != IS_ARRAY) {
+ throw_tprotocolexception("Attempt serialize from non-Thrift object", INVALID_DATA);
}
+
+ PHPOutputTransport transport(protocol);
+ protocol_writeMessageBegin(protocol, method_name, (int32_t) msgtype, (int32_t) seqID);
binary_serialize_spec(request_struct, transport, Z_ARRVAL_P(spec));
transport.flush();
+
} catch (const PHPExceptionWrapper& ex) {
- zend_throw_exception_object(ex TSRMLS_CC);
+ // ex will be destructed, so copy to a zval that zend_throw_exception_object can take ownership of
+ zval myex;
+ ZVAL_COPY(&myex, ex);
+ zend_throw_exception_object(&myex);
RETURN_NULL();
} catch (const std::exception& ex) {
- throw_zend_exception_from_std_exception(ex TSRMLS_CC);
+ throw_zend_exception_from_std_exception(ex);
RETURN_NULL();
}
}
+
// 4 params: $transport $response_Typename $strict_read $buffer_size
PHP_FUNCTION(thrift_protocol_read_binary) {
- int argc = ZEND_NUM_ARGS();
-
- if (argc < 3) {
- WRONG_PARAM_COUNT;
- }
-
- zval ***args = (zval***) emalloc(argc * sizeof(zval**));
- zend_get_parameters_array_ex(argc, args);
+ zval *protocol;
+ zend_string *obj_typename;
+ zend_bool strict_read;
+ size_t buffer_size = 8192;
- if (Z_TYPE_PP(args[0]) != IS_OBJECT) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "1st parameter is not an object (transport)");
- efree(args);
- RETURN_NULL();
- }
-
- if (Z_TYPE_PP(args[1]) != IS_STRING) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "2nd parameter is not a string (typename of expected response struct)");
- efree(args);
- RETURN_NULL();
- }
-
- if (argc == 4 && Z_TYPE_PP(args[3]) != IS_LONG) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "4nd parameter is not an integer (typename of expected buffer size)");
- efree(args);
- RETURN_NULL();
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "oSb|l", &protocol, &obj_typename, &strict_read, &buffer_size) == FAILURE) {
+ return;
}
try {
- size_t buffer_size = 8192;
- if (argc == 4) {
- buffer_size = Z_LVAL_PP(args[3]);
- }
-
- PHPInputTransport transport(*args[0], buffer_size);
- char* obj_typename = Z_STRVAL_PP(args[1]);
- convert_to_boolean(*args[2]);
- bool strict_read = Z_BVAL_PP(args[2]);
- efree(args);
- args = NULL;
-
+ PHPInputTransport transport(protocol, buffer_size);
int8_t messageType = 0;
int32_t sz = transport.readI32();
@@ -1104,81 +1088,64 @@ PHP_FUNCTION(thrift_protocol_read_binary) {
}
if (messageType == T_EXCEPTION) {
- zval* ex;
- MAKE_STD_ZVAL(ex);
- createObject("\\Thrift\\Exception\\TApplicationException", ex);
- zval* spec = zend_read_static_property(zend_get_class_entry(ex TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC);
- binary_deserialize_spec(ex, transport, Z_ARRVAL_P(spec));
- throw PHPExceptionWrapper(ex);
+ zval ex;
+ createObject("\\Thrift\\Exception\\TApplicationException", &ex);
+ zval* spec = zend_read_static_property(Z_OBJCE(ex), "_TSPEC", sizeof("_TPSEC")-1, false);
+ if (EG(exception)) {
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
+ throw PHPExceptionWrapper(ex);
+ }
+ binary_deserialize_spec(&ex, transport, Z_ARRVAL_P(spec));
+ throw PHPExceptionWrapper(&ex);
}
- createObject(obj_typename, return_value);
- zval* spec = zend_read_static_property(zend_get_class_entry(return_value TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC);
+ createObject(ZSTR_VAL(obj_typename), return_value);
+ zval* spec = zend_read_static_property(Z_OBJCE_P(return_value), "_TSPEC", sizeof("_TSPEC")-1, true);
+ if (!spec || Z_TYPE_P(spec) != IS_ARRAY) {
+ throw_tprotocolexception("Attempt deserialize to non-Thrift object", INVALID_DATA);
+ }
binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec));
} catch (const PHPExceptionWrapper& ex) {
- zend_throw_exception_object(ex TSRMLS_CC);
+ // ex will be destructed, so copy to a zval that zend_throw_exception_object can ownership of
+ zval myex;
+ ZVAL_COPY(&myex, ex);
+ zval_dtor(return_value);
+ zend_throw_exception_object(&myex);
RETURN_NULL();
} catch (const std::exception& ex) {
- throw_zend_exception_from_std_exception(ex TSRMLS_CC);
+ throw_zend_exception_from_std_exception(ex);
RETURN_NULL();
}
}
-
// 4 params: $transport $response_Typename $strict_read $buffer_size
PHP_FUNCTION(thrift_protocol_read_binary_after_message_begin) {
- int argc = ZEND_NUM_ARGS();
-
- if (argc < 3) {
- WRONG_PARAM_COUNT;
- }
-
- zval ***args = (zval***) emalloc(argc * sizeof(zval**));
- zend_get_parameters_array_ex(argc, args);
+ zval *protocol;
+ zend_string *obj_typename;
+ zend_bool strict_read;
+ size_t buffer_size = 8192;
- if (Z_TYPE_PP(args[0]) != IS_OBJECT) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "1st parameter is not an object (transport)");
- efree(args);
- RETURN_NULL();
- }
-
- if (Z_TYPE_PP(args[1]) != IS_STRING) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "2nd parameter is not a string (typename of expected response struct)");
- efree(args);
- RETURN_NULL();
- }
-
- if (argc == 4 && Z_TYPE_PP(args[3]) != IS_LONG) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "4nd parameter is not an integer (typename of expected buffer size)");
- efree(args);
- RETURN_NULL();
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "oSb|l", &protocol, &obj_typename, &strict_read, &buffer_size) == FAILURE) {
+ return;
}
try {
- size_t buffer_size = 8192;
- if (argc == 4) {
- buffer_size = Z_LVAL_PP(args[3]);
- }
-
- PHPInputTransport transport(*args[0], buffer_size);
- char* obj_typename = Z_STRVAL_PP(args[1]);
- convert_to_boolean(*args[2]);
- bool strict_read = Z_BVAL_PP(args[2]);
- efree(args);
- args = NULL;
+ PHPInputTransport transport(protocol, buffer_size);
- createObject(obj_typename, return_value);
- zval* spec = zend_read_static_property(zend_get_class_entry(return_value TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC);
+ createObject(ZSTR_VAL(obj_typename), return_value);
+ zval* spec = zend_read_static_property(Z_OBJCE_P(return_value), "_TSPEC", sizeof("_TSPEC")-1, false);
binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec));
} catch (const PHPExceptionWrapper& ex) {
- zend_throw_exception_object(ex TSRMLS_CC);
+ // ex will be destructed, so copy to a zval that zend_throw_exception_object can take ownership of
+ zval myex;
+ ZVAL_COPY(&myex, ex);
+ zend_throw_exception_object(&myex);
RETURN_NULL();
} catch (const std::exception& ex) {
- throw_zend_exception_from_std_exception(ex TSRMLS_CC);
+ throw_zend_exception_from_std_exception(ex);
RETURN_NULL();
}
}
-
-
-#endif /* PHP_VERSION_ID < 70000 && PHP_VERSION_ID > 50000 */
+#endif /* PHP_VERSION_ID >= 70000 */
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 dafc18501..000000000
--- 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 */