You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@thrift.apache.org by jk...@apache.org on 2017/10/25 12:59:56 UTC
[2/3] thrift git commit: THRIFT-4356: fix segment fault for
thrift_protocol NOTE: drops php5 support for PHP extension (thrift_protocol)
however library still can support PHP5 as evidenced by CI build Client: php
http://git-wip-us.apache.org/repos/asf/thrift/blob/9dff0efc/lib/php/src/ext/thrift_protocol/php_thrift_protocol7.cpp
----------------------------------------------------------------------
diff --git a/lib/php/src/ext/thrift_protocol/php_thrift_protocol7.cpp b/lib/php/src/ext/thrift_protocol/php_thrift_protocol7.cpp
deleted file mode 100644
index dafc185..0000000
--- a/lib/php/src/ext/thrift_protocol/php_thrift_protocol7.cpp
+++ /dev/null
@@ -1,1102 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "php.h"
-#include "zend_interfaces.h"
-#include "zend_exceptions.h"
-#include "php_thrift_protocol.h"
-
-#if PHP_VERSION_ID >= 70000
-
-#include <sys/types.h>
-#include <arpa/inet.h>
-
-#include <cstdint>
-#include <stdexcept>
-#include <algorithm>
-
-#ifndef bswap_64
-#define bswap_64(x) (((uint64_t)(x) << 56) | \
- (((uint64_t)(x) << 40) & 0xff000000000000ULL) | \
- (((uint64_t)(x) << 24) & 0xff0000000000ULL) | \
- (((uint64_t)(x) << 8) & 0xff00000000ULL) | \
- (((uint64_t)(x) >> 8) & 0xff000000ULL) | \
- (((uint64_t)(x) >> 24) & 0xff0000ULL) | \
- (((uint64_t)(x) >> 40) & 0xff00ULL) | \
- ((uint64_t)(x) >> 56))
-#endif
-
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-#define htonll(x) bswap_64(x)
-#define ntohll(x) bswap_64(x)
-#elif __BYTE_ORDER == __BIG_ENDIAN
-#define htonll(x) x
-#define ntohll(x) x
-#else
-#error Unknown __BYTE_ORDER
-#endif
-
-enum TType {
- T_STOP = 0,
- T_VOID = 1,
- T_BOOL = 2,
- T_BYTE = 3,
- T_I08 = 3,
- T_I16 = 6,
- T_I32 = 8,
- T_U64 = 9,
- T_I64 = 10,
- T_DOUBLE = 4,
- T_STRING = 11,
- T_UTF7 = 11,
- T_STRUCT = 12,
- T_MAP = 13,
- T_SET = 14,
- T_LIST = 15,
- T_UTF8 = 16,
- T_UTF16 = 17
-};
-
-const int32_t VERSION_MASK = 0xffff0000;
-const int32_t VERSION_1 = 0x80010000;
-const int8_t T_CALL = 1;
-const int8_t T_REPLY = 2;
-const int8_t T_EXCEPTION = 3;
-// tprotocolexception
-const int INVALID_DATA = 1;
-const int BAD_VERSION = 4;
-
-static zend_function_entry thrift_protocol_functions[] = {
- PHP_FE(thrift_protocol_write_binary, nullptr)
- PHP_FE(thrift_protocol_read_binary, nullptr)
- PHP_FE(thrift_protocol_read_binary_after_message_begin, nullptr)
- {nullptr, nullptr, nullptr}
-};
-
-zend_module_entry thrift_protocol_module_entry = {
- STANDARD_MODULE_HEADER,
- "thrift_protocol",
- thrift_protocol_functions,
- nullptr,
- nullptr,
- nullptr,
- nullptr,
- nullptr,
- "1.0",
- STANDARD_MODULE_PROPERTIES
-};
-
-#ifdef COMPILE_DL_THRIFT_PROTOCOL
-ZEND_GET_MODULE(thrift_protocol)
-#endif
-
-class PHPExceptionWrapper : public std::exception {
-public:
- PHPExceptionWrapper(zval* _ex) throw() {
- ZVAL_COPY(&ex, _ex);
- snprintf(_what, 40, "PHP exception zval=%p", _ex);
- }
-
- PHPExceptionWrapper(zend_object* _exobj) throw() {
- ZVAL_OBJ(&ex, _exobj);
- snprintf(_what, 40, "PHP exception zval=%p", _exobj);
- }
- ~PHPExceptionWrapper() throw() {
- zval_dtor(&ex);
- }
-
- const char* what() const throw() {
- return _what;
- }
- operator zval*() const throw() {
- return const_cast<zval*>(&ex);
- } // Zend API doesn't do 'const'...
-protected:
- zval ex;
- char _what[40];
-} ;
-
-class PHPTransport {
-protected:
- PHPTransport(zval* _p, size_t _buffer_size) {
- assert(Z_TYPE_P(_p) == IS_OBJECT);
-
- ZVAL_UNDEF(&t);
-
- buffer = reinterpret_cast<char*>(emalloc(_buffer_size));
- buffer_ptr = buffer;
- buffer_used = 0;
- buffer_size = _buffer_size;
-
- // Get the transport for the passed protocol
- zval gettransport;
- ZVAL_STRING(&gettransport, "getTransport");
- call_user_function(nullptr, _p, &gettransport, &t, 0, nullptr);
-
- zval_dtor(&gettransport);
-
- assert(Z_TYPE(t) == IS_OBJECT);
- }
-
- ~PHPTransport() {
- efree(buffer);
- zval_dtor(&t);
- }
-
- char* buffer;
- char* buffer_ptr;
- size_t buffer_used;
- size_t buffer_size;
-
- zval t;
-};
-
-
-class PHPOutputTransport : public PHPTransport {
-public:
- PHPOutputTransport(zval* _p, size_t _buffer_size = 8192) : PHPTransport(_p, _buffer_size) { }
- ~PHPOutputTransport() { }
-
- void write(const char* data, size_t len) {
- if ((len + buffer_used) > buffer_size) {
- internalFlush();
- }
- if (len > buffer_size) {
- directWrite(data, len);
- } else {
- memcpy(buffer_ptr, data, len);
- buffer_used += len;
- buffer_ptr += len;
- }
- }
-
- void writeI64(int64_t i) {
- i = htonll(i);
- write((const char*)&i, 8);
- }
-
- void writeU32(uint32_t i) {
- i = htonl(i);
- write((const char*)&i, 4);
- }
-
- void writeI32(int32_t i) {
- i = htonl(i);
- write((const char*)&i, 4);
- }
-
- void writeI16(int16_t i) {
- i = htons(i);
- write((const char*)&i, 2);
- }
-
- void writeI8(int8_t i) {
- write((const char*)&i, 1);
- }
-
- void writeString(const char* str, size_t len) {
- writeU32(len);
- write(str, len);
- }
-
- void flush() {
- internalFlush();
- directFlush();
- }
-
-protected:
- void internalFlush() {
- if (buffer_used) {
- directWrite(buffer, buffer_used);
- buffer_ptr = buffer;
- buffer_used = 0;
- }
- }
- void directFlush() {
- zval ret, flushfn;
- ZVAL_NULL(&ret);
- ZVAL_STRING(&flushfn, "flush");
-
- call_user_function(EG(function_table), &(this->t), &flushfn, &ret, 0, nullptr);
- zval_dtor(&flushfn);
- zval_dtor(&ret);
- }
- void directWrite(const char* data, size_t len) {
- zval args[1], ret, writefn;
-
- ZVAL_STRING(&writefn, "write");
- ZVAL_STRINGL(&args[0], data, len);
-
- ZVAL_NULL(&ret);
- call_user_function(EG(function_table), &(this->t), &writefn, &ret, 1, args);
-
- zval_dtor(&writefn);
- zval_dtor(&ret);
- zval_dtor(&args[0]);
-
- if (EG(exception)) {
- zend_object *ex = EG(exception);
- EG(exception) = nullptr;
- throw PHPExceptionWrapper(ex);
- }
- }
-};
-
-class PHPInputTransport : public PHPTransport {
-public:
- PHPInputTransport(zval* _p, size_t _buffer_size = 8192) : PHPTransport(_p, _buffer_size) {
- }
-
- ~PHPInputTransport() {
- put_back();
- }
-
- void put_back() {
- if (buffer_used) {
- zval args[1], ret, putbackfn;
- ZVAL_STRINGL(&args[0], buffer_ptr, buffer_used);
- ZVAL_STRING(&putbackfn, "putBack");
- ZVAL_NULL(&ret);
-
- call_user_function(EG(function_table), &(this->t), &putbackfn, &ret, 1, args);
-
- zval_dtor(&putbackfn);
- zval_dtor(&ret);
- zval_dtor(&args[0]);
- }
- buffer_used = 0;
- buffer_ptr = buffer;
- }
-
- void skip(size_t len) {
- while (len) {
- size_t chunk_size = std::min(len, buffer_used);
- if (chunk_size) {
- buffer_ptr = reinterpret_cast<char*>(buffer_ptr) + chunk_size;
- buffer_used -= chunk_size;
- len -= chunk_size;
- }
- if (! len) break;
- refill();
- }
- }
-
- void readBytes(void* buf, size_t len) {
- while (len) {
- size_t chunk_size = std::min(len, buffer_used);
- if (chunk_size) {
- memcpy(buf, buffer_ptr, chunk_size);
- buffer_ptr = reinterpret_cast<char*>(buffer_ptr) + chunk_size;
- buffer_used -= chunk_size;
- buf = reinterpret_cast<char*>(buf) + chunk_size;
- len -= chunk_size;
- }
- if (! len) break;
- refill();
- }
- }
-
- int8_t readI8() {
- int8_t c;
- readBytes(&c, 1);
- return c;
- }
-
- int16_t readI16() {
- int16_t c;
- readBytes(&c, 2);
- return (int16_t)ntohs(c);
- }
-
- uint32_t readU32() {
- uint32_t c;
- readBytes(&c, 4);
- return (uint32_t)ntohl(c);
- }
-
- int32_t readI32() {
- int32_t c;
- readBytes(&c, 4);
- return (int32_t)ntohl(c);
- }
-
-protected:
- void refill() {
- assert(buffer_used == 0);
- zval retval;
- zval args[1];
- zval funcname;
-
- ZVAL_NULL(&retval);
- ZVAL_LONG(&args[0], buffer_size);
-
- ZVAL_STRING(&funcname, "read");
-
- call_user_function(EG(function_table), &(this->t), &funcname, &retval, 1, args);
- zval_dtor(&args[0]);
- zval_dtor(&funcname);
-
- if (EG(exception)) {
- zval_dtor(&retval);
-
- zend_object *ex = EG(exception);
- EG(exception) = nullptr;
- throw PHPExceptionWrapper(ex);
- }
-
- buffer_used = Z_STRLEN(retval);
- memcpy(buffer, Z_STRVAL(retval), buffer_used);
-
- zval_dtor(&retval);
-
- buffer_ptr = buffer;
- }
-
-};
-
-static
-void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec);
-static
-void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec);
-static
-void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval* value, HashTable* fieldspec);
-
-// Create a PHP object given a typename and call the ctor, optionally passing up to 2 arguments
-static
-void createObject(const char* obj_typename, zval* return_value, int nargs = 0, zval* arg1 = nullptr, zval* arg2 = nullptr) {
- /* is there a better way to do that on the stack ? */
- zend_string *obj_name = zend_string_init(obj_typename, strlen(obj_typename), 0);
- zend_class_entry* ce = zend_fetch_class(obj_name, ZEND_FETCH_CLASS_DEFAULT);
- zend_string_release(obj_name);
-
- if (! ce) {
- php_error_docref(nullptr, E_ERROR, "Class %s does not exist", obj_typename);
- RETURN_NULL();
- }
-
- object_and_properties_init(return_value, ce, nullptr);
- zend_function* constructor = zend_std_get_constructor(Z_OBJ_P(return_value));
- zval ctor_rv;
- zend_call_method(return_value, ce, &constructor, NULL, 0, &ctor_rv, nargs, arg1, arg2);
- zval_dtor(&ctor_rv);
-}
-
-static
-void throw_tprotocolexception(const char* what, long errorcode) {
- zval zwhat, zerrorcode;
-
- ZVAL_STRING(&zwhat, what);
- ZVAL_LONG(&zerrorcode, errorcode);
-
- zval ex;
- createObject("\\Thrift\\Exception\\TProtocolException", &ex, 2, &zwhat, &zerrorcode);
-
- zval_dtor(&zwhat);
- zval_dtor(&zerrorcode);
-
- throw PHPExceptionWrapper(&ex);
-}
-
-// Sets EG(exception), call this and then RETURN_NULL();
-static
-void throw_zend_exception_from_std_exception(const std::exception& ex) {
- zend_throw_exception(zend_exception_get_default(), const_cast<char*>(ex.what()), 0);
-}
-
-static
-void skip_element(long thrift_typeID, PHPInputTransport& transport) {
- switch (thrift_typeID) {
- case T_STOP:
- case T_VOID:
- return;
- case T_STRUCT:
- while (true) {
- int8_t ttype = transport.readI8(); // get field type
- if (ttype == T_STOP) break;
- transport.skip(2); // skip field number, I16
- skip_element(ttype, transport); // skip field payload
- }
- return;
- case T_BOOL:
- case T_BYTE:
- transport.skip(1);
- return;
- case T_I16:
- transport.skip(2);
- return;
- case T_I32:
- transport.skip(4);
- return;
- case T_U64:
- case T_I64:
- case T_DOUBLE:
- transport.skip(8);
- return;
- //case T_UTF7: // aliases T_STRING
- case T_UTF8:
- case T_UTF16:
- case T_STRING: {
- uint32_t len = transport.readU32();
- transport.skip(len);
- } return;
- case T_MAP: {
- int8_t keytype = transport.readI8();
- int8_t valtype = transport.readI8();
- uint32_t size = transport.readU32();
- for (uint32_t i = 0; i < size; ++i) {
- skip_element(keytype, transport);
- skip_element(valtype, transport);
- }
- } return;
- case T_LIST:
- case T_SET: {
- int8_t valtype = transport.readI8();
- uint32_t size = transport.readU32();
- for (uint32_t i = 0; i < size; ++i) {
- skip_element(valtype, transport);
- }
- } return;
- };
-
- char errbuf[128];
- sprintf(errbuf, "Unknown thrift typeID %ld", thrift_typeID);
- throw_tprotocolexception(errbuf, INVALID_DATA);
-}
-
-static inline
-bool zval_is_bool(zval* v) {
- return Z_TYPE_P(v) == IS_TRUE || Z_TYPE_P(v) == IS_FALSE;
-}
-
-static
-void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval* return_value, HashTable* fieldspec) {
- ZVAL_NULL(return_value);
-
- switch (thrift_typeID) {
- case T_STOP:
- case T_VOID:
- RETURN_NULL();
- return;
- case T_STRUCT: {
- zval* val_ptr = zend_hash_str_find(fieldspec, "class", sizeof("class")-1);
- if (val_ptr == nullptr) {
- throw_tprotocolexception("no class type in spec", INVALID_DATA);
- skip_element(T_STRUCT, transport);
- RETURN_NULL();
- }
-
- char* structType = Z_STRVAL_P(val_ptr);
- // Create an object in PHP userland based on our spec
- createObject(structType, return_value);
- if (Z_TYPE_P(return_value) == IS_NULL) {
- // unable to create class entry
- skip_element(T_STRUCT, transport);
- RETURN_NULL();
- }
-
- zval* spec = zend_read_static_property(Z_OBJCE_P(return_value), "_TSPEC", sizeof("_TSPEC")-1, false);
- if (Z_TYPE_P(spec) != IS_ARRAY) {
- char errbuf[128];
- snprintf(errbuf, 128, "spec for %s is wrong type: %d\n", structType, Z_TYPE_P(spec));
- throw_tprotocolexception(errbuf, INVALID_DATA);
- RETURN_NULL();
- }
- binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec));
- return;
- } break;
- case T_BOOL: {
- uint8_t c;
- transport.readBytes(&c, 1);
- RETURN_BOOL(c != 0);
- }
- //case T_I08: // same numeric value as T_BYTE
- case T_BYTE: {
- uint8_t c;
- transport.readBytes(&c, 1);
- RETURN_LONG((int8_t)c);
- }
- case T_I16: {
- uint16_t c;
- transport.readBytes(&c, 2);
- RETURN_LONG((int16_t)ntohs(c));
- }
- case T_I32: {
- uint32_t c;
- transport.readBytes(&c, 4);
- RETURN_LONG((int32_t)ntohl(c));
- }
- case T_U64:
- case T_I64: {
- uint64_t c;
- transport.readBytes(&c, 8);
- RETURN_LONG((int64_t)ntohll(c));
- }
- case T_DOUBLE: {
- union {
- uint64_t c;
- double d;
- } a;
- transport.readBytes(&(a.c), 8);
- a.c = ntohll(a.c);
- RETURN_DOUBLE(a.d);
- }
- //case T_UTF7: // aliases T_STRING
- case T_UTF8:
- case T_UTF16:
- case T_STRING: {
- uint32_t size = transport.readU32();
- if (size) {
- char strbuf[size+1];
- transport.readBytes(strbuf, size);
- strbuf[size] = '\0';
- ZVAL_STRINGL(return_value, strbuf, size);
- } else {
- ZVAL_EMPTY_STRING(return_value);
- }
- return;
- }
- case T_MAP: { // array of key -> value
- uint8_t types[2];
- transport.readBytes(types, 2);
- uint32_t size = transport.readU32();
- array_init(return_value);
-
- zval *val_ptr;
- val_ptr = zend_hash_str_find(fieldspec, "key", sizeof("key")-1);
- HashTable* keyspec = Z_ARRVAL_P(val_ptr);
- val_ptr = zend_hash_str_find(fieldspec, "val", sizeof("val")-1);
- HashTable* valspec = Z_ARRVAL_P(val_ptr);
-
- for (uint32_t s = 0; s < size; ++s) {
- zval key, value;
-
- binary_deserialize(types[0], transport, &key, keyspec);
- binary_deserialize(types[1], transport, &value, valspec);
- if (Z_TYPE(key) == IS_LONG) {
- zend_hash_index_update(Z_ARR_P(return_value), Z_LVAL(key), &value);
- } else {
- if (Z_TYPE(key) != IS_STRING) convert_to_string(&key);
- zend_symtable_update(Z_ARR_P(return_value), Z_STR(key), &value);
- }
- zval_dtor(&key);
- }
- return; // return_value already populated
- }
- case T_LIST: { // array with autogenerated numeric keys
- int8_t type = transport.readI8();
- uint32_t size = transport.readU32();
- zval *val_ptr = zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1);
- HashTable* elemspec = Z_ARRVAL_P(val_ptr);
-
- array_init(return_value);
- for (uint32_t s = 0; s < size; ++s) {
- zval value;
- binary_deserialize(type, transport, &value, elemspec);
- zend_hash_next_index_insert(Z_ARR_P(return_value), &value);
- }
- return;
- }
- case T_SET: { // array of key -> TRUE
- uint8_t type;
- uint32_t size;
- transport.readBytes(&type, 1);
- transport.readBytes(&size, 4);
- size = ntohl(size);
- zval *val_ptr = zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1);
- HashTable* elemspec = Z_ARRVAL_P(val_ptr);
-
- array_init(return_value);
-
- for (uint32_t s = 0; s < size; ++s) {
- zval key, value;
- ZVAL_TRUE(&value);
-
- binary_deserialize(type, transport, &key, elemspec);
-
- if (Z_TYPE(key) == IS_LONG) {
- zend_hash_index_update(Z_ARR_P(return_value), Z_LVAL(key), &value);
- } else {
- if (Z_TYPE(key) != IS_STRING) convert_to_string(&key);
- zend_symtable_update(Z_ARR_P(return_value), Z_STR(key), &value);
- }
- zval_dtor(&key);
- }
- return;
- }
- };
-
- char errbuf[128];
- sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID);
- throw_tprotocolexception(errbuf, INVALID_DATA);
-}
-
-static
-void binary_serialize_hashtable_key(int8_t keytype, PHPOutputTransport& transport, HashTable* ht, HashPosition& ht_pos) {
- bool keytype_is_numeric = (!((keytype == T_STRING) || (keytype == T_UTF8) || (keytype == T_UTF16)));
-
- zend_string* key;
- uint key_len;
- long index = 0;
-
- zval z;
-
- int res = zend_hash_get_current_key_ex(ht, &key, (zend_ulong*)&index, &ht_pos);
- if (keytype_is_numeric) {
- if (res == HASH_KEY_IS_STRING) {
- index = strtol(ZSTR_VAL(key), nullptr, 10);
- }
- ZVAL_LONG(&z, index);
- } else {
- char buf[64];
- if (res == HASH_KEY_IS_STRING) {
- ZVAL_STR_COPY(&z, key);
- } else {
- snprintf(buf, 64, "%ld", index);
- ZVAL_STRING(&z, buf);
- }
- }
- binary_serialize(keytype, transport, &z, nullptr);
- zval_dtor(&z);
-}
-
-static
-void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval* value, HashTable* fieldspec) {
- // At this point the typeID (and field num, if applicable) should've already been written to the output so all we need to do is write the payload.
- switch (thrift_typeID) {
- case T_STOP:
- case T_VOID:
- return;
- case T_STRUCT: {
- if (Z_TYPE_P(value) != IS_OBJECT) {
- throw_tprotocolexception("Attempt to send non-object type as a T_STRUCT", INVALID_DATA);
- }
- zval* spec = zend_read_static_property(Z_OBJCE_P(value), "_TSPEC", sizeof("_TSPEC")-1, false);
- if (Z_TYPE_P(spec) != IS_ARRAY) {
- throw_tprotocolexception("Attempt to send non-Thrift object as a T_STRUCT", INVALID_DATA);
- }
- binary_serialize_spec(value, transport, Z_ARRVAL_P(spec));
- } return;
- case T_BOOL:
- if (!zval_is_bool(value)) convert_to_boolean(value);
- transport.writeI8(Z_TYPE_INFO_P(value) == IS_TRUE ? 1 : 0);
- return;
- case T_BYTE:
- if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
- transport.writeI8(Z_LVAL_P(value));
- return;
- case T_I16:
- if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
- transport.writeI16(Z_LVAL_P(value));
- return;
- case T_I32:
- if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
- transport.writeI32(Z_LVAL_P(value));
- return;
- case T_I64:
- case T_U64: {
- int64_t l_data;
-#if defined(_LP64) || defined(_WIN64)
- if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
- l_data = Z_LVAL_P(value);
-#else
- if (Z_TYPE_P(value) != IS_DOUBLE) convert_to_double(value);
- l_data = (int64_t)Z_DVAL_P(value);
-#endif
- transport.writeI64(l_data);
- } return;
- case T_DOUBLE: {
- union {
- int64_t c;
- double d;
- } a;
- if (Z_TYPE_P(value) != IS_DOUBLE) convert_to_double(value);
- a.d = Z_DVAL_P(value);
- transport.writeI64(a.c);
- } return;
- case T_UTF8:
- case T_UTF16:
- case T_STRING:
- if (Z_TYPE_P(value) != IS_STRING) convert_to_string(value);
- transport.writeString(Z_STRVAL_P(value), Z_STRLEN_P(value));
- return;
- case T_MAP: {
- if (Z_TYPE_P(value) != IS_ARRAY) convert_to_array(value);
- if (Z_TYPE_P(value) != IS_ARRAY) {
- throw_tprotocolexception("Attempt to send an incompatible type as an array (T_MAP)", INVALID_DATA);
- }
- HashTable* ht = Z_ARRVAL_P(value);
- zval* val_ptr;
-
- val_ptr = zend_hash_str_find(fieldspec, "ktype", sizeof("ktype")-1);
- if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
- uint8_t keytype = Z_LVAL_P(val_ptr);
- transport.writeI8(keytype);
- val_ptr = zend_hash_str_find(fieldspec, "vtype", sizeof("vtype")-1);
- if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
- uint8_t valtype = Z_LVAL_P(val_ptr);
- transport.writeI8(valtype);
-
- val_ptr = zend_hash_str_find(fieldspec, "val", sizeof("val")-1);
- HashTable* valspec = Z_ARRVAL_P(val_ptr);
-
- transport.writeI32(zend_hash_num_elements(ht));
- HashPosition key_ptr;
- for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr);
- (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr;
- zend_hash_move_forward_ex(ht, &key_ptr)) {
- binary_serialize_hashtable_key(keytype, transport, ht, key_ptr);
- binary_serialize(valtype, transport, val_ptr, valspec);
- }
- } return;
- case T_LIST: {
- if (Z_TYPE_P(value) != IS_ARRAY) convert_to_array(value);
- if (Z_TYPE_P(value) != IS_ARRAY) {
- throw_tprotocolexception("Attempt to send an incompatible type as an array (T_LIST)", INVALID_DATA);
- }
- HashTable* ht = Z_ARRVAL_P(value);
- zval* val_ptr;
-
- val_ptr = zend_hash_str_find(fieldspec, "etype", sizeof("etype")-1);
- if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
- uint8_t valtype = Z_LVAL_P(val_ptr);
- transport.writeI8(valtype);
-
- val_ptr = zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1);
- HashTable* valspec = Z_ARRVAL_P(val_ptr);
-
- transport.writeI32(zend_hash_num_elements(ht));
- HashPosition key_ptr;
- for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr);
- (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr;
- zend_hash_move_forward_ex(ht, &key_ptr)) {
- binary_serialize(valtype, transport, val_ptr, valspec);
- }
- } return;
- case T_SET: {
- if (Z_TYPE_P(value) != IS_ARRAY) convert_to_array(value);
- if (Z_TYPE_P(value) != IS_ARRAY) {
- throw_tprotocolexception("Attempt to send an incompatible type as an array (T_SET)", INVALID_DATA);
- }
- HashTable* ht = Z_ARRVAL_P(value);
- zval* val_ptr;
-
- val_ptr = zend_hash_str_find(fieldspec, "etype", sizeof("etype")-1);
- if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
- uint8_t keytype = Z_LVAL_P(val_ptr);
- transport.writeI8(keytype);
-
- transport.writeI32(zend_hash_num_elements(ht));
- HashPosition key_ptr;
- for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr);
- (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr;
- zend_hash_move_forward_ex(ht, &key_ptr)) {
- binary_serialize_hashtable_key(keytype, transport, ht, key_ptr);
- }
- } return;
- };
-
- char errbuf[128];
- snprintf(errbuf, 128, "Unknown thrift typeID %d", thrift_typeID);
- throw_tprotocolexception(errbuf, INVALID_DATA);
-}
-
-static
-void protocol_writeMessageBegin(zval* transport, zend_string* method_name, int32_t msgtype, int32_t seqID) {
- zval args[3];
- zval ret;
- zval writeMessagefn;
-
- ZVAL_STR_COPY(&args[0], method_name);
- ZVAL_LONG(&args[1], msgtype);
- ZVAL_LONG(&args[2], seqID);
- ZVAL_NULL(&ret);
- ZVAL_STRING(&writeMessagefn, "writeMessageBegin");
-
- call_user_function(EG(function_table), transport, &writeMessagefn, &ret, 3, args);
-
- zval_dtor(&writeMessagefn);
- zval_dtor(&args[2]); zval_dtor(&args[1]); zval_dtor(&args[0]);
- zval_dtor(&ret);
-}
-
-static inline
-bool ttype_is_int(int8_t t) {
- return ((t == T_BYTE) || ((t >= T_I16) && (t <= T_I64)));
-}
-
-static inline
-bool ttypes_are_compatible(int8_t t1, int8_t t2) {
- // Integer types of different widths are considered compatible;
- // otherwise the typeID must match.
- return ((t1 == t2) || (ttype_is_int(t1) && ttype_is_int(t2)));
-}
-
-//is used to validate objects before serialization and after deserialization. For now, only required fields are validated.
-static
-void validate_thrift_object(zval* object) {
- zend_class_entry* object_class_entry = Z_OBJCE_P(object);
- zval* is_validate = zend_read_static_property(object_class_entry, "isValidate", sizeof("isValidate")-1, false);
- zval* spec = zend_read_static_property(object_class_entry, "_TSPEC", sizeof("_TSPEC")-1, false);
- HashPosition key_ptr;
- zval* val_ptr;
-
- if (Z_TYPE_INFO_P(is_validate) == IS_TRUE) {
- for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(spec), &key_ptr);
- (val_ptr = zend_hash_get_current_data_ex(Z_ARRVAL_P(spec), &key_ptr)) != nullptr;
- zend_hash_move_forward_ex(Z_ARRVAL_P(spec), &key_ptr)) {
-
- zend_ulong fieldno;
- if (zend_hash_get_current_key_ex(Z_ARRVAL_P(spec), nullptr, &fieldno, &key_ptr) != HASH_KEY_IS_LONG) {
- throw_tprotocolexception("Bad keytype in TSPEC (expected 'long')", INVALID_DATA);
- return;
- }
- HashTable* fieldspec = Z_ARRVAL_P(val_ptr);
-
- // field name
- zval* zvarname = zend_hash_str_find(fieldspec, "var", sizeof("var")-1);
- char* varname = Z_STRVAL_P(zvarname);
-
- zval* is_required = zend_hash_str_find(fieldspec, "isRequired", sizeof("isRequired")-1);
- zval rv;
- zval* prop = zend_read_property(object_class_entry, object, varname, strlen(varname), false, &rv);
-
- if (Z_TYPE_INFO_P(is_required) == IS_TRUE && Z_TYPE_P(prop) == IS_NULL) {
- char errbuf[128];
- snprintf(errbuf, 128, "Required field %s.%s is unset!", ZSTR_VAL(object_class_entry->name), varname);
- throw_tprotocolexception(errbuf, INVALID_DATA);
- }
- }
- }
-}
-
-static
-void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec) {
- // SET and LIST have 'elem' => array('type', [optional] 'class')
- // MAP has 'val' => array('type', [optiona] 'class')
- zend_class_entry* ce = Z_OBJCE_P(zthis);
- while (true) {
- int8_t ttype = transport.readI8();
- if (ttype == T_STOP) {
- validate_thrift_object(zthis);
- return;
- }
-
- int16_t fieldno = transport.readI16();
- zval* val_ptr = zend_hash_index_find(spec, fieldno);
- if (val_ptr != nullptr) {
- HashTable* fieldspec = Z_ARRVAL_P(val_ptr);
- // pull the field name
- val_ptr = zend_hash_str_find(fieldspec, "var", sizeof("var")-1);
- char* varname = Z_STRVAL_P(val_ptr);
-
- // and the type
- val_ptr = zend_hash_str_find(fieldspec, "type", sizeof("type")-1);
- if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
- int8_t expected_ttype = Z_LVAL_P(val_ptr);
-
- if (ttypes_are_compatible(ttype, expected_ttype)) {
- zval rv;
- ZVAL_UNDEF(&rv);
-
- binary_deserialize(ttype, transport, &rv, fieldspec);
- zend_update_property(ce, zthis, varname, strlen(varname), &rv);
-
- zval_ptr_dtor(&rv);
- } else {
- skip_element(ttype, transport);
- }
- } else {
- skip_element(ttype, transport);
- }
- }
-}
-
-static
-void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec) {
-
- validate_thrift_object(zthis);
-
- HashPosition key_ptr;
- zval* val_ptr;
-
- for (zend_hash_internal_pointer_reset_ex(spec, &key_ptr);
- (val_ptr = zend_hash_get_current_data_ex(spec, &key_ptr)) != nullptr;
- zend_hash_move_forward_ex(spec, &key_ptr)) {
-
- zend_ulong fieldno;
- if (zend_hash_get_current_key_ex(spec, nullptr, &fieldno, &key_ptr) != HASH_KEY_IS_LONG) {
- throw_tprotocolexception("Bad keytype in TSPEC (expected 'long')", INVALID_DATA);
- return;
- }
- HashTable* fieldspec = Z_ARRVAL_P(val_ptr);
-
- // field name
- val_ptr = zend_hash_str_find(fieldspec, "var", sizeof("var")-1);
- char* varname = Z_STRVAL_P(val_ptr);
-
- // thrift type
- val_ptr = zend_hash_str_find(fieldspec, "type", sizeof("type")-1);
- if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
- int8_t ttype = Z_LVAL_P(val_ptr);
-
- zval rv;
- zval* prop = zend_read_property(Z_OBJCE_P(zthis), zthis, varname, strlen(varname), false, &rv);
-
- if (Z_TYPE_P(prop) == IS_REFERENCE){
- ZVAL_UNREF(prop);
- }
- if (Z_TYPE_P(prop) != IS_NULL) {
- transport.writeI8(ttype);
- transport.writeI16(fieldno);
- binary_serialize(ttype, transport, prop, fieldspec);
- }
- }
- transport.writeI8(T_STOP); // struct end
-}
-
-// 6 params: $transport $method_name $ttype $request_struct $seqID $strict_write
-PHP_FUNCTION(thrift_protocol_write_binary) {
- zval *protocol;
- zval *request_struct;
- zend_string *method_name;
- long msgtype, seqID;
- zend_bool strict_write;
-
- if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "oSlolb",
- &protocol, &method_name, &msgtype,
- &request_struct, &seqID, &strict_write) == FAILURE) {
- return;
- }
-
- try {
- zval* spec = zend_read_static_property(Z_OBJCE_P(request_struct), "_TSPEC", sizeof("_TSPEC")-1, false);
-
- if (Z_TYPE_P(spec) != IS_ARRAY) {
- throw_tprotocolexception("Attempt to send non-Thrift object", INVALID_DATA);
- }
-
- PHPOutputTransport transport(protocol);
- protocol_writeMessageBegin(protocol, method_name, (int32_t) msgtype, (int32_t) seqID);
- binary_serialize_spec(request_struct, transport, Z_ARRVAL_P(spec));
- transport.flush();
-
- } catch (const PHPExceptionWrapper& ex) {
- // ex will be destructed, so copy to a zval that zend_throw_exception_object can take ownership of
- zval myex;
- ZVAL_COPY(&myex, ex);
- zend_throw_exception_object(&myex);
- RETURN_NULL();
- } catch (const std::exception& ex) {
- throw_zend_exception_from_std_exception(ex);
- RETURN_NULL();
- }
-}
-
-
-// 4 params: $transport $response_Typename $strict_read $buffer_size
-PHP_FUNCTION(thrift_protocol_read_binary) {
- zval *protocol;
- zend_string *obj_typename;
- zend_bool strict_read;
- size_t buffer_size = 8192;
-
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "oSb|l", &protocol, &obj_typename, &strict_read, &buffer_size) == FAILURE) {
- return;
- }
-
- try {
- PHPInputTransport transport(protocol, buffer_size);
- int8_t messageType = 0;
- int32_t sz = transport.readI32();
-
- if (sz < 0) {
- // Check for correct version number
- int32_t version = sz & VERSION_MASK;
- if (version != VERSION_1) {
- throw_tprotocolexception("Bad version identifier", BAD_VERSION);
- }
- messageType = (sz & 0x000000ff);
- int32_t namelen = transport.readI32();
- // skip the name string and the sequence ID, we don't care about those
- transport.skip(namelen + 4);
- } else {
- if (strict_read) {
- throw_tprotocolexception("No version identifier... old protocol client in strict mode?", BAD_VERSION);
- } else {
- // Handle pre-versioned input
- transport.skip(sz); // skip string body
- messageType = transport.readI8();
- transport.skip(4); // skip sequence number
- }
- }
-
- if (messageType == T_EXCEPTION) {
- zval ex;
- createObject("\\Thrift\\Exception\\TApplicationException", &ex);
- zval* spec = zend_read_static_property(Z_OBJCE(ex), "_TSPEC", sizeof("_TPSEC")-1, false);
- binary_deserialize_spec(&ex, transport, Z_ARRVAL_P(spec));
- throw PHPExceptionWrapper(&ex);
- }
-
- createObject(ZSTR_VAL(obj_typename), return_value);
- zval* spec = zend_read_static_property(Z_OBJCE_P(return_value), "_TSPEC", sizeof("_TSPEC")-1, false);
- binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec));
- } catch (const PHPExceptionWrapper& ex) {
- // ex will be destructed, so copy to a zval that zend_throw_exception_object can ownership of
- zval myex;
- ZVAL_COPY(&myex, ex);
- zval_dtor(return_value);
- zend_throw_exception_object(&myex);
- RETURN_NULL();
- } catch (const std::exception& ex) {
- throw_zend_exception_from_std_exception(ex);
- RETURN_NULL();
- }
-}
-
-// 4 params: $transport $response_Typename $strict_read $buffer_size
-PHP_FUNCTION(thrift_protocol_read_binary_after_message_begin) {
- zval *protocol;
- zend_string *obj_typename;
- zend_bool strict_read;
- size_t buffer_size = 8192;
-
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "oSb|l", &protocol, &obj_typename, &strict_read, &buffer_size) == FAILURE) {
- return;
- }
-
- try {
- PHPInputTransport transport(protocol, buffer_size);
-
- createObject(ZSTR_VAL(obj_typename), return_value);
- zval* spec = zend_read_static_property(Z_OBJCE_P(return_value), "_TSPEC", sizeof("_TSPEC")-1, false);
- binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec));
- } catch (const PHPExceptionWrapper& ex) {
- zend_throw_exception_object(ex);
- RETURN_NULL();
- } catch (const std::exception& ex) {
- throw_zend_exception_from_std_exception(ex);
- RETURN_NULL();
- }
-}
-
-#endif /* PHP_VERSION_ID >= 70000 */