[thrift] PHP thrift_protocol extension, revision 2.0
Summary:
All PHP serialization and deserialization can now happen in extension-land,
which should be much faster. This includes reading message headers and all
complex types (structs, exceptions, whatever).
The compiler has been updated to always emit the $_TSPEC static array for
generated PHP code, since the new extension depends on it.
As before, the PHP code gates enabling the accelerated serialization on
the protocol being an instance of TBinaryProtocolAcclerated and the function
for the [de]serialization operation existing.
The function names have changed since the last version of the extension,
so old and new generated code can coexist, and new generated code can run on
a server with the old extension (but it will not use accelerated serialization).
Reviewed by: hzhao
Test Plan: Generated a couple of endpoints and called their services through the
new extension. Both use a variety of nested complex types. Built and ran the
extension in php-5.2.3 in debug mode, killed the reported memory leaks.
git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665548 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/php/src/ext/thrift_protocol/trunk/config.m4 b/lib/php/src/ext/thrift_protocol/config.m4
similarity index 100%
rename from lib/php/src/ext/thrift_protocol/trunk/config.m4
rename to lib/php/src/ext/thrift_protocol/config.m4
diff --git a/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp b/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp
new file mode 100644
index 0000000..32b5039
--- /dev/null
+++ b/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp
@@ -0,0 +1,953 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <endian.h>
+#include <byteswap.h>
+#include <stdexcept>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htonll(x) bswap_64(x)
+#define ntohll(x) bswap_64(x)
+#else
+#define htonll(x) x
+#define ntohll(x) x
+#endif
+
+enum TType {
+ T_STOP = 0,
+ T_VOID = 1,
+ T_BOOL = 2,
+ T_BYTE = 3,
+ T_I08 = 3,
+ T_I16 = 6,
+ T_I32 = 8,
+ T_U64 = 9,
+ T_I64 = 10,
+ T_DOUBLE = 4,
+ T_STRING = 11,
+ T_UTF7 = 11,
+ T_STRUCT = 12,
+ T_MAP = 13,
+ T_SET = 14,
+ T_LIST = 15,
+ T_UTF8 = 16,
+ T_UTF16 = 17
+};
+
+const int32_t VERSION_MASK = 0xffff0000;
+const int32_t VERSION_1 = 0x80010000;
+const int8_t T_CALL = 1;
+const int8_t T_REPLY = 2;
+const int8_t T_EXCEPTION = 3;
+// tprotocolexception
+const int INVALID_DATA = 1;
+const int BAD_VERSION = 4;
+
+#include "php.h"
+#include "zend_interfaces.h"
+#include "zend_exceptions.h"
+#include "php_thrift_protocol.h"
+
+static function_entry thrift_protocol_functions[] = {
+ PHP_FE(thrift_protocol_write_binary, NULL)
+ PHP_FE(thrift_protocol_read_binary, NULL)
+ {NULL, NULL, NULL}
+} ;
+
+zend_module_entry thrift_protocol_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "thrift_protocol",
+ thrift_protocol_functions,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "1.0",
+ STANDARD_MODULE_PROPERTIES
+};
+
+#ifdef COMPILE_DL_THRIFT_PROTOCOL
+ZEND_GET_MODULE(thrift_protocol)
+#endif
+
+class PHPTransport {
+public:
+ zval* protocol() { return p; }
+ zval* transport() { return t; }
+protected:
+ PHPTransport() {}
+
+ void construct_with_zval(zval* _p, size_t _buffer_size) {
+ buffer = reinterpret_cast<char*>(emalloc(_buffer_size));
+ buffer_ptr = buffer;
+ buffer_used = 0;
+ buffer_size = _buffer_size;
+ p = _p;
+
+ // Get the transport for the passed protocol
+ zval gettransport;
+ ZVAL_STRING(&gettransport, "getTransport", 0);
+ MAKE_STD_ZVAL(t);
+ ZVAL_NULL(t);
+ TSRMLS_FETCH();
+ call_user_function(EG(function_table), &p, &gettransport, t, 0, NULL TSRMLS_CC);
+ }
+ ~PHPTransport() {
+ efree(buffer);
+ zval_ptr_dtor(&t);
+ }
+
+ char* buffer;
+ char* buffer_ptr;
+ size_t buffer_used;
+ size_t buffer_size;
+
+ zval* p;
+ zval* t;
+};
+
+
+class PHPOutputTransport : public PHPTransport {
+public:
+ PHPOutputTransport(zval* _p, size_t _buffer_size = 8192) {
+ construct_with_zval(_p, _buffer_size);
+ }
+
+ ~PHPOutputTransport() {
+ flush();
+ directFlush();
+ }
+
+ void write(const char* data, size_t len) {
+ if ((len + buffer_used) > buffer_size) {
+ flush();
+ }
+ if (len > buffer_size) {
+ directWrite(data, len);
+ } else {
+ memcpy(buffer_ptr, data, len);
+ buffer_used += len;
+ buffer_ptr += len;
+ }
+ }
+
+ void writeI64(int64_t i) {
+ i = htonll(i);
+ write((const char*)&i, 8);
+ }
+
+ void writeU32(uint32_t i) {
+ i = htonl(i);
+ write((const char*)&i, 4);
+ }
+
+ void writeI32(int32_t i) {
+ i = htonl(i);
+ write((const char*)&i, 4);
+ }
+
+ void writeI16(int16_t i) {
+ i = htons(i);
+ write((const char*)&i, 2);
+ }
+
+ void writeI8(int8_t i) {
+ write((const char*)&i, 1);
+ }
+
+ void writeString(const char* str, size_t len) {
+ writeU32(len);
+ write(str, len);
+ }
+
+ void flush() {
+ if (buffer_used) {
+ directWrite(buffer, buffer_used);
+ buffer_ptr = buffer;
+ buffer_used = 0;
+ }
+ }
+
+protected:
+ void directFlush() {
+ zval ret;
+ ZVAL_NULL(&ret);
+ zval flushfn;
+ ZVAL_STRING(&flushfn, "flush", 0);
+ TSRMLS_FETCH();
+ call_user_function(EG(function_table), &t, &flushfn, &ret, 0, NULL TSRMLS_CC);
+ zval_dtor(&ret);
+ }
+ void directWrite(const char* data, size_t len) {
+ zval writefn;
+ ZVAL_STRING(&writefn, "write", 0);
+ char* newbuf = (char*)emalloc(buffer_used + 1);
+ memcpy(newbuf, buffer, buffer_used);
+ newbuf[buffer_used] = '\0';
+ zval *args[1];
+ MAKE_STD_ZVAL(args[0]);
+ ZVAL_STRINGL(args[0], newbuf, buffer_used, 0);
+ TSRMLS_FETCH();
+ zval ret;
+ ZVAL_NULL(&ret);
+ call_user_function(EG(function_table), &t, &writefn, &ret, 1, args TSRMLS_CC);
+ zval_ptr_dtor(args);
+ zval_dtor(&ret);
+ }
+};
+
+class PHPInputTransport : public PHPTransport {
+public:
+ PHPInputTransport(zval* _p, size_t _buffer_size = 8192) {
+ construct_with_zval(_p, _buffer_size);
+ }
+
+ ~PHPInputTransport() {
+ put_back();
+ }
+
+ void put_back() {
+ if (buffer_used) {
+ zval putbackfn;
+ ZVAL_STRING(&putbackfn, "putBack", 0);
+
+ char* newbuf = (char*)emalloc(buffer_used + 1);
+ memcpy(newbuf, buffer_ptr, buffer_used);
+ newbuf[buffer_used] = '\0';
+
+ zval *args[1];
+ MAKE_STD_ZVAL(args[0]);
+ ZVAL_STRINGL(args[0], newbuf, buffer_used, 0);
+
+ TSRMLS_FETCH();
+
+ zval ret;
+ ZVAL_NULL(&ret);
+ call_user_function(EG(function_table), &t, &putbackfn, &ret, 1, args TSRMLS_CC);
+ zval_ptr_dtor(args);
+ zval_dtor(&ret);
+ }
+ buffer_used = 0;
+ buffer_ptr = buffer;
+ }
+
+ void skip(size_t len) {
+ while (len) {
+ size_t chunk_size = MIN(len, buffer_used);
+ if (chunk_size) {
+ buffer_ptr = reinterpret_cast<char*>(buffer_ptr) + chunk_size;
+ buffer_used -= chunk_size;
+ len -= chunk_size;
+ }
+ if (! len) break;
+ refill();
+ }
+ }
+
+ void readBytes(void* buf, size_t len) {
+ while (len) {
+ size_t chunk_size = MIN(len, buffer_used);
+ if (chunk_size) {
+ memcpy(buf, buffer_ptr, chunk_size);
+ buffer_ptr = reinterpret_cast<char*>(buffer_ptr) + chunk_size;
+ buffer_used -= chunk_size;
+ buf = reinterpret_cast<char*>(buf) + chunk_size;
+ len -= chunk_size;
+ }
+ if (! len) break;
+ refill();
+ }
+ }
+
+ int8_t readI8() {
+ int8_t c;
+ readBytes(&c, 1);
+ return c;
+ }
+
+ int16_t readI16() {
+ int16_t c;
+ readBytes(&c, 2);
+ return (int16_t)ntohs(c);
+ }
+
+ uint32_t readU32() {
+ uint32_t c;
+ readBytes(&c, 4);
+ return (uint32_t)ntohl(c);
+ }
+
+ int32_t readI32() {
+ int32_t c;
+ readBytes(&c, 4);
+ return (int32_t)ntohl(c);
+ }
+
+protected:
+ void refill() {
+ assert(buffer_used == 0);
+ zval retval;
+ ZVAL_NULL(&retval);
+
+ zval *args[1];
+ MAKE_STD_ZVAL(args[0]);
+ ZVAL_LONG(args[0], buffer_size);
+
+ TSRMLS_FETCH();
+
+ zval funcname;
+ ZVAL_STRING(&funcname, "read", 0);
+
+ call_user_function(EG(function_table), &t, &funcname, &retval, 1, args TSRMLS_CC);
+ zval_ptr_dtor(args);
+
+ buffer_used = Z_STRLEN(retval);
+ memcpy(buffer, Z_STRVAL(retval), buffer_used);
+ zval_dtor(&retval);
+
+ buffer_ptr = buffer;
+ }
+
+};
+
+void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec);
+void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec);
+void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval** value, HashTable* fieldspec);
+void skip_element(long thrift_typeID, PHPInputTransport& transport);
+
+// Create a PHP object given a typename and call the ctor, optionally passing up to 2 arguments
+void createObject(char* obj_typename, zval* return_value, int nargs = 0, zval* arg1 = NULL, zval* arg2 = NULL) {
+ TSRMLS_FETCH();
+ size_t obj_typename_len = strlen(obj_typename);
+ zend_class_entry* ce = zend_fetch_class(obj_typename, obj_typename_len, ZEND_FETCH_CLASS_DEFAULT TSRMLS_CC);
+ if (! ce) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "Class %s does not exist", obj_typename);
+ RETURN_NULL();
+ }
+
+ object_and_properties_init(return_value, ce, NULL);
+ zend_function* constructor = zend_std_get_constructor(return_value TSRMLS_CC);
+ zval* ctor_rv = NULL;
+ zend_call_method(&return_value, ce, &constructor, NULL, 0, &ctor_rv, nargs, arg1, arg2 TSRMLS_CC);
+ zval_ptr_dtor(&ctor_rv);
+}
+
+class PHPExceptionWrapper : public std::exception {
+public:
+ PHPExceptionWrapper(zval* _ex) throw() : ex(_ex) {
+ snprintf(_what, 40, "PHP exception zval=%p", ex);
+ }
+ const char* what() const throw() { return _what; }
+ ~PHPExceptionWrapper() throw() {}
+ operator zval*() const throw() { return const_cast<zval*>(ex); } // Zend API doesn't do 'const'...
+protected:
+ zval* ex;
+ char _what[40];
+} ;
+
+void throw_tprotocolexception(char* what, long errorcode) {
+ TSRMLS_FETCH();
+
+ zval *zwhat, *zerrorcode;
+ MAKE_STD_ZVAL(zwhat);
+ MAKE_STD_ZVAL(zerrorcode);
+
+ ZVAL_STRING(zwhat, what, 1);
+ ZVAL_LONG(zerrorcode, errorcode);
+
+ zval* ex;
+ MAKE_STD_ZVAL(ex);
+ createObject("TProtocolException", ex, 2, zwhat, zerrorcode);
+ zval_ptr_dtor(&zwhat);
+ zval_ptr_dtor(&zerrorcode);
+ throw PHPExceptionWrapper(ex);
+}
+
+void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval* return_value, HashTable* fieldspec) {
+ zval** val_ptr;
+ Z_TYPE_P(return_value) = IS_NULL; // just in case
+
+ switch (thrift_typeID) {
+ case T_STOP:
+ case T_VOID:
+ RETURN_NULL();
+ return;
+ case T_STRUCT: {
+ if (zend_hash_find(fieldspec, "class", 6, (void**)&val_ptr) != SUCCESS) {
+ throw_tprotocolexception("no class type in spec", INVALID_DATA);
+ skip_element(T_STRUCT, transport);
+ RETURN_NULL();
+ }
+ char* structType = Z_STRVAL_PP(val_ptr);
+ createObject(structType, return_value);
+ if (Z_TYPE_P(return_value) == IS_NULL) {
+ // unable to create class entry
+ skip_element(T_STRUCT, transport);
+ RETURN_NULL();
+ }
+ TSRMLS_FETCH();
+ zval* spec = zend_read_static_property(zend_get_class_entry(return_value TSRMLS_CC), "_TSPEC", 6, false);
+ if (Z_TYPE_P(spec) != IS_ARRAY) {
+ char errbuf[128];
+ snprintf(errbuf, 128, "spec for %s is wrong type: %d\n", structType, Z_TYPE_P(spec));
+ throw_tprotocolexception(errbuf, INVALID_DATA);
+ RETURN_NULL();
+ }
+ binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec));
+ return;
+ } break;
+ case T_BOOL: {
+ uint8_t c;
+ transport.readBytes(&c, 1);
+ RETURN_BOOL(c != 0);
+ }
+ //case T_I08: // same numeric value as T_BYTE
+ case T_BYTE: {
+ uint8_t c;
+ transport.readBytes(&c, 1);
+ RETURN_LONG(c);
+ }
+ case T_I16: {
+ uint16_t c;
+ transport.readBytes(&c, 2);
+ RETURN_LONG(ntohs(c));
+ }
+ case T_I32: {
+ uint32_t c;
+ transport.readBytes(&c, 4);
+ RETURN_LONG(ntohl(c));
+ }
+ case T_U64:
+ case T_I64: {
+ uint64_t c;
+ transport.readBytes(&c, 8);
+ RETURN_LONG(ntohll(c));
+ }
+ case T_DOUBLE: {
+ union {
+ uint64_t c;
+ double d;
+ } a;
+ transport.readBytes(&(a.c), 8);
+ a.c = ntohll(a.c);
+ RETURN_DOUBLE(a.d);
+ }
+ //case T_UTF7: // aliases T_STRING
+ case T_UTF8:
+ case T_UTF16:
+ case T_STRING: {
+ uint32_t size = transport.readU32();
+ if (size) {
+ char* strbuf = (char*) emalloc(size + 1);
+ transport.readBytes(strbuf, size);
+ strbuf[size] = '\0';
+ ZVAL_STRINGL(return_value, strbuf, size, 0);
+ } else {
+ ZVAL_EMPTY_STRING(return_value);
+ }
+ return;
+ }
+ case T_MAP: { // array of key -> value
+ uint8_t types[2];
+ transport.readBytes(types, 2);
+ uint32_t size = transport.readU32();
+ array_init(return_value);
+
+ zend_hash_find(fieldspec, "key", 4, (void**)&val_ptr);
+ HashTable* keyspec = Z_ARRVAL_PP(val_ptr);
+ zend_hash_find(fieldspec, "val", 4, (void**)&val_ptr);
+ HashTable* valspec = Z_ARRVAL_PP(val_ptr);
+
+ for (uint32_t s = 0; s < size; ++s) {
+ zval *value;
+ MAKE_STD_ZVAL(value);
+
+ zval* key;
+ MAKE_STD_ZVAL(key);
+
+ binary_deserialize(types[0], transport, key, keyspec);
+ binary_deserialize(types[1], transport, value, valspec);
+ if (Z_TYPE_P(key) == IS_LONG) {
+ zend_hash_index_update(return_value->value.ht, Z_LVAL_P(key), &value, sizeof(zval *), NULL);
+ }
+ else {
+ convert_to_string_ex(&key);
+ zend_hash_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL);
+ }
+ zval_ptr_dtor(&key);
+ }
+ return; // return_value already populated
+ }
+ case T_LIST: { // array with autogenerated numeric keys
+ int8_t type = transport.readI8();
+ uint32_t size = transport.readU32();
+ zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr);
+ HashTable* elemspec = Z_ARRVAL_PP(val_ptr);
+
+ array_init(return_value);
+ for (uint32_t s = 0; s < size; ++s) {
+ zval *value;
+ MAKE_STD_ZVAL(value);
+ binary_deserialize(type, transport, value, elemspec);
+ zend_hash_next_index_insert(return_value->value.ht, &value, sizeof(zval *), NULL);
+ }
+ return;
+ }
+ case T_SET: { // array of key -> TRUE
+ uint8_t type;
+ uint32_t size;
+ transport.readBytes(&type, 1);
+ transport.readBytes(&size, 4);
+ size = ntohl(size);
+ zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr);
+ HashTable* elemspec = Z_ARRVAL_PP(val_ptr);
+
+ array_init(return_value);
+
+ for (uint32_t s = 0; s < size; ++s) {
+ zval* key;
+ zval* value;
+ MAKE_STD_ZVAL(key);
+ MAKE_STD_ZVAL(value);
+ ZVAL_TRUE(value);
+
+ binary_deserialize(type, transport, key, elemspec);
+
+ if (Z_TYPE_P(key) == IS_LONG) {
+ zend_hash_index_update(return_value->value.ht, Z_LVAL_P(key), &value, sizeof(zval *), NULL);
+ }
+ else {
+ convert_to_string_ex(&key);
+ zend_hash_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL);
+ }
+ zval_ptr_dtor(&key);
+ }
+ return;
+ }
+ };
+
+ char errbuf[128];
+ sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID);
+ throw_tprotocolexception(errbuf, INVALID_DATA);
+}
+
+void skip_element(long thrift_typeID, PHPInputTransport& transport) {
+ switch (thrift_typeID) {
+ case T_STOP:
+ case T_VOID:
+ return;
+ case T_STRUCT:
+ while (true) {
+ int8_t ttype = transport.readI8(); // get field type
+ if (ttype == T_STOP) break;
+ transport.skip(2); // skip field number, I16
+ skip_element(ttype, transport); // skip field payload
+ }
+ return;
+ case T_BOOL:
+ case T_BYTE:
+ transport.skip(1);
+ return;
+ case T_I16:
+ transport.skip(2);
+ return;
+ case T_I32:
+ transport.skip(4);
+ return;
+ case T_U64:
+ case T_I64:
+ case T_DOUBLE:
+ transport.skip(8);
+ return;
+ //case T_UTF7: // aliases T_STRING
+ case T_UTF8:
+ case T_UTF16:
+ case T_STRING: {
+ uint32_t len = transport.readU32();
+ transport.skip(len);
+ } return;
+ case T_MAP: {
+ int8_t keytype = transport.readI8();
+ int8_t valtype = transport.readI8();
+ uint32_t size = transport.readU32();
+ for (uint32_t i = 0; i < size; ++i) {
+ skip_element(keytype, transport);
+ skip_element(valtype, transport);
+ }
+ } return;
+ case T_LIST:
+ case T_SET: {
+ int8_t valtype = transport.readI8();
+ uint32_t size = transport.readU32();
+ for (uint32_t i = 0; i < size; ++i) {
+ skip_element(valtype, transport);
+ }
+ } return;
+ };
+
+ char errbuf[128];
+ sprintf(errbuf, "Unknown thrift typeID %ld", thrift_typeID);
+ throw_tprotocolexception(errbuf, INVALID_DATA);
+}
+
+void binary_serialize_hashtable_key(int8_t keytype, PHPOutputTransport& transport, HashTable* ht, HashPosition& ht_pos) {
+ bool keytype_is_numeric = (!((keytype == T_STRING) || (keytype == T_UTF8) || (keytype == T_UTF16)));
+
+ char* key;
+ uint key_len;
+ long index = 0;
+
+ zval* z;
+ MAKE_STD_ZVAL(z);
+
+ int res = zend_hash_get_current_key_ex(ht, &key, &key_len, (ulong*)&index, 0, &ht_pos);
+ if (keytype_is_numeric) {
+ if (res == HASH_KEY_IS_STRING) {
+ index = strtol(key, NULL, 10);
+ }
+ ZVAL_LONG(z, index);
+ } else {
+ char buf[64];
+ if (res == HASH_KEY_IS_STRING) {
+ key_len -= 1; // skip the null terminator
+ } else {
+ sprintf(buf, "%ld", index);
+ key = buf; key_len = strlen(buf);
+ }
+ ZVAL_STRINGL(z, key, key_len, 1);
+ }
+ binary_serialize(keytype, transport, &z, NULL);
+ zval_ptr_dtor(&z);
+}
+
+inline bool ttype_is_int(int8_t t) {
+ return ((t == T_BYTE) || ((t >= T_I16) && (t <= T_I64)));
+}
+
+inline bool ttypes_are_compatible(int8_t t1, int8_t t2) {
+ // Integer types of different widths are considered compatible;
+ // otherwise the typeID must match.
+ return ((t1 == t2) || (ttype_is_int(t1) && ttype_is_int(t2)));
+}
+
+void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec) {
+ // SET and LIST have 'elem' => array('type', [optional] 'class')
+ // MAP has 'val' => array('type', [optiona] 'class')
+ TSRMLS_FETCH();
+ zend_class_entry* ce = zend_get_class_entry(zthis TSRMLS_CC);
+ while (true) {
+ zval** val_ptr = NULL;
+
+ int8_t ttype = transport.readI8();
+ if (ttype == T_STOP) return;
+ int16_t fieldno = transport.readI16();
+ if (zend_hash_index_find(spec, fieldno, (void**)&val_ptr) == SUCCESS) {
+ HashTable* fieldspec = Z_ARRVAL_PP(val_ptr);
+ // pull the field name
+ // zend hash tables use the null at the end in the length... so strlen(hash key) + 1.
+ zend_hash_find(fieldspec, "var", 4, (void**)&val_ptr);
+ char* varname = Z_STRVAL_PP(val_ptr);
+
+ // and the type
+ zend_hash_find(fieldspec, "type", 5, (void**)&val_ptr);
+ convert_to_long_ex(val_ptr);
+ int8_t expected_ttype = Z_LVAL_PP(val_ptr);
+
+ if (ttypes_are_compatible(ttype, expected_ttype)) {
+ zval* rv = NULL;
+ MAKE_STD_ZVAL(rv);
+ binary_deserialize(ttype, transport, rv, fieldspec);
+ zend_update_property(ce, zthis, varname, strlen(varname), rv TSRMLS_CC);
+ zval_ptr_dtor(&rv);
+ } else {
+ skip_element(ttype, transport);
+ }
+ } else {
+ skip_element(ttype, transport);
+ }
+ }
+}
+
+void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval** value, HashTable* fieldspec) {
+ // At this point the typeID (and field num, if applicable) should've already been written to the output so all we need to do is write the payload.
+ switch (thrift_typeID) {
+ case T_STOP:
+ case T_VOID:
+ return;
+ case T_STRUCT: {
+ TSRMLS_FETCH();
+ zval* spec = zend_read_static_property(zend_get_class_entry(*value TSRMLS_CC), "_TSPEC", 6, false);
+ binary_serialize_spec(*value, transport, Z_ARRVAL_P(spec));
+ } return;
+ case T_BOOL:
+ convert_to_boolean_ex(value);
+ transport.writeI8(Z_BVAL_PP(value) ? 1 : 0);
+ return;
+ case T_BYTE:
+ convert_to_long_ex(value);
+ transport.writeI8(Z_LVAL_PP(value));
+ return;
+ case T_I16:
+ convert_to_long_ex(value);
+ transport.writeI16(Z_LVAL_PP(value));
+ return;
+ case T_I32:
+ convert_to_long_ex(value);
+ transport.writeI32(Z_LVAL_PP(value));
+ return;
+ case T_I64:
+ case T_U64:
+ convert_to_long_ex(value);
+ transport.writeI64(Z_LVAL_PP(value));
+ return;
+ case T_DOUBLE: {
+ union {
+ int64_t c;
+ double d;
+ } a;
+ convert_to_double_ex(value);
+ a.d = Z_DVAL_PP(value);
+ transport.writeI64(a.c);
+ } return;
+ //case T_UTF7:
+ case T_UTF8:
+ case T_UTF16:
+ case T_STRING:
+ convert_to_string(*value);
+ transport.writeString(Z_STRVAL_PP(value), Z_STRLEN_PP(value));
+ return;
+ case T_MAP: {
+ HashTable* ht = Z_ARRVAL_PP(value);
+ zval** val_ptr;
+
+ zend_hash_find(fieldspec, "ktype", 6, (void**)&val_ptr);
+ convert_to_long_ex(val_ptr);
+ uint8_t keytype = Z_LVAL_PP(val_ptr);
+ transport.writeI8(keytype);
+ zend_hash_find(fieldspec, "vtype", 6, (void**)&val_ptr);
+ convert_to_long_ex(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: {
+ HashTable* ht = Z_ARRVAL_PP(value);
+ zval** val_ptr;
+
+ zend_hash_find(fieldspec, "etype", 6, (void**)&val_ptr);
+ convert_to_long_ex(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: {
+ HashTable* ht = Z_ARRVAL_PP(value);
+ zval** val_ptr;
+
+ zend_hash_find(fieldspec, "etype", 6, (void**)&val_ptr);
+ convert_to_long_ex(val_ptr);
+ uint8_t keytype = Z_LVAL_PP(val_ptr);
+ transport.writeI8(keytype);
+
+ transport.writeI32(zend_hash_num_elements(ht));
+ HashPosition key_ptr;
+ for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); zend_hash_get_current_data_ex(ht, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(ht, &key_ptr)) {
+ binary_serialize_hashtable_key(keytype, transport, ht, key_ptr);
+ }
+ } return;
+ };
+ char errbuf[128];
+ sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID);
+ throw_tprotocolexception(errbuf, INVALID_DATA);
+}
+
+
+void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec) {
+ HashPosition key_ptr;
+ zval** val_ptr;
+
+ TSRMLS_FETCH();
+ zend_class_entry* ce = zend_get_class_entry(zthis TSRMLS_CC);
+
+ for (zend_hash_internal_pointer_reset_ex(spec, &key_ptr); zend_hash_get_current_data_ex(spec, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(spec, &key_ptr)) {
+ ulong fieldno;
+ if (zend_hash_get_current_key_ex(spec, NULL, NULL, &fieldno, 0, &key_ptr) != HASH_KEY_IS_LONG) {
+ throw_tprotocolexception("Bad keytype in TSPEC (expected 'long')", INVALID_DATA);
+ return;
+ }
+ HashTable* fieldspec = Z_ARRVAL_PP(val_ptr);
+
+ // field name
+ zend_hash_find(fieldspec, "var", 4, (void**)&val_ptr);
+ char* varname = Z_STRVAL_PP(val_ptr);
+
+ // thrift type
+ zend_hash_find(fieldspec, "type", 5, (void**)&val_ptr);
+ convert_to_long_ex(val_ptr);
+ int8_t ttype = Z_LVAL_PP(val_ptr);
+
+ zval* prop = zend_read_property(ce, zthis, varname, strlen(varname), false TSRMLS_CC);
+ if (Z_TYPE_P(prop) != IS_NULL) {
+ transport.writeI8(ttype);
+ transport.writeI16(fieldno);
+ binary_serialize(ttype, transport, &prop, fieldspec);
+ }
+ }
+ transport.writeI8(T_STOP); // struct end
+}
+
+// 6 params: $transport $method_name $ttype $request_struct $seqID $strict_write
+PHP_FUNCTION(thrift_protocol_write_binary) {
+ int argc = ZEND_NUM_ARGS();
+ if (argc < 6) {
+ WRONG_PARAM_COUNT;
+ }
+
+ zval ***args = (zval***) emalloc(argc * sizeof(zval**));
+ zend_get_parameters_array_ex(argc, args);
+
+ if (Z_TYPE_PP(args[0]) != IS_OBJECT) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "1st parameter is not an object (transport)");
+ efree(args);
+ RETURN_NULL();
+ }
+
+ if (Z_TYPE_PP(args[1]) != IS_STRING) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "2nd parameter is not a string (method name)");
+ efree(args);
+ RETURN_NULL();
+ }
+
+ if (Z_TYPE_PP(args[3]) != IS_OBJECT) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "4th parameter is not an object (request struct)");
+ efree(args);
+ RETURN_NULL();
+ }
+
+ PHPOutputTransport transport(*args[0]);
+ const char* method_name = Z_STRVAL_PP(args[1]);
+ convert_to_long_ex(args[2]);
+ int32_t msgtype = Z_LVAL_PP(args[2]);
+ zval* request_struct = *args[3];
+ convert_to_long_ex(args[4]);
+ int32_t seqID = Z_LVAL_PP(args[4]);
+ convert_to_boolean_ex(args[5]);
+ bool strictWrite = Z_BVAL_PP(args[5]);
+ efree(args);
+ args = NULL;
+
+ try {
+ if (strictWrite) {
+ int32_t version = VERSION_1 | msgtype;
+ transport.writeI32(version);
+ transport.writeString(method_name, strlen(method_name));
+ transport.writeI32(seqID);
+ } else {
+ transport.writeString(method_name, strlen(method_name));
+ transport.writeI8(msgtype);
+ transport.writeI32(seqID);
+ }
+
+ zval* spec = zend_read_static_property(zend_get_class_entry(request_struct TSRMLS_CC), "_TSPEC", 6, false);
+ binary_serialize_spec(request_struct, transport, Z_ARRVAL_P(spec));
+ } catch (const PHPExceptionWrapper& ex) {
+ zend_throw_exception_object(ex TSRMLS_CC);
+ RETURN_NULL();
+ }
+}
+
+// 3 params: $transport $response_Typename $strict_read
+PHP_FUNCTION(thrift_protocol_read_binary) {
+ int argc = ZEND_NUM_ARGS();
+
+ if (argc < 3) {
+ WRONG_PARAM_COUNT;
+ }
+
+ zval ***args = (zval***) emalloc(argc * sizeof(zval**));
+ zend_get_parameters_array_ex(argc, args);
+
+ if (Z_TYPE_PP(args[0]) != IS_OBJECT) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "1st parameter is not an object (transport)");
+ efree(args);
+ RETURN_NULL();
+ }
+
+ if (Z_TYPE_PP(args[1]) != IS_STRING) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "2nd parameter is not a string (typename of expected response struct)");
+ efree(args);
+ RETURN_NULL();
+ }
+
+ PHPInputTransport transport(*args[0]);
+ char* obj_typename = Z_STRVAL_PP(args[1]);
+ convert_to_boolean_ex(args[2]);
+ bool strict_read = Z_BVAL_PP(args[2]);
+ efree(args);
+ args = NULL;
+
+ try {
+ int8_t messageType = 0;
+ int32_t sz = transport.readI32();
+
+ if (sz < 0) {
+ // Check for correct version number
+ int32_t version = sz & VERSION_MASK;
+ if (version != VERSION_1) {
+ throw_tprotocolexception("Bad version identifier", BAD_VERSION);
+ }
+ messageType = (sz & 0x000000ff);
+ int32_t namelen = transport.readI32();
+ // skip the name string and the sequence ID, we don't care about those
+ transport.skip(namelen + 4);
+ } else {
+ if (strict_read) {
+ throw_tprotocolexception("No version identifier... old protocol client in strict mode?", BAD_VERSION);
+ } else {
+ // Handle pre-versioned input
+ transport.skip(sz); // skip string body
+ messageType = transport.readI8();
+ transport.skip(4); // skip sequence number
+ }
+ }
+
+ if (messageType == T_EXCEPTION) {
+ zval* ex;
+ MAKE_STD_ZVAL(ex);
+ createObject("TApplicationException", ex);
+ zval* spec = zend_read_static_property(zend_get_class_entry(ex TSRMLS_CC), "_TSPEC", 6, false);
+ 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);
+ binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec));
+ } catch (const PHPExceptionWrapper& ex) {
+ zend_throw_exception_object(ex TSRMLS_CC);
+ RETURN_NULL();
+ }
+}
+
diff --git a/lib/php/src/ext/thrift_protocol/php_thrift_protocol.h b/lib/php/src/ext/thrift_protocol/php_thrift_protocol.h
new file mode 100644
index 0000000..1db4bd0
--- /dev/null
+++ b/lib/php/src/ext/thrift_protocol/php_thrift_protocol.h
@@ -0,0 +1,7 @@
+#pragma once
+
+PHP_FUNCTION(thrift_protocol_write_binary);
+PHP_FUNCTION(thrift_protocol_read_binary);
+
+extern zend_module_entry thrift_protocole_module_entry;
+
diff --git a/lib/php/src/ext/thrift_protocol/tags/1.0.0/config.m4 b/lib/php/src/ext/thrift_protocol/tags/1.0.0/config.m4
deleted file mode 100644
index b5932f5..0000000
--- a/lib/php/src/ext/thrift_protocol/tags/1.0.0/config.m4
+++ /dev/null
@@ -1,8 +0,0 @@
-PHP_ARG_ENABLE(thrift_protocol, whether to enable the thrift_protocol extension,
-[ --enable-thrift_protocol Enable the fbthrift_protocol extension])
-
-if test "$PHP_THRIFT_PROTOCOL" != "no"; then
- PHP_REQUIRE_CXX()
- PHP_NEW_EXTENSION(thrift_protocol, php_thrift_protocol.cpp, $ext_shared)
-fi
-
diff --git a/lib/php/src/ext/thrift_protocol/tags/1.0.0/php_thrift_protocol.cpp b/lib/php/src/ext/thrift_protocol/tags/1.0.0/php_thrift_protocol.cpp
deleted file mode 100644
index 1df54d5..0000000
--- a/lib/php/src/ext/thrift_protocol/tags/1.0.0/php_thrift_protocol.cpp
+++ /dev/null
@@ -1,361 +0,0 @@
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <unistd.h>
-#include <endian.h>
-#include <byteswap.h>
-
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-#define ntohll(x) bswap_64(x)
-#else
-#define ntohll(x) x
-#endif
-
-enum TType {
- T_STOP = 0,
- T_VOID = 1,
- T_BOOL = 2,
- T_BYTE = 3,
- T_I08 = 3,
- T_I16 = 6,
- T_I32 = 8,
- T_U64 = 9,
- T_I64 = 10,
- T_DOUBLE = 4,
- T_STRING = 11,
- T_UTF7 = 11,
- T_STRUCT = 12,
- T_MAP = 13,
- T_SET = 14,
- T_LIST = 15,
- T_UTF8 = 16,
- T_UTF16 = 17
-};
-
-#include "php.h"
-#include "php_thrift_protocol.h"
-
-static function_entry thrift_protocol_functions[] = {
- PHP_FE(thrift_protocol_binary_deserialize, NULL)
- {NULL, NULL, NULL}
-} ;
-
-zend_module_entry thrift_protocol_module_entry = {
- STANDARD_MODULE_HEADER,
- "thrift_protocol",
- thrift_protocol_functions,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- "1.0",
- STANDARD_MODULE_PROPERTIES
-};
-
-#ifdef COMPILE_DL_THRIFT_PROTOCOL
-ZEND_GET_MODULE(thrift_protocol)
-#endif
-
-class PHPTransport {
-public:
- PHPTransport(zval* _p, size_t _buffer_size = 1024) : buffer(reinterpret_cast<char*>(emalloc(_buffer_size))), buffer_remaining(0), buffer_size(_buffer_size), p(_p) {
- ZVAL_STRING(&funcname, "read", 0);
- // Get the transport for the passed protocol
- zval gettransport;
- ZVAL_STRING(&gettransport, "getTransport", 0);
- MAKE_STD_ZVAL(t);
- ZVAL_NULL(t);
- TSRMLS_FETCH();
- call_user_function(EG(function_table), &p, &gettransport, t, 0, NULL TSRMLS_CC);
- }
-
- ~PHPTransport() {
- put_back();
- efree(buffer);
- zval_ptr_dtor(&t);
- }
-
- void put_back() {
- if (buffer_remaining) {
- zval putbackfn;
- ZVAL_STRING(&putbackfn, "putBack", 0);
-
- char* newbuf = (char*)emalloc(buffer_remaining + 1);
- memcpy(newbuf, buffer_ptr, buffer_remaining);
- newbuf[buffer_remaining] = '\0';
-
- zval *args[1];
- MAKE_STD_ZVAL(args[0]);
- ZVAL_STRINGL(args[0], newbuf, buffer_remaining, 0);
-
- TSRMLS_FETCH();
-
- zval ret;
- call_user_function(EG(function_table), &t, &putbackfn, &ret, 1, args TSRMLS_CC);
- zval_ptr_dtor(args);
- zval_dtor(&ret);
- }
- buffer_remaining = 0;
- buffer_ptr = buffer;
- }
-
- zval* protocol() { return p; }
- zval* transport() { return t; }
-
- void readBytes(void* buf, size_t len) {
- while (len) {
- size_t chunk_size = MIN(len, buffer_remaining);
- if (chunk_size) {
- memcpy(buf, buffer_ptr, chunk_size);
- buffer_ptr = reinterpret_cast<char*>(buffer_ptr) + chunk_size;
- buffer_remaining -= chunk_size;
- buf = reinterpret_cast<char*>(buf) + chunk_size;
- len -= chunk_size;
- }
- if (! len) break;
- refill();
- }
- }
-
-protected:
- void refill() {
- assert(buffer_remaining == 0);
- zval retval;
- ZVAL_NULL(&retval);
-
- zval *args[1];
- MAKE_STD_ZVAL(args[0]);
- ZVAL_LONG(args[0], buffer_size);
-
- TSRMLS_FETCH();
-
- call_user_function(EG(function_table), &t, &funcname, &retval, 1, args TSRMLS_CC);
- zval_ptr_dtor(args);
-
- buffer_remaining = Z_STRLEN(retval);
- memcpy(buffer, Z_STRVAL(retval), buffer_remaining);
- zval_dtor(&retval);
-
- buffer_ptr = buffer;
- }
-
- char* buffer;
- char* buffer_ptr;
- size_t buffer_remaining;
- size_t buffer_size;
-
- zval* p;
- zval* t;
- zval funcname;
-};
-
-// Does not call the ctor on the object, all fields will be NULL
-void createObject(char* obj_typename, zval* return_value) {
- TSRMLS_FETCH();
- size_t obj_typename_len = strlen(obj_typename);
- zend_class_entry* ce = zend_fetch_class(obj_typename, obj_typename_len, ZEND_FETCH_CLASS_DEFAULT TSRMLS_CC);
- if (! ce) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "Class %s does not exist", obj_typename);
- RETURN_NULL();
- }
-
- object_and_properties_init(return_value, ce, NULL);
-}
-
-void binary_deserialize(long thrift_typeID, PHPTransport& transport, zval* return_value, char* structType) {
- 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: {
- assert(structType);
- createObject(structType, return_value);
- zval retval;
- ZVAL_NULL(&retval);
- zval *args[1];
- args[0] = transport.protocol();
- zval funcname;
- ZVAL_STRING(&funcname, "read", 0);
- transport.put_back(); // return our buffer to the userland T{Framed,Buffered}Transport for reading the field headers and such
- TSRMLS_FETCH();
- call_user_function(EG(function_table), &return_value, &funcname, &retval, 1, args TSRMLS_CC);
- zval_dtor(&retval);
- return;
- } break;
- case T_BOOL: {
- uint8_t c;
- transport.readBytes(&c, 1);
- RETURN_BOOL(c != 0);
- }
- //case T_I08: // same numeric value as T_BYTE
- case T_BYTE: {
- uint8_t c;
- transport.readBytes(&c, 1);
- RETURN_LONG(c);
- }
- case T_I16: {
- uint16_t c;
- transport.readBytes(&c, 2);
- RETURN_LONG(ntohs(c));
- }
- case T_I32: {
- uint32_t c;
- transport.readBytes(&c, 4);
- RETURN_LONG(ntohl(c));
- }
- case T_U64:
- case T_I64: {
- uint64_t c;
- transport.readBytes(&c, 8);
- RETURN_LONG(ntohll(c));
- }
- case T_DOUBLE: {
- union {
- uint64_t c;
- double d;
- } a;
- transport.readBytes(&(a.c), 8);
- a.c = ntohll(a.c);
- RETURN_DOUBLE(a.d);
- }
- //case T_UTF7: // aliases T_STRING
- case T_UTF8:
- case T_UTF16:
- case T_STRING: {
- uint32_t size;
- transport.readBytes(&size, 4);
- size = ntohl(size);
- char* strbuf = (char*) emalloc(size + 1);
- if (size) {
- transport.readBytes(strbuf, size);
- }
- strbuf[size] = '\0';
- ZVAL_STRINGL(return_value, strbuf, size, 0);
- return;
- }
- case T_MAP: { // array of key -> value
- uint8_t types[2];
- uint32_t size;
- transport.readBytes(types, 2);
- transport.readBytes(&size, 4);
- size = ntohl(size);
- array_init(return_value);
-
- 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, NULL);
- binary_deserialize(types[1], transport, value, structType);
- if (Z_TYPE_P(key) == IS_LONG) {
- zend_hash_index_update(return_value->value.ht, Z_LVAL_P(key), &value, sizeof(zval *), NULL);
- }
- else {
- convert_to_string_ex(&key);
- zend_hash_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL);
- }
- zval_ptr_dtor(&key);
- }
- return; // return_value already populated
- }
- case T_LIST: { // array with autogenerated numeric keys
- uint8_t type;
- uint32_t size;
- transport.readBytes(&type, 1);
- transport.readBytes(&size, 4);
- size = ntohl(size);
-
- array_init(return_value);
- for (uint32_t s = 0; s < size; ++s) {
- zval *value;
- MAKE_STD_ZVAL(value);
- binary_deserialize(type, transport, value, structType);
- 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);
- 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, NULL);
-
- if (Z_TYPE_P(key) == IS_LONG) {
- zend_hash_index_update(return_value->value.ht, Z_LVAL_P(key), &value, sizeof(zval *), NULL);
- }
- else {
- convert_to_string_ex(&key);
- zend_hash_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL);
- }
- zval_ptr_dtor(&key);
- }
- return;
- }
- default:
- TSRMLS_FETCH();
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "Unknown thrift typeID %ld", thrift_typeID);
- RETURN_NULL();
- };
- assert(0); // should never get here...
-}
-
-PHP_FUNCTION(thrift_protocol_binary_deserialize) {
- int argc = ZEND_NUM_ARGS();
- long thrift_typeID;
-
- if (argc < 2) {
- WRONG_PARAM_COUNT;
- }
-
- zval ***args = (zval***) emalloc(argc * sizeof(zval**));
- zend_get_parameters_array_ex(argc, args);
- convert_to_long_ex(args[0]);
- thrift_typeID = Z_LVAL_PP(args[0]);
-
- if (Z_TYPE_PP(args[1]) != IS_OBJECT) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "2nd parameter is not an object");
- efree(args);
- RETURN_NULL();
- }
-
- char* structType = NULL;
- if (argc >= 3) {
- if (Z_TYPE_PP(args[2]) == IS_STRING) {
- for (int s = 0; s < Z_STRLEN_PP(args[2]); ++s) {
- if (isalpha(Z_STRVAL_PP(args[2])[s])) Z_STRVAL_PP(args[2])[s] = tolower(Z_STRVAL_PP(args[2])[s]);
- }
- structType = Z_STRVAL_PP(args[2]);
- } else {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "3rd parameter (if present) must be a string");
- efree(args);
- RETURN_NULL();
- }
- }
-
- PHPTransport transport(*args[1]);
-
- binary_deserialize(thrift_typeID, transport, return_value, structType);
- efree(args);
-}
-
diff --git a/lib/php/src/ext/thrift_protocol/tags/1.0.0/php_thrift_protocol.h b/lib/php/src/ext/thrift_protocol/tags/1.0.0/php_thrift_protocol.h
deleted file mode 100644
index 3f016ba..0000000
--- a/lib/php/src/ext/thrift_protocol/tags/1.0.0/php_thrift_protocol.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#pragma once
-
-PHP_FUNCTION(thrift_protocol_binary_deserialize);
-
-extern zend_module_entry thrift_protocole_module_entry;
-
diff --git a/lib/php/src/ext/thrift_protocol/trunk/php_thrift_protocol.cpp b/lib/php/src/ext/thrift_protocol/trunk/php_thrift_protocol.cpp
deleted file mode 100644
index a666128..0000000
--- a/lib/php/src/ext/thrift_protocol/trunk/php_thrift_protocol.cpp
+++ /dev/null
@@ -1,361 +0,0 @@
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <unistd.h>
-#include <endian.h>
-#include <byteswap.h>
-
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-#define ntohll(x) bswap_64(x)
-#else
-#define ntohll(x) x
-#endif
-
-enum TType {
- T_STOP = 0,
- T_VOID = 1,
- T_BOOL = 2,
- T_BYTE = 3,
- T_I08 = 3,
- T_I16 = 6,
- T_I32 = 8,
- T_U64 = 9,
- T_I64 = 10,
- T_DOUBLE = 4,
- T_STRING = 11,
- T_UTF7 = 11,
- T_STRUCT = 12,
- T_MAP = 13,
- T_SET = 14,
- T_LIST = 15,
- T_UTF8 = 16,
- T_UTF16 = 17
-};
-
-#include "php.h"
-#include "php_thrift_protocol.h"
-
-static function_entry thrift_protocol_functions[] = {
- PHP_FE(thrift_protocol_binary_deserialize, NULL)
- {NULL, NULL, NULL}
-} ;
-
-zend_module_entry thrift_protocol_module_entry = {
- STANDARD_MODULE_HEADER,
- "thrift_protocol",
- thrift_protocol_functions,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- "1.0",
- STANDARD_MODULE_PROPERTIES
-};
-
-#ifdef COMPILE_DL_THRIFT_PROTOCOL
-ZEND_GET_MODULE(thrift_protocol)
-#endif
-
-class PHPTransport {
-public:
- PHPTransport(zval* _p, size_t _buffer_size = 1024) : buffer(reinterpret_cast<char*>(emalloc(_buffer_size))), buffer_remaining(0), buffer_size(_buffer_size), p(_p) {
- ZVAL_STRING(&funcname, "read", 0);
- // Get the transport for the passed protocol
- zval gettransport;
- ZVAL_STRING(&gettransport, "getTransport", 0);
- MAKE_STD_ZVAL(t);
- ZVAL_NULL(t);
- TSRMLS_FETCH();
- call_user_function(EG(function_table), &p, &gettransport, t, 0, NULL TSRMLS_CC);
- }
-
- ~PHPTransport() {
- put_back();
- efree(buffer);
- zval_ptr_dtor(&t);
- }
-
- void put_back() {
- if (buffer_remaining) {
- zval putbackfn;
- ZVAL_STRING(&putbackfn, "putBack", 0);
-
- char* newbuf = (char*)emalloc(buffer_remaining + 1);
- memcpy(newbuf, buffer_ptr, buffer_remaining);
- newbuf[buffer_remaining] = '\0';
-
- zval *args[1];
- MAKE_STD_ZVAL(args[0]);
- ZVAL_STRINGL(args[0], newbuf, buffer_remaining, 0);
-
- TSRMLS_FETCH();
-
- zval ret;
- call_user_function(EG(function_table), &t, &putbackfn, &ret, 1, args TSRMLS_CC);
- zval_ptr_dtor(args);
- zval_dtor(&ret);
- }
- buffer_remaining = 0;
- buffer_ptr = buffer;
- }
-
- zval* protocol() { return p; }
- zval* transport() { return t; }
-
- void readBytes(void* buf, size_t len) {
- while (len) {
- size_t chunk_size = MIN(len, buffer_remaining);
- if (chunk_size) {
- memcpy(buf, buffer_ptr, chunk_size);
- buffer_ptr = reinterpret_cast<char*>(buffer_ptr) + chunk_size;
- buffer_remaining -= chunk_size;
- buf = reinterpret_cast<char*>(buf) + chunk_size;
- len -= chunk_size;
- }
- if (! len) break;
- refill();
- }
- }
-
-protected:
- void refill() {
- assert(buffer_remaining == 0);
- zval retval;
- ZVAL_NULL(&retval);
-
- zval *args[1];
- MAKE_STD_ZVAL(args[0]);
- ZVAL_LONG(args[0], buffer_size);
-
- TSRMLS_FETCH();
-
- call_user_function(EG(function_table), &t, &funcname, &retval, 1, args TSRMLS_CC);
- zval_ptr_dtor(args);
-
- buffer_remaining = Z_STRLEN(retval);
- memcpy(buffer, Z_STRVAL(retval), buffer_remaining);
- zval_dtor(&retval);
-
- buffer_ptr = buffer;
- }
-
- char* buffer;
- char* buffer_ptr;
- size_t buffer_remaining;
- size_t buffer_size;
-
- zval* p;
- zval* t;
- zval funcname;
-};
-
-// Does not call the ctor on the object, all fields will be NULL
-void createObject(char* obj_typename, zval* return_value) {
- TSRMLS_FETCH();
- size_t obj_typename_len = strlen(obj_typename);
- zend_class_entry* ce = zend_fetch_class(obj_typename, obj_typename_len, ZEND_FETCH_CLASS_DEFAULT TSRMLS_CC);
- if (! ce) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "Class %s does not exist", obj_typename);
- RETURN_NULL();
- }
-
- object_and_properties_init(return_value, ce, NULL);
-}
-
-void binary_deserialize(long thrift_typeID, PHPTransport& transport, zval* return_value, char* structType) {
- 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: {
- assert(structType);
- createObject(structType, return_value);
- zval retval;
- ZVAL_NULL(&retval);
- zval *args[1];
- args[0] = transport.protocol();
- zval funcname;
- ZVAL_STRING(&funcname, "read", 0);
- transport.put_back(); // return our buffer to the userland T{Framed,Buffered}Transport for reading the field headers and such
- TSRMLS_FETCH();
- call_user_function(EG(function_table), &return_value, &funcname, &retval, 1, args TSRMLS_CC);
- zval_dtor(&retval);
- return;
- } break;
- case T_BOOL: {
- uint8_t c;
- transport.readBytes(&c, 1);
- RETURN_BOOL(c != 0);
- }
- //case T_I08: // same numeric value as T_BYTE
- case T_BYTE: {
- uint8_t c;
- transport.readBytes(&c, 1);
- RETURN_LONG(c);
- }
- case T_I16: {
- uint16_t c;
- transport.readBytes(&c, 2);
- RETURN_LONG(ntohs(c));
- }
- case T_I32: {
- uint32_t c;
- transport.readBytes(&c, 4);
- RETURN_LONG(ntohl(c));
- }
- case T_U64:
- case T_I64: {
- uint64_t c;
- transport.readBytes(&c, 8);
- RETURN_LONG(ntohll(c));
- }
- case T_DOUBLE: {
- union {
- uint64_t c;
- double d;
- } a;
- transport.readBytes(&(a.c), 8);
- a.c = ntohll(a.c);
- RETURN_DOUBLE(a.d);
- }
- //case T_UTF7: // aliases T_STRING
- case T_UTF8:
- case T_UTF16:
- case T_STRING: {
- uint32_t size;
- transport.readBytes(&size, 4);
- size = ntohl(size);
- char* strbuf = (char*) emalloc(size + 1);
- if (size) {
- transport.readBytes(strbuf, size);
- }
- strbuf[size] = '\0';
- ZVAL_STRINGL(return_value, strbuf, size, 0);
- return;
- }
- case T_MAP: { // array of key -> value
- uint8_t types[2];
- uint32_t size;
- transport.readBytes(types, 2);
- transport.readBytes(&size, 4);
- size = ntohl(size);
- array_init(return_value);
-
- 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, NULL);
- binary_deserialize(types[1], transport, value, structType);
- if (Z_TYPE_P(key) == IS_LONG) {
- zend_hash_index_update(return_value->value.ht, Z_LVAL_P(key), &value, sizeof(zval *), NULL);
- }
- else {
- convert_to_string_ex(&key);
- zend_hash_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL);
- }
- zval_ptr_dtor(&key);
- }
- return; // return_value already populated
- }
- case T_LIST: { // array with autogenerated numeric keys
- uint8_t type;
- uint32_t size;
- transport.readBytes(&type, 1);
- transport.readBytes(&size, 4);
- size = ntohl(size);
-
- array_init(return_value);
- for (uint32_t s = 0; s < size; ++s) {
- zval *value;
- MAKE_STD_ZVAL(value);
- binary_deserialize(type, transport, value, structType);
- 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);
- 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, NULL);
-
- if (Z_TYPE_P(key) == IS_LONG) {
- zend_hash_index_update(return_value->value.ht, Z_LVAL_P(key), &value, sizeof(zval *), NULL);
- }
- else {
- convert_to_string_ex(&key);
- zend_hash_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL);
- }
- zval_ptr_dtor(&key);
- }
- return;
- }
- default:
- TSRMLS_FETCH();
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "Unknown thrift typeID %ld", thrift_typeID);
- RETURN_NULL();
- };
- assert(0); // should never get here...
-}
-
-PHP_FUNCTION(thrift_protocol_binary_deserialize) {
- int argc = ZEND_NUM_ARGS();
- long thrift_typeID;
-
- if (argc < 2) {
- WRONG_PARAM_COUNT;
- }
-
- zval ***args = (zval***) emalloc(argc * sizeof(zval**));
- zend_get_parameters_array_ex(argc, args);
- convert_to_long_ex(args[0]);
- thrift_typeID = Z_LVAL_PP(args[0]);
-
- if (Z_TYPE_PP(args[1]) != IS_OBJECT) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "2nd parameter is not an object");
- efree(args);
- RETURN_NULL();
- }
-
- char* structType = NULL;
- if (argc >= 3) {
- if (Z_TYPE_PP(args[2]) == IS_STRING) {
- for (int s = 0; s < Z_STRLEN_PP(args[2]); ++s) {
- if (isalpha(Z_STRVAL_PP(args[2])[s])) Z_STRVAL_PP(args[2])[s] = tolower(Z_STRVAL_PP(args[2])[s]);
- }
- structType = Z_STRVAL_PP(args[2]);
- } else {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "3rd parameter (if present) must be a string");
- efree(args);
- RETURN_NULL();
- }
- }
-
- PHPTransport transport(*args[1]);
-
- binary_deserialize(thrift_typeID, transport, return_value, structType);
- efree(args);
-}
-
diff --git a/lib/php/src/ext/thrift_protocol/trunk/php_thrift_protocol.h b/lib/php/src/ext/thrift_protocol/trunk/php_thrift_protocol.h
deleted file mode 100644
index 3f016ba..0000000
--- a/lib/php/src/ext/thrift_protocol/trunk/php_thrift_protocol.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#pragma once
-
-PHP_FUNCTION(thrift_protocol_binary_deserialize);
-
-extern zend_module_entry thrift_protocole_module_entry;
-
diff --git a/lib/php/src/protocol/TBinaryProtocol.php b/lib/php/src/protocol/TBinaryProtocol.php
index 874a548..4877112 100644
--- a/lib/php/src/protocol/TBinaryProtocol.php
+++ b/lib/php/src/protocol/TBinaryProtocol.php
@@ -23,8 +23,8 @@
const VERSION_MASK = 0xffff0000;
const VERSION_1 = 0x80010000;
- private $strictRead_ = false;
- private $strictWrite_ = true;
+ protected $strictRead_ = false;
+ protected $strictWrite_ = true;
public function __construct($trans, $strictRead=false, $strictWrite=true) {
parent::__construct($trans);
@@ -412,6 +412,12 @@
}
parent::__construct($trans, $strictRead, $strictWrite);
}
+ public function isStrictRead() {
+ return $this->strictRead_;
+ }
+ public function isStrictWrite() {
+ return $this->strictWrite_;
+ }
}
?>