From: Mark Slee Date: Fri, 30 Jun 2006 18:28:50 +0000 (+0000) Subject: Thrift now works in PHP, hot stuff X-Git-Tag: 0.2.0~1752 X-Git-Url: https://source.supwisdom.com/gerrit/gitweb?a=commitdiff_plain;h=6e53644ce5ebbf5e3a5f1fb3df2398534b1c965e;p=common%2Fthrift.git Thrift now works in PHP, hot stuff Summary: End to end communication working in Thrift with PHP Problem: It's a bit slower than pillar still. Need to find out why. Reviewed By: aditya Test Plan: Unit tests are in the test directory. Get lucas on the PHP case... git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@664720 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/compiler/Makefile b/compiler/Makefile index 938ef50f..26a8b319 100644 --- a/compiler/Makefile +++ b/compiler/Makefile @@ -36,7 +36,8 @@ BIN_DIR = bin/ SRC_FILES = main.cc \ generate/t_generator.cc \ generate/t_cpp_generator.cc \ - generate/t_java_generator.cc + generate/t_java_generator.cc \ + generate/t_php_generator.cc # Autogenerated files GEN_FILES = thrift.tab.hh \ diff --git a/compiler/src/generate/t_cpp_generator.cc b/compiler/src/generate/t_cpp_generator.cc index 18cfa312..1b36c825 100644 --- a/compiler/src/generate/t_cpp_generator.cc +++ b/compiler/src/generate/t_cpp_generator.cc @@ -140,7 +140,8 @@ void t_cpp_generator::generate_service(t_service* tservice) { f_service_.open(f_service_name.c_str()); // Print header file includes - f_header_ << autogen_comment(); + f_header_ << + autogen_comment(); f_header_ << "#ifndef " << service_name_ << "_H" << endl << "#define " << service_name_ << "_H" << endl << @@ -150,7 +151,8 @@ void t_cpp_generator::generate_service(t_service* tservice) { "#include \"protocol/TProtocol.h\"" << endl << "#include \"" << program_name_ << "Types.h\"" << endl << endl; - f_service_ << autogen_comment(); + f_service_ << + autogen_comment(); f_service_ << "#include \"" << service_name_ << ".h\"" << endl << endl; @@ -643,9 +645,8 @@ void t_cpp_generator::generate_deserialize_container(t_type* ttype, string etype = tmp("_etype"); indent(f_service_) << - "uint32_t " << size << ";" << endl; + "int32_t " << size << ";" << endl; - // Declare variables, read header if (ttype->is_map()) { f_service_ << @@ -669,7 +670,7 @@ void t_cpp_generator::generate_deserialize_container(t_type* ttype, // For loop iterates over elements string i = tmp("_i"); indent(f_service_) << - "uint32_t " << i << ";" << endl; + "int32_t " << i << ";" << endl; indent(f_service_) << "for (" << i << " = 0; " << i << " < " << size << "; ++" << i << ")" << endl; diff --git a/compiler/src/generate/t_php_generator.cc b/compiler/src/generate/t_php_generator.cc new file mode 100644 index 00000000..d7185acd --- /dev/null +++ b/compiler/src/generate/t_php_generator.cc @@ -0,0 +1,924 @@ +#include +#include +#include +#include "t_php_generator.h" +using namespace std; + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + * + * @param tprogram The program to generate + */ +void t_php_generator::init_generator(t_program* tprogram) { + // Make output directory + mkdir(T_PHP_DIR, S_IREAD | S_IWRITE | S_IEXEC); + + // Make output file + string f_types_name = string(T_PHP_DIR)+"/"+program_name_+"Types.php"; + f_types_.open(f_types_name.c_str()); + + // Print header + f_types_ << + "" << endl; + f_types_.close(); +} + +/** + * Generates a typedef. This is not done in PHP, types are all implicit. + * + * @param ttypedef The type definition + */ +void t_php_generator::generate_typedef(t_typedef* ttypedef) {} + +/** + * Generates code for an enumerated type. Since define is expensive to lookup + * in PHP, we use a global array for this. + * + * @param tenum The enumeration + */ +void t_php_generator::generate_enum(t_enum* tenum) { + f_types_ << + "$GLOBALS['E_" << tenum->get_name() << "'] = array(" << endl; + + vector constants = tenum->get_constants(); + vector::iterator c_iter; + int value = -1; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + if ((*c_iter)->has_value()) { + value = (*c_iter)->get_value(); + } else { + ++value; + } + + f_types_ << + " '" << (*c_iter)->get_name() << "' => " << value << "," << endl; + } + + f_types_ << + ");" << endl << endl; + + + // We're also doing it this way to see how it performs. It's more legible + // code but you can't do things like an 'extract' on it, which is a bit of + // a downer. + + f_types_ << + "final class " << tenum->get_name() << " {" << endl; + indent_up(); + + value = -1; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + if ((*c_iter)->has_value()) { + value = (*c_iter)->get_value(); + } else { + ++value; + } + + indent(f_types_) << + "const " << (*c_iter)->get_name() << " = " << value << ";" << endl; + } + + indent_down(); + f_types_ << "}" << endl << endl; +} + +/** + * Generates a struct definition for a thrift data type. This is nothing in PHP + * where the objects are all just associative arrays (unless of course we + * decide to start using objects for them...) + * + * @param tstruct The struct definition + */ +void t_php_generator::generate_struct(t_struct* tstruct) { + f_types_ << + "class " << tstruct->get_name() << " {" << endl; + indent_up(); + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + indent(f_types_) << + "public " << declare_field(*m_iter, true) << endl; + } + + indent_down(); + + f_types_ << + indent() << "}" << endl << + endl; +} + +/** + * Generates a thrift service. In C++, this comprises an entirely separate + * header and source file. The header file defines the methods and includes + * the data types defined in the main header file, and the implementation + * file contains implementations of the basic printer and default interfaces. + * + * @param tservice The service definition + */ +void t_php_generator::generate_service(t_service* tservice) { + string f_service_name = string(T_PHP_DIR)+"/"+service_name_+".php"; + f_service_.open(f_service_name.c_str()); + + f_service_ << + "" << endl; + f_service_.close(); +} + +/** + * Generates a service interface definition. + * + * @param tservice The service to generate a header definition for + */ +void t_php_generator::generate_service_interface(t_service* tservice) { + f_service_ << + "abstract class " << service_name_ << "If {" << endl; + indent_up(); + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + indent(f_service_) << + "public abstract function " << function_signature(*f_iter) << ";" << endl; + } + indent_down(); + f_service_ << + "}" << endl << endl; +} + +/** + * Generates a service client definition. + * + * @param tservice The service to generate a server for. + */ +void t_php_generator::generate_service_client(t_service* tservice) { + f_service_ << + "class " << service_name_ << "Client " << + "extends " << service_name_ << "If {" << endl; + indent_up(); + + // Private members + f_service_ << + indent() << "private $_itrans = null;" << endl << + indent() << "private $_otrans = null;" << endl << + indent() << "private $_iprot = null;" << endl << + indent() << "private $_oprot = null;" << endl << endl; + + // Constructor function + f_service_ << + indent() << "public function __construct() {" << endl << + indent() << " $argv = func_get_args();" << endl << + indent() << " $argc = count($argv);" << endl << + indent() << " if ($argc == 2) {" << endl << + indent() << " $this->_itrans = $this->_otrans = $argv[0];" << endl << + indent() << " $this->_iprot = $this->_oprot = $argv[1];" << endl << + indent() << " } else if ($argc == 4) {" << endl << + indent() << " $this->_itrans = $argv[0];" << endl << + indent() << " $this->_otrans = $argv[1];" << endl << + indent() << " $this->_iprot = $argv[2];" << endl << + indent() << " $this->_oprot = $argv[3];" << endl << + indent() << " }" << endl << + indent() << "}" << endl << endl; + + // Generate client method implementations + vector functions = tservice->get_functions(); + vector::const_iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + string funname = (*f_iter)->get_name(); + + // Open function + indent(f_service_) << + "public function " << function_signature(*f_iter) << endl; + scope_up(f_service_); + + // Serialize the request + f_service_ << + indent() << + "$this->_oprot->writeStructBegin($this->_otrans, 'function');" << endl << + indent() << + "$this->_oprot->writeFieldBegin($this->_otrans, 'name', TType::STRING, 0);" << endl << + indent() << + "$this->_oprot->writeString($this->_otrans, '" << funname << "');" << endl << + indent() << + "$this->_oprot->writeFieldEnd($this->_otrans);" << endl << + indent() << + "$this->_oprot->writeFieldBegin($this->_otrans, 'args', TType::STRUCT, 1);" << endl; + generate_serialize_struct((*f_iter)->get_arglist()); + f_service_ << + indent() << + "$this->_oprot->writeFieldEnd($this->_otrans);" << endl << + indent() << + "$this->_oprot->writeFieldStop($this->_otrans);" << endl << + indent() << + "$this->_oprot->writeStructEnd($this->_otrans);" << endl; + + // Flush the request + indent(f_service_) << + "$this->_otrans->flush();" << endl; + + // Read the response + t_struct result_struct((*f_iter)->get_name() + "_result"); + t_field result_field((*f_iter)->get_returntype(), "_result"); + + // Add a field to the return struct if non void + if (!(*f_iter)->get_returntype()->is_void()) { + indent(f_service_) << + declare_field(&result_field, true, true) << endl; + result_struct.append(&result_field); + } + + // Deserialize response struct + generate_deserialize_struct(&result_struct); + + // Careful, only return _result if not a void function + if (!(*f_iter)->get_returntype()->is_void()) { + indent(f_service_) << + "return $_result;" << endl; + } else { + indent(f_service_) << + "return;" << endl; + } + + // Close function + scope_down(f_service_); + f_service_ << endl; + } + + indent_down(); + f_service_ << + "}" << endl; + f_service_.close(); +} + +/** + * Deserializes a field of any type. + */ +void t_php_generator::generate_deserialize_field(t_field* tfield, + string prefix) { + t_type* type = tfield->get_type(); + while (type->is_typedef()) { + type = ((t_typedef*)type)->get_type(); + } + + if (type->is_void()) { + throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + + prefix + tfield->get_name(); + } + + string name = prefix + tfield->get_name(); + + if (type->is_struct()) { + generate_deserialize_struct((t_struct*)(tfield->get_type()), + name + "->"); + } else if (type->is_container()) { + generate_deserialize_container(tfield->get_type(), name); + } else if (type->is_base_type() || type->is_enum()) { + + indent(f_service_) << + "$this->_iprot->"; + + if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + switch (tbase) { + case t_base_type::TYPE_VOID: + throw "compiler error: cannot serialize void field in a struct: " + + name; + break; + case t_base_type::TYPE_STRING: + f_service_ << "readString($this->_itrans, $" << name << ");"; + break; + case t_base_type::TYPE_BYTE: + f_service_ << "readByte($this->_itrans, $" << name << ");"; + break; + case t_base_type::TYPE_I32: + f_service_ << "readI32($this->_itrans, $" << name << ");"; + break; + case t_base_type::TYPE_U32: + f_service_ << "readU32($this->_itrans, $" << name << ");"; + break; + case t_base_type::TYPE_I64: + f_service_ << "readI64($this->_itrans, $" << name << ");"; + break; + case t_base_type::TYPE_U64: + f_service_ << "readU64($this->_itrans, $" << name << ");"; + break; + default: + throw "compiler error: no C++ name for base type " + tbase; + } + } else if (type->is_enum()) { + f_service_ << "readI32($this->_itrans, $" << name << ");"; + } + f_service_ << + endl; + } else { + printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", + tfield->get_name().c_str(), type_name(type).c_str()); + } +} + +/** + * Generates an unserializer for a variable. This makes two key assumptions, + * first that there is a const char* variable named data that points to the + * buffer for deserialization, and that there is a variable protocol which + * is a reference to a TProtocol serialization object. + */ +void t_php_generator::generate_deserialize_struct(t_struct* tstruct, + string prefix) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + scope_up(f_service_); + + // Read the struct fields from the protocol + string fid = tmp("_fid"); + string ftype = tmp("_ftype"); + string fname = tmp("_name"); + + // Declare stack tmp variables + f_service_ << + indent() << "$" << fname << " = null;" << endl << + indent() << "$" << ftype << " = null;" << endl << + indent() << "$" << fid << " = 0;" << endl << + indent() << "$this->_iprot->readStructBegin($this->_itrans, $" << fname << ");" << endl; + + // Loop over reading in fields + indent(f_service_) << + "while (true)" << endl; + + scope_up(f_service_); + + // Read beginning field marker + indent(f_service_) << + "$this->_iprot->readFieldBegin($this->_itrans, " << + "$" << fname << ", $" << ftype << ", $" << fid << ");" << endl; + + // Check for field STOP marker and break + indent(f_service_) << + "if ($" << ftype << " == TType::STOP) { " << endl; + indent_up(); + indent(f_service_) << + "break;" << endl; + indent_down(); + indent(f_service_) << + "}" << endl; + + // Switch statement on the field we are reading + indent(f_service_) << + "switch ($" << fid << ")" << endl; + + scope_up(f_service_); + + // Generate deserialization code for known cases + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + indent(f_service_) << + "case " << (*f_iter)->get_key() << ":" << endl; + indent_up(); + generate_deserialize_field(*f_iter, prefix); + indent(f_service_) << + "break;" << endl; + indent_down(); + } + + // In the default case we skip the field + f_service_ << + indent() << "default:" << endl << + indent() << " $this->_iprot->skip($this->_itrans, $" << ftype << ");" << endl << + indent() << " break;" << endl; + + scope_down(f_service_); + + // Read field end marker + indent(f_service_) << + "$this->_iprot->readFieldEnd($this->_itrans);" << endl; + + scope_down(f_service_); + + indent(f_service_) << + "$this->_iprot->readStructEnd($this->_itrans);" << endl; + + scope_down(f_service_); +} + +void t_php_generator::generate_deserialize_container(t_type* ttype, + string prefix) { + scope_up(f_service_); + + string size = tmp("_size"); + string ktype = tmp("_ktype"); + string vtype = tmp("_vtype"); + string etype = tmp("_etype"); + + indent(f_service_) << + "$" << size << " = 0;" << endl; + + // Declare variables, read header + if (ttype->is_map()) { + f_service_ << + indent() << "$" << ktype << " = 0;" << endl << + indent() << "$" << vtype << " = 0;" << endl << + indent() << "$this->_iprot->readMapBegin($this->_itrans, " << + "$" << ktype << ", $" << vtype << ", $" << size << ");" << endl; + } else if (ttype->is_set()) { + f_service_ << + indent() << "$" << etype << " = 0;" << endl << + indent() << "$this->_iprot->readSetBegin($this->_itrans, " << + "$" << etype << ", $" << size << ");" << endl; + } else if (ttype->is_list()) { + f_service_ << + indent() << "$" << etype << " = 0;" << endl << + indent() << "$this->_iprot->readListBegin($this->_itrans, " << + "$" << etype << ", $" << size << ");" << endl; + } + + // For loop iterates over elements + string i = tmp("_i"); + indent(f_service_) << + "for ($" << + i << " = 0; $" << i << " < $" << size << "; ++$" << i << ")" << endl; + + scope_up(f_service_); + + if (ttype->is_map()) { + generate_deserialize_map_element((t_map*)ttype, prefix); + } else if (ttype->is_set()) { + generate_deserialize_set_element((t_set*)ttype, prefix); + } else if (ttype->is_list()) { + generate_deserialize_list_element((t_list*)ttype, prefix); + } + + scope_down(f_service_); + + // Read container end + if (ttype->is_map()) { + indent(f_service_) << "$this->_iprot->readMapEnd($this->_itrans);" << endl; + } else if (ttype->is_set()) { + indent(f_service_) << "$this->_iprot->readSetEnd($this->_itrans);" << endl; + } else if (ttype->is_list()) { + indent(f_service_) << "$this->_iprot->readListEnd($this->_itrans);" << endl; + } + + scope_down(f_service_); +} + + +/** + * Generates code to deserialize a map + */ +void t_php_generator::generate_deserialize_map_element(t_map* tmap, + string prefix) { + string key = tmp("_key"); + string val = tmp("_val"); + t_field fkey(tmap->get_key_type(), key); + t_field fval(tmap->get_val_type(), val); + + indent(f_service_) << + declare_field(&fkey, true, true) << endl; + indent(f_service_) << + declare_field(&fval, true, true) << endl; + + generate_deserialize_field(&fkey); + generate_deserialize_field(&fval); + + indent(f_service_) << + "$" << prefix << "[$" << key << "] = $" << val << ";" << endl; +} + +void t_php_generator::generate_deserialize_set_element(t_set* tset, + string prefix) { + string elem = tmp("_elem"); + t_field felem(tset->get_elem_type(), elem); + + indent(f_service_) << + "$" << elem << " = null;" << endl; + + generate_deserialize_field(&felem); + + indent(f_service_) << + "$" << prefix << " []= $" << elem << ";" << endl; +} + +void t_php_generator::generate_deserialize_list_element(t_list* tlist, + string prefix) { + string elem = tmp("_elem"); + t_field felem(tlist->get_elem_type(), elem); + + indent(f_service_) << + "$" << elem << " = null;" << endl; + + generate_deserialize_field(&felem); + + indent(f_service_) << + "$" << prefix << " []= $" << elem << ";" << endl; +} + + +/** + * Serializes a field of any type. + * + * @param tfield The field to serialize + * @param prefix Name to prepend to field name + */ +void t_php_generator::generate_serialize_field(t_field* tfield, + string prefix) { + t_type* type = tfield->get_type(); + while (type->is_typedef()) { + type = ((t_typedef*)type)->get_type(); + } + + // Do nothing for void types + if (type->is_void()) { + throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + + prefix + tfield->get_name(); + } + + if (type->is_struct()) { + generate_serialize_struct((t_struct*)(tfield->get_type()), + prefix + tfield->get_name() + "->"); + } else if (type->is_container()) { + generate_serialize_container(tfield->get_type(), + prefix + tfield->get_name()); + } else if (type->is_base_type() || type->is_enum()) { + + string name = prefix + tfield->get_name(); + indent(f_service_) << + "$this->_oprot->"; + + if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + switch (tbase) { + case t_base_type::TYPE_VOID: + throw + "compiler error: cannot serialize void field in a struct: " + name; + break; + case t_base_type::TYPE_STRING: + f_service_ << "writeString($this->_otrans, $" << name << ");"; + break; + case t_base_type::TYPE_BYTE: + f_service_ << "writeByte($this->_otrans, $" << name << ");"; + break; + case t_base_type::TYPE_I32: + f_service_ << "writeI32($this->_otrans, $" << name << ");"; + break; + case t_base_type::TYPE_U32: + f_service_ << "writeU32($this->_otrans, $" << name << ");"; + break; + case t_base_type::TYPE_I64: + f_service_ << "writeI64($this->_otrans, $" << name << ");"; + break; + case t_base_type::TYPE_U64: + f_service_ << "writeU64($this->_otrans, $" << name << ");"; + break; + default: + throw "compiler error: no C++ name for base type " + tbase; + } + } else if (type->is_enum()) { + f_service_ << "writeI32($this->_otrans, $" << name << ");"; + } + f_service_ << endl; + } else { + printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n", + prefix.c_str(), + tfield->get_name().c_str(), + type_name(type).c_str()); + } +} + +/** + * Serializes all the members of a struct. + * + * @param tstruct The struct to serialize + * @param prefix String prefix to attach to all fields + */ +void t_php_generator::generate_serialize_struct(t_struct* tstruct, + string prefix) { + string name = tstruct->get_name(); + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + scope_up(f_service_); + indent(f_service_) << + "$this->_oprot->writeStructBegin($this->_otrans, '" << name << "');" << endl; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + // Write field header + indent(f_service_) << + "$this->_oprot->writeFieldBegin($this->_otrans, " << + "'" << (*f_iter)->get_name() << "', " << + type_to_enum((*f_iter)->get_type()) << ", " << + (*f_iter)->get_key() << ");" << endl; + // Write field contents + generate_serialize_field(*f_iter, prefix); + // Write field closer + indent(f_service_) << + "$this->_oprot->writeFieldEnd($this->_otrans);" << endl; + } + // Write the struct map + f_service_ << + indent() << "$this->_oprot->writeFieldStop($this->_otrans);" << endl << + indent() << "$this->_oprot->writeStructEnd($this->_otrans);" << endl; + + scope_down(f_service_); +} + +void t_php_generator::generate_serialize_container(t_type* ttype, + string prefix) { + scope_up(f_service_); + + if (ttype->is_map()) { + indent(f_service_) << + "$this->_oprot->writeMapBegin($this->_otrans, " << + type_to_enum(((t_map*)ttype)->get_key_type()) << ", " << + type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << + "count($" << prefix << "));" << endl; + } else if (ttype->is_set()) { + indent(f_service_) << + "$this->_oprot->writeSetBegin($this->_otrans, " << + type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " << + "count($" << prefix << "));" << endl; + } else if (ttype->is_list()) { + indent(f_service_) << + "$this->_oprot->writeListBegin($this->_otrans, " << + type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << + "count($" << prefix << "));" << endl; + } + + scope_up(f_service_); + + if (ttype->is_map()) { + string kiter = tmp("_kiter"); + string viter = tmp("_viter"); + indent(f_service_) << + "foreach ($" << prefix << " as " << + "$" << kiter << " => $" << viter << ")" << endl; + scope_up(f_service_); + generate_serialize_map_element((t_map*)ttype, kiter, viter); + scope_down(f_service_); + } else if (ttype->is_set()) { + string iter = tmp("_iter"); + indent(f_service_) << + "foreach ($" << prefix << " as $" << iter << ")" << endl; + scope_up(f_service_); + generate_serialize_set_element((t_set*)ttype, iter); + scope_down(f_service_); + } else if (ttype->is_list()) { + string iter = tmp("_iter"); + indent(f_service_) << + "foreach ($" << prefix << " as $" << iter << ")" << endl; + scope_up(f_service_); + generate_serialize_list_element((t_list*)ttype, iter); + scope_down(f_service_); + } + + if (ttype->is_map()) { + indent(f_service_) << + "$this->_oprot->writeMapEnd($this->_otrans);" << endl; + } else if (ttype->is_set()) { + indent(f_service_) << + "$this->_oprot->writeSetEnd($this->_otrans);" << endl; + } else if (ttype->is_list()) { + indent(f_service_) << + "$this->_oprot->writeListEnd($this->_otrans);" << endl; + } + + scope_down(f_service_); + + scope_down(f_service_); +} + +/** + * Serializes the members of a map. + * + */ +void t_php_generator::generate_serialize_map_element(t_map* tmap, + string kiter, + string viter) { + t_field kfield(tmap->get_key_type(), kiter); + generate_serialize_field(&kfield, ""); + + t_field vfield(tmap->get_val_type(), viter); + generate_serialize_field(&vfield, ""); +} + +/** + * Serializes the members of a set. + */ +void t_php_generator::generate_serialize_set_element(t_set* tset, + string iter) { + t_field efield(tset->get_elem_type(), iter); + generate_serialize_field(&efield, ""); +} + +/** + * Serializes the members of a list. + */ +void t_php_generator::generate_serialize_list_element(t_list* tlist, + string iter) { + t_field efield(tlist->get_elem_type(), iter); + generate_serialize_field(&efield, ""); +} + +/** + * Returns a Java type name + * + * @param ttype The type + */ +string t_php_generator::type_name(t_type* ttype) { + // In Java typedefs are just resolved to their real type + while (ttype->is_typedef()) { + ttype = ((t_typedef*)ttype)->get_type(); + } + + if (ttype->is_base_type()) { + return base_type_name(((t_base_type*)ttype)->get_base()); + } else if (ttype->is_enum()) { + return "Int32"; + } else if (ttype->is_map()) { + t_map* tmap = (t_map*) ttype; + return "HashMap<" + + type_name(tmap->get_key_type()) + "," + + type_name(tmap->get_val_type()) + ">"; + } else if (ttype->is_set()) { + t_set* tset = (t_set*) ttype; + return "HashSet<" + type_name(tset->get_elem_type()) + ">"; + } else if (ttype->is_list()) { + t_list* tlist = (t_list*) ttype; + return "ArrayList<" + type_name(tlist->get_elem_type()) + ">"; + } else { + return ttype->get_name(); + } +} + +/** + * Returns the C++ type that corresponds to the thrift type. + * + * @param tbase The base type + */ +string t_php_generator::base_type_name(t_base_type::t_base tbase) { + switch (tbase) { + case t_base_type::TYPE_VOID: + return "void"; + case t_base_type::TYPE_STRING: + return "TString"; + case t_base_type::TYPE_BYTE: + return "UInt8"; + case t_base_type::TYPE_I32: + return "Int32"; + case t_base_type::TYPE_U32: + return "UInt32"; + case t_base_type::TYPE_I64: + return "Int64"; + case t_base_type::TYPE_U64: + return "UInt64"; + default: + throw "compiler error: no C++ name for base type " + tbase; + } +} + +/** + * Declares a field, which may include initialization as necessary. + * + * @param ttype The type + */ +string t_php_generator::declare_field(t_field* tfield, bool init, bool obj) { + string result = "$" + tfield->get_name(); + if (init) { + t_type* type = tfield->get_type(); + while (type->is_typedef()) { + type = ((t_typedef*)type)->get_type(); + } + if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + switch (tbase) { + case t_base_type::TYPE_VOID: + break; + case t_base_type::TYPE_STRING: + result += " = ''"; + break; + case t_base_type::TYPE_BYTE: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_U32: + case t_base_type::TYPE_I64: + case t_base_type::TYPE_U64: + result += " = 0"; + break; + default: + throw "compiler error: no PHP initializer for base type " + tbase; + } + } else if (type->is_enum()) { + result += " = 0"; + } else if (type->is_container()) { + result += " = array()"; + } else if (type->is_struct()) { + if (obj) { + result += " = new " + type->get_name() + "()"; + } else { + result += " = null"; + } + } + } + return result + ";"; +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_php_generator::function_signature(t_function* tfunction, + string prefix) { + return + prefix + tfunction->get_name() + + "(" + argument_list(tfunction->get_arglist()) + ")"; +} + +/** + * Renders a field list + */ +string t_php_generator::argument_list(t_struct* tstruct) { + string result = ""; + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + result += ", "; + } + result += "$" + (*f_iter)->get_name(); + } + return result; +} + +/** + * Converts the parse type to a C++ enum string for the given type. + */ +string t_php_generator ::type_to_enum(t_type* type) { + while (type->is_typedef()) { + type = ((t_typedef*)type)->get_type(); + } + + if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + switch (tbase) { + case t_base_type::TYPE_VOID: + throw "NO T_VOID CONSTRUCT"; + case t_base_type::TYPE_STRING: + return "TType::STRING"; + case t_base_type::TYPE_BYTE: + return "TType::BYTE"; + case t_base_type::TYPE_I32: + return "TType::I32"; + case t_base_type::TYPE_U32: + return "TType::U32"; + case t_base_type::TYPE_I64: + return "TType::I64"; + case t_base_type::TYPE_U64: + return "TType::U64"; + } + } else if (type->is_enum()) { + return "TType::I32"; + } else if (type->is_struct()) { + return "TType::STRUCT"; + } else if (type->is_map()) { + return "TType::MAP"; + } else if (type->is_set()) { + return "TType::SET"; + } else if (type->is_list()) { + return "TType::LST"; + } + + throw "INVALID TYPE IN type_to_enum: " + type->get_name(); +} diff --git a/compiler/src/generate/t_php_generator.h b/compiler/src/generate/t_php_generator.h new file mode 100644 index 00000000..d01acd20 --- /dev/null +++ b/compiler/src/generate/t_php_generator.h @@ -0,0 +1,98 @@ +#ifndef T_PHP_GENERATOR_H +#define T_PHP_GENERATOR_H + +#include +#include +#include +#include + +#include "t_oop_generator.h" + +// TODO(mcslee: Paramaterize the output dir +#define T_PHP_DIR "gen-php" + +/** + * Java code generator. + * + * @author Mark Slee + */ +class t_php_generator : public t_oop_generator { + public: + t_php_generator() {} + ~t_php_generator() {} + + /** Init and close methods */ + + void init_generator(t_program *tprogram); + void close_generator(); + + /** Program-level generation functions */ + + void generate_typedef (t_typedef* ttypedef); + void generate_enum (t_enum* tenum); + void generate_struct (t_struct* tstruct); + void generate_service (t_service* tservice); + + /** Service-level generation functions */ + + void generate_service_interface (t_service* tservice); + void generate_service_client (t_service* tservice); + + /** Serialization constructs */ + + void generate_deserialize_field (t_field* tfield, + std::string prefix=""); + + void generate_deserialize_struct (t_struct* tstruct, + std::string prefix=""); + + void generate_deserialize_container (t_type* ttype, + std::string prefix=""); + + void generate_deserialize_set_element (t_set* tset, + std::string prefix=""); + + void generate_deserialize_map_element (t_map* tmap, + std::string prefix=""); + + void generate_deserialize_list_element (t_list* tlist, + std::string prefix=""); + + void generate_serialize_field (t_field* tfield, + std::string prefix=""); + + void generate_serialize_struct (t_struct* tstruct, + std::string prefix=""); + + void generate_serialize_container (t_type* ttype, + std::string prefix=""); + + void generate_serialize_map_element (t_map* tmap, + std::string kiter, + std::string viter); + + void generate_serialize_set_element (t_set* tmap, + std::string iter); + + void generate_serialize_list_element (t_list* tlist, + std::string iter); + + /** Helper rendering functions */ + + std::string php_includes(); + std::string type_name(t_type* ttype); + std::string base_type_name(t_base_type::t_base tbase); + std::string declare_field(t_field* tfield, bool init=false, bool obj=false); + std::string function_signature(t_function* tfunction, std::string prefix=""); + std::string argument_list(t_struct* tstruct); + std::string type_to_enum(t_type* ttype); + + private: + + /** File streams */ + + std::ofstream f_types_; + std::ofstream f_service_; +}; + +#endif diff --git a/compiler/src/main.cc b/compiler/src/main.cc index 7f87368b..87833093 100644 --- a/compiler/src/main.cc +++ b/compiler/src/main.cc @@ -20,6 +20,7 @@ #include "parse/t_program.h" #include "generate/t_cpp_generator.h" #include "generate/t_java_generator.h" +#include "generate/t_php_generator.h" using namespace std; @@ -96,7 +97,7 @@ void usage() { fprintf(stderr, "Options:\n"); fprintf(stderr, " -cpp Generate C++ output files\n"); fprintf(stderr, " -java Generate Java output files\n"); - //fprintf(stderr, " -php Generate PHP output files\n"); + fprintf(stderr, " -php Generate PHP output files\n"); //fprintf(stderr, " -python Generate Python output files\n"); fprintf(stderr, " -d Print parse debugging to standard output\n"); exit(1); @@ -109,6 +110,7 @@ int main(int argc, char** argv) { int i; bool gen_cpp = false; bool gen_java = false; + bool gen_php = false; // Setup time string time_t now = time(NULL); @@ -126,13 +128,15 @@ int main(int argc, char** argv) { gen_cpp = true; } else if (strcmp(argv[i], "-java") == 0) { gen_java = true; + } else if (strcmp(argv[i], "-php") == 0) { + gen_php = true; } else { fprintf(stderr, "!!! Unrecognized option: %s\n", argv[i]); usage(); } } - if (!gen_cpp && !gen_java) { + if (!gen_cpp && !gen_java && !gen_php) { fprintf(stderr, "!!! No output language(s) specified\n\n"); usage(); } @@ -176,6 +180,11 @@ int main(int argc, char** argv) { delete java; } + if (gen_php) { + t_php_generator* php = new t_php_generator(); + php->generate_program(g_program); + delete php; + } } catch (string s) { printf("Error: %s\n", s.c_str()); } catch (const char* exc) { diff --git a/lib/cpp/Makefile b/lib/cpp/Makefile index 15664faa..8403d7c6 100644 --- a/lib/cpp/Makefile +++ b/lib/cpp/Makefile @@ -17,6 +17,7 @@ LDFL = -shared -Wall -Isrc -fPIC -Wl,-soname=libthrift.so # Source files SRCS = src/protocol/TBinaryProtocol.cc \ src/transport/TBufferedTransport.cc \ + src/transport/TChunkedTransport.cc \ src/transport/TSocket.cc \ src/transport/TServerSocket.cc \ src/server/TSimpleServer.cc diff --git a/lib/cpp/src/protocol/TBinaryProtocol.cc b/lib/cpp/src/protocol/TBinaryProtocol.cc index 666a6a4e..6ac028a4 100644 --- a/lib/cpp/src/protocol/TBinaryProtocol.cc +++ b/lib/cpp/src/protocol/TBinaryProtocol.cc @@ -240,8 +240,12 @@ uint32_t TBinaryProtocol::readString(TTransport* in, uint32_t result; int32_t size; result = readI32(in, size); - uint8_t b[size]; + + // Use the heap here to prevent stack overflow for v. large strings + uint8_t *b = new uint8_t[size]; in->readAll(b, size); str = string((char*)b, size); + delete [] b; + return result + (uint32_t)size; } diff --git a/lib/cpp/src/transport/TBufferedTransport.cc b/lib/cpp/src/transport/TBufferedTransport.cc index 3fccc580..d7ce56a5 100644 --- a/lib/cpp/src/transport/TBufferedTransport.cc +++ b/lib/cpp/src/transport/TBufferedTransport.cc @@ -7,7 +7,7 @@ uint32_t TBufferedTransport::read(uint8_t* buf, uint32_t len) { // We don't have enough data yet if (rLen_-rPos_ < need) { // Copy out whatever we have - if (rLen_ > 0) { + if (rLen_-rPos_ > 0) { memcpy(buf, rBuf_+rPos_, rLen_-rPos_); need -= rLen_-rPos_; buf += rLen_-rPos_; diff --git a/lib/cpp/src/transport/TBufferedTransport.h b/lib/cpp/src/transport/TBufferedTransport.h index 991b50c8..43c31f1b 100644 --- a/lib/cpp/src/transport/TBufferedTransport.h +++ b/lib/cpp/src/transport/TBufferedTransport.h @@ -53,6 +53,10 @@ class TBufferedTransport : public TTransport { void close() { transport_->close(); } + + uint32_t readAll(uint8_t* buf, uint32_t len) { + return transport_->readAll(buf, len); + } uint32_t read(uint8_t* buf, uint32_t len); diff --git a/lib/cpp/src/transport/TChunkedTransport.cc b/lib/cpp/src/transport/TChunkedTransport.cc new file mode 100644 index 00000000..f35d7472 --- /dev/null +++ b/lib/cpp/src/transport/TChunkedTransport.cc @@ -0,0 +1,97 @@ +#include "TChunkedTransport.h" +using std::string; + +uint32_t TChunkedTransport::read(uint8_t* buf, uint32_t len) { + uint32_t need = len; + + // We don't have enough data yet + if (rLen_-rPos_ < need) { + // Copy out whatever we have + if (rLen_-rPos_ > 0) { + memcpy(buf, rBuf_+rPos_, rLen_-rPos_); + need -= rLen_-rPos_; + buf += rLen_-rPos_; + } + + // Read another chunk + readChunk(); + } + + // Hand over whatever we have + uint32_t give = need; + if (rLen_-rPos_ < give) { + give = rLen_-rPos_; + } + memcpy(buf, rBuf_+rPos_, give); + rPos_ += give; + need -= give; + return (len - need); +} + +void TChunkedTransport::readChunk() { + // Get rid of the old chunk + if (rBuf_ != NULL) { + delete [] rBuf_; + rBuf_ = NULL; + } + + // Read in the next chunk size + int32_t sz; + transport_->readAll((uint8_t*)&sz, 4); + + if (sz < 0) { + throw new TTransportException("Next chunk has negative size"); + } + + // Read the chunk payload, reset markers + rBuf_ = new uint8_t[sz]; + transport_->readAll(rBuf_, sz); + rPos_ = 0; + rLen_ = sz; +} + +void TChunkedTransport::write(const uint8_t* buf, uint32_t len) { + if (len == 0) { + return; + } + + // Need to grow the buffer + if (len + wLen_ >= wBufSize_) { + + // Double buffer size until sufficient + while (wBufSize_ < len + wLen_) { + wBufSize_ *= 2; + } + + // Allocate new buffer + uint8_t* wBuf2 = new uint8_t[wBufSize_]; + + // Copy the old buffer to the new one + memcpy(wBuf2, wBuf_, wLen_); + + // Now point buf to the new one + delete [] wBuf_; + wBuf_ = wBuf2; + } + + // Copy data into buffer + memcpy(wBuf_ + wLen_, buf, len); + wLen_ += len; +} + +void TChunkedTransport::flush() { + // Write chunk size + int32_t sz = wLen_; + transport_->write((const uint8_t*)&sz, 4); + + // Write chunk body + if (sz > 0) { + transport_->write(wBuf_, wLen_); + } + + // All done + wLen_ = 0; + + // Flush the underlying + transport_->flush(); +} diff --git a/lib/cpp/src/transport/TChunkedTransport.h b/lib/cpp/src/transport/TChunkedTransport.h new file mode 100644 index 00000000..07bdbb5a --- /dev/null +++ b/lib/cpp/src/transport/TChunkedTransport.h @@ -0,0 +1,76 @@ +#ifndef T_CHUNKED_TRANSPORT_H +#define T_CHUNKED_TRANSPORT_H + +#include "transport/TTransport.h" +#include + +/** + * Chunked transport. All writes go into an in-memory buffer until flush is + * called, at which point the transport writes the length of the entire + * binary chunk followed by the data payload. This allows the receiver on the + * other end to always do fixed-length reads. + * + * @author Mark Slee + */ +class TChunkedTransport : public TTransport { + public: + TChunkedTransport(TTransport* transport) : + transport_(transport), + rPos_(0), rLen_(0), + wBufSize_(512), wLen_(0) { + rBuf_ = NULL; + wBuf_ = new uint8_t[wBufSize_]; + } + + TChunkedTransport(TTransport* transport, uint32_t sz) : + transport_(transport), + rPos_(0), rLen_(0), + wBufSize_(sz), wLen_(0) { + rBuf_ = NULL; + wBuf_ = new uint8_t[wBufSize_]; + } + + ~TChunkedTransport() { + if (rBuf_ != NULL) { + delete [] rBuf_; + } + if (wBuf_ != NULL) { + delete [] wBuf_; + } + } + + bool isOpen() { + return transport_->isOpen(); + } + + void open() { + transport_->open(); + } + + void close() { + transport_->close(); + } + + uint32_t read(uint8_t* buf, uint32_t len); + + void write(const uint8_t* buf, uint32_t len); + + void flush(); + + protected: + TTransport* transport_; + uint8_t* rBuf_; + uint32_t rPos_; + uint32_t rLen_; + + uint8_t* wBuf_; + uint32_t wBufSize_; + uint32_t wLen_; + + /** + * Reads a chunk of input from the underlying stream. + */ + void readChunk(); +}; + +#endif diff --git a/lib/php/src/Thrift.php b/lib/php/src/Thrift.php new file mode 100644 index 00000000..8a256114 --- /dev/null +++ b/lib/php/src/Thrift.php @@ -0,0 +1,9 @@ + diff --git a/lib/php/src/protocol/TBinaryProtocol.php b/lib/php/src/protocol/TBinaryProtocol.php new file mode 100644 index 00000000..867a8aa1 --- /dev/null +++ b/lib/php/src/protocol/TBinaryProtocol.php @@ -0,0 +1,195 @@ + + */ +class TBinaryProtocol extends TProtocol { + + public function writeStructBegin($out, $name) { + return 0; + } + + public function writeStructEnd($out) { + return 0; + } + + public function writeFieldBegin($out, $fieldName, $fieldType, $fieldId) { + return + $this->writeByte($out, $fieldType) + + $this->writeI32($out, $fieldId); + } + + public function writeFieldEnd($out) { + return 0; + } + + public function writeFieldStop($out) { + return + $this->writeByte($out, TType::STOP); + } + + public function writeMapBegin($out, $keyType, $valType, $size) { + return + $this->writeByte($out, $keyType) + + $this->writeByte($out, $valType) + + $this->writeI32($out, $size); + } + + public function writeMapEnd($out) { + return 0; + } + + public function writeListBegin($out, $elemType, $size) { + return + $this->writeByte($out, $elemType) + + $this->writeI32($out, $size); + } + + public function writeListEnd($out) { + return 0; + } + + public function writeSetBegin($out, $elemType, $size) { + return + $this->writeByte($out, $elemType) + + $this->writeI32($out, $size); + } + + public function writeSetEnd($out) { + return 0; + } + + public function writeByte($out, $byte) { + $data = pack('c', $byte); + $out->write($data, 1); + return 1; + } + + public function writeI32($out, $i32) { + $data = pack('l', $i32); + //if (!defined('BIG_ENDIAN')) { + $data = strrev($data); + //} + $out->write($data, 4); + return 4; + } + + public function writeI64($out, $i64) { + $hi = $i64 >> 32; + $lo = $i64 & 0xFFFFFFFF; + if (!defined('BIG_ENDIAN')) { + $data = pack('N2', $hi, $lo); + } else { + $data = pack('N2', $lo, $hi); + } + $out->write($data, 8); + return 8; + } + + public function writeString($out, $str) { + $len = strlen($str); + $result = $this->writeI32($out, $len); + $out->write($str, $len); + return $result + $len; + } + + public function readStructBegin($in, &$name) { + $name = ''; + return 0; + } + + public function readStructEnd($in) { + return 0; + } + + public function readFieldBegin($in, &$name, &$fieldType, &$fieldId) { + $result = $this->readByte($in, $fieldType); + if ($fieldType == TType::STOP) { + $fieldId = 0; + return $result; + } + $result += $this->readI32($in, $fieldId); + return $result; + } + + public function readFieldEnd($in) { + return 0; + } + + public function readMapBegin($in, &$keyType, &$valType, &$size) { + $result = $this->readByte($in, $keyType); + $result += $this->readByte($in, $valType); + $result += $this->readI32($in, $size); + return $result; + } + + public function readMapEnd($in) { + return 0; + } + + public function readListBegin($in, &$elemType, &$size) { + $result = $this->readByte($in, $elemType); + $result += $this->readI32($in, $size); + return $result; + } + + public function readListEnd($in) { + return 0; + } + + public function readSetBegin($in, &$elemType, &$size) { + $result = $this->readByte($in, $elemType); + $result += $this->readI32($in, $size); + return $result; + } + + public function readSetEnd($in) { + return 0; + } + + public function readByte($in, &$byte) { + $data = $in->readAll(1); + $arr = unpack('c', $data); + $byte = $arr[1]; + return 1; + } + + public function readI32($in, &$i32) { + $data = $in->readAll(4); + if (!defined('BIG_ENDIAN')) { + $data = strrev($data); + } + $arr = unpack('l', $data); + $i32 = $arr[1]; + return 4; + } + + public function readI64($in, &$i64) { + $data = $in->readAll(8); + $arr = unpack('N2', $data); + + // Check for a negative + if ($arr[1] & 0x80000000) { + $arr[1] = $arr[1] ^ 0xFFFFFFFF; + $arr[2] = $arr[2] ^ 0xFFFFFFFF; + $i64 = 0 - $arr[1]*4294967296 - $arr[2] - 1; + } else { + $i64 = $arr[1]*4294967296 + $arr[2]; + } + return 8; + } + + public function readString($in, &$str) { + $result = $this->readI32($in, $len); + $str = $in->readAll($len); + return $result + $len; + } +} + +?> \ No newline at end of file diff --git a/lib/php/src/protocol/TProtocol.php b/lib/php/src/protocol/TProtocol.php new file mode 100644 index 00000000..5a69bbdd --- /dev/null +++ b/lib/php/src/protocol/TProtocol.php @@ -0,0 +1,157 @@ + + */ +abstract class TProtocol { + + /** + * Writes a struct header. + * + * @param TTransport $out Output transport + * @param string $name Struct name + * @throws TException on write error + * @return int How many bytes written + */ + public abstract function writeStructBegin($out, $name); + + + /** + * Close a struct. + * + * @param TTransport $out Output transport + * @throws TException on write error + * @return int How many bytes written + */ + public abstract function writeStructEnd($out); + + /* + * Starts a field. + * + * @param TTransport $out Output transport + * @param string $name Field name + * @param int $type Field type + * @param int $fid Field id + * @throws TException on write error + * @return int How many bytes written + */ + public abstract function writeFieldBegin($out, $fieldName, $fieldType, $fieldId); + + public abstract function writeFieldEnd($out); + + public abstract function writeFieldStop($out); + + public abstract function writeMapBegin($out, $keyType, $valType, $size); + + public abstract function writeMapEnd($out); + + public abstract function writeListBegin($out, $elemType, $size); + + public abstract function writeListEnd($out); + + public abstract function writeSetBegin($out, $elemType, $size); + + public abstract function writeSetEnd($out); + + public abstract function writeByte($out, $byte); + + public abstract function writeI32($out, $i32); + + public abstract function writeI64($out, $i64); + + public abstract function writeString($out, $str); + + + public abstract function readStructBegin($in, &$name); + + public abstract function readStructEnd($in); + + public abstract function readFieldBegin($in, &$name, &$fieldType, &$fieldId); + + public abstract function readFieldEnd($in); + + public abstract function readMapBegin($in, &$keyType, &$valType, &$size); + + public abstract function readMapEnd($in); + + public abstract function readListBegin($in, &$elemType, &$size); + + public abstract function readListEnd($in); + + public abstract function readSetBegin($in, &$elemType, &$size); + + public abstract function readSetEnd($in); + + public abstract function readByte($in, &$byte); + + public abstract function readI32($in, &$i32); + + public abstract function readI64($in, &$i64); + + public abstract function readString($in, &$str); + + public function skip($in, $type) { + switch ($type) { + case TType::BYTE: + return $this->readByte($in, $byte); + case TType::I32: + return $this->readI32($in, $i32); + case TType::I64: + return $this->readI64($in, $i64); + case TType::STRING: + return $this->readString($in, $str); + case TType::STRUCT: + { + $result = $this->readStructBegin($in, $name); + while (true) { + $result += $this->readFieldBegin($in, $name, $ftype, $fid); + if ($ftype == TType::STOP) { + break; + } + $result += $this->skip($in, $ftype); + $result += $this->readFieldEnd($in); + } + $result += $this->readStructEnd($in); + return $result; + } + case TType::MAP: + { + $result = $this->readMapBegin($in, $keyType, $valType, $size); + for ($i = 0; $i < $size; $i++) { + $result += $this->skip($in, $keyType); + $result += $this->skip($in, $valType); + } + $result += $this->readMapEnd($in); + return $result; + } + case TType::SET: + { + $result = $this->readSetBegin($in, $elemType, $size); + for ($i = 0; $i < $size; $i++) { + $result += $this->skip($in, $elemType); + } + $result += $this->readSetEnd($in); + return $result; + } + case TType::LST: + { + $result = $this->readListBegin($in, $elemType, $size); + for ($i = 0; $i < $size; $i++) { + $result += $this->skip($in, $elemType); + } + $result += $this->readListEnd($in); + return $result; + } + default: + return 0; + } + } +} + +?> diff --git a/lib/php/src/protocol/TType.php b/lib/php/src/protocol/TType.php new file mode 100644 index 00000000..957efe6b --- /dev/null +++ b/lib/php/src/protocol/TType.php @@ -0,0 +1,19 @@ + + */ +class TType { + const STOP = 1; + const BYTE = 2; + const I32 = 6; + const I64 = 8; + const STRING = 9; + const STRUCT = 10; + const MAP = 11; + const SET = 12; + const LST = 13; // cannot use LIST keyword in PHP! +} diff --git a/lib/php/src/transport/TBufferedTransport.php b/lib/php/src/transport/TBufferedTransport.php new file mode 100644 index 00000000..dad96ff5 --- /dev/null +++ b/lib/php/src/transport/TBufferedTransport.php @@ -0,0 +1,108 @@ + + */ +class TBufferedTransport extends TTransport { + + /** + * Constructor. Creates a buffered transport around an underlying transport + */ + public function __construct($transport=null, $rBufSize=512, $wBufSize=512) { + $this->transport_ = $transport; + $this->rBufSize_ = $rBufSize; + $this->wBufSize_ = $wBufSize; + } + + /** + * The underlying transport + * + * @var TTransport + */ + protected $transport_ = null; + + /** + * The receive buffer size + * + * @var int + */ + protected $rBufSize_ = 512; + + /** + * The write buffer size + * + * @var int + */ + protected $wBufSize_ = 512; + + /** + * The write buffer. + * + * @var string + */ + protected $wBuf_ = ''; + + /** + * The read buffer. + * + * @var string + */ + protected $rBuf_ = ''; + + public function isOpen() { + return $this->transport_->isOpen(); + } + + public function open() { + $this->transport_->open(); + } + + public function close() { + $this->transport_->close(); + } + + public function readAll($len) { + return $this->transport_->readAll($len); + } + + public function read($len) { + // Methinks PHP is already buffering these for us + return $this->transport_->read($len); + + if (strlen($this->rBuf_) >= $len) { + $ret = substr($this->rBuf_, 0, $len); + $this->rBuf_ = substr($this->rBuf_, $len); + return $ret; + } + + $this->rBuf_ .= $this->transport_->read($this->rBufSize_); + $give = min(strlen($this->rBuf_), $len); + $ret = substr($this->rBuf_, 0, $give); + $this->rBuf_ = substr($this->rBuf_, $give); + return $ret; + } + + public function write($buf) { + $this->wBuf_ .= $buf; + if (strlen($this->wBuf_) >= $this->wBufSize_) { + $this->transport_->write($this->wBuf_); + $this->wBuf_ = ''; + } + } + + public function flush() { + if (!empty($this->wBuf_)) { + $this->transport_->write($this->wBuf_); + $this->wBuf_ = ''; + } + } + +} + +?> + diff --git a/lib/php/src/transport/TChunkedTransport.php b/lib/php/src/transport/TChunkedTransport.php new file mode 100644 index 00000000..04fba1ff --- /dev/null +++ b/lib/php/src/transport/TChunkedTransport.php @@ -0,0 +1,106 @@ + + */ +class TChunkedTransport extends TTransport { + + /** + * Underlying transport object. + * + * @var TTransport + */ + private $transport_; + + /** + * Buffer for read data. + * + * @var string + */ + private $rBuf_; + + /** + * Buffer for queued output data + * + * @var string + */ + private $wBuf_; + + /** + * Constructor. + * + * @param TTransport $transport Underlying transport + */ + public __construct($transport=null) { + $this->transport_ = $transport; + } + + /** + * Reads from the buffer. When more data is required reads another entire + * chunk and serves future reads out of that. + * + * @param int $len How much data + */ + public function read($len) { + $out = ''; + $need = $len; + $have = strlen($this->rBuf_); + if ($need > $have) { + $out = $this->rBuf_; + $need -= $have; + $this->readChunk(); + } + + $give = $need; + if (strlen($this->rBuf_) < $give) { + $out .= $this->rBuf_; + $this->rBuf_ = ''; + } else { + $out .= substr($this->rBuf_, 0, $give); + $this->rBuf_ = substr($this->rBuf_, $give); + } + + return $out; + } + + /** + * Reads a chunk of data into the internal read buffer. + */ + private function readChunk() { + $buf = $this->transport_->readAll(4); + $val = unpack('N', $buf); + $sz = $val[1]; + + $this->rBuf_ = $this->transport_->readAll($sz); + } + + /** + * Writes some data to the pending output buffer. + * + * @param string $buf The data + * @param int $len Limit of bytes to write + */ + public function write($buf, $len=null) { + if ($len !== null && $len < strlen($buf)) { + $buf = substr($buf, 0, $len); + } + $this->wBuf_ .= $buf; + } + + /** + * Writes the output buffer to the stream in the format of a 4-byte length + * followed by the actual data. + */ + public function flush() { + $out = pack('N', strlen($this->wBuf_)); + $out .= $this->wBuf_; + $this->transport_->write($out); + $this->transport_->flush(); + $this->wBuf_ = ''; + } + +} \ No newline at end of file diff --git a/lib/php/src/transport/TSocket.php b/lib/php/src/transport/TSocket.php new file mode 100644 index 00000000..0a3b090e --- /dev/null +++ b/lib/php/src/transport/TSocket.php @@ -0,0 +1,126 @@ + + */ +class TSocket extends TTransport { + + /** + * Handle to PHP socket + * + * @var resource + */ + private $handle_ = null; + + /** + * Remote hostname + * + * @var string + */ + private $host_ = 'localhost'; + + /** + * Remote port + * + * @var int + */ + private $port_ = '9090'; + + /** + * Persistent socket or plain? + * + * @var bool + */ + private $persist_ = false; + + /** + * Socket constructor + * + * @param string $host Remote hostname + * @param int $port Remote port + * @param bool $persist Whether to use a persistent socket + */ + public function __construct($host='localhost', $port=9090, $persist=false) { + $this->host_ = $host; + $this->port_ = $port; + $this->persist_ = $persist; + } + + /** + * Tests whether this is open + * + * @return bool true if the socket is open + */ + public function isOpen() { + return is_resource($this->handle_); + } + + /** + * Connects the socket. + */ + public function open() { + if ($this->persist_) { + $this->handle_ = pfsockopen($this->host_, $this->port_); + } else { + $this->handle_ = fsockopen($this->host_, $this->port_); + } + if ($this->handle_ === FALSE) { + throw new Exception('TSocket: Could not connect to '. + $this->host_.':'.$this->port_); + } + } + + /** + * Closes the socket + */ + public function close() { + if (!$this->persist_) { + fclose($this->handle_); + } + } + + /** + * Uses stream get contents to do the reading + */ + public function readAll($len) { + return stream_get_contents($this->handle_, $len); + } + + /** + * Read from the socket + */ + public function read($len) { + $data = fread($this->handle_, 1); + if ($data === FALSE) { + throw new Exception('TSocket: Could not read '.$len.' bytes from '. + $this->host_.':'.$this->port_); + } + return $data; + } + + /** + * Write to the socket. + */ + public function write($buf) { + while (!empty($buf)) { + $got = fwrite($this->handle_, $buf); + if ($got == false) { + throw new Exception('TSocket: Could not write '.strlen($buf).' bytes '. + $this->host_.':'.$this->port_); + } + $buf = substr($buf, $got); + } + } + + /** + * Flush output to the socket. + */ + public function flush() { + fflush($this->handle_); + } +} + +?> diff --git a/lib/php/src/transport/TTransport.php b/lib/php/src/transport/TTransport.php new file mode 100644 index 00000000..8d57d2ae --- /dev/null +++ b/lib/php/src/transport/TTransport.php @@ -0,0 +1,72 @@ + + */ +abstract class TTransport { + + /** + * Whether this transport is open. + * + * @return boolean true if open + */ + public abstract function isOpen(); + + /** + * Open the transport for reading/writing + * + * @throws TTransportException if cannot open + */ + public abstract function open(); + + /** + * Close the transport. + */ + public abstract function close(); + + /** + * Read some data into the array. + * + * @param int $len How much to read + * @return string The data that has been read + * @throws TTransportException if cannot read any more data + */ + public abstract function read($len); + + /** + * Guarantees that the full amount of data is read. + * + * @return string The data, of exact length + * @throws TTransportException if cannot read data + */ + public function readAll($len) { + // return $this->read($len); + + $data = ''; + $got = 0; + while (($got = strlen($data)) < $len) { + $data .= $this->read($len - $got); + } + return $data; + } + + /** + * Writes the given data out. + * + * @param string $buf The data to write + * @throws TTransportException if writing fails + */ + public abstract function write($buf); + + /** + * Flushes any pending data out of a buffer + * + * @throws TTransportException if a writing error occurs + */ + public function flush() {} +} + +?> diff --git a/test/ThriftTest.thrift b/test/ThriftTest.thrift index 6999f4e5..6f722160 100644 --- a/test/ThriftTest.thrift +++ b/test/ThriftTest.thrift @@ -8,15 +8,13 @@ enum Numberz EIGHT = 8 } -typedef u64 UserId +typedef i64 UserId struct Xtruct { string string_thing = 0, byte byte_thing = 1, - u32 u32_thing = 2, i32 i32_thing = 3, - u64 u64_thing = 4, i64 i64_thing = 5 } @@ -38,9 +36,7 @@ service ThriftTest void testVoid() string testString(string thing = 0) byte testByte(byte thing = 0) - u32 testU32(u32 thing = 0) i32 testI32(i32 thing = 0) - u64 testU64(u64 thing = 0) i64 testI64(i64 thing = 0) Xtruct testStruct(Xtruct thing = 0) Xtruct2 testNest(Xtruct2 thing = 0) diff --git a/test/cpp/Makefile b/test/cpp/Makefile index 21c09ec5..36abb434 100644 --- a/test/cpp/Makefile +++ b/test/cpp/Makefile @@ -12,19 +12,28 @@ CC = g++ LD = g++ # Compiler flags -LIBS = ../../lib/cpp/server/TSimpleServer.cc \ - ../../lib/cpp/protocol/TBinaryProtocol.cc \ - ../../lib/cpp/transport/TBufferedTransport.cc \ - ../../lib/cpp/transport/TServerSocket.cc \ - ../../lib/cpp/transport/TSocket.cc -CFL = -Wall -O3 -Igen-cpp -I../../lib/cpp $(LIBS) -CFL = -Wall -O3 -Igen-cpp -I../../lib/cpp -lthrift +LIBS = ../../lib/cpp/src/server/TSimpleServer.cc \ + ../../lib/cpp/src/protocol/TBinaryProtocol.cc \ + ../../lib/cpp/src/transport/TBufferedTransport.cc \ + ../../lib/cpp/src/transport/TChunkedTransport.cc \ + ../../lib/cpp/src/transport/TServerSocket.cc \ + ../../lib/cpp/src/transport/TSocket.cc +DCFL = -Wall -O3 -g -Igen-cpp -I../../lib/cpp/src $(LIBS) +CFL = -Wall -O3 -Igen-cpp -I../../lib/cpp/src -lthrift all: server client +debug: server-debug client-debug + stubs: ../ThriftTest.thrift $(THRIFT) -cpp ../ThriftTest.thrift +server-debug: stubs + g++ -o TestServer $(DCFL) src/TestServer.cc gen-cpp/ThriftTest.cc + +client-debug: stubs + g++ -o TestClient $(DCFL) src/TestClient.cc gen-cpp/ThriftTest.cc + server: stubs g++ -o TestServer $(CFL) src/TestServer.cc gen-cpp/ThriftTest.cc diff --git a/test/cpp/src/TestClient.cc b/test/cpp/src/TestClient.cc index 6bd06b9d..083661f3 100644 --- a/test/cpp/src/TestClient.cc +++ b/test/cpp/src/TestClient.cc @@ -77,14 +77,7 @@ int main(int argc, char** argv) { printf("testByte(1)"); uint8_t u8 = testClient.testByte(1); printf(" = %d\n", (int)u8); - - /** - * U32 TEST - */ - printf("testU32(1)"); - uint32_t u32 = testClient.testU32(1); - printf(" = %u\n", u32); - + /** * I32 TEST */ @@ -92,13 +85,6 @@ int main(int argc, char** argv) { int32_t i32 = testClient.testI32(-1); printf(" = %d\n", i32); - /** - * U64 TEST - */ - printf("testU64(34359738368)"); - uint64_t u64 = testClient.testU64(34359738368); - printf(" = %lu\n", u64); - /** * I64 TEST */ @@ -109,40 +95,34 @@ int main(int argc, char** argv) { /** * STRUCT TEST */ - printf("testStruct({\"Zero\", 1, 2, -3, 4, -5})"); + printf("testStruct({\"Zero\", 1, -3, -5})"); Xtruct out; out.string_thing = "Zero"; out.byte_thing = 1; - out.u32_thing = 2; out.i32_thing = -3; - out.u64_thing = 4; out.i64_thing = -5; Xtruct in = testClient.testStruct(out); - printf(" = {\"%s\", %d, %u, %d, %lu, %ld}\n", + printf(" = {\"%s\", %d, %d, %ld}\n", in.string_thing.c_str(), (int)in.byte_thing, - in.u32_thing, in.i32_thing, - in.u64_thing, in.i64_thing); /** * NESTED STRUCT TEST */ - printf("testNest({1, {\"Zero\", 1, 2, -3, 4, -5}), 5}"); + printf("testNest({1, {\"Zero\", 1, -3, -5}), 5}"); Xtruct2 out2; out2.byte_thing = 1; out2.struct_thing = out; out2.i32_thing = 5; Xtruct2 in2 = testClient.testNest(out2); in = in2.struct_thing; - printf(" = {%d, {\"%s\", %d, %u, %d, %lu, %ld}, %d}\n", + printf(" = {%d, {\"%s\", %d, %d, %ld}, %d}\n", in2.byte_thing, in.string_thing.c_str(), (int)in.byte_thing, - in.u32_thing, in.i32_thing, - in.u64_thing, in.i64_thing, in2.i32_thing); @@ -270,7 +250,7 @@ int main(int argc, char** argv) { */ printf("testTypedef(309858235082523)"); UserId uid = testClient.testTypedef(309858235082523); - printf(" = %lu\n", uid); + printf(" = %ld\n", uid); /** * NESTED MAP TEST @@ -297,9 +277,7 @@ int main(int argc, char** argv) { Xtruct truck; truck.string_thing = "Truck"; truck.byte_thing = 8; - truck.u32_thing = 8; truck.i32_thing = 8; - truck.u64_thing = 8; truck.i64_thing = 8; insane.xtructs.push_back(truck); printf("testInsanity()"); @@ -307,7 +285,7 @@ int main(int argc, char** argv) { printf(" = {"); map >::const_iterator i_iter; for (i_iter = whoa.begin(); i_iter != whoa.end(); ++i_iter) { - printf("%lu => {", i_iter->first); + printf("%ld => {", i_iter->first); map::const_iterator i2_iter; for (i2_iter = i_iter->second.begin(); i2_iter != i_iter->second.end(); @@ -317,7 +295,7 @@ int main(int argc, char** argv) { map::const_iterator um; printf("{"); for (um = userMap.begin(); um != userMap.end(); ++um) { - printf("%d => %lu, ", um->first, um->second); + printf("%d => %ld, ", um->first, um->second); } printf("}, "); @@ -325,12 +303,10 @@ int main(int argc, char** argv) { list::const_iterator x; printf("{"); for (x = xtructs.begin(); x != xtructs.end(); ++x) { - printf("{\"%s\", %d, %u, %d, %lu, %ld}, ", + printf("{\"%s\", %d, %d, %ld}, ", x->string_thing.c_str(), (int)x->byte_thing, - x->u32_thing, x->i32_thing, - x->u64_thing, x->i64_thing); } printf("}"); diff --git a/test/cpp/src/TestServer.cc b/test/cpp/src/TestServer.cc index 206b6f56..c0df233e 100644 --- a/test/cpp/src/TestServer.cc +++ b/test/cpp/src/TestServer.cc @@ -24,46 +24,32 @@ class TestServer : public ThriftTestServerIf { return thing; } - uint32_t testU32(uint32_t thing) { - printf("testU32(%u)\n", thing); - return thing; - } - int32_t testI32(int32_t thing) { printf("testI32(%d)\n", thing); return thing; } - uint64_t testU64(uint64_t thing) { - printf("testU64(%lu)\n", thing); - return thing; - } - int64_t testI64(int64_t thing) { printf("testI64(%ld)\n", thing); return thing; } Xtruct testStruct(Xtruct thing) { - printf("testStruct({\"%s\", %d, %u, %d, %lu, %ld})\n", + printf("testStruct({\"%s\", %d, %d, %ld})\n", thing.string_thing.c_str(), (int)thing.byte_thing, - thing.u32_thing, thing.i32_thing, - thing.u64_thing, thing.i64_thing); return thing; } Xtruct2 testNest(Xtruct2 nest) { Xtruct thing = nest.struct_thing; - printf("testNest({%d, {\"%s\", %d, %u, %d, %lu, %ld}, %d})\n", + printf("testNest({%d, {\"%s\", %d, %d, %ld}, %d})\n", (int)nest.byte_thing, thing.string_thing.c_str(), (int)thing.byte_thing, - thing.u32_thing, thing.i32_thing, - thing.u64_thing, thing.i64_thing, nest.i32_thing); return nest; @@ -123,7 +109,7 @@ class TestServer : public ThriftTestServerIf { } UserId testTypedef(UserId thing) { - printf("testTypedef(%lu)\n", thing); + printf("testTypedef(%ld)\n", thing); return thing; } @@ -150,17 +136,13 @@ class TestServer : public ThriftTestServerIf { Xtruct hello; hello.string_thing = "Hello2"; hello.byte_thing = 2; - hello.u32_thing = 2; hello.i32_thing = 2; - hello.u64_thing = 2; hello.i64_thing = 2; Xtruct goodbye; goodbye.string_thing = "Goodbye4"; goodbye.byte_thing = 4; - goodbye.u32_thing = 4; goodbye.i32_thing = 4; - goodbye.u64_thing = 4; goodbye.i64_thing = 4; Insanity crazy; @@ -187,7 +169,7 @@ class TestServer : public ThriftTestServerIf { printf(" = {"); map >::const_iterator i_iter; for (i_iter = insane.begin(); i_iter != insane.end(); ++i_iter) { - printf("%lu => {", i_iter->first); + printf("%ld => {", i_iter->first); map::const_iterator i2_iter; for (i2_iter = i_iter->second.begin(); i2_iter != i_iter->second.end(); @@ -197,7 +179,7 @@ class TestServer : public ThriftTestServerIf { map::const_iterator um; printf("{"); for (um = userMap.begin(); um != userMap.end(); ++um) { - printf("%d => %lu, ", um->first, um->second); + printf("%d => %ld, ", um->first, um->second); } printf("}, "); @@ -205,12 +187,10 @@ class TestServer : public ThriftTestServerIf { list::const_iterator x; printf("{"); for (x = xtructs.begin(); x != xtructs.end(); ++x) { - printf("{\"%s\", %d, %u, %d, %lu, %ld}, ", + printf("{\"%s\", %d, %d, %ld}, ", x->string_thing.c_str(), (int)x->byte_thing, - x->u32_thing, x->i32_thing, - x->u64_thing, x->i64_thing); } printf("}"); diff --git a/test/java/src/TestClient.java b/test/java/src/TestClient.java index 70cf826b..cbcd8021 100644 --- a/test/java/src/TestClient.java +++ b/test/java/src/TestClient.java @@ -78,14 +78,7 @@ public class TestClient { System.out.print("testByte(1)"); UInt8 u8 = testClient.testByte(new UInt8((short)1)); System.out.print(" = " + u8.get() + "\n"); - - /** - * U32 TEST - */ - System.out.print("testU32(1)"); - UInt32 u32 = testClient.testU32(new UInt32(1)); - System.out.print(" = " + u32.get() + "\n"); - + /** * I32 TEST */ @@ -93,13 +86,6 @@ public class TestClient { Int32 i32 = testClient.testI32(new Int32(-1)); System.out.print(" = " + i32.get() + "\n"); - /** - * U64 TEST - */ - System.out.print("testU64(34359738368)"); - UInt64 u64 = testClient.testU64(new UInt64(34359738368L)); - System.out.print(" = " + u64.toLong() + "\n"); - /** * I64 TEST */ @@ -110,27 +96,23 @@ public class TestClient { /** * STRUCT TEST */ - System.out.print("testStruct({\"Zero\", 1, 2, -3, 4, -5})"); + System.out.print("testStruct({\"Zero\", 1, -3, -5})"); Xtruct out = new Xtruct(); out.string_thing.value = "Zero"; out.byte_thing.set((short)1); - out.u32_thing.set(2); out.i32_thing.set(-3); - out.u64_thing.set(4); out.i64_thing.set(-5); Xtruct in = testClient.testStruct(out); System.out.print(" = {" + "\"" + in.string_thing.value + "\", " + in.byte_thing.get() + ", " + - in.u32_thing.get() + ", " + in.i32_thing.get() + ", " + - in.u64_thing.toLong() + ", " + in.i64_thing.get() + "}\n"); /** * NESTED STRUCT TEST */ - System.out.print("testNest({1, {\"Zero\", 1, 2, -3, 4, -5}), 5}"); + System.out.print("testNest({1, {\"Zero\", 1, -3, -5}), 5}"); Xtruct2 out2 = new Xtruct2(); out2.byte_thing.set((short)1); out2.struct_thing = out; @@ -141,9 +123,7 @@ public class TestClient { in2.byte_thing.get() + ", {" + "\"" + in.string_thing.value + "\", " + in.byte_thing.get() + ", " + - in.u32_thing.get() + ", " + in.i32_thing.get() + ", " + - in.u64_thing.toLong() + ", " + in.i64_thing.get() + "}, " + in2.i32_thing.get() + "}\n"); @@ -267,8 +247,8 @@ public class TestClient { * TYPEDEF TEST */ System.out.print("testTypedef(309858235082523)"); - UInt64 uid = testClient.testTypedef(new UInt64(309858235082523L)); - System.out.print(" = " + uid.toLong() + "\n"); + Int64 uid = testClient.testTypedef(new Int64(309858235082523L)); + System.out.print(" = " + uid.get() + "\n"); /** * NESTED MAP TEST @@ -291,31 +271,29 @@ public class TestClient { * INSANITY TEST */ Insanity insane = new Insanity(); - insane.userMap.put(Numberz.FIVE, new UInt64(5000)); + insane.userMap.put(Numberz.FIVE, new Int64(5000)); Xtruct truck = new Xtruct(); truck.string_thing.value = "Truck"; truck.byte_thing.set((short)8); - truck.u32_thing.set(8); truck.i32_thing.set(8); - truck.u64_thing.set(8); truck.i64_thing.set(8); insane.xtructs.add(truck); System.out.print("testInsanity()"); - HashMap> whoa = + HashMap> whoa = testClient.testInsanity(insane); System.out.print(" = {"); - for (UInt64 key : whoa.keySet()) { + for (Int64 key : whoa.keySet()) { HashMap val = whoa.get(key); - System.out.print(key.toLong() + " => {"); + System.out.print(key.get() + " => {"); for (Int32 k2 : val.keySet()) { Insanity v2 = val.get(k2); System.out.print(k2.get() + " => {"); - HashMap userMap = v2.userMap; + HashMap userMap = v2.userMap; System.out.print("{"); for (Int32 k3 : userMap.keySet()) { System.out.print(k3.get() + " => " + - userMap.get(k3).toLong() + ", "); + userMap.get(k3).get() + ", "); } System.out.print("}, "); @@ -325,9 +303,7 @@ public class TestClient { System.out.print("{" + "\"" + x.string_thing.value + "\", " + x.byte_thing.get() + ", " + - x.u32_thing.get() + ", "+ x.i32_thing.get() + ", "+ - x.u64_thing.toLong() + ", "+ x.i64_thing.get() + "}, "); } System.out.print("}"); diff --git a/test/java/src/TestServer.java b/test/java/src/TestServer.java index c5edca47..0acc8a21 100644 --- a/test/java/src/TestServer.java +++ b/test/java/src/TestServer.java @@ -35,21 +35,11 @@ public class TestServer extends ThriftTestServerIf { return thing; } - public UInt32 testU32(UInt32 thing) { - System.out.print("testU32(" + thing.get() + ")\n"); - return thing; - } - public Int32 testI32(Int32 thing) { System.out.print("testI32(" + thing.get() + ")\n"); return thing; } - public UInt64 testU64(UInt64 thing) { - System.out.print("testU64(" + thing.toLong() + ")\n"); - return thing; - } - public Int64 testI64(Int64 thing) { System.out.print("testI64(" + thing.get() + ")\n"); return thing; @@ -59,9 +49,7 @@ public class TestServer extends ThriftTestServerIf { System.out.print("testStruct({" + "\"" + thing.string_thing.value + "\", " + thing.byte_thing.get() + ", " + - thing.u32_thing.get() + ", " + thing.i32_thing.get() + ", " + - thing.u64_thing.toLong() + ", " + thing.i64_thing.get() + "})\n"); return thing; } @@ -72,9 +60,7 @@ public class TestServer extends ThriftTestServerIf { nest.byte_thing.get() + ", {" + "\"" + thing.string_thing.value + "\", " + thing.byte_thing.get() + ", " + - thing.u32_thing.get() + ", " + thing.i32_thing.get() + ", " + - thing.u64_thing.toLong() + ", " + thing.i64_thing.get() + "}, " + nest.i32_thing.get() + "})\n"); return nest; @@ -130,8 +116,8 @@ public class TestServer extends ThriftTestServerIf { return thing; } - public UInt64 testTypedef(UInt64 thing) { - System.out.print("testTypedef(" + thing.toLong() + ")\n"); + public Int64 testTypedef(Int64 thing) { + System.out.print("testTypedef(" + thing.get() + ")\n"); return thing; } @@ -153,31 +139,27 @@ public class TestServer extends ThriftTestServerIf { return mapmap; } - public HashMap> testInsanity(Insanity argument) { + public HashMap> testInsanity(Insanity argument) { System.out.print("testInsanity()\n"); Xtruct hello = new Xtruct(); hello.string_thing.value = "Hello2"; hello.byte_thing.set((short)2); - hello.u32_thing.set(2); hello.i32_thing.set(2); - hello.u64_thing.set(2); hello.i64_thing.set(2); Xtruct goodbye = new Xtruct(); goodbye.string_thing.value = "Goodbye4"; goodbye.byte_thing.set((short)4); - goodbye.u32_thing.set(4); goodbye.i32_thing.set(4); - goodbye.u64_thing.set(4); goodbye.i64_thing.set(4); Insanity crazy = new Insanity(); - crazy.userMap.put(Numberz.EIGHT, new UInt64(8)); + crazy.userMap.put(Numberz.EIGHT, new Int64(8)); crazy.xtructs.add(goodbye); Insanity looney = new Insanity(); - crazy.userMap.put(Numberz.FIVE, new UInt64(5)); + crazy.userMap.put(Numberz.FIVE, new Int64(5)); crazy.xtructs.add(hello); HashMap first_map = new HashMap(); @@ -188,10 +170,10 @@ public class TestServer extends ThriftTestServerIf { second_map.put(Numberz.SIX, looney); - HashMap> insane = - new HashMap>(); - insane.put(new UInt64(1), first_map); - insane.put(new UInt64(2), second_map); + HashMap> insane = + new HashMap>(); + insane.put(new Int64(1), first_map); + insane.put(new Int64(2), second_map); return insane; } diff --git a/test/php/Makefile b/test/php/Makefile new file mode 100644 index 00000000..2e6bec52 --- /dev/null +++ b/test/php/Makefile @@ -0,0 +1,27 @@ +# Makefile for Thrift test project. +# +# Author: +# Mark Slee + +# Default target is everything +target: all + +# Tools +THRIFT = thrift + +# Compiler flags +LIBS = ../../lib/cpp/server/TSimpleServer.cc \ + ../../lib/cpp/protocol/TBinaryProtocol.cc \ + ../../lib/cpp/transport/TBufferedTransport.cc \ + ../../lib/cpp/transport/TServerSocket.cc \ + ../../lib/cpp/transport/TSocket.cc +CFL = -Wall -O3 -Igen-cpp -I../../lib/cpp $(LIBS) +CFL = -Wall -O3 -Igen-cpp -I../../lib/cpp -lthrift + +all: stubs + +stubs: ../ThriftTest.thrift + $(THRIFT) -php ../ThriftTest.thrift + +clean: + rm -fr gen-php diff --git a/test/php/TestClient.php b/test/php/TestClient.php new file mode 100644 index 00000000..8f765fa7 --- /dev/null +++ b/test/php/TestClient.php @@ -0,0 +1,327 @@ + 1) { + $host = $argv[0]; +} + +if ($argc > 2) { + $host = $argv[1]; +} + +$socket = new TSocket($host, $port); +$bufferedSocket = new TBufferedTransport($socket, 1024, 1024); +$binary = new TBinaryProtocol(); + +$testClient = new ThriftTestClient($bufferedSocket, $binary); +$bufferedSocket->open(); + +$start = microtime(true); + +/** + * VOID TEST + */ +print_r("testVoid()"); +$testClient->testVoid(); +print_r(" = void\n"); + +/** + * STRING TEST + */ +print_r("testString(\"Test\")"); +$s = $testClient->testString("Test"); +print_r(" = \"$s\"\n"); + +/** + * BYTE TEST + */ +print_r("testByte(1)"); +$u8 = $testClient->testByte(1); +print_r(" = $u8\n"); + +/** + * I32 TEST + */ +print_r("testI32(-1)"); +$i32 = $testClient->testI32(-1); +print_r(" = $i32\n"); + +/** + * I64 TEST + */ +print_r("testI64(-34359738368)"); +$i64 = $testClient->testI64(-34359738368); +print_r(" = $i64\n"); + +/** + * STRUCT TEST + */ +print_r("testStruct({\"Zero\", 1, -3, -5})"); +$out = new Xtruct(); +$out->string_thing = "Zero"; +$out->byte_thing = 1; +$out->i32_thing = -3; +$out->i64_thing = -5; +$in = $testClient->testStruct($out); +print_r(" = {\"".$in->string_thing."\", ". + $in->byte_thing.", ". + $in->i32_thing.", ". + $in->i64_thing."}\n"); + +/** + * NESTED STRUCT TEST + */ +print_r("testNest({1, {\"Zero\", 1, -3, -5}), 5}"); +$out2 = new Xtruct2(); +$out2->byte_thing = 1; +$out2->struct_thing = $out; +$out2->i32_thing = 5; +$in2 = $testClient->testNest($out2); +$in = $in2->struct_thing; +print_r(" = {".$in2->byte_thing.", {\"". + $in->string_thing."\", ". + $in->byte_thing.", ". + $in->i32_thing.", ". + $in->i64_thing."}, ". + $in2->i32_thing."}\n"); + +/** + * MAP TEST + */ +$mapout = array(); +for ($i = 0; $i < 5; ++$i) { + $mapout[$i] = $i-10; +} +print_r("testMap({"); +$first = true; +foreach ($mapout as $key => $val) { + if ($first) { + $first = false; + } else { + print_r(", "); + } + print_r("$key => $val"); +} +print_r("})"); + +$mapin = $testClient->testMap($mapout); +print_r(" = {"); +$first = true; +foreach ($mapin as $key => $val) { + if ($first) { + $first = false; + } else { + print_r(", "); + } + print_r("$key => $val"); +} +print_r("}\n"); + +/** + * SET TEST + */ +$setout = array();; +for ($i = -2; $i < 3; ++$i) { + $setout []= $i; +} +print_r("testSet({"); +$first = true; +foreach ($setout as $val) { + if ($first) { + $first = false; + } else { + print_r(", "); + } + print_r($val); +} +print_r("})"); +$setin = $testClient->testSet($setout); +print_r(" = {"); +$first = true; +foreach ($setin as $val) { + if ($first) { + $first = false; + } else { + print_r(", "); + } + print_r($val); +} +print_r("}\n"); + +/** + * LIST TEST + */ +$listout = array(); +for ($i = -2; $i < 3; ++$i) { + $listout []= $i; +} +print_r("testList({"); +$first = true; +foreach ($listout as $val) { + if ($first) { + $first = false; + } else { + print_r(", "); + } + print_r($val); +} +print_r("})"); +$listin = $testClient->testList($listout); +print_r(" = {"); +$first = true; +foreach ($listin as $val) { + if ($first) { + $first = false; + } else { + print_r(", "); + } + print_r($val); +} +print_r("}\n"); + +/** + * ENUM TEST + */ +print_r("testEnum(ONE)"); +$ret = $testClient->testEnum(Numberz::ONE); +print_r(" = $ret\n"); + +print_r("testEnum(TWO)"); +$ret = $testClient->testEnum(Numberz::TWO); +print_r(" = $ret\n"); + +print_r("testEnum(THREE)"); +$ret = $testClient->testEnum(Numberz::THREE); +print_r(" = $ret\n"); + +print_r("testEnum(FIVE)"); +$ret = $testClient->testEnum(Numberz::FIVE); +print_r(" = $ret\n"); + +print_r("testEnum(EIGHT)"); +$ret = $testClient->testEnum(Numberz::EIGHT); +print_r(" = $ret\n"); + +/** + * TYPEDEF TEST + */ +print_r("testTypedef(309858235082523)"); +$uid = $testClient->testTypedef(309858235082523); +print_r(" = $uid\n"); + +/** + * NESTED MAP TEST + */ +print_r("testMapMap(1)"); +$mm = $testClient->testMapMap(1); +print_r(" = {"); +foreach ($mm as $key => $val) { + print_r("$key => {"); + foreach ($val as $k2 => $v2) { + print_r("$k2 => $v2, "); + } + print_r("}, "); +} +print_r("}\n"); + +/** + * INSANITY TEST + */ +$insane = new Insanity(); +$insane->userMap[Numberz::FIVE] = 5000; +$truck = new Xtruct(); +$truck->string_thing = "Truck"; +$truck->byte_thing = 8; +$truck->i32_thing = 8; +$truck->i64_thing = 8; +$insane->xtructs []= $truck; +print_r("testInsanity()"); +$whoa = $testClient->testInsanity($insane); +print_r(" = {"); +foreach ($whoa as $key => $val) { + print_r("$key => {"); + foreach ($val as $k2 => $v2) { + print_r("$k2 => {"); + $userMap = $v2->userMap; + print_r("{"); + foreach ($userMap as $k3 => $v3) { + print_r("$k3 => $v3, "); + } + print_r("}, "); + + $xtructs = $v2->xtructs; + print_r("{"); + foreach ($xtructs as $x) { + print_r("{\"".$x->string_thing."\", ". + $x->byte_thing.", ".$x->i32_thing.", ".$x->i64_thing."}, "); + } + print_r("}"); + + print_r("}, "); + } + print_r("}, "); +} +print_r("}\n"); + + +/** + * Normal tests done. + */ + +$stop = microtime(true); +$elp = round(1000*($stop - $start), 0); +print_r("Total time: $elp ms\n"); + +/** + * Extraneous "I don't trust PHP to pack/unpack integer" tests + */ + +// Max I32 +$num = pow(2, 30) + (pow(2, 30) - 1); +$num2 = $testClient->testI32($num); +if ($num != $num2) { + print "Missed $num = $num2\n"; +} + +// Min I32 +$num = 0 - pow(2, 31); +$num2 = $testClient->testI32($num); +if ($num != $num2) { + print "Missed $num = $num2\n"; +} + +// Max I64 +$num = pow(2, 62) + (pow(2, 62) - 1); +$num2 = $testClient->testI64($num); +if ($num != $num2) { + print "Missed $num = $num2\n"; +} + +// Min I64 +$num = 0 - pow(2, 63); +$num2 = $testClient->testI64($num); +if ($num != $num2) { + print "Missed $num = $num2\n"; +} + +$bufferedSocket->close(); +return; + +?>