From 21c0a85079e3e1b5ee475bd6654512079fe5df68 Mon Sep 17 00:00:00 2001 From: Roger Meier Date: Wed, 5 Sep 2012 19:47:14 +0000 Subject: [PATCH] THRIFT-1615 PHP Namespace (was Thrift for Symfony 2) Patch: Xavier HAUSHERR git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1381323 13f79535-47bb-0310-9956-ffa450edef68 --- compiler/cpp/src/generate/t_php_generator.cc | 475 +++++----- lib/php/Makefile.am | 71 +- lib/php/README | 22 +- lib/php/README.apache | 26 +- lib/php/lib/Thrift/Base/TBase.php | 367 ++++++++ .../Thrift/ClassLoader/ThriftClassLoader.php | 223 +++++ .../Exception/TApplicationException.php | 69 ++ lib/php/lib/Thrift/Exception/TException.php | 369 ++++++++ .../Thrift/Exception/TProtocolException.php | 48 ++ .../Thrift/Exception/TTransportException.php | 41 + .../Thrift/Factory/TBinaryProtocolFactory.php | 43 + .../Factory/TCompactProtocolFactory.php | 39 + .../Thrift/Factory/TJSONProtocolFactory.php | 41 + .../lib/Thrift/Factory/TProtocolFactory.php | 35 + .../lib/Thrift/Factory/TStringFuncFactory.php | 63 ++ .../lib/Thrift/Factory/TTransportFactory.php | 16 + .../lib/Thrift/Protocol/JSON/BaseContext.php | 39 + .../lib/Thrift/Protocol/JSON/ListContext.php | 52 ++ .../Thrift/Protocol/JSON/LookaheadReader.php | 54 ++ .../lib/Thrift/Protocol/JSON/PairContext.php | 60 ++ .../lib/Thrift/Protocol/TBinaryProtocol.php | 396 +++++++++ .../Protocol/TBinaryProtocolAccelerated.php | 47 + .../lib/Thrift/Protocol/TCompactProtocol.php | 669 +++++++++++++++ lib/php/lib/Thrift/Protocol/TJSONProtocol.php | 694 +++++++++++++++ lib/php/lib/Thrift/Protocol/TProtocol.php | 346 ++++++++ .../Thrift/Serializer/TBinarySerializer.php | 73 ++ lib/php/lib/Thrift/Server/TForkingServer.php | 119 +++ lib/php/lib/Thrift/Server/TServer.php | 101 +++ lib/php/lib/Thrift/Server/TServerSocket.php | 98 +++ .../lib/Thrift/Server/TServerTransport.php | 54 ++ lib/php/lib/Thrift/Server/TSimpleServer.php | 57 ++ lib/php/lib/Thrift/StringFunc/Core.php | 38 + lib/php/lib/Thrift/StringFunc/Mbstring.php | 45 + lib/php/lib/Thrift/StringFunc/TStringFunc.php | 27 + .../Thrift/Transport/TBufferedTransport.php | 165 ++++ .../lib/Thrift/Transport/TFramedTransport.php | 183 ++++ lib/php/lib/Thrift/Transport/THttpClient.php | 205 +++++ .../lib/Thrift/Transport/TMemoryBuffer.php | 87 ++ .../lib/Thrift/Transport/TNullTransport.php | 50 ++ lib/php/lib/Thrift/Transport/TPhpStream.php | 114 +++ lib/php/lib/Thrift/Transport/TSocket.php | 326 +++++++ lib/php/lib/Thrift/Transport/TSocketPool.php | 295 +++++++ lib/php/lib/Thrift/Transport/TTransport.php | 93 ++ lib/php/lib/Thrift/Type/TMessageType.php | 33 + lib/php/lib/Thrift/Type/TType.php | 46 + lib/php/src/protocol/TBinaryProtocol.php | 429 ---------- lib/php/src/protocol/TBinarySerializer.php | 70 -- lib/php/src/protocol/TCompactProtocol.php | 678 --------------- lib/php/src/protocol/TJSONProtocol.php | 808 ------------------ lib/php/src/protocol/TProtocol.php | 376 -------- lib/php/src/server/TForkingServer.php | 114 --- lib/php/src/server/TServer.php | 95 -- lib/php/src/server/TSimpleServer.php | 54 -- lib/php/src/transport/TBufferedTransport.php | 161 ---- lib/php/src/transport/TFramedTransport.php | 179 ---- lib/php/src/transport/THttpClient.php | 200 ----- lib/php/src/transport/TMemoryBuffer.php | 82 -- lib/php/src/transport/TNullTransport.php | 46 - lib/php/src/transport/TPhpStream.php | 109 --- lib/php/src/transport/TServerSocket.php | 96 --- lib/php/src/transport/TServerTransport.php | 50 -- lib/php/src/transport/TSocket.php | 320 ------- lib/php/src/transport/TSocketPool.php | 294 ------- lib/php/src/transport/TTransport.php | 106 --- lib/php/src/transport/TTransportFactory.php | 12 - lib/php/test/Makefile.am | 8 +- lib/php/test/Test/Thrift/Fixtures.php | 192 +++++ .../Thrift/Protocol/TestTJSONProtocol.php | 561 ++++++++++++ test/php/TestClient.php | 66 +- tutorial/php/PhpClient.php | 55 +- tutorial/php/PhpServer.php | 64 +- 71 files changed, 7064 insertions(+), 4675 deletions(-) create mode 100644 lib/php/lib/Thrift/Base/TBase.php create mode 100644 lib/php/lib/Thrift/ClassLoader/ThriftClassLoader.php create mode 100644 lib/php/lib/Thrift/Exception/TApplicationException.php create mode 100644 lib/php/lib/Thrift/Exception/TException.php create mode 100644 lib/php/lib/Thrift/Exception/TProtocolException.php create mode 100644 lib/php/lib/Thrift/Exception/TTransportException.php create mode 100644 lib/php/lib/Thrift/Factory/TBinaryProtocolFactory.php create mode 100644 lib/php/lib/Thrift/Factory/TCompactProtocolFactory.php create mode 100644 lib/php/lib/Thrift/Factory/TJSONProtocolFactory.php create mode 100644 lib/php/lib/Thrift/Factory/TProtocolFactory.php create mode 100644 lib/php/lib/Thrift/Factory/TStringFuncFactory.php create mode 100644 lib/php/lib/Thrift/Factory/TTransportFactory.php create mode 100644 lib/php/lib/Thrift/Protocol/JSON/BaseContext.php create mode 100644 lib/php/lib/Thrift/Protocol/JSON/ListContext.php create mode 100644 lib/php/lib/Thrift/Protocol/JSON/LookaheadReader.php create mode 100644 lib/php/lib/Thrift/Protocol/JSON/PairContext.php create mode 100644 lib/php/lib/Thrift/Protocol/TBinaryProtocol.php create mode 100644 lib/php/lib/Thrift/Protocol/TBinaryProtocolAccelerated.php create mode 100644 lib/php/lib/Thrift/Protocol/TCompactProtocol.php create mode 100644 lib/php/lib/Thrift/Protocol/TJSONProtocol.php create mode 100644 lib/php/lib/Thrift/Protocol/TProtocol.php create mode 100644 lib/php/lib/Thrift/Serializer/TBinarySerializer.php create mode 100644 lib/php/lib/Thrift/Server/TForkingServer.php create mode 100644 lib/php/lib/Thrift/Server/TServer.php create mode 100644 lib/php/lib/Thrift/Server/TServerSocket.php create mode 100644 lib/php/lib/Thrift/Server/TServerTransport.php create mode 100644 lib/php/lib/Thrift/Server/TSimpleServer.php create mode 100644 lib/php/lib/Thrift/StringFunc/Core.php create mode 100644 lib/php/lib/Thrift/StringFunc/Mbstring.php create mode 100644 lib/php/lib/Thrift/StringFunc/TStringFunc.php create mode 100644 lib/php/lib/Thrift/Transport/TBufferedTransport.php create mode 100644 lib/php/lib/Thrift/Transport/TFramedTransport.php create mode 100644 lib/php/lib/Thrift/Transport/THttpClient.php create mode 100644 lib/php/lib/Thrift/Transport/TMemoryBuffer.php create mode 100644 lib/php/lib/Thrift/Transport/TNullTransport.php create mode 100644 lib/php/lib/Thrift/Transport/TPhpStream.php create mode 100644 lib/php/lib/Thrift/Transport/TSocket.php create mode 100644 lib/php/lib/Thrift/Transport/TSocketPool.php create mode 100644 lib/php/lib/Thrift/Transport/TTransport.php create mode 100644 lib/php/lib/Thrift/Type/TMessageType.php create mode 100644 lib/php/lib/Thrift/Type/TType.php create mode 100644 lib/php/test/Test/Thrift/Fixtures.php create mode 100644 lib/php/test/Test/Thrift/Protocol/TestTJSONProtocol.php diff --git a/compiler/cpp/src/generate/t_php_generator.cc b/compiler/cpp/src/generate/t_php_generator.cc index 91beb733..74c3af33 100644 --- a/compiler/cpp/src/generate/t_php_generator.cc +++ b/compiler/cpp/src/generate/t_php_generator.cc @@ -33,7 +33,6 @@ using namespace std; #define NSGLOBAL_A ("\\" + NSGLOBAL ) #define NSGLOBAL_B ( NSGLOBAL + "\\") #define NSGLOBAL_AB ("\\" + NSGLOBAL + "\\") -#define NS_ROOT ( namespace_php_ ? "\\" : "") /** @@ -60,19 +59,12 @@ class t_php_generator : public t_oop_generator { iter = parsed_options.find("server"); phps_ = (iter != parsed_options.end()); - iter = parsed_options.find("autoload"); - autoload_ = (iter != parsed_options.end()); - iter = parsed_options.find("oop"); oop_ = (iter != parsed_options.end()); - iter = parsed_options.find("namespace"); - namespace_php_ = (iter != parsed_options.end()); - iter = parsed_options.find("nsglobal"); if(iter != parsed_options.end()) { - if(namespace_php_) nsglobal_ = iter->second; - else throw "cannot use nsglobal without namespace."; + nsglobal_ = iter->second; } else { nsglobal_ = ""; // by default global namespace is empty @@ -114,7 +106,6 @@ class t_php_generator : public t_oop_generator { void generate_php_struct(t_struct* tstruct, bool is_exception); void generate_php_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception=false); - void _generate_php_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception=false); void generate_php_struct_reader(std::ofstream& out, t_struct* tstruct); void generate_php_struct_writer(std::ofstream& out, t_struct* tstruct); void generate_php_function_helpers(t_function* tfunction); @@ -130,7 +121,6 @@ class t_php_generator : public t_oop_generator { void generate_service_interface (t_service* tservice); void generate_service_rest (t_service* tservice); void generate_service_client (t_service* tservice); - void _generate_service_client (std::ofstream &out, t_service* tservice); void generate_service_processor (t_service* tservice); void generate_process_function (t_service* tservice, t_function* tfunction); @@ -195,13 +185,13 @@ class t_php_generator : public t_oop_generator { std::string php_includes(); 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 argument_list(t_struct* tstruct, bool addStructSignature = true); std::string type_to_cast(t_type* ttype); std::string type_to_enum(t_type* ttype); - std::string php_namespace_base(const t_program* p, bool modern) { + std::string php_namespace_base(const t_program* p) { std::string ns = p->get_namespace("php"); - const char * delimiter = modern ? "\\" : "_"; + const char * delimiter = "\\"; size_t position = ns.find('.'); while (position != string::npos) { ns.replace(position, 1, delimiter); @@ -212,35 +202,35 @@ class t_php_generator : public t_oop_generator { //general use namespace prefixing: \my\namespace\ or my_namespace_ string php_namespace(const t_program* p) { - string ns = php_namespace_base(p, namespace_php_); - if(namespace_php_) return (nsglobal_.size() ? NSGLOBAL_AB : NSGLOBAL_B) + (ns.size() ? (ns + "\\") : ""); - else return (nsglobal_.size() ? NSGLOBAL + "_" : "") + (ns.size() ? (ns + "_") : ""); + string ns = php_namespace_base(p); + return (nsglobal_.size() ? NSGLOBAL_AB : NSGLOBAL_B) + (ns.size() ? (ns + "\\") : ""); } //setting the namespace of a file: my\namespace string php_namespace_suffix(const t_program* p) { - string ns = php_namespace_base(p, namespace_php_); - if (namespace_php_) return (nsglobal_.size() ? NSGLOBAL_B : NSGLOBAL) + ns; - //provides consistent behavior even though this function is never called when namespace_php_ is false - else return (nsglobal_.size() ? (NSGLOBAL + "_") : "") + ns; + string ns = php_namespace_base(p); + + return (nsglobal_.size() ? NSGLOBAL_B : NSGLOBAL) + ns; } - //writing an autload identifier into globals: my\namespace\ or my_namespace_ - string php_namespace_autoload(const t_program* p) { - std::string ns = php_namespace_base(p, namespace_php_); - if (namespace_php_) return (nsglobal_.size() ? NSGLOBAL_B : NSGLOBAL) + (ns.size() ? (ns + "\\") : ""); - else return (nsglobal_.size() ? (NSGLOBAL + "_") : "") + (ns.size() ? (ns + "_") : ""); + //add a directory to allready existing namespace + string php_namespace_directory(string directory, bool end = true) { + if(end) { + return ";"; + } else { + return ""; + } } - string php_namespace_constant(const t_program* p) { - std::string ns = php_namespace_base(p, false); - return ns.size() ? (ns + "_") : ""; + //writing an autload identifier into globa;ls: my\namespace\ or my_namespace_ + string php_namespace_autoload(const t_program* p) { + std::string ns = php_namespace_base(p); + return (nsglobal_.size() ? NSGLOBAL_B : NSGLOBAL) + (ns.size() ? (ns + "\\") : ""); } //declaring a type: typename or my_namespace_typename string php_namespace_declaration(t_type* t) { - if(namespace_php_) return t->get_name(); - return php_namespace(t->get_program()) + t->get_name(); + return t->get_name(); } std::string php_path(t_program* p) { @@ -256,7 +246,69 @@ class t_php_generator : public t_oop_generator { } } - return ((namespace_php_) ? ns + '/' : "" ) + p->get_name(); + return ns + '/'; + } + + /** + * Transform class_method into ClassMethod + * + * @param str + * @return stirng + */ + string classify(string str) + { + string classe = ""; + + vector x = split(str, '_'); + + for (size_t i = 0; i < x.size(); ++i) { + classe = classe + capitalize(x[i]); + } + + return classe; + } + + /** + * Split method + * @param s + * @param delim + * @param elems + * @return + */ + vector &split(const string &s, char delim, vector &elems) { + stringstream ss(s); + string item; + + while(getline(ss, item, delim)) { + elems.push_back(item); + } + + return elems; + } + + vector split(const string &s, char delim) { + vector elems; + + return split(s, delim, elems); + } + + /** + * Capitalize method + * @param str + * @return + */ + string capitalize(string str) + { + string::iterator it(str.begin()); + + if (it != str.end()) + str[0] = toupper((unsigned char)str[0]); + +// while(++it != str.end()) +// { +// *it = tolower((unsigned char)*it); +// } + return str; } private: @@ -265,7 +317,6 @@ class t_php_generator : public t_oop_generator { * File streams */ std::ofstream f_types_; - std::ofstream f_consts_; std::ofstream f_helpers_; std::ofstream f_service_; @@ -285,21 +336,11 @@ class t_php_generator : public t_oop_generator { */ bool phps_; - /** - * Generate PHP code that uses autoload - */ - bool autoload_; - /** * Whether to use OOP base class TBase */ bool oop_; - - /** - * Generate namespaces in PHP5.3 style - */ - bool namespace_php_; - + /** * Global namespace for PHP 5.3 */ @@ -321,49 +362,42 @@ bool t_php_generator::is_valid_namespace(const std::string& sub_namespace) { void t_php_generator::init_generator() { // Make output directory MKDIR(get_out_dir().c_str()); - package_dir_ = get_out_dir()+"/"+program_name_+"/"; - MKDIR(package_dir_.c_str()); + + // Create Real directory Namespaces + vector NSx = split(php_namespace_suffix(get_program()), '\\'); + package_dir_ = get_out_dir(); + + for (size_t i = 0; i < NSx.size(); ++i) { + package_dir_ = package_dir_ + "/" + NSx[i] + "/"; + MKDIR(package_dir_.c_str()); + } + // Make output file - string f_types_name = package_dir_+program_name_+"_types.php"; + string f_types_name = package_dir_+"Types.php"; f_types_.open(f_types_name.c_str()); // Print header f_types_ << "& includes = program_->get_includes(); - for (size_t i = 0; i < includes.size(); ++i) { - string package = includes[i]->get_name(); - string prefix = php_path(includes[i]); - f_types_ << - "include_once $GLOBALS['THRIFT_ROOT'].'/packages/" << prefix << "/" << package << "_types.php';" << endl; - } f_types_ << endl; - - // Print header - if (!program_->get_consts().empty()) { - string f_consts_name = package_dir_+program_name_+"_constants.php"; - f_consts_.open(f_consts_name.c_str()); - f_consts_ << - "" << endl; + f_types_ << endl; f_types_.close(); - - if (!program_->get_consts().empty()) { - f_consts_ << "?>" << endl; - f_consts_.close(); - } } /** @@ -396,26 +425,14 @@ void t_php_generator::generate_typedef(t_typedef* ttypedef) { * @param tenum The enumeration */ void t_php_generator::generate_enum(t_enum* tenum) { - f_types_ << - "$GLOBALS['" << php_namespace(tenum->get_program()) << "E_" << tenum->get_name() << "'] = array(" << endl; - vector constants = tenum->get_constants(); vector::iterator c_iter; - for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { - int value = (*c_iter)->get_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 " << php_namespace(tenum->get_program()) << tenum->get_name() << " {" << endl; + "final class " << tenum->get_name() << " {" << endl; indent_up(); for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { @@ -446,9 +463,9 @@ void t_php_generator::generate_const(t_const* tconst) { string name = tconst->get_name(); t_const_value* value = tconst->get_value(); - f_consts_ << "$GLOBALS['" << php_namespace_constant(get_program()) << program_name_ << "_CONSTANTS']['" << name << "'] = "; - f_consts_ << render_const_value(type, value); - f_consts_ << ";" << endl << endl; + f_types_ << "$GLOBALS['" << program_name_ << "_CONSTANTS']['" << name << "'] = "; + f_types_ << render_const_value(type, value); + f_types_ << ";" << endl << endl; } /** @@ -487,7 +504,7 @@ string t_php_generator::render_const_value(t_type* type, t_const_value* value) { } else if (type->is_enum()) { indent(out) << value->get_integer(); } else if (type->is_struct() || type->is_xception()) { - out << "new " << NS_ROOT << php_namespace(type->get_program()) << type->get_name() << "(array(" << endl; + out << "new " << php_namespace(type->get_program()) << type->get_name() << "(array(" << endl; indent_up(); const vector& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; @@ -580,7 +597,7 @@ void t_php_generator::generate_php_struct(t_struct* tstruct, void t_php_generator::generate_php_type_spec(ofstream& out, t_type* t) { t = get_true_type(t); - indent(out) << "'type' => " << NS_ROOT << type_to_enum(t) << "," << endl; + indent(out) << "'type' => " << type_to_enum(t) << "," << endl; if (t->is_base_type() || t->is_enum()) { // Noop, type is all we need @@ -589,8 +606,8 @@ void t_php_generator::generate_php_type_spec(ofstream& out, } else if (t->is_map()) { t_type* ktype = get_true_type(((t_map*)t)->get_key_type()); t_type* vtype = get_true_type(((t_map*)t)->get_val_type()); - indent(out) << "'ktype' => " << NS_ROOT << type_to_enum(ktype) << "," << endl; - indent(out) << "'vtype' => " << NS_ROOT << type_to_enum(vtype) << "," << endl; + indent(out) << "'ktype' => " << type_to_enum(ktype) << "," << endl; + indent(out) << "'vtype' => " << type_to_enum(vtype) << "," << endl; indent(out) << "'key' => array(" << endl; indent_up(); generate_php_type_spec(out, ktype); @@ -608,7 +625,7 @@ void t_php_generator::generate_php_type_spec(ofstream& out, } else { etype = get_true_type(((t_set*)t)->get_elem_type()); } - indent(out) << "'etype' => " << NS_ROOT << type_to_enum(etype) <<"," << endl; + indent(out) << "'etype' => " << type_to_enum(etype) <<"," << endl; indent(out) << "'elem' => array(" << endl; indent_up(); generate_php_type_spec(out, etype); @@ -651,33 +668,6 @@ void t_php_generator::generate_php_struct_spec(ofstream& out, indent(out) << "}" << endl; } - -void t_php_generator::generate_php_struct_definition(ofstream& out, - t_struct* tstruct, - bool is_exception) { - if (autoload_) { - // Make output file - ofstream autoload_out; - string f_struct = program_name_+"."+(tstruct->get_name())+".php"; - string f_struct_name = package_dir_+f_struct; - autoload_out.open(f_struct_name.c_str()); - autoload_out << "get_program()) << ";" << endl; - _generate_php_struct_definition(autoload_out, tstruct, is_exception); - autoload_out << endl << "?>" << endl; - autoload_out.close(); - - f_types_ << - "$GLOBALS['THRIFT_AUTOLOAD']['" << - lowercase(php_namespace_autoload(tstruct->get_program()) + tstruct->get_name()) << - "'] = '" << program_name_ << "/" << f_struct << "';" << endl; - - } else { - _generate_php_struct_definition(out, tstruct, is_exception); - } -} - /** * 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 @@ -685,7 +675,7 @@ void t_php_generator::generate_php_struct_definition(ofstream& out, * * @param tstruct The struct definition */ -void t_php_generator::_generate_php_struct_definition(ofstream& out, +void t_php_generator::generate_php_struct_definition(ofstream& out, t_struct* tstruct, bool is_exception) { const vector& members = tstruct->get_members(); @@ -694,9 +684,9 @@ void t_php_generator::_generate_php_struct_definition(ofstream& out, out << "class " << php_namespace_declaration(tstruct); if (is_exception) { - out << " extends " << NS_ROOT << "TException"; + out << " extends " << "TException"; } else if (oop_) { - out << " extends " << NS_ROOT << "TBase"; + out << " extends " << "TBase"; } out << " {" << endl; @@ -808,7 +798,7 @@ void t_php_generator::generate_php_struct_reader(ofstream& out, t_field ffid(g_type_i16, "fid"); generate_deserialize_field(out, &fftype); out << - indent() << "if ($ftype == " << NS_ROOT << "TType::STOP) {" << endl << + indent() << "if ($ftype == " << "TType::STOP) {" << endl << indent() << " break;" << endl << indent() << "}" << endl; generate_deserialize_field(out, &ffid); @@ -817,7 +807,7 @@ void t_php_generator::generate_php_struct_reader(ofstream& out, "$xfer += $input->readFieldBegin($fname, $ftype, $fid);" << endl; // Check for field STOP marker and break indent(out) << - "if ($ftype == " << NS_ROOT << "TType::STOP) {" << endl; + "if ($ftype == " << "TType::STOP) {" << endl; indent_up(); indent(out) << "break;" << endl; @@ -837,14 +827,14 @@ void t_php_generator::generate_php_struct_reader(ofstream& out, indent(out) << "case " << (*f_iter)->get_key() << ":" << endl; indent_up(); - indent(out) << "if ($ftype == " << NS_ROOT << type_to_enum((*f_iter)->get_type()) << ") {" << endl; + indent(out) << "if ($ftype == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl; indent_up(); generate_deserialize_field(out, *f_iter, "this->"); indent_down(); out << indent() << "} else {" << endl; if (binary_inline_) { - indent(out) << " $xfer += " << NS_ROOT << "TProtocol::skipBinary($input, $ftype);" << endl; + indent(out) << " $xfer += " << "TProtocol::skipBinary($input, $ftype);" << endl; } else { indent(out) << " $xfer += $input->skip($ftype);" << endl; } @@ -857,7 +847,7 @@ void t_php_generator::generate_php_struct_reader(ofstream& out, // In the default case we skip the field indent(out) << "default:" << endl; if (binary_inline_) { - indent(out) << " $xfer += " << NS_ROOT << "TProtocol::skipBinary($input, $ftype);" << endl; + indent(out) << " $xfer += " << "TProtocol::skipBinary($input, $ftype);" << endl; } else { indent(out) << " $xfer += $input->skip($ftype);" << endl; } @@ -936,7 +926,7 @@ void t_php_generator::generate_php_struct_writer(ofstream& out, indent() << "if (!is_" << expect << "($this->" << (*f_iter)->get_name() << ")) {" << endl; indent_up(); out << - indent() << "throw new " << NS_ROOT << "TProtocolException('Bad type in structure.', " << NS_ROOT << "TProtocolException::INVALID_DATA);" << endl; + indent() << "throw new " << "TProtocolException('Bad type in structure.', " << "TProtocolException::INVALID_DATA);" << endl; scope_down(out); } @@ -949,7 +939,7 @@ void t_php_generator::generate_php_struct_writer(ofstream& out, indent(out) << "$xfer += $output->writeFieldBegin(" << "'" << (*f_iter)->get_name() << "', " << - NS_ROOT << type_to_enum((*f_iter)->get_type()) << ", " << + type_to_enum((*f_iter)->get_type()) << ", " << (*f_iter)->get_key() << ");" << endl; } @@ -969,7 +959,7 @@ void t_php_generator::generate_php_struct_writer(ofstream& out, if (binary_inline_) { out << - indent() << "$output .= pack('c', " << NS_ROOT << "TType::STOP);" << endl; + indent() << "$output .= pack('c', " << "TType::STOP);" << endl; } else { out << indent() << "$xfer += $output->writeFieldStop();" << endl << @@ -995,18 +985,10 @@ void t_php_generator::generate_service(t_service* tservice) { f_service_.open(f_service_name.c_str()); f_service_ << "get_program()) << ";" << endl; + f_service_ << "namespace " << php_namespace_suffix(tservice->get_program()) << ";" << endl; f_service_ << autogen_comment() << php_includes(); - f_service_ << - "include_once $GLOBALS['THRIFT_ROOT'].'/packages/" << php_path(program_) << "/" << program_name_ << "_types.php';" << endl; - - if (tservice->get_extends() != NULL) { - f_service_ << - "include_once $GLOBALS['THRIFT_ROOT'].'/packages/" << php_path(tservice->get_extends()->get_program()) << "/" << tservice->get_extends()->get_name() << ".php';" << endl; - } - f_service_ << endl; @@ -1022,7 +1004,7 @@ void t_php_generator::generate_service(t_service* tservice) { } // Close service file - f_service_ << "?>" << endl; + f_service_ << endl; f_service_.close(); } @@ -1095,13 +1077,13 @@ void t_php_generator::generate_service_processor(t_service* tservice) { indent() << "if (!method_exists($this, $methodname)) {" << endl; if (binary_inline_) { f_service_ << - indent() << " throw new Exception('Function '.$fname.' not implemented.');" << endl; + indent() << " throw new \\Exception('Function '.$fname.' not implemented.');" << endl; } else { f_service_ << - indent() << " $input->skip(" << NS_ROOT << "TType::STRUCT);" << endl << + indent() << " $input->skip(" << "TType::STRUCT);" << endl << indent() << " $input->readMessageEnd();" << endl << - indent() << " $x = new " << NS_ROOT << "TApplicationException('Function '.$fname.' not implemented.', " << NS_ROOT << "TApplicationException::UNKNOWN_METHOD);" << endl << - indent() << " $output->writeMessageBegin($fname, " << NS_ROOT << "TMessageType::EXCEPTION, $rseqid);" << endl << + indent() << " $x = new " << "TApplicationException('Function '.$fname.' not implemented.', " << "TApplicationException::UNKNOWN_METHOD);" << endl << + indent() << " $output->writeMessageBegin($fname, " << "TMessageType::EXCEPTION, $rseqid);" << endl << indent() << " $x->write($output);" << endl << indent() << " $output->writeMessageEnd();" << endl << indent() << " $output->getTransport()->flush();" << endl << @@ -1192,7 +1174,7 @@ void t_php_generator::generate_process_function(t_service* tservice, indent_down(); for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { f_service_ << - indent() << "} catch (" << php_namespace((*x_iter)->get_type()->get_program()) << (*x_iter)->get_type()->get_name() << " $" << (*x_iter)->get_name() << ") {" << endl; + indent() << "} catch (" << php_namespace(get_true_type((*x_iter)->get_type())->get_program()) << (*x_iter)->get_type()->get_name() << " $" << (*x_iter)->get_name() << ") {" << endl; if (!tfunction->is_oneway()) { indent_up(); f_service_ << @@ -1215,14 +1197,14 @@ void t_php_generator::generate_process_function(t_service* tservice, } f_service_ << - indent() << "$bin_accel = ($output instanceof " << NS_ROOT << "TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary');" << endl; + indent() << "$bin_accel = ($output instanceof " << "TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary');" << endl; f_service_ << indent() << "if ($bin_accel)" << endl; scope_up(f_service_); f_service_ << - indent() << "thrift_protocol_write_binary($output, '" << tfunction->get_name() << "', " << NS_ROOT << "TMessageType::REPLY, $result, $seqid, $output->isStrictWrite());" << endl; + indent() << "thrift_protocol_write_binary($output, '" << tfunction->get_name() << "', " << "TMessageType::REPLY, $result, $seqid, $output->isStrictWrite());" << endl; scope_down(f_service_); f_service_ << @@ -1232,7 +1214,7 @@ void t_php_generator::generate_process_function(t_service* tservice, // Serialize the request header if (binary_inline_) { f_service_ << - indent() << "$buff = pack('N', (0x80010000 | " << NS_ROOT << "TMessageType::REPLY)); " << endl << + indent() << "$buff = pack('N', (0x80010000 | " << "TMessageType::REPLY)); " << endl << indent() << "$buff .= pack('N', strlen('" << tfunction->get_name() << "'));" << endl << indent() << "$buff .= '" << tfunction->get_name() << "';" << endl << indent() << "$buff .= pack('N', $seqid);" << endl << @@ -1241,7 +1223,7 @@ void t_php_generator::generate_process_function(t_service* tservice, indent() << "$output->flush();" << endl; } else { f_service_ << - indent() << "$output->writeMessageBegin('" << tfunction->get_name() << "', " << NS_ROOT << "TMessageType::REPLY, $seqid);" << endl << + indent() << "$output->writeMessageBegin('" << tfunction->get_name() << "', " << "TMessageType::REPLY, $seqid);" << endl << indent() << "$result->write($output);" << endl << indent() << "$output->writeMessageEnd();" << endl << indent() << "$output->getTransport()->flush();" << endl; @@ -1390,7 +1372,7 @@ void t_php_generator::generate_service_rest(t_service* tservice) { } } f_service_ << - indent() << "return $this->impl_->" << (*f_iter)->get_name() << "(" << argument_list((*f_iter)->get_arglist()) << ");" << endl; + indent() << "return $this->impl_->" << (*f_iter)->get_name() << "(" << argument_list((*f_iter)->get_arglist(), false) << ");" << endl; indent_down(); indent(f_service_) << "}" << endl << @@ -1401,36 +1383,12 @@ void t_php_generator::generate_service_rest(t_service* tservice) { "}" << endl << endl; } -void t_php_generator::generate_service_client(t_service* tservice) { - if (autoload_) { - // Make output file - ofstream autoload_out; - string f_struct = program_name_+"."+(tservice->get_name())+".client.php"; - string f_struct_name = package_dir_+f_struct; - autoload_out.open(f_struct_name.c_str()); - autoload_out << "get_program()) << ";" << endl << endl; - _generate_service_client(autoload_out, tservice); - autoload_out << endl << "?>" << endl; - autoload_out.close(); - - f_service_ << - "$GLOBALS['THRIFT_AUTOLOAD']['" << - lowercase(php_namespace_autoload(get_program()) + service_name_ + "Client") << - "'] = '" << program_name_ << "/" << f_struct << "';" << endl; - - } else { - _generate_service_client(f_service_, tservice); - } -} - /** * Generates a service client definition. * * @param tservice The service to generate a server for. */ -void t_php_generator::_generate_service_client(ofstream& out, t_service* tservice) { +void t_php_generator::generate_service_client(t_service* tservice) { string extends = ""; string extends_client = ""; if (tservice->get_extends() != NULL) { @@ -1438,33 +1396,33 @@ void t_php_generator::_generate_service_client(ofstream& out, t_service* tservic extends_client = " extends " + php_namespace(tservice->get_extends()->get_program()) + extends + "Client"; } - out << + f_service_ << "class " << php_namespace_declaration(tservice) << "Client" << extends_client << " implements " << php_namespace(tservice->get_program()) << service_name_ << "If {" << endl; indent_up(); // Private members if (extends.empty()) { - out << + f_service_ << indent() << "protected $input_ = null;" << endl << indent() << "protected $output_ = null;" << endl << endl; - out << + f_service_ << indent() << "protected $seqid_ = 0;" << endl << endl; } // Constructor function - out << + f_service_ << indent() << "public function __construct($input, $output=null) {" << endl; if (!extends.empty()) { - out << + f_service_ << indent() << " parent::__construct($input, $output);" << endl; } else { - out << + f_service_ << indent() << " $this->input_ = $input;" << endl << indent() << " $this->output_ = $output ? $output : $input;" << endl; } - out << + f_service_ << indent() << "}" << endl << endl; // Generate client method implementations @@ -1477,10 +1435,10 @@ void t_php_generator::_generate_service_client(ofstream& out, t_service* tservic string funname = (*f_iter)->get_name(); // Open function - indent(out) << + indent(f_service_) << "public function " << function_signature(*f_iter) << endl; - scope_up(out); - indent(out) << + scope_up(f_service_); + indent(f_service_) << "$this->send_" << funname << "("; bool first = true; @@ -1488,80 +1446,80 @@ void t_php_generator::_generate_service_client(ofstream& out, t_service* tservic if (first) { first = false; } else { - out << ", "; + f_service_ << ", "; } - out << "$" << (*fld_iter)->get_name(); + f_service_ << "$" << (*fld_iter)->get_name(); } - out << ");" << endl; + f_service_ << ");" << endl; if (!(*f_iter)->is_oneway()) { - out << indent(); + f_service_ << indent(); if (!(*f_iter)->get_returntype()->is_void()) { - out << "return "; + f_service_ << "return "; } - out << + f_service_ << "$this->recv_" << funname << "();" << endl; } - scope_down(out); - out << endl; + scope_down(f_service_); + f_service_ << endl; - indent(out) << + indent(f_service_) << "public function send_" << function_signature(*f_iter) << endl; - scope_up(out); + scope_up(f_service_); std::string argsname = php_namespace(tservice->get_program()) + service_name_ + "_" + (*f_iter)->get_name() + "_args"; - out << + f_service_ << indent() << "$args = new " << argsname << "();" << endl; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { - out << + f_service_ << indent() << "$args->" << (*fld_iter)->get_name() << " = $" << (*fld_iter)->get_name() << ";" << endl; } - out << - indent() << "$bin_accel = ($this->output_ instanceof " << NS_ROOT << "TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary');" << endl; + f_service_ << + indent() << "$bin_accel = ($this->output_ instanceof " << "TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary');" << endl; - out << + f_service_ << indent() << "if ($bin_accel)" << endl; - scope_up(out); + scope_up(f_service_); - out << - indent() << "thrift_protocol_write_binary($this->output_, '" << (*f_iter)->get_name() << "', " << NS_ROOT << "TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite());" << endl; + f_service_ << + indent() << "thrift_protocol_write_binary($this->output_, '" << (*f_iter)->get_name() << "', " << "TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite());" << endl; - scope_down(out); - out << + scope_down(f_service_); + f_service_ << indent() << "else" << endl; - scope_up(out); + scope_up(f_service_); // Serialize the request header if (binary_inline_) { - out << - indent() << "$buff = pack('N', (0x80010000 | " << NS_ROOT << "TMessageType::CALL));" << endl << + f_service_ << + indent() << "$buff = pack('N', (0x80010000 | " << "TMessageType::CALL));" << endl << indent() << "$buff .= pack('N', strlen('" << funname << "'));" << endl << indent() << "$buff .= '" << funname << "';" << endl << indent() << "$buff .= pack('N', $this->seqid_);" << endl; } else { - out << - indent() << "$this->output_->writeMessageBegin('" << (*f_iter)->get_name() << "', " << NS_ROOT << "TMessageType::CALL, $this->seqid_);" << endl; + f_service_ << + indent() << "$this->output_->writeMessageBegin('" << (*f_iter)->get_name() << "', " << "TMessageType::CALL, $this->seqid_);" << endl; } // Write to the stream if (binary_inline_) { - out << + f_service_ << indent() << "$args->write($buff);" << endl << indent() << "$this->output_->write($buff);" << endl << indent() << "$this->output_->flush();" << endl; } else { - out << + f_service_ << indent() << "$args->write($this->output_);" << endl << indent() << "$this->output_->writeMessageEnd();" << endl << indent() << "$this->output_->getTransport()->flush();" << endl; } - scope_down(out); + scope_down(f_service_); - scope_down(out); + scope_down(f_service_); if (!(*f_iter)->is_oneway()) { @@ -1572,22 +1530,22 @@ void t_php_generator::_generate_service_client(ofstream& out, t_service* tservic string("recv_") + (*f_iter)->get_name(), &noargs); // Open function - out << + f_service_ << endl << indent() << "public function " << function_signature(&recv_function) << endl; - scope_up(out); + scope_up(f_service_); - out << - indent() << "$bin_accel = ($this->input_ instanceof " << NS_ROOT << "TProtocol::$TBINARYPROTOCOLACCELERATED)" + f_service_ << + indent() << "$bin_accel = ($this->input_ instanceof " << "TProtocol::$TBINARYPROTOCOLACCELERATED)" << " && function_exists('thrift_protocol_read_binary');" << endl; - out << + f_service_ << indent() << "if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, '" << resultname << "', $this->input_->isStrictRead());" << endl; - out << + f_service_ << indent() << "else" << endl; - scope_up(out); + scope_up(f_service_); - out << + f_service_ << indent() << "$rseqid = 0;" << endl << indent() << "$fname = null;" << endl << indent() << "$mtype = 0;" << endl << @@ -1596,39 +1554,39 @@ void t_php_generator::_generate_service_client(ofstream& out, t_service* tservic if (binary_inline_) { t_field ffname(g_type_string, "fname"); t_field fseqid(g_type_i32, "rseqid"); - out << + f_service_ << indent() << "$ver = unpack('N', $this->input_->readAll(4));" << endl << indent() << "$ver = $ver[1];" << endl << indent() << "$mtype = $ver & 0xff;" << endl << indent() << "$ver = $ver & 0xffff0000;" << endl << - indent() << "if ($ver != 0x80010000) throw new " << NS_ROOT << "TProtocolException('Bad version identifier: '.$ver, " << NS_ROOT << "TProtocolException::BAD_VERSION);" << endl; - generate_deserialize_field(out, &ffname, "", true); - generate_deserialize_field(out, &fseqid, "", true); + indent() << "if ($ver != 0x80010000) throw new " << "TProtocolException('Bad version identifier: '.$ver, " << "TProtocolException::BAD_VERSION);" << endl; + generate_deserialize_field(f_service_, &ffname, "", true); + generate_deserialize_field(f_service_, &fseqid, "", true); } else { - out << + f_service_ << indent() << "$this->input_->readMessageBegin($fname, $mtype, $rseqid);" << endl << - indent() << "if ($mtype == " << NS_ROOT << "TMessageType::EXCEPTION) {" << endl << - indent() << " $x = new " << NS_ROOT << "TApplicationException();" << endl << + indent() << "if ($mtype == " << "TMessageType::EXCEPTION) {" << endl << + indent() << " $x = new " << "TApplicationException();" << endl << indent() << " $x->read($this->input_);" << endl << indent() << " $this->input_->readMessageEnd();" << endl << indent() << " throw $x;" << endl << indent() << "}" << endl; } - out << + f_service_ << indent() << "$result = new " << resultname << "();" << endl << indent() << "$result->read($this->input_);" << endl; if (!binary_inline_) { - out << + f_service_ << indent() << "$this->input_->readMessageEnd();" << endl; } - scope_down(out); + scope_down(f_service_); // Careful, only return result if not a void function if (!(*f_iter)->get_returntype()->is_void()) { - out << + f_service_ << indent() << "if ($result->success !== null) {" << endl << indent() << " return $result->success;" << endl << indent() << "}" << endl; @@ -1638,7 +1596,7 @@ void t_php_generator::_generate_service_client(ofstream& out, t_service* tservic const std::vector& xceptions = xs->get_members(); vector::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { - out << + f_service_ << indent() << "if ($result->" << (*x_iter)->get_name() << " !== null) {" << endl << indent() << " throw $result->" << (*x_iter)->get_name() << ";" << endl << indent() << "}" << endl; @@ -1646,22 +1604,22 @@ void t_php_generator::_generate_service_client(ofstream& out, t_service* tservic // Careful, only return _result if not a void function if ((*f_iter)->get_returntype()->is_void()) { - indent(out) << + indent(f_service_) << "return;" << endl; } else { - out << - indent() << "throw new " << NS_ROOT << "Exception(\"" << (*f_iter)->get_name() << " failed: unknown result\");" << endl; + f_service_ << + indent() << "throw new \\Exception(\"" << (*f_iter)->get_name() << " failed: unknown result\");" << endl; } // Close function - scope_down(out); - out << endl; + scope_down(f_service_); + f_service_ << endl; } } indent_down(); - out << + f_service_ << "}" << endl << endl; } @@ -2122,8 +2080,8 @@ void t_php_generator::generate_serialize_container(ofstream &out, } else { indent(out) << "$output->writeMapBegin(" << - NS_ROOT << type_to_enum(((t_map*)ttype)->get_key_type()) << ", " << - NS_ROOT << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << + 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()) { @@ -2135,7 +2093,7 @@ void t_php_generator::generate_serialize_container(ofstream &out, } else { indent(out) << "$output->writeSetBegin(" << - NS_ROOT << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " << + type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " << "count($" << prefix << "));" << endl; } } else if (ttype->is_list()) { @@ -2147,7 +2105,7 @@ void t_php_generator::generate_serialize_container(ofstream &out, } else { indent(out) << "$output->writeListBegin(" << - NS_ROOT << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << + type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << "count($" << prefix << "));" << endl; } } @@ -2300,7 +2258,7 @@ string t_php_generator::function_signature(t_function* tfunction, /** * Renders a field list */ -string t_php_generator::argument_list(t_struct* tstruct) { +string t_php_generator::argument_list(t_struct* tstruct, bool addStructSignature) { string result = ""; const vector& fields = tstruct->get_members(); @@ -2312,6 +2270,17 @@ string t_php_generator::argument_list(t_struct* tstruct) { } else { result += ", "; } + + t_type* type = (*f_iter)->get_type(); + + //Set type name + if(addStructSignature and type->is_struct()) + { + string className = php_namespace(type->get_program()) + php_namespace_directory("Definition", false) + classify(type->get_name()); + + result += className + " "; + } + result += "$" + (*f_iter)->get_name(); } return result; @@ -2388,9 +2357,7 @@ string t_php_generator ::type_to_enum(t_type* type) { THRIFT_REGISTER_GENERATOR(php, "PHP", " inlined: Generate PHP inlined files\n" " server: Generate PHP server stubs\n" -" autoload: Generate PHP with autoload\n" " oop: Generate PHP with object oriented subclasses\n" " rest: Generate PHP REST processors\n" -" namespace: Generate PHP namespaces as defined in PHP >= 5.3\n" ) diff --git a/lib/php/Makefile.am b/lib/php/Makefile.am index b317197f..5c4d0f40 100755 --- a/lib/php/Makefile.am +++ b/lib/php/Makefile.am @@ -34,29 +34,62 @@ phpmoduledir = `php-config --extension-dir` phpmodule_SCRIPTS = src/ext/thrift_protocol/modules/thrift_protocol.so endif -phpdir = $(PHP_PREFIX) -php_DATA = \ - src/autoload.php \ - src/Thrift.php +phpdir = $(PHP_PREFIX)/Thrift -phpprotocoldir = $(phpdir)/protocol +phpbasedir = $(phpdir)/Base +phpbase_DATA = \ + lib/Thrift/Base/TBase.php + +phpexceptiondir = $(phpdir)/Exception +phpexception_DATA = \ + lib/Thrift/Exception/TApplicationException.php \ + lib/Thrift/Exception/TException.php \ + lib/Thrift/Exception/TProtocolException.php \ + lib/Thrift/Exception/TTransportException.php + +phpfactorydir = $(phpdir)/Factory +phpfactory_DATA = \ + lib/Thrift/Factory/TBinaryProtocolFactory.php \ + lib/Thrift/Factory/TCompactProtocolFactory.php \ + lib/Thrift/Factory/TJSONProtocolFactory.php \ + lib/Thrift/Factory/TProtocolFactory.php \ + lib/Thrift/Factory/TTransportFactory.php + +phpprotocoldir = $(phpdir)/Protocol phpprotocol_DATA = \ - src/protocol/TBinaryProtocol.php \ - src/protocol/TBinarySerializer.php \ - src/protocol/TProtocol.php \ - src/protocol/TJSONProtocol.php + lib/Thrift/Protocol/TBinaryProtocolAccelerated.php \ + lib/Thrift/Protocol/TBinaryProtocol.php \ + lib/Thrift/Protocol/TCompactProtocol.php \ + lib/Thrift/Protocol/TJSONProtocol.php \ + lib/Thrift/Protocol/TProtocol.php -phptransportdir = $(phpdir)/transport +phpprotocoljsondir = $(phpprotocoldir)/JSON +phpprotocoljson_DATA = \ + lib/Thrift/Protocol/JSON/BaseContext.php \ + lib/Thrift/Protocol/JSON/ListContext.php \ + lib/Thrift/Protocol/JSON/LookaheadReader.php \ + lib/Thrift/Protocol/JSON/PairContext.php + +phpserializerdir = $(phpdir)/Serializer +phpserializer_DATA = \ + lib/Thrift/Serializer/TBinarySerializer.php + +phptransportdir = $(phpdir)/Transport phptransport_DATA = \ - src/transport/TBufferedTransport.php \ - src/transport/TFramedTransport.php \ - src/transport/THttpClient.php \ - src/transport/TMemoryBuffer.php \ - src/transport/TNullTransport.php \ - src/transport/TPhpStream.php \ - src/transport/TSocket.php \ - src/transport/TSocketPool.php \ - src/transport/TTransport.php + lib/Thrift/Transport/TBufferedTransport.php \ + lib/Thrift/Transport/TFramedTransport.php \ + lib/Thrift/Transport/THttpClient.php \ + lib/Thrift/Transport/TMemoryBuffer.php \ + lib/Thrift/Transport/TNullTransport.php \ + lib/Thrift/Transport/TPhpStream.php \ + lib/Thrift/Transport/TSocket.php \ + lib/Thrift/Transport/TSocketPool.php \ + lib/Thrift/Transport/TTransport.php + +phptypedir = $(phpdir)/Type +phptype_DATA = \ + lib/Thrift/Type/TMessageType.php \ + lib/Thrift/Type/TType.php EXTRA_DIST = src thrift_protocol.ini README.apache diff --git a/lib/php/README b/lib/php/README index bb566f42..af6cf88d 100644 --- a/lib/php/README +++ b/lib/php/README @@ -29,24 +29,14 @@ features (i.e. APC cacheing using asbolute path URLs) as simple as possible. To use Thrift in your PHP codebase, take the following steps: -#1) Copy all of thrift/lib/php/src into your PHP codebase -#2) Set $GLOBALS['THRIFT_ROOT'] to the path you installed Thrift -#3) include_once $GLOBALS['THRIFT_ROOT'].'/Thrift.php'; +#1) Copy all of thrift/lib/php/lib into your PHP codebase +#2) Configure Symfony Autoloader (or whatever you usually use) -Note that #3 must be done before including any other Thrift files. -If you do not do #2, Thrift.php will set this global for you, but it will be -done using dirname(__FILE__), which is less efficient than providing the static -string yourself. +After thaht, you have to manually include the Thrift package +created by the compiler: -When you generate a Thrift package using the compiler, it makes an assumption -about where your generated code will live. If your file is "MyPackage.thrift", -the generated files must be installed into: - -$GLOBALS['THRIFT_ROOT'].'/packages/MyPackage/'; - -This allows the code generator to compile your code without any extra flags -for the target directory names while still allowing your include paths to -be absolute (if you have an absolute THRIFT_ROOT). +require_once 'packages/Service/Service.php'; +require_once 'packages/Service/Types.php'; Dependencies ============ diff --git a/lib/php/README.apache b/lib/php/README.apache index 8c41833d..5e925897 100644 --- a/lib/php/README.apache +++ b/lib/php/README.apache @@ -32,17 +32,29 @@ Sample Code registerNamespace('Thrift', $THRIFT_ROOT); +$loader->registerDefinition('Thrift', $THRIFT_ROOT . '/packages'); +$loader->register(); -include_once $GLOBALS['THRIFT_ROOT'].'/Thrift.php'; -include_once $GLOBALS['THRIFT_ROOT'].'/packages/Service/Service.php'; -include_once $GLOBALS['THRIFT_ROOT'].'/transport/TPhpStream.php'; -include_once $GLOBALS['THRIFT_ROOT'].'/protocol/TBinaryProtocol.php'; +use Thrift\Transport\TPhpStream; +use Thrift\Protocol\TBinaryProtocol; + +/** + * Example of how to build a Thrift server in Apache/PHP + */ class ServiceHandler implements ServiceIf { // Implement your interface and methods here diff --git a/lib/php/lib/Thrift/Base/TBase.php b/lib/php/lib/Thrift/Base/TBase.php new file mode 100644 index 00000000..3d5b526e --- /dev/null +++ b/lib/php/lib/Thrift/Base/TBase.php @@ -0,0 +1,367 @@ + 'Bool', + TType::BYTE => 'Byte', + TType::I16 => 'I16', + TType::I32 => 'I32', + TType::I64 => 'I64', + TType::DOUBLE => 'Double', + TType::STRING => 'String'); + + abstract function read($input); + + abstract function write($output); + + public function __construct($spec=null, $vals=null) { + if (is_array($spec) && is_array($vals)) { + foreach ($spec as $fid => $fspec) { + $var = $fspec['var']; + if (isset($vals[$var])) { + $this->$var = $vals[$var]; + } + } + } + } + + public function __wakeup() + { + $this->__construct(get_object_vars($this)); + } + + private function _readMap(&$var, $spec, $input) { + $xfer = 0; + $ktype = $spec['ktype']; + $vtype = $spec['vtype']; + $kread = $vread = null; + if (isset(TBase::$tmethod[$ktype])) { + $kread = 'read'.TBase::$tmethod[$ktype]; + } else { + $kspec = $spec['key']; + } + if (isset(TBase::$tmethod[$vtype])) { + $vread = 'read'.TBase::$tmethod[$vtype]; + } else { + $vspec = $spec['val']; + } + $var = array(); + $_ktype = $_vtype = $size = 0; + $xfer += $input->readMapBegin($_ktype, $_vtype, $size); + for ($i = 0; $i < $size; ++$i) { + $key = $val = null; + if ($kread !== null) { + $xfer += $input->$kread($key); + } else { + switch ($ktype) { + case TType::STRUCT: + $class = $kspec['class']; + $key = new $class(); + $xfer += $key->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($key, $kspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($key, $kspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($key, $kspec, $input, true); + break; + } + } + if ($vread !== null) { + $xfer += $input->$vread($val); + } else { + switch ($vtype) { + case TType::STRUCT: + $class = $vspec['class']; + $val = new $class(); + $xfer += $val->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($val, $vspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($val, $vspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($val, $vspec, $input, true); + break; + } + } + $var[$key] = $val; + } + $xfer += $input->readMapEnd(); + return $xfer; + } + + private function _readList(&$var, $spec, $input, $set=false) { + $xfer = 0; + $etype = $spec['etype']; + $eread = $vread = null; + if (isset(TBase::$tmethod[$etype])) { + $eread = 'read'.TBase::$tmethod[$etype]; + } else { + $espec = $spec['elem']; + } + $var = array(); + $_etype = $size = 0; + if ($set) { + $xfer += $input->readSetBegin($_etype, $size); + } else { + $xfer += $input->readListBegin($_etype, $size); + } + for ($i = 0; $i < $size; ++$i) { + $elem = null; + if ($eread !== null) { + $xfer += $input->$eread($elem); + } else { + $espec = $spec['elem']; + switch ($etype) { + case TType::STRUCT: + $class = $espec['class']; + $elem = new $class(); + $xfer += $elem->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($elem, $espec, $input); + break; + case TType::LST: + $xfer += $this->_readList($elem, $espec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($elem, $espec, $input, true); + break; + } + } + if ($set) { + $var[$elem] = true; + } else { + $var []= $elem; + } + } + if ($set) { + $xfer += $input->readSetEnd(); + } else { + $xfer += $input->readListEnd(); + } + return $xfer; + } + + protected function _read($class, $spec, $input) { + $xfer = 0; + $fname = null; + $ftype = 0; + $fid = 0; + $xfer += $input->readStructBegin($fname); + while (true) { + $xfer += $input->readFieldBegin($fname, $ftype, $fid); + if ($ftype == TType::STOP) { + break; + } + if (isset($spec[$fid])) { + $fspec = $spec[$fid]; + $var = $fspec['var']; + if ($ftype == $fspec['type']) { + $xfer = 0; + if (isset(TBase::$tmethod[$ftype])) { + $func = 'read'.TBase::$tmethod[$ftype]; + $xfer += $input->$func($this->$var); + } else { + switch ($ftype) { + case TType::STRUCT: + $class = $fspec['class']; + $this->$var = new $class(); + $xfer += $this->$var->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($this->$var, $fspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($this->$var, $fspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($this->$var, $fspec, $input, true); + break; + } + } + } else { + $xfer += $input->skip($ftype); + } + } else { + $xfer += $input->skip($ftype); + } + $xfer += $input->readFieldEnd(); + } + $xfer += $input->readStructEnd(); + return $xfer; + } + + private function _writeMap($var, $spec, $output) { + $xfer = 0; + $ktype = $spec['ktype']; + $vtype = $spec['vtype']; + $kwrite = $vwrite = null; + if (isset(TBase::$tmethod[$ktype])) { + $kwrite = 'write'.TBase::$tmethod[$ktype]; + } else { + $kspec = $spec['key']; + } + if (isset(TBase::$tmethod[$vtype])) { + $vwrite = 'write'.TBase::$tmethod[$vtype]; + } else { + $vspec = $spec['val']; + } + $xfer += $output->writeMapBegin($ktype, $vtype, count($var)); + foreach ($var as $key => $val) { + if (isset($kwrite)) { + $xfer += $output->$kwrite($key); + } else { + switch ($ktype) { + case TType::STRUCT: + $xfer += $key->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($key, $kspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($key, $kspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($key, $kspec, $output, true); + break; + } + } + if (isset($vwrite)) { + $xfer += $output->$vwrite($val); + } else { + switch ($vtype) { + case TType::STRUCT: + $xfer += $val->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($val, $vspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($val, $vspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($val, $vspec, $output, true); + break; + } + } + } + $xfer += $output->writeMapEnd(); + return $xfer; + } + + private function _writeList($var, $spec, $output, $set=false) { + $xfer = 0; + $etype = $spec['etype']; + $ewrite = null; + if (isset(TBase::$tmethod[$etype])) { + $ewrite = 'write'.TBase::$tmethod[$etype]; + } else { + $espec = $spec['elem']; + } + if ($set) { + $xfer += $output->writeSetBegin($etype, count($var)); + } else { + $xfer += $output->writeListBegin($etype, count($var)); + } + foreach ($var as $key => $val) { + $elem = $set ? $key : $val; + if (isset($ewrite)) { + $xfer += $output->$ewrite($elem); + } else { + switch ($etype) { + case TType::STRUCT: + $xfer += $elem->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($elem, $espec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($elem, $espec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($elem, $espec, $output, true); + break; + } + } + } + if ($set) { + $xfer += $output->writeSetEnd(); + } else { + $xfer += $output->writeListEnd(); + } + return $xfer; + } + + protected function _write($class, $spec, $output) { + $xfer = 0; + $xfer += $output->writeStructBegin($class); + foreach ($spec as $fid => $fspec) { + $var = $fspec['var']; + if ($this->$var !== null) { + $ftype = $fspec['type']; + $xfer += $output->writeFieldBegin($var, $ftype, $fid); + if (isset(TBase::$tmethod[$ftype])) { + $func = 'write'.TBase::$tmethod[$ftype]; + $xfer += $output->$func($this->$var); + } else { + switch ($ftype) { + case TType::STRUCT: + $xfer += $this->$var->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($this->$var, $fspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($this->$var, $fspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($this->$var, $fspec, $output, true); + break; + } + } + $xfer += $output->writeFieldEnd(); + } + } + $xfer += $output->writeFieldStop(); + $xfer += $output->writeStructEnd(); + return $xfer; + } +} diff --git a/lib/php/lib/Thrift/ClassLoader/ThriftClassLoader.php b/lib/php/lib/Thrift/ClassLoader/ThriftClassLoader.php new file mode 100644 index 00000000..bce93f5d --- /dev/null +++ b/lib/php/lib/Thrift/ClassLoader/ThriftClassLoader.php @@ -0,0 +1,223 @@ +apc = $apc; + $this->apc_prefix = $apc_prefix; + } + + /** + * Registers a namespace. + * + * @param string $namespace The namespace + * @param array|string $paths The location(s) of the namespace + */ + public function registerNamespace($namespace, $paths) + { + $this->namespaces[$namespace] = (array) $paths; + } + + /** + * Registers a Thrift definition namespace. + * + * @param string $namespace The definition namespace + * @param array|string $paths The location(s) of the definition namespace + */ + public function registerDefinition($namespace, $paths) + { + $this->definitions[$namespace] = (array) $paths; + } + + /** + * Registers this instance as an autoloader. + * + * @param Boolean $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Loads the given class, definition or interface. + * + * @param string $class The name of the class + */ + public function loadClass($class) + { + if ( + (true === $this->apc && ($file = $this->findFileInApc($class))) or + ($file = $this->findFile($class)) + ) + { + require_once $file; + } + } + + /** + * Loads the given class or interface in APC. + * @param string $class The name of the class + * @return string + */ + protected function findFileInApc($class) + { + if (false === $file = apc_fetch($this->apc_prefix.$class)) { + apc_store($this->apc_prefix.$class, $file = $this->findFile($class)); + } + + return $file; + } + + /** + * Find class in namespaces or definitions directories + * @param string $class + * @return string + */ + public function findFile($class) + { + // Remove first backslash + if ('\\' == $class[0]) + { + $class = substr($class, 1); + } + + if (false !== $pos = strrpos($class, '\\')) + { + // Namespaced class name + $namespace = substr($class, 0, $pos); + + // Iterate in normal namespaces + foreach ($this->namespaces as $ns => $dirs) + { + //Don't interfere with other autoloaders + if (0 !== strpos($namespace, $ns)) + { + continue; + } + + foreach ($dirs as $dir) + { + $className = substr($class, $pos + 1); + + $file = $dir.DIRECTORY_SEPARATOR. + str_replace('\\', DIRECTORY_SEPARATOR, $namespace). + DIRECTORY_SEPARATOR. + $className.'.php'; + + if (file_exists($file)) + { + return $file; + } + } + } + + // Iterate in Thrift namespaces + + // Remove first part of namespace + $m = explode('\\', $class); + + // Ignore wrong call + if(count($m) <= 1) + { + return; + } + + $class = array_pop($m); + $namespace = implode('\\', $m); + + foreach ($this->definitions as $ns => $dirs) + { + //Don't interfere with other autoloaders + if (0 !== strpos($namespace, $ns)) + { + continue; + } + + foreach ($dirs as $dir) + { + /** + * Available in service: Interface, Client, Processor, Rest + * And every service methods (_.+) + */ + if( + 0 === preg_match('#(.+)(if|client|processor|rest)$#i', $class, $n) and + 0 === preg_match('#(.+)_[a-z0-9]+_(args|result)$#i', $class, $n) + ) + { + $className = 'Types'; + } + else + { + $className = $n[1]; + } + + $file = $dir.DIRECTORY_SEPARATOR . + str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . + DIRECTORY_SEPARATOR . + $className . '.php'; + + if (file_exists($file)) + { + return $file; + } + } + } + } + } +} diff --git a/lib/php/lib/Thrift/Exception/TApplicationException.php b/lib/php/lib/Thrift/Exception/TApplicationException.php new file mode 100644 index 00000000..55d46e60 --- /dev/null +++ b/lib/php/lib/Thrift/Exception/TApplicationException.php @@ -0,0 +1,69 @@ + array('var' => 'message', + 'type' => TType::STRING), + 2 => array('var' => 'code', + 'type' => TType::I32)); + + const UNKNOWN = 0; + const UNKNOWN_METHOD = 1; + const INVALID_MESSAGE_TYPE = 2; + const WRONG_METHOD_NAME = 3; + const BAD_SEQUENCE_ID = 4; + const MISSING_RESULT = 5; + const INTERNAL_ERROR = 6; + const PROTOCOL_ERROR = 7; + + function __construct($message=null, $code=0) { + parent::__construct($message, $code); + } + + public function read($output) { + return $this->_read('TApplicationException', self::$_TSPEC, $output); + } + + public function write($output) { + $xfer = 0; + $xfer += $output->writeStructBegin('TApplicationException'); + if ($message = $this->getMessage()) { + $xfer += $output->writeFieldBegin('message', TType::STRING, 1); + $xfer += $output->writeString($message); + $xfer += $output->writeFieldEnd(); + } + if ($code = $this->getCode()) { + $xfer += $output->writeFieldBegin('type', TType::I32, 2); + $xfer += $output->writeI32($code); + $xfer += $output->writeFieldEnd(); + } + $xfer += $output->writeFieldStop(); + $xfer += $output->writeStructEnd(); + return $xfer; + } +} diff --git a/lib/php/lib/Thrift/Exception/TException.php b/lib/php/lib/Thrift/Exception/TException.php new file mode 100644 index 00000000..8e8cd28d --- /dev/null +++ b/lib/php/lib/Thrift/Exception/TException.php @@ -0,0 +1,369 @@ + $fspec) { + $var = $fspec['var']; + if (isset($vals[$var])) { + $this->$var = $vals[$var]; + } + } + } else { + parent::__construct($p1, $p2); + } + } + + static $tmethod = array(TType::BOOL => 'Bool', + TType::BYTE => 'Byte', + TType::I16 => 'I16', + TType::I32 => 'I32', + TType::I64 => 'I64', + TType::DOUBLE => 'Double', + TType::STRING => 'String'); + + private function _readMap(&$var, $spec, $input) { + $xfer = 0; + $ktype = $spec['ktype']; + $vtype = $spec['vtype']; + $kread = $vread = null; + if (isset(TBase::$tmethod[$ktype])) { + $kread = 'read'.TBase::$tmethod[$ktype]; + } else { + $kspec = $spec['key']; + } + if (isset(TBase::$tmethod[$vtype])) { + $vread = 'read'.TBase::$tmethod[$vtype]; + } else { + $vspec = $spec['val']; + } + $var = array(); + $_ktype = $_vtype = $size = 0; + $xfer += $input->readMapBegin($_ktype, $_vtype, $size); + for ($i = 0; $i < $size; ++$i) { + $key = $val = null; + if ($kread !== null) { + $xfer += $input->$kread($key); + } else { + switch ($ktype) { + case TType::STRUCT: + $class = $kspec['class']; + $key = new $class(); + $xfer += $key->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($key, $kspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($key, $kspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($key, $kspec, $input, true); + break; + } + } + if ($vread !== null) { + $xfer += $input->$vread($val); + } else { + switch ($vtype) { + case TType::STRUCT: + $class = $vspec['class']; + $val = new $class(); + $xfer += $val->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($val, $vspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($val, $vspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($val, $vspec, $input, true); + break; + } + } + $var[$key] = $val; + } + $xfer += $input->readMapEnd(); + return $xfer; + } + + private function _readList(&$var, $spec, $input, $set=false) { + $xfer = 0; + $etype = $spec['etype']; + $eread = $vread = null; + if (isset(TBase::$tmethod[$etype])) { + $eread = 'read'.TBase::$tmethod[$etype]; + } else { + $espec = $spec['elem']; + } + $var = array(); + $_etype = $size = 0; + if ($set) { + $xfer += $input->readSetBegin($_etype, $size); + } else { + $xfer += $input->readListBegin($_etype, $size); + } + for ($i = 0; $i < $size; ++$i) { + $elem = null; + if ($eread !== null) { + $xfer += $input->$eread($elem); + } else { + $espec = $spec['elem']; + switch ($etype) { + case TType::STRUCT: + $class = $espec['class']; + $elem = new $class(); + $xfer += $elem->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($elem, $espec, $input); + break; + case TType::LST: + $xfer += $this->_readList($elem, $espec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($elem, $espec, $input, true); + break; + } + } + if ($set) { + $var[$elem] = true; + } else { + $var []= $elem; + } + } + if ($set) { + $xfer += $input->readSetEnd(); + } else { + $xfer += $input->readListEnd(); + } + return $xfer; + } + + protected function _read($class, $spec, $input) { + $xfer = 0; + $fname = null; + $ftype = 0; + $fid = 0; + $xfer += $input->readStructBegin($fname); + while (true) { + $xfer += $input->readFieldBegin($fname, $ftype, $fid); + if ($ftype == TType::STOP) { + break; + } + if (isset($spec[$fid])) { + $fspec = $spec[$fid]; + $var = $fspec['var']; + if ($ftype == $fspec['type']) { + $xfer = 0; + if (isset(TBase::$tmethod[$ftype])) { + $func = 'read'.TBase::$tmethod[$ftype]; + $xfer += $input->$func($this->$var); + } else { + switch ($ftype) { + case TType::STRUCT: + $class = $fspec['class']; + $this->$var = new $class(); + $xfer += $this->$var->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($this->$var, $fspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($this->$var, $fspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($this->$var, $fspec, $input, true); + break; + } + } + } else { + $xfer += $input->skip($ftype); + } + } else { + $xfer += $input->skip($ftype); + } + $xfer += $input->readFieldEnd(); + } + $xfer += $input->readStructEnd(); + return $xfer; + } + + private function _writeMap($var, $spec, $output) { + $xfer = 0; + $ktype = $spec['ktype']; + $vtype = $spec['vtype']; + $kwrite = $vwrite = null; + if (isset(TBase::$tmethod[$ktype])) { + $kwrite = 'write'.TBase::$tmethod[$ktype]; + } else { + $kspec = $spec['key']; + } + if (isset(TBase::$tmethod[$vtype])) { + $vwrite = 'write'.TBase::$tmethod[$vtype]; + } else { + $vspec = $spec['val']; + } + $xfer += $output->writeMapBegin($ktype, $vtype, count($var)); + foreach ($var as $key => $val) { + if (isset($kwrite)) { + $xfer += $output->$kwrite($key); + } else { + switch ($ktype) { + case TType::STRUCT: + $xfer += $key->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($key, $kspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($key, $kspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($key, $kspec, $output, true); + break; + } + } + if (isset($vwrite)) { + $xfer += $output->$vwrite($val); + } else { + switch ($vtype) { + case TType::STRUCT: + $xfer += $val->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($val, $vspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($val, $vspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($val, $vspec, $output, true); + break; + } + } + } + $xfer += $output->writeMapEnd(); + return $xfer; + } + + private function _writeList($var, $spec, $output, $set=false) { + $xfer = 0; + $etype = $spec['etype']; + $ewrite = null; + if (isset(TBase::$tmethod[$etype])) { + $ewrite = 'write'.TBase::$tmethod[$etype]; + } else { + $espec = $spec['elem']; + } + if ($set) { + $xfer += $output->writeSetBegin($etype, count($var)); + } else { + $xfer += $output->writeListBegin($etype, count($var)); + } + foreach ($var as $key => $val) { + $elem = $set ? $key : $val; + if (isset($ewrite)) { + $xfer += $output->$ewrite($elem); + } else { + switch ($etype) { + case TType::STRUCT: + $xfer += $elem->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($elem, $espec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($elem, $espec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($elem, $espec, $output, true); + break; + } + } + } + if ($set) { + $xfer += $output->writeSetEnd(); + } else { + $xfer += $output->writeListEnd(); + } + return $xfer; + } + + protected function _write($class, $spec, $output) { + $xfer = 0; + $xfer += $output->writeStructBegin($class); + foreach ($spec as $fid => $fspec) { + $var = $fspec['var']; + if ($this->$var !== null) { + $ftype = $fspec['type']; + $xfer += $output->writeFieldBegin($var, $ftype, $fid); + if (isset(TBase::$tmethod[$ftype])) { + $func = 'write'.TBase::$tmethod[$ftype]; + $xfer += $output->$func($this->$var); + } else { + switch ($ftype) { + case TType::STRUCT: + $xfer += $this->$var->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($this->$var, $fspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($this->$var, $fspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($this->$var, $fspec, $output, true); + break; + } + } + $xfer += $output->writeFieldEnd(); + } + } + $xfer += $output->writeFieldStop(); + $xfer += $output->writeStructEnd(); + return $xfer; + } + +} diff --git a/lib/php/lib/Thrift/Exception/TProtocolException.php b/lib/php/lib/Thrift/Exception/TProtocolException.php new file mode 100644 index 00000000..98a8d9d4 --- /dev/null +++ b/lib/php/lib/Thrift/Exception/TProtocolException.php @@ -0,0 +1,48 @@ +strictRead_ = $strictRead; + $this->strictWrite_ = $strictWrite; + } + + public function getProtocol($trans) { + return new TBinaryProtocol($trans, $this->strictRead_, $this->strictWrite_); + } +} \ No newline at end of file diff --git a/lib/php/lib/Thrift/Factory/TCompactProtocolFactory.php b/lib/php/lib/Thrift/Factory/TCompactProtocolFactory.php new file mode 100644 index 00000000..9f972aab --- /dev/null +++ b/lib/php/lib/Thrift/Factory/TCompactProtocolFactory.php @@ -0,0 +1,39 @@ +p_ = $p; + } + + public function write() { + if ($this->first_) { + $this->first_ = false; + } else { + $this->p_->getTransport()->write(TJSONProtocol::COMMA); + } + } + + public function read() { + if ($this->first_) { + $this->first_ = false; + } else { + $this->p_->readJSONSyntaxChar(TJSONProtocol::COMMA); + } + } +} \ No newline at end of file diff --git a/lib/php/lib/Thrift/Protocol/JSON/LookaheadReader.php b/lib/php/lib/Thrift/Protocol/JSON/LookaheadReader.php new file mode 100644 index 00000000..128b5fcc --- /dev/null +++ b/lib/php/lib/Thrift/Protocol/JSON/LookaheadReader.php @@ -0,0 +1,54 @@ +p_ = $p; + } + + public function read() { + if ($this->hasData_) { + $this->hasData_ = false; + } else { + $this->data_ = $this->p_->getTransport()->readAll(1); + } + + return substr($this->data_, 0, 1); + } + + public function peek() { + if (!$this->hasData_) { + $this->data_ = $this->p_->getTransport()->readAll(1); + } + + $this->hasData_ = true; + return substr($this->data_, 0, 1); + } +} \ No newline at end of file diff --git a/lib/php/lib/Thrift/Protocol/JSON/PairContext.php b/lib/php/lib/Thrift/Protocol/JSON/PairContext.php new file mode 100644 index 00000000..1c87dd37 --- /dev/null +++ b/lib/php/lib/Thrift/Protocol/JSON/PairContext.php @@ -0,0 +1,60 @@ +p_ = $p; + } + + public function write() { + if ($this->first_) { + $this->first_ = false; + $this->colon_ = true; + } else { + $this->p_->getTransport()->write($this->colon_ ? TJSONProtocol::COLON : TJSONProtocol::COMMA); + $this->colon_ = !$this->colon_; + } + } + + public function read() { + if ($this->first_) { + $this->first_ = false; + $this->colon_ = true; + } else { + $this->p_->readJSONSyntaxChar($this->colon_ ? TJSONProtocol::COLON : TJSONProtocol::COMMA); + $this->colon_ = !$this->colon_; + } + } + + public function escapeNum() { + return $this->colon_; + } +} \ No newline at end of file diff --git a/lib/php/lib/Thrift/Protocol/TBinaryProtocol.php b/lib/php/lib/Thrift/Protocol/TBinaryProtocol.php new file mode 100644 index 00000000..b1fddac1 --- /dev/null +++ b/lib/php/lib/Thrift/Protocol/TBinaryProtocol.php @@ -0,0 +1,396 @@ +strictRead_ = $strictRead; + $this->strictWrite_ = $strictWrite; + } + + public function writeMessageBegin($name, $type, $seqid) { + if ($this->strictWrite_) { + $version = self::VERSION_1 | $type; + return + $this->writeI32($version) + + $this->writeString($name) + + $this->writeI32($seqid); + } else { + return + $this->writeString($name) + + $this->writeByte($type) + + $this->writeI32($seqid); + } + } + + public function writeMessageEnd() { + return 0; + } + + public function writeStructBegin($name) { + return 0; + } + + public function writeStructEnd() { + return 0; + } + + public function writeFieldBegin($fieldName, $fieldType, $fieldId) { + return + $this->writeByte($fieldType) + + $this->writeI16($fieldId); + } + + public function writeFieldEnd() { + return 0; + } + + public function writeFieldStop() { + return + $this->writeByte(TType::STOP); + } + + public function writeMapBegin($keyType, $valType, $size) { + return + $this->writeByte($keyType) + + $this->writeByte($valType) + + $this->writeI32($size); + } + + public function writeMapEnd() { + return 0; + } + + public function writeListBegin($elemType, $size) { + return + $this->writeByte($elemType) + + $this->writeI32($size); + } + + public function writeListEnd() { + return 0; + } + + public function writeSetBegin($elemType, $size) { + return + $this->writeByte($elemType) + + $this->writeI32($size); + } + + public function writeSetEnd() { + return 0; + } + + public function writeBool($value) { + $data = pack('c', $value ? 1 : 0); + $this->trans_->write($data, 1); + return 1; + } + + public function writeByte($value) { + $data = pack('c', $value); + $this->trans_->write($data, 1); + return 1; + } + + public function writeI16($value) { + $data = pack('n', $value); + $this->trans_->write($data, 2); + return 2; + } + + public function writeI32($value) { + $data = pack('N', $value); + $this->trans_->write($data, 4); + return 4; + } + + public function writeI64($value) { + // If we are on a 32bit architecture we have to explicitly deal with + // 64-bit twos-complement arithmetic since PHP wants to treat all ints + // as signed and any int over 2^31 - 1 as a float + if (PHP_INT_SIZE == 4) { + $neg = $value < 0; + + if ($neg) { + $value *= -1; + } + + $hi = (int)($value / 4294967296); + $lo = (int)$value; + + if ($neg) { + $hi = ~$hi; + $lo = ~$lo; + if (($lo & (int)0xffffffff) == (int)0xffffffff) { + $lo = 0; + $hi++; + } else { + $lo++; + } + } + $data = pack('N2', $hi, $lo); + + } else { + $hi = $value >> 32; + $lo = $value & 0xFFFFFFFF; + $data = pack('N2', $hi, $lo); + } + + $this->trans_->write($data, 8); + return 8; + } + + public function writeDouble($value) { + $data = pack('d', $value); + $this->trans_->write(strrev($data), 8); + return 8; + } + + public function writeString($value) { + $len = TStringFuncFactory::create()->strlen($value); + $result = $this->writeI32($len); + if ($len) { + $this->trans_->write($value, $len); + } + return $result + $len; + } + + public function readMessageBegin(&$name, &$type, &$seqid) { + $result = $this->readI32($sz); + if ($sz < 0) { + $version = (int) ($sz & self::VERSION_MASK); + if ($version != (int) self::VERSION_1) { + throw new TProtocolException('Bad version identifier: '.$sz, TProtocolException::BAD_VERSION); + } + $type = $sz & 0x000000ff; + $result += + $this->readString($name) + + $this->readI32($seqid); + } else { + if ($this->strictRead_) { + throw new TProtocolException('No version identifier, old protocol client?', TProtocolException::BAD_VERSION); + } else { + // Handle pre-versioned input + $name = $this->trans_->readAll($sz); + $result += + $sz + + $this->readByte($type) + + $this->readI32($seqid); + } + } + return $result; + } + + public function readMessageEnd() { + return 0; + } + + public function readStructBegin(&$name) { + $name = ''; + return 0; + } + + public function readStructEnd() { + return 0; + } + + public function readFieldBegin(&$name, &$fieldType, &$fieldId) { + $result = $this->readByte($fieldType); + if ($fieldType == TType::STOP) { + $fieldId = 0; + return $result; + } + $result += $this->readI16($fieldId); + return $result; + } + + public function readFieldEnd() { + return 0; + } + + public function readMapBegin(&$keyType, &$valType, &$size) { + return + $this->readByte($keyType) + + $this->readByte($valType) + + $this->readI32($size); + } + + public function readMapEnd() { + return 0; + } + + public function readListBegin(&$elemType, &$size) { + return + $this->readByte($elemType) + + $this->readI32($size); + } + + public function readListEnd() { + return 0; + } + + public function readSetBegin(&$elemType, &$size) { + return + $this->readByte($elemType) + + $this->readI32($size); + } + + public function readSetEnd() { + return 0; + } + + public function readBool(&$value) { + $data = $this->trans_->readAll(1); + $arr = unpack('c', $data); + $value = $arr[1] == 1; + return 1; + } + + public function readByte(&$value) { + $data = $this->trans_->readAll(1); + $arr = unpack('c', $data); + $value = $arr[1]; + return 1; + } + + public function readI16(&$value) { + $data = $this->trans_->readAll(2); + $arr = unpack('n', $data); + $value = $arr[1]; + if ($value > 0x7fff) { + $value = 0 - (($value - 1) ^ 0xffff); + } + return 2; + } + + public function readI32(&$value) { + $data = $this->trans_->readAll(4); + $arr = unpack('N', $data); + $value = $arr[1]; + if ($value > 0x7fffffff) { + $value = 0 - (($value - 1) ^ 0xffffffff); + } + return 4; + } + + public function readI64(&$value) { + $data = $this->trans_->readAll(8); + + $arr = unpack('N2', $data); + + // If we are on a 32bit architecture we have to explicitly deal with + // 64-bit twos-complement arithmetic since PHP wants to treat all ints + // as signed and any int over 2^31 - 1 as a float + if (PHP_INT_SIZE == 4) { + + $hi = $arr[1]; + $lo = $arr[2]; + $isNeg = $hi < 0; + + // Check for a negative + if ($isNeg) { + $hi = ~$hi & (int)0xffffffff; + $lo = ~$lo & (int)0xffffffff; + + if ($lo == (int)0xffffffff) { + $hi++; + $lo = 0; + } else { + $lo++; + } + } + + // Force 32bit words in excess of 2G to pe positive - we deal wigh sign + // explicitly below + + if ($hi & (int)0x80000000) { + $hi &= (int)0x7fffffff; + $hi += 0x80000000; + } + + if ($lo & (int)0x80000000) { + $lo &= (int)0x7fffffff; + $lo += 0x80000000; + } + + $value = $hi * 4294967296 + $lo; + + if ($isNeg) { + $value = 0 - $value; + } + } else { + + // Upcast negatives in LSB bit + if ($arr[2] & 0x80000000) { + $arr[2] = $arr[2] & 0xffffffff; + } + + // Check for a negative + if ($arr[1] & 0x80000000) { + $arr[1] = $arr[1] & 0xffffffff; + $arr[1] = $arr[1] ^ 0xffffffff; + $arr[2] = $arr[2] ^ 0xffffffff; + $value = 0 - $arr[1]*4294967296 - $arr[2] - 1; + } else { + $value = $arr[1]*4294967296 + $arr[2]; + } + } + + return 8; + } + + public function readDouble(&$value) { + $data = strrev($this->trans_->readAll(8)); + $arr = unpack('d', $data); + $value = $arr[1]; + return 8; + } + + public function readString(&$value) { + $result = $this->readI32($len); + if ($len) { + $value = $this->trans_->readAll($len); + } else { + $value = ''; + } + return $result + $len; + } +} diff --git a/lib/php/lib/Thrift/Protocol/TBinaryProtocolAccelerated.php b/lib/php/lib/Thrift/Protocol/TBinaryProtocolAccelerated.php new file mode 100644 index 00000000..392aa21f --- /dev/null +++ b/lib/php/lib/Thrift/Protocol/TBinaryProtocolAccelerated.php @@ -0,0 +1,47 @@ +strictRead_; + } + public function isStrictWrite() { + return $this->strictWrite_; + } +} \ No newline at end of file diff --git a/lib/php/lib/Thrift/Protocol/TCompactProtocol.php b/lib/php/lib/Thrift/Protocol/TCompactProtocol.php new file mode 100644 index 00000000..e637a59b --- /dev/null +++ b/lib/php/lib/Thrift/Protocol/TCompactProtocol.php @@ -0,0 +1,669 @@ + TCompactProtocol::COMPACT_STOP, + TType::BOOL => TCompactProtocol::COMPACT_TRUE, // used for collection + TType::BYTE => TCompactProtocol::COMPACT_BYTE, + TType::I16 => TCompactProtocol::COMPACT_I16, + TType::I32 => TCompactProtocol::COMPACT_I32, + TType::I64 => TCompactProtocol::COMPACT_I64, + TType::DOUBLE => TCompactProtocol::COMPACT_DOUBLE, + TType::STRING => TCompactProtocol::COMPACT_BINARY, + TType::STRUCT => TCompactProtocol::COMPACT_STRUCT, + TType::LST => TCompactProtocol::COMPACT_LIST, + TType::SET => TCompactProtocol::COMPACT_SET, + TType::MAP => TCompactProtocol::COMPACT_MAP, + ); + + protected static $ttypes = array( + TCompactProtocol::COMPACT_STOP => TType::STOP , + TCompactProtocol::COMPACT_TRUE => TType::BOOL, // used for collection + TCompactProtocol::COMPACT_FALSE => TType::BOOL, + TCompactProtocol::COMPACT_BYTE => TType::BYTE, + TCompactProtocol::COMPACT_I16 => TType::I16, + TCompactProtocol::COMPACT_I32 => TType::I32, + TCompactProtocol::COMPACT_I64 => TType::I64, + TCompactProtocol::COMPACT_DOUBLE => TType::DOUBLE, + TCompactProtocol::COMPACT_BINARY => TType::STRING, + TCompactProtocol::COMPACT_STRUCT => TType::STRUCT, + TCompactProtocol::COMPACT_LIST => TType::LST, + TCompactProtocol::COMPACT_SET => TType::SET, + TCompactProtocol::COMPACT_MAP => TType::MAP, + ); + + protected $state = TCompactProtocol::STATE_CLEAR; + protected $lastFid = 0; + protected $boolFid = null; + protected $boolValue = null; + protected $structs = array(); + protected $containers = array(); + + // Some varint / zigzag helper methods + public function toZigZag($n, $bits) { + return ($n << 1) ^ ($n >> ($bits - 1)); + } + + public function fromZigZag($n) { + return ($n >> 1) ^ -($n & 1); + } + + public function getVarint($data) { + $out = ""; + while (true) { + if (($data & ~0x7f) === 0) { + $out .= chr($data); + break; + } else { + $out .= chr(($data & 0xff) | 0x80); + $data = $data >> 7; + } + } + return $out; + } + + public function writeVarint($data) { + $out = $this->getVarint($data); + $result = TStringFuncFactory::create()->strlen($out); + $this->trans_->write($out, $result); + return $result; + } + + public function readVarint(&$result) { + $idx = 0; + $shift = 0; + $result = 0; + while (true) { + $x = $this->trans_->readAll(1); + $arr = unpack('C', $x); + $byte = $arr[1]; + $idx += 1; + $result |= ($byte & 0x7f) << $shift; + if (($byte >> 7) === 0) { + return $idx; + } + $shift += 7; + } + + return $idx; + } + + public function __construct($trans) { + parent::__construct($trans); + } + + public function writeMessageBegin($name, $type, $seqid) { + $written = + $this->writeUByte(TCompactProtocol::PROTOCOL_ID) + + $this->writeUByte(TCompactProtocol::VERSION | + ($type << TCompactProtocol::TYPE_SHIFT_AMOUNT)) + + $this->writeVarint($seqid) + + $this->writeString($name); + $this->state = TCompactProtocol::STATE_VALUE_WRITE; + return $written; + } + + public function writeMessageEnd() { + $this->state = TCompactProtocol::STATE_CLEAR; + return 0; + } + + public function writeStructBegin($name) { + $this->structs[] = array($this->state, $this->lastFid); + $this->state = TCompactProtocol::STATE_FIELD_WRITE; + $this->lastFid = 0; + return 0; + } + + public function writeStructEnd() { + $old_values = array_pop($this->structs); + $this->state = $old_values[0]; + $this->lastFid = $old_values[1]; + return 0; + } + + public function writeFieldStop() { + return $this->writeByte(0); + } + + public function writeFieldHeader($type, $fid) { + $written = 0; + $delta = $fid - $this->lastFid; + if (0 < $delta && $delta <= 15) { + $written = $this->writeUByte(($delta << 4) | $type); + } else { + $written = $this->writeByte($type) + + $this->writeI16($fid); + } + $this->lastFid = $fid; + return $written; + } + + public function writeFieldBegin($field_name, $field_type, $field_id) { + if ($field_type == TTYPE::BOOL) { + $this->state = TCompactProtocol::STATE_BOOL_WRITE; + $this->boolFid = $field_id; + return 0; + } else { + $this->state = TCompactProtocol::STATE_VALUE_WRITE; + return $this->writeFieldHeader(self::$ctypes[$field_type], $field_id); + } + } + + public function writeFieldEnd() { + $this->state = TCompactProtocol::STATE_FIELD_WRITE; + return 0; + } + + public function writeCollectionBegin($etype, $size) { + $written = 0; + if ($size <= 14) { + $written = $this->writeUByte($size << 4 | + self::$ctypes[$etype]); + } else { + $written = $this->writeUByte(0xf0 | + self::$ctypes[$etype]) + + $this->writeVarint($size); + } + $this->containers[] = $this->state; + $this->state = TCompactProtocol::STATE_CONTAINER_WRITE; + + return $written; + } + + public function writeMapBegin($key_type, $val_type, $size) { + $written = 0; + if ($size == 0) { + $written = $this->writeByte(0); + } else { + $written = $this->writeVarint($size) + + $this->writeUByte(self::$ctypes[$key_type] << 4 | + self::$ctypes[$val_type]); + } + $this->containers[] = $this->state; + return $written; + } + + public function writeCollectionEnd() { + $this->state = array_pop($this->containers); + return 0; + } + + public function writeMapEnd() { + return $this->writeCollectionEnd(); + } + + public function writeListBegin($elem_type, $size) { + return $this->writeCollectionBegin($elem_type, $size); + } + + public function writeListEnd() { + return $this->writeCollectionEnd(); + } + + public function writeSetBegin($elem_type, $size) { + return $this->writeCollectionBegin($elem_type, $size); + } + + public function writeSetEnd() { + return $this->writeCollectionEnd(); + } + + public function writeBool($value) { + if ($this->state == TCompactProtocol::STATE_BOOL_WRITE) { + $ctype = TCompactProtocol::COMPACT_FALSE; + if ($value) { + $ctype = TCompactProtocol::COMPACT_TRUE; + } + return $this->writeFieldHeader($ctype, $this->boolFid); + } else if ($this->state == TCompactProtocol::STATE_CONTAINER_WRITE) { + return $this->writeByte($value ? 1 : 0); + } else { + throw new TProtocolException('Invalid state in compact protocol'); + } + } + + public function writeByte($value) { + $data = pack('c', $value); + $this->trans_->write($data, 1); + return 1; + } + + public function writeUByte($byte) { + $this->trans_->write(pack('C', $byte), 1); + return 1; + } + + public function writeI16($value) { + $thing = $this->toZigZag($value, 16); + return $this->writeVarint($thing); + } + + public function writeI32($value) { + $thing = $this->toZigZag($value, 32); + return $this->writeVarint($thing); + } + + public function writeDouble($value) { + $data = pack('d', $value); + $this->trans_->write(strrev($data), 8); + return 8; + } + + public function writeString($value) { + $len = TStringFuncFactory::create()->strlen($value); + $result = $this->writeVarint($len); + if ($len) { + $this->trans_->write($value, $len); + } + return $result + $len; + } + + public function readFieldBegin(&$name, &$field_type, &$field_id) { + $result = $this->readUByte($field_type); + + if (($field_type & 0x0f) == TType::STOP) { + $field_id = 0; + return $result; + } + $delta = $field_type >> 4; + if ($delta == 0) { + $result += $this->readI16($field_id); + } else { + $field_id = $this->lastFid + $delta; + } + $this->lastFid = $field_id; + $field_type = $this->getTType($field_type & 0x0f); + if ($field_type == TCompactProtocol::COMPACT_TRUE) { + $this->state = TCompactProtocol::STATE_BOOL_READ; + $this->boolValue = true; + } else if ($field_type == TCompactProtocol::COMPACT_FALSE) { + $this->state = TCompactProtocol::STATE_BOOL_READ; + $this->boolValue = false; + } else { + $this->state = TCompactProtocol::STATE_VALUE_READ; + } + return $result; + } + + public function readFieldEnd() { + $this->state = TCompactProtocol::STATE_FIELD_READ; + return 0; + } + + public function readUByte(&$value) { + $data = $this->trans_->readAll(1); + $arr = unpack('C', $data); + $value = $arr[1]; + return 1; + } + + public function readByte(&$value) { + $data = $this->trans_->readAll(1); + $arr = unpack('c', $data); + $value = $arr[1]; + return 1; + } + + public function readZigZag(&$value) { + $result = $this->readVarint($value); + $value = $this->fromZigZag($value); + return $result; + } + + public function readMessageBegin(&$name, &$type, &$seqid) { + $protoId = 0; + $result = $this->readUByte($protoId); + if ($protoId != TCompactProtocol::PROTOCOL_ID) { + throw new TProtocolException('Bad protocol id in TCompact message'); + } + $verType = 0; + $result += $this->readUByte($verType); + $type = ($verType & TCompactProtocol::TYPE_MASK) >> + TCompactProtocol::TYPE_SHIFT_AMOUNT; + $version = $verType & TCompactProtocol::VERSION_MASK; + if ($version != TCompactProtocol::VERSION) { + throw new TProtocolException('Bad version in TCompact message'); + } + $result += $this->readVarint($seqId); + $name += $this->readString($name); + + return $result; + } + + public function readMessageEnd() { + return 0; + } + + public function readStructBegin(&$name) { + $name = ''; // unused + $this->structs[] = array($this->state, $this->lastFid); + $this->state = TCompactProtocol::STATE_FIELD_READ; + $this->lastFid = 0; + return 0; + } + + public function readStructEnd() { + $last = array_pop($this->structs); + $this->state = $last[0]; + $this->lastFid = $last[1]; + return 0; + } + + public function readCollectionBegin(&$type, &$size) { + $sizeType = 0; + $result = $this->readUByte($sizeType); + $size = $sizeType >> 4; + $type = $this->getTType($sizeType); + if ($size == 15) { + $result += $this->readVarint($size); + } + $this->containers[] = $this->state; + $this->state = TCompactProtocol::STATE_CONTAINER_READ; + + return $result; + } + + public function readMapBegin(&$key_type, &$val_type, &$size) { + $result = $this->readVarint($size); + $types = 0; + if ($size > 0) { + $result += $this->readUByte($types); + } + $val_type = $this->getTType($types); + $key_type = $this->getTType($types >> 4); + $this->containers[] = $this->state; + $this->state = TCompactProtocol::STATE_CONTAINER_READ; + + return $result; + } + + public function readCollectionEnd() { + $this->state = array_pop($this->containers); + return 0; + } + + public function readMapEnd() { + return $this->readCollectionEnd(); + } + + public function readListBegin(&$elem_type, &$size) { + return $this->readCollectionBegin($elem_type, $size); + } + + public function readListEnd() { + return $this->readCollectionEnd(); + } + + public function readSetBegin(&$elem_type, &$size) { + return $this->readCollectionBegin($elem_type, $size); + } + + public function readSetEnd() { + return $this->readCollectionEnd(); + } + + public function readBool(&$value) { + if ($this->state == TCompactProtocol::STATE_BOOL_READ) { + $value = $this->boolValue; + return 0; + } else if ($this->state == TCompactProtocol::STATE_CONTAINER_READ) { + return $this->readByte($value); + } else { + throw new TProtocolException('Invalid state in compact protocol'); + } + } + + public function readI16(&$value) { + return $this->readZigZag($value); + } + + public function readI32(&$value) { + return $this->readZigZag($value); + } + + public function readDouble(&$value) { + $data = strrev($this->trans_->readAll(8)); + $arr = unpack('d', $data); + $value = $arr[1]; + return 8; + } + + public function readString(&$value) { + $result = $this->readVarint($len); + if ($len) { + $value = $this->trans_->readAll($len); + } else { + $value = ''; + } + return $result + $len; + } + + public function getTType($byte) { + return self::$ttypes[$byte & 0x0f]; + } + + // If we are on a 32bit architecture we have to explicitly deal with + // 64-bit twos-complement arithmetic since PHP wants to treat all ints + // as signed and any int over 2^31 - 1 as a float + + // Read and write I64 as two 32 bit numbers $hi and $lo + + public function readI64(&$value) { + // Read varint from wire + $hi = 0; + $lo = 0; + + $idx = 0; + $shift = 0; + + while (true) { + $x = $this->trans_->readAll(1); + $arr = unpack('C', $x); + $byte = $arr[1]; + $idx += 1; + if ($shift < 32) { + $lo |= (($byte & 0x7f) << $shift) & + 0x00000000ffffffff; + } + // Shift hi and lo together. + if ($shift >= 32) { + $hi |= (($byte & 0x7f) << ($shift - 32)); + } else if ($shift > 25) { + $hi |= (($byte & 0x7f) >> ($shift - 25)); + } + if (($byte >> 7) === 0) { + break; + } + $shift += 7; + } + + // Now, unzig it. + $xorer = 0; + if ($lo & 1) { + $xorer = 0xffffffff; + } + $lo = ($lo >> 1) & 0x7fffffff; + $lo = $lo | (($hi & 1) << 31); + $hi = ($hi >> 1) ^ $xorer; + $lo = $lo ^ $xorer; + + // Now put $hi and $lo back together + if (true) { + $isNeg = $hi < 0; + + // Check for a negative + if ($isNeg) { + $hi = ~$hi & (int)0xffffffff; + $lo = ~$lo & (int)0xffffffff; + + if ($lo == (int)0xffffffff) { + $hi++; + $lo = 0; + } else { + $lo++; + } + } + + // Force 32bit words in excess of 2G to be positive - we deal with sign + // explicitly below + + if ($hi & (int)0x80000000) { + $hi &= (int)0x7fffffff; + $hi += 0x80000000; + } + + if ($lo & (int)0x80000000) { + $lo &= (int)0x7fffffff; + $lo += 0x80000000; + } + + $value = $hi * 4294967296 + $lo; + + if ($isNeg) { + $value = 0 - $value; + } + } else { + + // Upcast negatives in LSB bit + if ($arr[2] & 0x80000000) { + $arr[2] = $arr[2] & 0xffffffff; + } + + // Check for a negative + if ($arr[1] & 0x80000000) { + $arr[1] = $arr[1] & 0xffffffff; + $arr[1] = $arr[1] ^ 0xffffffff; + $arr[2] = $arr[2] ^ 0xffffffff; + $value = 0 - $arr[1] * 4294967296 - $arr[2] - 1; + } else { + $value = $arr[1] * 4294967296 + $arr[2]; + } + } + + return $idx; + } + + public function writeI64($value) { + // If we are in an I32 range, use the easy method below. + if (($value > 4294967296) || ($value < -4294967296)) { + // Convert $value to $hi and $lo + $neg = $value < 0; + + if ($neg) { + $value *= -1; + } + + $hi = (int)$value >> 32; + $lo = (int)$value & 0xffffffff; + + if ($neg) { + $hi = ~$hi; + $lo = ~$lo; + if (($lo & (int)0xffffffff) == (int)0xffffffff) { + $lo = 0; + $hi++; + } else { + $lo++; + } + } + + // Now do the zigging and zagging. + $xorer = 0; + if ($neg) { + $xorer = 0xffffffff; + } + $lowbit = ($lo >> 31) & 1; + $hi = ($hi << 1) | $lowbit; + $lo = ($lo << 1); + $lo = ($lo ^ $xorer) & 0xffffffff; + $hi = ($hi ^ $xorer) & 0xffffffff; + + // now write out the varint, ensuring we shift both hi and lo + $out = ""; + while (true) { + if (($lo & ~0x7f) === 0 && + $hi === 0) { + $out .= chr($lo); + break; + } else { + $out .= chr(($lo & 0xff) | 0x80); + $lo = $lo >> 7; + $lo = $lo | ($hi << 25); + $hi = $hi >> 7; + // Right shift carries sign, but we don't want it to. + $hi = $hi & (127 << 25); + } + } + + $ret = TStringFuncFactory::create()->strlen($out); + $this->trans_->write($out, $ret); + + return $ret; + } else { + return $this->writeVarint($this->toZigZag($value, 64)); + } + } +} diff --git a/lib/php/lib/Thrift/Protocol/TJSONProtocol.php b/lib/php/lib/Thrift/Protocol/TJSONProtocol.php new file mode 100644 index 00000000..3d395833 --- /dev/null +++ b/lib/php/lib/Thrift/Protocol/TJSONProtocol.php @@ -0,0 +1,694 @@ + 1) { + switch (substr($name, 0, 1)) { + case 'd': + $result = TType::DOUBLE; + break; + case 'i': + switch (substr($name, 1, 1)) { + case '8': + $result = TType::BYTE; + break; + case '1': + $result = TType::I16; + break; + case '3': + $result = TType::I32; + break; + case '6': + $result = TType::I64; + break; + } + break; + case 'l': + $result = TType::LST; + break; + case 'm': + $result = TType::MAP; + break; + case 'r': + $result = TType::STRUCT; + break; + case 's': + if (substr($name, 1, 1) == 't') { + $result = TType::STRING; + } + else if (substr($name, 1, 1) == 'e') { + $result = TType::SET; + } + break; + case 't': + $result = TType::BOOL; + break; + } + } + if ($result == TType::STOP) { + throw new TProtocolException("Unrecognized type", TProtocolException::INVALID_DATA); + } + return $result; + } + + public $contextStack_ = array(); + public $context_; + public $reader_; + + private function pushContext($c) { + array_push($this->contextStack_, $this->context_); + $this->context_ = $c; + } + + private function popContext() { + $this->context_ = array_pop($this->contextStack_); + } + + public function __construct($trans) { + parent::__construct($trans); + $this->context_ = new BaseContext(); + $this->reader_ = new LookaheadReader($this); + } + + public function reset() { + $this->contextStack_ = array(); + $this->context_ = new BaseContext(); + $this->reader_ = new LookaheadReader($this); + } + + private $tmpbuf_ = array(4); + + public function readJSONSyntaxChar($b) { + $ch = $this->reader_->read(); + + if (substr($ch, 0, 1) != $b) { + throw new TProtocolException("Unexpected character: " . $ch, TProtocolException::INVALID_DATA); + } + } + + private function hexVal($s) { + for ($i = 0; $i < strlen($s); $i++) { + $ch = substr($s, $i, 1); + + if (!($ch >= "a" && $ch <= "f") && !($ch >= "0" && $ch <= "9")) { + throw new TProtocolException("Expected hex character " . $ch, TProtocolException::INVALID_DATA); + } + } + + return hexdec($s); + } + + private function hexChar($val) { + return dechex($val); + } + + private function writeJSONString($b) { + $this->context_->write(); + + if (is_numeric($b) && $this->context_->escapeNum()) { + $this->trans_->write(self::QUOTE); + } + + $this->trans_->write(json_encode($b)); + + if (is_numeric($b) && $this->context_->escapeNum()) { + $this->trans_->write(self::QUOTE); + } + } + + private function writeJSONInteger($num) { + $this->context_->write(); + + if ($this->context_->escapeNum()) { + $this->trans_->write(self::QUOTE); + } + + $this->trans_->write($num); + + if ($this->context_->escapeNum()) { + $this->trans_->write(self::QUOTE); + } + } + + private function writeJSONDouble($num) { + $this->context_->write(); + + if ($this->context_->escapeNum()) { + $this->trans_->write(self::QUOTE); + } + + $this->trans_->write(json_encode($num)); + + if ($this->context_->escapeNum()) { + $this->trans_->write(self::QUOTE); + } + } + + private function writeJSONBase64($data) { + $this->context_->write(); + $this->trans_->write(self::QUOTE); + $this->trans_->write(json_encode(base64_encode($data))); + $this->trans_->write(self::QUOTE); + } + + private function writeJSONObjectStart() { + $this->context_->write(); + $this->trans_->write(self::LBRACE); + $this->pushContext(new PairContext($this)); + } + + private function writeJSONObjectEnd() { + $this->popContext(); + $this->trans_->write(self::RBRACE); + } + + private function writeJSONArrayStart() { + $this->context_->write(); + $this->trans_->write(self::LBRACKET); + $this->pushContext(new ListContext($this)); + } + + private function writeJSONArrayEnd() { + $this->popContext(); + $this->trans_->write(self::RBRACKET); + } + + private function readJSONString($skipContext) { + if (!$skipContext) { + $this->context_->read(); + } + + $jsonString = ''; + $lastChar = NULL; + while (true) { + $ch = $this->reader_->read(); + $jsonString .= $ch; + if ($ch == self::QUOTE && + $lastChar !== NULL && + $lastChar !== self::ESCSEQ) { + break; + } + if ($ch == self::ESCSEQ && $lastChar == self::ESCSEQ) { + $lastChar = self::DOUBLEESC; + } else { + $lastChar = $ch; + } + } + return json_decode($jsonString); + } + + private function isJSONNumeric($b) { + switch ($b) { + case '+': + case '-': + case '.': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'E': + case 'e': + return true; + } + return false; + } + + private function readJSONNumericChars() { + $strbld = array(); + + while (true) { + $ch = $this->reader_->peek(); + + if (!$this->isJSONNumeric($ch)) { + break; + } + + $strbld[] = $this->reader_->read(); + } + + return implode("", $strbld); + } + + private function readJSONInteger() { + $this->context_->read(); + + if ($this->context_->escapeNum()) { + $this->readJSONSyntaxChar(self::QUOTE); + } + + $str = $this->readJSONNumericChars(); + + if ($this->context_->escapeNum()) { + $this->readJSONSyntaxChar(self::QUOTE); + } + + if (!is_numeric($str)) { + throw new TProtocolException("Invalid data in numeric: " . $str, TProtocolException::INVALID_DATA); + } + + return intval($str); + } + + /** + * Identical to readJSONInteger but without the final cast. + * Needed for proper handling of i64 on 32 bit machines. Why a + * separate function? So we don't have to force the rest of the + * use cases through the extra conditional. + */ + private function readJSONIntegerAsString() { + $this->context_->read(); + + if ($this->context_->escapeNum()) { + $this->readJSONSyntaxChar(self::QUOTE); + } + + $str = $this->readJSONNumericChars(); + + if ($this->context_->escapeNum()) { + $this->readJSONSyntaxChar(self::QUOTE); + } + + if (!is_numeric($str)) { + throw new TProtocolException("Invalid data in numeric: " . $str, TProtocolException::INVALID_DATA); + } + + return $str; + } + + private function readJSONDouble() { + $this->context_->read(); + + if (substr($this->reader_->peek(), 0, 1) == self::QUOTE) { + $arr = $this->readJSONString(true); + + if ($arr == "NaN") { + return NAN; + } else if ($arr == "Infinity") { + return INF; + } else if (!$this->context_->escapeNum()) { + throw new TProtocolException("Numeric data unexpectedly quoted " . $arr, + TProtocolException::INVALID_DATA); + } + + return floatval($arr); + } else { + if ($this->context_->escapeNum()) { + $this->readJSONSyntaxChar(self::QUOTE); + } + + return floatval($this->readJSONNumericChars()); + } + } + + private function readJSONBase64() { + $arr = $this->readJSONString(false); + $data = base64_decode($arr, true); + + if ($data === false) { + throw new TProtocolException("Invalid base64 data " . $arr, TProtocolException::INVALID_DATA); + } + + return $data; + } + + private function readJSONObjectStart() { + $this->context_->read(); + $this->readJSONSyntaxChar(self::LBRACE); + $this->pushContext(new PairContext($this)); + } + + private function readJSONObjectEnd() { + $this->readJSONSyntaxChar(self::RBRACE); + $this->popContext(); + } + + private function readJSONArrayStart() + { + $this->context_->read(); + $this->readJSONSyntaxChar(self::LBRACKET); + $this->pushContext(new ListContext($this)); + } + + private function readJSONArrayEnd() { + $this->readJSONSyntaxChar(self::RBRACKET); + $this->popContext(); + } + + /** + * Writes the message header + * + * @param string $name Function name + * @param int $type message type TMessageType::CALL or TMessageType::REPLY + * @param int $seqid The sequence id of this message + */ + public function writeMessageBegin($name, $type, $seqid) { + $this->writeJSONArrayStart(); + $this->writeJSONInteger(self::VERSION); + $this->writeJSONString($name); + $this->writeJSONInteger($type); + $this->writeJSONInteger($seqid); + } + + /** + * Close the message + */ + public function writeMessageEnd() { + $this->writeJSONArrayEnd(); + } + + /** + * Writes a struct header. + * + * @param string $name Struct name + * @throws TException on write error + * @return int How many bytes written + */ + public function writeStructBegin($name) { + $this->writeJSONObjectStart(); + } + + /** + * Close a struct. + * + * @throws TException on write error + * @return int How many bytes written + */ + public function writeStructEnd() { + $this->writeJSONObjectEnd(); + } + + public function writeFieldBegin($fieldName, $fieldType, $fieldId) { + $this->writeJSONInteger($fieldId); + $this->writeJSONObjectStart(); + $this->writeJSONString($this->getTypeNameForTypeID($fieldType)); + } + + public function writeFieldEnd() { + $this->writeJsonObjectEnd(); + } + + public function writeFieldStop() { + } + + public function writeMapBegin($keyType, $valType, $size) { + $this->writeJSONArrayStart(); + $this->writeJSONString($this->getTypeNameForTypeID($keyType)); + $this->writeJSONString($this->getTypeNameForTypeID($valType)); + $this->writeJSONInteger($size); + $this->writeJSONObjectStart(); + } + + public function writeMapEnd() { + $this->writeJSONObjectEnd(); + $this->writeJSONArrayEnd(); + } + + public function writeListBegin($elemType, $size) { + $this->writeJSONArrayStart(); + $this->writeJSONString($this->getTypeNameForTypeID($elemType)); + $this->writeJSONInteger($size); + } + + public function writeListEnd() { + $this->writeJSONArrayEnd(); + } + + public function writeSetBegin($elemType, $size) { + $this->writeJSONArrayStart(); + $this->writeJSONString($this->getTypeNameForTypeID($elemType)); + $this->writeJSONInteger($size); + } + + public function writeSetEnd() { + $this->writeJSONArrayEnd(); + } + + public function writeBool($bool) { + $this->writeJSONInteger($bool ? 1 : 0); + } + + public function writeByte($byte) { + $this->writeJSONInteger($byte); + } + + public function writeI16($i16) { + $this->writeJSONInteger($i16); + } + + public function writeI32($i32) { + $this->writeJSONInteger($i32); + } + + public function writeI64($i64) { + $this->writeJSONInteger($i64); + } + + public function writeDouble($dub) { + $this->writeJSONDouble($dub); + } + + public function writeString($str) { + $this->writeJSONString($str); + } + + /** + * Reads the message header + * + * @param string $name Function name + * @param int $type message type TMessageType::CALL or TMessageType::REPLY + * @parem int $seqid The sequence id of this message + */ + public function readMessageBegin(&$name, &$type, &$seqid) { + $this->readJSONArrayStart(); + + if ($this->readJSONInteger() != self::VERSION) { + throw new TProtocolException("Message contained bad version", TProtocolException::BAD_VERSION); + } + + $name = $this->readJSONString(false); + $type = $this->readJSONInteger(); + $seqid = $this->readJSONInteger(); + + return true; + } + + /** + * Read the close of message + */ + public function readMessageEnd() { + $this->readJSONArrayEnd(); + } + + public function readStructBegin(&$name) { + $this->readJSONObjectStart(); + return 0; + } + + public function readStructEnd() { + $this->readJSONObjectEnd(); + } + + public function readFieldBegin(&$name, &$fieldType, &$fieldId) { + $ch = $this->reader_->peek(); + $name = ""; + + if (substr($ch, 0, 1) == self::RBRACE) { + $fieldType = TType::STOP; + } else { + $fieldId = $this->readJSONInteger(); + $this->readJSONObjectStart(); + $fieldType = $this->getTypeIDForTypeName($this->readJSONString(false)); + } + } + + public function readFieldEnd() { + $this->readJSONObjectEnd(); + } + + public function readMapBegin(&$keyType, &$valType, &$size) { + $this->readJSONArrayStart(); + $keyType = $this->getTypeIDForTypeName($this->readJSONString(false)); + $valType = $this->getTypeIDForTypeName($this->readJSONString(false)); + $size = $this->readJSONInteger(); + $this->readJSONObjectStart(); + } + + public function readMapEnd() { + $this->readJSONObjectEnd(); + $this->readJSONArrayEnd(); + } + + public function readListBegin(&$elemType, &$size) { + $this->readJSONArrayStart(); + $elemType = $this->getTypeIDForTypeName($this->readJSONString(false)); + $size = $this->readJSONInteger(); + return true; + } + + public function readListEnd() { + $this->readJSONArrayEnd(); + } + + public function readSetBegin(&$elemType, &$size) { + $this->readJSONArrayStart(); + $elemType = $this->getTypeIDForTypeName($this->readJSONString(false)); + $size = $this->readJSONInteger(); + return true; + } + + public function readSetEnd() { + $this->readJSONArrayEnd(); + } + + public function readBool(&$bool) { + $bool = $this->readJSONInteger() == 0 ? false : true; + return true; + } + + public function readByte(&$byte) { + $byte = $this->readJSONInteger(); + return true; + } + + public function readI16(&$i16) { + $i16 = $this->readJSONInteger(); + return true; + } + + public function readI32(&$i32) { + $i32 = $this->readJSONInteger(); + return true; + } + + public function readI64(&$i64) { + if ( PHP_INT_SIZE === 4 ) { + $i64 = $this->readJSONIntegerAsString(); + } else { + $i64 = $this->readJSONInteger(); + } + return true; + } + + public function readDouble(&$dub) { + $dub = $this->readJSONDouble(); + return true; + } + + public function readString(&$str) { + $str = $this->readJSONString(false); + return true; + } +} diff --git a/lib/php/lib/Thrift/Protocol/TProtocol.php b/lib/php/lib/Thrift/Protocol/TProtocol.php new file mode 100644 index 00000000..86fff404 --- /dev/null +++ b/lib/php/lib/Thrift/Protocol/TProtocol.php @@ -0,0 +1,346 @@ +trans_ = $trans; + } + + /** + * Accessor for transport + * + * @return TTransport + */ + public function getTransport() { + return $this->trans_; + } + + /** + * Writes the message header + * + * @param string $name Function name + * @param int $type message type TMessageType::CALL or TMessageType::REPLY + * @param int $seqid The sequence id of this message + */ + public abstract function writeMessageBegin($name, $type, $seqid); + + /** + * Close the message + */ + public abstract function writeMessageEnd(); + + /** + * Writes a struct header. + * + * @param string $name Struct name + * @throws TException on write error + * @return int How many bytes written + */ + public abstract function writeStructBegin($name); + + /** + * Close a struct. + * + * @throws TException on write error + * @return int How many bytes written + */ + public abstract function writeStructEnd(); + + /* + * Starts a field. + * + * @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($fieldName, $fieldType, $fieldId); + + public abstract function writeFieldEnd(); + + public abstract function writeFieldStop(); + + public abstract function writeMapBegin($keyType, $valType, $size); + + public abstract function writeMapEnd(); + + public abstract function writeListBegin($elemType, $size); + + public abstract function writeListEnd(); + + public abstract function writeSetBegin($elemType, $size); + + public abstract function writeSetEnd(); + + public abstract function writeBool($bool); + + public abstract function writeByte($byte); + + public abstract function writeI16($i16); + + public abstract function writeI32($i32); + + public abstract function writeI64($i64); + + public abstract function writeDouble($dub); + + public abstract function writeString($str); + + /** + * Reads the message header + * + * @param string $name Function name + * @param int $type message type TMessageType::CALL or TMessageType::REPLY + * @parem int $seqid The sequence id of this message + */ + public abstract function readMessageBegin(&$name, &$type, &$seqid); + + /** + * Read the close of message + */ + public abstract function readMessageEnd(); + + public abstract function readStructBegin(&$name); + + public abstract function readStructEnd(); + + public abstract function readFieldBegin(&$name, &$fieldType, &$fieldId); + + public abstract function readFieldEnd(); + + public abstract function readMapBegin(&$keyType, &$valType, &$size); + + public abstract function readMapEnd(); + + public abstract function readListBegin(&$elemType, &$size); + + public abstract function readListEnd(); + + public abstract function readSetBegin(&$elemType, &$size); + + public abstract function readSetEnd(); + + public abstract function readBool(&$bool); + + public abstract function readByte(&$byte); + + public abstract function readI16(&$i16); + + public abstract function readI32(&$i32); + + public abstract function readI64(&$i64); + + public abstract function readDouble(&$dub); + + public abstract function readString(&$str); + + /** + * The skip function is a utility to parse over unrecognized date without + * causing corruption. + * + * @param TType $type What type is it + */ + public function skip($type) { + switch ($type) { + case TType::BOOL: + return $this->readBool($bool); + case TType::BYTE: + return $this->readByte($byte); + case TType::I16: + return $this->readI16($i16); + case TType::I32: + return $this->readI32($i32); + case TType::I64: + return $this->readI64($i64); + case TType::DOUBLE: + return $this->readDouble($dub); + case TType::STRING: + return $this->readString($str); + case TType::STRUCT: + { + $result = $this->readStructBegin($name); + while (true) { + $result += $this->readFieldBegin($name, $ftype, $fid); + if ($ftype == TType::STOP) { + break; + } + $result += $this->skip($ftype); + $result += $this->readFieldEnd(); + } + $result += $this->readStructEnd(); + return $result; + } + case TType::MAP: + { + $result = $this->readMapBegin($keyType, $valType, $size); + for ($i = 0; $i < $size; $i++) { + $result += $this->skip($keyType); + $result += $this->skip($valType); + } + $result += $this->readMapEnd(); + return $result; + } + case TType::SET: + { + $result = $this->readSetBegin($elemType, $size); + for ($i = 0; $i < $size; $i++) { + $result += $this->skip($elemType); + } + $result += $this->readSetEnd(); + return $result; + } + case TType::LST: + { + $result = $this->readListBegin($elemType, $size); + for ($i = 0; $i < $size; $i++) { + $result += $this->skip($elemType); + } + $result += $this->readListEnd(); + return $result; + } + default: + throw new TProtocolException('Unknown field type: '.$type, + TProtocolException::INVALID_DATA); + } + } + + /** + * Utility for skipping binary data + * + * @param TTransport $itrans TTransport object + * @param int $type Field type + */ + public static function skipBinary($itrans, $type) { + switch ($type) { + case TType::BOOL: + return $itrans->readAll(1); + case TType::BYTE: + return $itrans->readAll(1); + case TType::I16: + return $itrans->readAll(2); + case TType::I32: + return $itrans->readAll(4); + case TType::I64: + return $itrans->readAll(8); + case TType::DOUBLE: + return $itrans->readAll(8); + case TType::STRING: + $len = unpack('N', $itrans->readAll(4)); + $len = $len[1]; + if ($len > 0x7fffffff) { + $len = 0 - (($len - 1) ^ 0xffffffff); + } + return 4 + $itrans->readAll($len); + case TType::STRUCT: + { + $result = 0; + while (true) { + $ftype = 0; + $fid = 0; + $data = $itrans->readAll(1); + $arr = unpack('c', $data); + $ftype = $arr[1]; + if ($ftype == TType::STOP) { + break; + } + // I16 field id + $result += $itrans->readAll(2); + $result += self::skipBinary($itrans, $ftype); + } + return $result; + } + case TType::MAP: + { + // Ktype + $data = $itrans->readAll(1); + $arr = unpack('c', $data); + $ktype = $arr[1]; + // Vtype + $data = $itrans->readAll(1); + $arr = unpack('c', $data); + $vtype = $arr[1]; + // Size + $data = $itrans->readAll(4); + $arr = unpack('N', $data); + $size = $arr[1]; + if ($size > 0x7fffffff) { + $size = 0 - (($size - 1) ^ 0xffffffff); + } + $result = 6; + for ($i = 0; $i < $size; $i++) { + $result += self::skipBinary($itrans, $ktype); + $result += self::skipBinary($itrans, $vtype); + } + return $result; + } + case TType::SET: + case TType::LST: + { + // Vtype + $data = $itrans->readAll(1); + $arr = unpack('c', $data); + $vtype = $arr[1]; + // Size + $data = $itrans->readAll(4); + $arr = unpack('N', $data); + $size = $arr[1]; + if ($size > 0x7fffffff) { + $size = 0 - (($size - 1) ^ 0xffffffff); + } + $result = 5; + for ($i = 0; $i < $size; $i++) { + $result += self::skipBinary($itrans, $vtype); + } + return $result; + } + default: + throw new TProtocolException('Unknown field type: '.$type, + TProtocolException::INVALID_DATA); + } + } +} diff --git a/lib/php/lib/Thrift/Serializer/TBinarySerializer.php b/lib/php/lib/Thrift/Serializer/TBinarySerializer.php new file mode 100644 index 00000000..2a7cc3e0 --- /dev/null +++ b/lib/php/lib/Thrift/Serializer/TBinarySerializer.php @@ -0,0 +1,73 @@ +getName(), + TMessageType::REPLY, $object, + 0, $protocol->isStrictWrite()); + + $protocol->readMessageBegin($unused_name, $unused_type, + $unused_seqid); + } else { + $object->write($protocol); + } + $protocol->getTransport()->flush(); + return $transport->getBuffer(); + } + + public static function deserialize($string_object, $class_name) { + $transport = new TMemoryBuffer(); + $protocol = new TBinaryProtocolAccelerated($transport); + if (function_exists('thrift_protocol_read_binary')) { + $protocol->writeMessageBegin('', TMessageType::REPLY, 0); + $transport->write($string_object); + return thrift_protocol_read_binary($protocol, $class_name, + $protocol->isStrictRead()); + } else { + $transport->write($string_object); + $object = new $class_name(); + $object->read($protocol); + return $object; + } + } +} diff --git a/lib/php/lib/Thrift/Server/TForkingServer.php b/lib/php/lib/Thrift/Server/TForkingServer.php new file mode 100644 index 00000000..6fca3059 --- /dev/null +++ b/lib/php/lib/Thrift/Server/TForkingServer.php @@ -0,0 +1,119 @@ +transport_->listen(); + + while (!$this->stop_) { + try { + $transport = $this->transport_->accept(); + + if ($transport != null) { + $pid = pcntl_fork(); + + if ($pid > 0) { + $this->handleParent($transport, $pid); + } + else if ($pid === 0) { + $this->handleChild($transport); + } + else { + throw new TException('Failed to fork'); + } + } + } + catch (TTransportException $e) { } + + $this->collectChildren(); + } + } + + /** + * Code run by the parent + * + * @param TTransport $transport + * @param int $pid + * @return void + */ + private function handleParent(TTransport $transport, $pid) { + $this->children_[$pid] = $transport; + } + + /** + * Code run by the child. + * + * @param TTransport $transport + * @return void + */ + private function handleChild(TTransport $transport) { + try { + $inputTransport = $this->inputTransportFactory_->getTransport($transport); + $outputTransport = $this->outputTransportFactory_->getTransport($transport); + $inputProtocol = $this->inputProtocolFactory_->getProtocol($inputTransport); + $outputProtocol = $this->outputProtocolFactory_->getProtocol($outputTransport); + while ($this->processor_->process($inputProtocol, $outputProtocol)) { } + @$transport->close(); + } + catch (TTransportException $e) { } + + exit(0); + } + + /** + * Collects any children we may have + * + * @return void + */ + private function collectChildren() { + foreach ($this->children_ as $pid => $transport) { + if (pcntl_waitpid($pid, $status, WNOHANG) > 0) { + unset($this->children_[$pid]); + if ($transport) @$transport->close(); + } + } + } + + /** + * Stops the server running. Kills the transport + * and then stops the main serving loop + * + * @return void + */ + public function stop() { + $this->transport_->close(); + $this->stop_ = true; + } +} diff --git a/lib/php/lib/Thrift/Server/TServer.php b/lib/php/lib/Thrift/Server/TServer.php new file mode 100644 index 00000000..343bf4b1 --- /dev/null +++ b/lib/php/lib/Thrift/Server/TServer.php @@ -0,0 +1,101 @@ +processor_ = $processor; + $this->transport_ = $transport; + $this->inputTransportFactory_ = $inputTransportFactory; + $this->outputTransportFactory_ = $outputTransportFactory; + $this->inputProtocolFactory_ = $inputProtocolFactory; + $this->outputProtocolFactory_ = $outputProtocolFactory; + } + + /** + * Serves the server. This should never return + * unless a problem permits it to do so or it + * is interrupted intentionally + * + * @abstract + * @return void + */ + abstract public function serve(); + + /** + * Stops the server serving + * + * @abstract + * @return void + */ + abstract public function stop(); +} diff --git a/lib/php/lib/Thrift/Server/TServerSocket.php b/lib/php/lib/Thrift/Server/TServerSocket.php new file mode 100644 index 00000000..00a6fb9d --- /dev/null +++ b/lib/php/lib/Thrift/Server/TServerSocket.php @@ -0,0 +1,98 @@ +host_ = $host; + $this->port_ = $port; + } + + /** + * Sets the accept timeout + * + * @param int $acceptTimeout + * @return void + */ + public function setAcceptTimeout($acceptTimeout) { + $this->acceptTimeout_ = $acceptTimeout; + } + + /** + * Opens a new socket server handle + * + * @return void + */ + public function listen() { + $this->listener_ = stream_socket_server('tcp://' . $this->host_ . ':' . $this->port_); + } + + /** + * Closes the socket server handle + * + * @return void + */ + public function close() { + @fclose($this->listener_); + $this->listener_ = null; + } + + /** + * Implementation of accept. If not client is accepted in the given time + * + * @return TSocket + */ + protected function acceptImpl() { + $handle = @stream_socket_accept($this->listener_, $this->acceptTimeout_ / 1000.0); + if(!$handle) return null; + + $socket = new TSocket(); + $socket->setHandle($handle); + + return $socket; + } +} diff --git a/lib/php/lib/Thrift/Server/TServerTransport.php b/lib/php/lib/Thrift/Server/TServerTransport.php new file mode 100644 index 00000000..53247129 --- /dev/null +++ b/lib/php/lib/Thrift/Server/TServerTransport.php @@ -0,0 +1,54 @@ +acceptImpl(); + + if ($transport == null) { + throw new TTransportException("accept() may not return NULL"); + } + + return $transport; + } +} diff --git a/lib/php/lib/Thrift/Server/TSimpleServer.php b/lib/php/lib/Thrift/Server/TSimpleServer.php new file mode 100644 index 00000000..790e48fd --- /dev/null +++ b/lib/php/lib/Thrift/Server/TSimpleServer.php @@ -0,0 +1,57 @@ +transport_->listen(); + + while (!$this->stop_) { + try { + $transport = $this->transport_->accept(); + + if ($transport != null) { + $inputTransport = $this->inputTransportFactory_->getTransport($transport); + $outputTransport = $this->outputTransportFactory_->getTransport($transport); + $inputProtocol = $this->inputProtocolFactory_->getProtocol($inputTransport); + $outputProtocol = $this->outputProtocolFactory_->getProtocol($outputTransport); + while ($this->processor_->process($inputProtocol, $outputProtocol)) { } + } + } + catch (TTransportException $e) { } + } + } + + /** + * Stops the server running. Kills the transport + * and then stops the main serving loop + * + * @return void + */ + public function stop() { + $this->transport_->close(); + $this->stop_ = true; + } +} diff --git a/lib/php/lib/Thrift/StringFunc/Core.php b/lib/php/lib/Thrift/StringFunc/Core.php new file mode 100644 index 00000000..e38a5b26 --- /dev/null +++ b/lib/php/lib/Thrift/StringFunc/Core.php @@ -0,0 +1,38 @@ +strlen($str) - $start; + } + + return mb_substr($str, $start, $length, '8bit'); + } + + public function strlen($str) { + return mb_strlen($str, '8bit'); + } +} \ No newline at end of file diff --git a/lib/php/lib/Thrift/StringFunc/TStringFunc.php b/lib/php/lib/Thrift/StringFunc/TStringFunc.php new file mode 100644 index 00000000..c5bb390c --- /dev/null +++ b/lib/php/lib/Thrift/StringFunc/TStringFunc.php @@ -0,0 +1,27 @@ +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 putBack($data) { + if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) { + $this->rBuf_ = $data; + } else { + $this->rBuf_ = ($data . $this->rBuf_); + } + } + + /** + * The reason that we customize readAll here is that the majority of PHP + * streams are already internally buffered by PHP. The socket stream, for + * example, buffers internally and blocks if you call read with $len greater + * than the amount of data available, unlike recv() in C. + * + * Therefore, use the readAll method of the wrapped transport inside + * the buffered readAll. + */ + public function readAll($len) { + $have = TStringFuncFactory::create()->strlen($this->rBuf_); + if ($have == 0) { + $data = $this->transport_->readAll($len); + } else if ($have < $len) { + $data = $this->rBuf_; + $this->rBuf_ = ''; + $data .= $this->transport_->readAll($len - $have); + } else if ($have == $len) { + $data = $this->rBuf_; + $this->rBuf_ = ''; + } else if ($have > $len) { + $data = TStringFuncFactory::create()->substr($this->rBuf_, 0, $len); + $this->rBuf_ = TStringFuncFactory::create()->substr($this->rBuf_, $len); + } + return $data; + } + + public function read($len) { + if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) { + $this->rBuf_ = $this->transport_->read($this->rBufSize_); + } + + if (TStringFuncFactory::create()->strlen($this->rBuf_) <= $len) { + $ret = $this->rBuf_; + $this->rBuf_ = ''; + return $ret; + } + + $ret = TStringFuncFactory::create()->substr($this->rBuf_, 0, $len); + $this->rBuf_ = TStringFuncFactory::create()->substr($this->rBuf_, $len); + return $ret; + } + + public function write($buf) { + $this->wBuf_ .= $buf; + if (TStringFuncFactory::create()->strlen($this->wBuf_) >= $this->wBufSize_) { + $out = $this->wBuf_; + + // Note that we clear the internal wBuf_ prior to the underlying write + // to ensure we're in a sane state (i.e. internal buffer cleaned) + // if the underlying write throws up an exception + $this->wBuf_ = ''; + $this->transport_->write($out); + } + } + + public function flush() { + if (TStringFuncFactory::create()->strlen($this->wBuf_) > 0) { + $this->transport_->write($this->wBuf_); + $this->wBuf_ = ''; + } + $this->transport_->flush(); + } + +} diff --git a/lib/php/lib/Thrift/Transport/TFramedTransport.php b/lib/php/lib/Thrift/Transport/TFramedTransport.php new file mode 100644 index 00000000..d80d548b --- /dev/null +++ b/lib/php/lib/Thrift/Transport/TFramedTransport.php @@ -0,0 +1,183 @@ +transport_ = $transport; + $this->read_ = $read; + $this->write_ = $write; + } + + public function isOpen() { + return $this->transport_->isOpen(); + } + + public function open() { + $this->transport_->open(); + } + + public function close() { + $this->transport_->close(); + } + + /** + * 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) { + if (!$this->read_) { + return $this->transport_->read($len); + } + + if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) { + $this->readFrame(); + } + + // Just return full buff + if ($len >= TStringFuncFactory::create()->strlen($this->rBuf_)) { + $out = $this->rBuf_; + $this->rBuf_ = null; + return $out; + } + + // Return TStringFuncFactory::create()->substr + $out = TStringFuncFactory::create()->substr($this->rBuf_, 0, $len); + $this->rBuf_ = TStringFuncFactory::create()->substr($this->rBuf_, $len); + return $out; + } + + /** + * Put previously read data back into the buffer + * + * @param string $data data to return + */ + public function putBack($data) { + if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) { + $this->rBuf_ = $data; + } else { + $this->rBuf_ = ($data . $this->rBuf_); + } + } + + /** + * Reads a chunk of data into the internal read buffer. + */ + private function readFrame() { + $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 (!$this->write_) { + return $this->transport_->write($buf, $len); + } + + if ($len !== null && $len < TStringFuncFactory::create()->strlen($buf)) { + $buf = TStringFuncFactory::create()->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() { + if (!$this->write_ || TStringFuncFactory::create()->strlen($this->wBuf_) == 0) { + return $this->transport_->flush(); + } + + $out = pack('N', TStringFuncFactory::create()->strlen($this->wBuf_)); + $out .= $this->wBuf_; + + // Note that we clear the internal wBuf_ prior to the underlying write + // to ensure we're in a sane state (i.e. internal buffer cleaned) + // if the underlying write throws up an exception + $this->wBuf_ = ''; + $this->transport_->write($out); + $this->transport_->flush(); + } + +} diff --git a/lib/php/lib/Thrift/Transport/THttpClient.php b/lib/php/lib/Thrift/Transport/THttpClient.php new file mode 100644 index 00000000..87f2871a --- /dev/null +++ b/lib/php/lib/Thrift/Transport/THttpClient.php @@ -0,0 +1,205 @@ +strlen($uri) > 0) && ($uri{0} != '/')) { + $uri = '/'.$uri; + } + $this->scheme_ = $scheme; + $this->host_ = $host; + $this->port_ = $port; + $this->uri_ = $uri; + $this->buf_ = ''; + $this->handle_ = null; + $this->timeout_ = null; + } + + /** + * Set read timeout + * + * @param float $timeout + */ + public function setTimeoutSecs($timeout) { + $this->timeout_ = $timeout; + } + + /** + * Whether this transport is open. + * + * @return boolean true if open + */ + public function isOpen() { + return true; + } + + /** + * Open the transport for reading/writing + * + * @throws TTransportException if cannot open + */ + public function open() {} + + /** + * Close the transport. + */ + public function close() { + if ($this->handle_) { + @fclose($this->handle_); + $this->handle_ = null; + } + } + + /** + * 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 function read($len) { + $data = @fread($this->handle_, $len); + if ($data === FALSE || $data === '') { + $md = stream_get_meta_data($this->handle_); + if ($md['timed_out']) { + throw new TTransportException('THttpClient: timed out reading '.$len.' bytes from '.$this->host_.':'.$this->port_.$this->uri_, TTransportException::TIMED_OUT); + } else { + throw new TTransportException('THttpClient: Could not read '.$len.' bytes from '.$this->host_.':'.$this->port_.$this->uri_, TTransportException::UNKNOWN); + } + } + return $data; + } + + /** + * Writes some data into the pending buffer + * + * @param string $buf The data to write + * @throws TTransportException if writing fails + */ + public function write($buf) { + $this->buf_ .= $buf; + } + + /** + * Opens and sends the actual request over the HTTP connection + * + * @throws TTransportException if a writing error occurs + */ + public function flush() { + // God, PHP really has some esoteric ways of doing simple things. + $host = $this->host_.($this->port_ != 80 ? ':'.$this->port_ : ''); + + $headers = array('Host: '.$host, + 'Accept: application/x-thrift', + 'User-Agent: PHP/THttpClient', + 'Content-Type: application/x-thrift', + 'Content-Length: '.TStringFuncFactory::create()->strlen($this->buf_)); + + $options = array('method' => 'POST', + 'header' => implode("\r\n", $headers), + 'max_redirects' => 1, + 'content' => $this->buf_); + if ($this->timeout_ > 0) { + $options['timeout'] = $this->timeout_; + } + $this->buf_ = ''; + + $contextid = stream_context_create(array('http' => $options)); + $this->handle_ = @fopen($this->scheme_.'://'.$host.$this->uri_, 'r', false, $contextid); + + // Connect failed? + if ($this->handle_ === FALSE) { + $this->handle_ = null; + $error = 'THttpClient: Could not connect to '.$host.$this->uri_; + throw new TTransportException($error, TTransportException::NOT_OPEN); + } + } + +} diff --git a/lib/php/lib/Thrift/Transport/TMemoryBuffer.php b/lib/php/lib/Thrift/Transport/TMemoryBuffer.php new file mode 100644 index 00000000..cfe5e0ec --- /dev/null +++ b/lib/php/lib/Thrift/Transport/TMemoryBuffer.php @@ -0,0 +1,87 @@ +buf_ = $buf; + } + + protected $buf_ = ''; + + public function isOpen() { + return true; + } + + public function open() {} + + public function close() {} + + public function write($buf) { + $this->buf_ .= $buf; + } + + public function read($len) { + if (TStringFuncFactory::create()->strlen($this->buf_) === 0) { + throw new TTransportException('TMemoryBuffer: Could not read ' . + $len . ' bytes from buffer.', + TTransportException::UNKNOWN); + } + + if (TStringFuncFactory::create()->strlen($this->buf_) <= $len) { + $ret = $this->buf_; + $this->buf_ = ''; + return $ret; + } + + $ret = TStringFuncFactory::create()->substr($this->buf_, 0, $len); + $this->buf_ = TStringFuncFactory::create()->substr($this->buf_, $len); + + return $ret; + } + + function getBuffer() { + return $this->buf_; + } + + public function available() { + return TStringFuncFactory::create()->strlen($this->buf_); + } +} diff --git a/lib/php/lib/Thrift/Transport/TNullTransport.php b/lib/php/lib/Thrift/Transport/TNullTransport.php new file mode 100644 index 00000000..4bf10edf --- /dev/null +++ b/lib/php/lib/Thrift/Transport/TNullTransport.php @@ -0,0 +1,50 @@ +read_ = $mode & self::MODE_R; + $this->write_ = $mode & self::MODE_W; + } + + public function open() { + if ($this->read_) { + $this->inStream_ = @fopen(self::inStreamName(), 'r'); + if (!is_resource($this->inStream_)) { + throw new TException('TPhpStream: Could not open php://input'); + } + } + if ($this->write_) { + $this->outStream_ = @fopen('php://output', 'w'); + if (!is_resource($this->outStream_)) { + throw new TException('TPhpStream: Could not open php://output'); + } + } + } + + public function close() { + if ($this->read_) { + @fclose($this->inStream_); + $this->inStream_ = null; + } + if ($this->write_) { + @fclose($this->outStream_); + $this->outStream_ = null; + } + } + + public function isOpen() { + return + (!$this->read_ || is_resource($this->inStream_)) && + (!$this->write_ || is_resource($this->outStream_)); + } + + public function read($len) { + $data = @fread($this->inStream_, $len); + if ($data === FALSE || $data === '') { + throw new TException('TPhpStream: Could not read '.$len.' bytes'); + } + return $data; + } + + public function write($buf) { + while (TStringFuncFactory::create()->strlen($buf) > 0) { + $got = @fwrite($this->outStream_, $buf); + if ($got === 0 || $got === FALSE) { + throw new TException('TPhpStream: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes'); + } + $buf = TStringFuncFactory::create()->substr($buf, $got); + } + } + + public function flush() { + @fflush($this->outStream_); + } + + private static function inStreamName() { + if (php_sapi_name() == 'cli') { + return 'php://stdin'; + } + return 'php://input'; + } + +} diff --git a/lib/php/lib/Thrift/Transport/TSocket.php b/lib/php/lib/Thrift/Transport/TSocket.php new file mode 100644 index 00000000..3ad3bf70 --- /dev/null +++ b/lib/php/lib/Thrift/Transport/TSocket.php @@ -0,0 +1,326 @@ +host_ = $host; + $this->port_ = $port; + $this->persist_ = $persist; + $this->debugHandler_ = $debugHandler ? $debugHandler : 'error_log'; + } + + /** + * @param resource $handle + * @return void + */ + public function setHandle($handle) { + $this->handle_ = $handle; + } + + /** + * Sets the send timeout. + * + * @param int $timeout Timeout in milliseconds. + */ + public function setSendTimeout($timeout) { + $this->sendTimeoutSec_ = floor($timeout / 1000); + $this->sendTimeoutUsec_ = + ($timeout - ($this->sendTimeoutSec_ * 1000)) * 1000; + } + + /** + * Sets the receive timeout. + * + * @param int $timeout Timeout in milliseconds. + */ + public function setRecvTimeout($timeout) { + $this->recvTimeoutSec_ = floor($timeout / 1000); + $this->recvTimeoutUsec_ = + ($timeout - ($this->recvTimeoutSec_ * 1000)) * 1000; + } + + /** + * Sets debugging output on or off + * + * @param bool $debug + */ + public function setDebug($debug) { + $this->debug_ = $debug; + } + + /** + * Get the host that this socket is connected to + * + * @return string host + */ + public function getHost() { + return $this->host_; + } + + /** + * Get the remote port that this socket is connected to + * + * @return int port + */ + public function getPort() { + return $this->port_; + } + + /** + * 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->isOpen()) { + throw new TTransportException('Socket already connected', TTransportException::ALREADY_OPEN); + } + + if (empty($this->host_)) { + throw new TTransportException('Cannot open null host', TTransportException::NOT_OPEN); + } + + if ($this->port_ <= 0) { + throw new TTransportException('Cannot open without port', TTransportException::NOT_OPEN); + } + + if ($this->persist_) { + $this->handle_ = @pfsockopen($this->host_, + $this->port_, + $errno, + $errstr, + $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000)); + } else { + $this->handle_ = @fsockopen($this->host_, + $this->port_, + $errno, + $errstr, + $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000)); + } + + // Connect failed? + if ($this->handle_ === FALSE) { + $error = 'TSocket: Could not connect to '.$this->host_.':'.$this->port_.' ('.$errstr.' ['.$errno.'])'; + if ($this->debug_) { + call_user_func($this->debugHandler_, $error); + } + throw new TException($error); + } + } + + /** + * Closes the socket. + */ + public function close() { + if (!$this->persist_) { + @fclose($this->handle_); + $this->handle_ = null; + } + } + + /** + * Read from the socket at most $len bytes. + * + * This method will not wait for all the requested data, it will return as + * soon as any data is received. + * + * @param int $len Maximum number of bytes to read. + * @return string Binary data + */ + public function read($len) { + $null = null; + $read = array($this->handle_); + $readable = @stream_select($read, $null, $null, $this->recvTimeoutSec_, $this->recvTimeoutUsec_); + + if ($readable > 0) { + $data = @stream_socket_recvfrom($this->handle_, $len); + if ($data === false) { + throw new TTransportException('TSocket: Could not read '.$len.' bytes from '. + $this->host_.':'.$this->port_); + } elseif($data == '' && feof($this->handle_)) { + throw new TTransportException('TSocket read 0 bytes'); + } + + return $data; + } else if ($readable === 0) { + throw new TTransportException('TSocket: timed out reading '.$len.' bytes from '. + $this->host_.':'.$this->port_); + } else { + throw new TTransportException('TSocket: Could not read '.$len.' bytes from '. + $this->host_.':'.$this->port_); + } + } + + /** + * Write to the socket. + * + * @param string $buf The data to write + */ + public function write($buf) { + $null = null; + $write = array($this->handle_); + + // keep writing until all the data has been written + while (TStringFuncFactory::create()->strlen($buf) > 0) { + // wait for stream to become available for writing + $writable = @stream_select($null, $write, $null, $this->sendTimeoutSec_, $this->sendTimeoutUsec_); + if ($writable > 0) { + // write buffer to stream + $written = @stream_socket_sendto($this->handle_, $buf); + if ($written === -1 || $written === false) { + throw new TTransportException('TSocket: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes '. + $this->host_.':'.$this->port_); + } + // determine how much of the buffer is left to write + $buf = TStringFuncFactory::create()->substr($buf, $written); + } else if ($writable === 0) { + throw new TTransportException('TSocket: timed out writing '.TStringFuncFactory::create()->strlen($buf).' bytes from '. + $this->host_.':'.$this->port_); + } else { + throw new TTransportException('TSocket: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes '. + $this->host_.':'.$this->port_); + } + } + } + + /** + * Flush output to the socket. + * + * Since read(), readAll() and write() operate on the sockets directly, + * this is a no-op + * + * If you wish to have flushable buffering behaviour, wrap this TSocket + * in a TBufferedTransport. + */ + public function flush() { + // no-op + } + } diff --git a/lib/php/lib/Thrift/Transport/TSocketPool.php b/lib/php/lib/Thrift/Transport/TSocketPool.php new file mode 100644 index 00000000..e1610cbd --- /dev/null +++ b/lib/php/lib/Thrift/Transport/TSocketPool.php @@ -0,0 +1,295 @@ + $val) { + $ports[$key] = $port; + } + } + + foreach ($hosts as $key => $host) { + $this->servers_ []= array('host' => $host, + 'port' => $ports[$key]); + } + } + + /** + * Add a server to the pool + * + * This function does not prevent you from adding a duplicate server entry. + * + * @param string $host hostname or IP + * @param int $port port + */ + public function addServer($host, $port) { + $this->servers_[] = array('host' => $host, 'port' => $port); + } + + /** + * Sets how many time to keep retrying a host in the connect function. + * + * @param int $numRetries + */ + public function setNumRetries($numRetries) { + $this->numRetries_ = $numRetries; + } + + /** + * Sets how long to wait until retrying a host if it was marked down + * + * @param int $numRetries + */ + public function setRetryInterval($retryInterval) { + $this->retryInterval_ = $retryInterval; + } + + /** + * Sets how many time to keep retrying a host before marking it as down. + * + * @param int $numRetries + */ + public function setMaxConsecutiveFailures($maxConsecutiveFailures) { + $this->maxConsecutiveFailures_ = $maxConsecutiveFailures; + } + + /** + * Turns randomization in connect order on or off. + * + * @param bool $randomize + */ + public function setRandomize($randomize) { + $this->randomize_ = $randomize; + } + + /** + * Whether to always try the last server. + * + * @param bool $alwaysTryLast + */ + public function setAlwaysTryLast($alwaysTryLast) { + $this->alwaysTryLast_ = $alwaysTryLast; + } + + + /** + * Connects the socket by iterating through all the servers in the pool + * and trying to find one that works. + */ + public function open() { + // Check if we want order randomization + if ($this->randomize_) { + shuffle($this->servers_); + } + + // Count servers to identify the "last" one + $numServers = count($this->servers_); + + for ($i = 0; $i < $numServers; ++$i) { + + // This extracts the $host and $port variables + extract($this->servers_[$i]); + + // Check APC cache for a record of this server being down + $failtimeKey = 'thrift_failtime:'.$host.':'.$port.'~'; + + // Cache miss? Assume it's OK + $lastFailtime = apc_fetch($failtimeKey); + if ($lastFailtime === FALSE) { + $lastFailtime = 0; + } + + $retryIntervalPassed = FALSE; + + // Cache hit...make sure enough the retry interval has elapsed + if ($lastFailtime > 0) { + $elapsed = time() - $lastFailtime; + if ($elapsed > $this->retryInterval_) { + $retryIntervalPassed = TRUE; + if ($this->debug_) { + call_user_func($this->debugHandler_, + 'TSocketPool: retryInterval '. + '('.$this->retryInterval_.') '. + 'has passed for host '.$host.':'.$port); + } + } + } + + // Only connect if not in the middle of a fail interval, OR if this + // is the LAST server we are trying, just hammer away on it + $isLastServer = FALSE; + if ($this->alwaysTryLast_) { + $isLastServer = ($i == ($numServers - 1)); + } + + if (($lastFailtime === 0) || + ($isLastServer) || + ($lastFailtime > 0 && $retryIntervalPassed)) { + + // Set underlying TSocket params to this one + $this->host_ = $host; + $this->port_ = $port; + + // Try up to numRetries_ connections per server + for ($attempt = 0; $attempt < $this->numRetries_; $attempt++) { + try { + // Use the underlying TSocket open function + parent::open(); + + // Only clear the failure counts if required to do so + if ($lastFailtime > 0) { + apc_store($failtimeKey, 0); + } + + // Successful connection, return now + return; + + } catch (TException $tx) { + // Connection failed + } + } + + // Mark failure of this host in the cache + $consecfailsKey = 'thrift_consecfails:'.$host.':'.$port.'~'; + + // Ignore cache misses + $consecfails = apc_fetch($consecfailsKey); + if ($consecfails === FALSE) { + $consecfails = 0; + } + + // Increment by one + $consecfails++; + + // Log and cache this failure + if ($consecfails >= $this->maxConsecutiveFailures_) { + if ($this->debug_) { + call_user_func($this->debugHandler_, + 'TSocketPool: marking '.$host.':'.$port. + ' as down for '.$this->retryInterval_.' secs '. + 'after '.$consecfails.' failed attempts.'); + } + // Store the failure time + apc_store($failtimeKey, time()); + + // Clear the count of consecutive failures + apc_store($consecfailsKey, 0); + } else { + apc_store($consecfailsKey, $consecfails); + } + } + } + + // Oh no; we failed them all. The system is totally ill! + $error = 'TSocketPool: All hosts in pool are down. '; + $hosts = array(); + foreach ($this->servers_ as $server) { + $hosts []= $server['host'].':'.$server['port']; + } + $hostlist = implode(',', $hosts); + $error .= '('.$hostlist.')'; + if ($this->debug_) { + call_user_func($this->debugHandler_, $error); + } + throw new TException($error); + } +} diff --git a/lib/php/lib/Thrift/Transport/TTransport.php b/lib/php/lib/Thrift/Transport/TTransport.php new file mode 100644 index 00000000..2e44366d --- /dev/null +++ b/lib/php/lib/Thrift/Transport/TTransport.php @@ -0,0 +1,93 @@ +read($len); + + $data = ''; + $got = 0; + while (($got = TStringFuncFactory::create()->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/lib/php/lib/Thrift/Type/TMessageType.php b/lib/php/lib/Thrift/Type/TMessageType.php new file mode 100644 index 00000000..681c45c2 --- /dev/null +++ b/lib/php/lib/Thrift/Type/TMessageType.php @@ -0,0 +1,33 @@ +strictRead_ = $strictRead; - $this->strictWrite_ = $strictWrite; - } - - public function writeMessageBegin($name, $type, $seqid) { - if ($this->strictWrite_) { - $version = self::VERSION_1 | $type; - return - $this->writeI32($version) + - $this->writeString($name) + - $this->writeI32($seqid); - } else { - return - $this->writeString($name) + - $this->writeByte($type) + - $this->writeI32($seqid); - } - } - - public function writeMessageEnd() { - return 0; - } - - public function writeStructBegin($name) { - return 0; - } - - public function writeStructEnd() { - return 0; - } - - public function writeFieldBegin($fieldName, $fieldType, $fieldId) { - return - $this->writeByte($fieldType) + - $this->writeI16($fieldId); - } - - public function writeFieldEnd() { - return 0; - } - - public function writeFieldStop() { - return - $this->writeByte(TType::STOP); - } - - public function writeMapBegin($keyType, $valType, $size) { - return - $this->writeByte($keyType) + - $this->writeByte($valType) + - $this->writeI32($size); - } - - public function writeMapEnd() { - return 0; - } - - public function writeListBegin($elemType, $size) { - return - $this->writeByte($elemType) + - $this->writeI32($size); - } - - public function writeListEnd() { - return 0; - } - - public function writeSetBegin($elemType, $size) { - return - $this->writeByte($elemType) + - $this->writeI32($size); - } - - public function writeSetEnd() { - return 0; - } - - public function writeBool($value) { - $data = pack('c', $value ? 1 : 0); - $this->trans_->write($data, 1); - return 1; - } - - public function writeByte($value) { - $data = pack('c', $value); - $this->trans_->write($data, 1); - return 1; - } - - public function writeI16($value) { - $data = pack('n', $value); - $this->trans_->write($data, 2); - return 2; - } - - public function writeI32($value) { - $data = pack('N', $value); - $this->trans_->write($data, 4); - return 4; - } - - public function writeI64($value) { - // If we are on a 32bit architecture we have to explicitly deal with - // 64-bit twos-complement arithmetic since PHP wants to treat all ints - // as signed and any int over 2^31 - 1 as a float - if (PHP_INT_SIZE == 4) { - $neg = $value < 0; - - if ($neg) { - $value *= -1; - } - - $hi = (int)($value / 4294967296); - $lo = (int)$value; - - if ($neg) { - $hi = ~$hi; - $lo = ~$lo; - if (($lo & (int)0xffffffff) == (int)0xffffffff) { - $lo = 0; - $hi++; - } else { - $lo++; - } - } - $data = pack('N2', $hi, $lo); - - } else { - $hi = $value >> 32; - $lo = $value & 0xFFFFFFFF; - $data = pack('N2', $hi, $lo); - } - - $this->trans_->write($data, 8); - return 8; - } - - public function writeDouble($value) { - $data = pack('d', $value); - $this->trans_->write(strrev($data), 8); - return 8; - } - - public function writeString($value) { - $len = TStringFuncFactory::create()->strlen($value); - $result = $this->writeI32($len); - if ($len) { - $this->trans_->write($value, $len); - } - return $result + $len; - } - - public function readMessageBegin(&$name, &$type, &$seqid) { - $result = $this->readI32($sz); - if ($sz < 0) { - $version = (int) ($sz & self::VERSION_MASK); - if ($version != (int) self::VERSION_1) { - throw new TProtocolException('Bad version identifier: '.$sz, TProtocolException::BAD_VERSION); - } - $type = $sz & 0x000000ff; - $result += - $this->readString($name) + - $this->readI32($seqid); - } else { - if ($this->strictRead_) { - throw new TProtocolException('No version identifier, old protocol client?', TProtocolException::BAD_VERSION); - } else { - // Handle pre-versioned input - $name = $this->trans_->readAll($sz); - $result += - $sz + - $this->readByte($type) + - $this->readI32($seqid); - } - } - return $result; - } - - public function readMessageEnd() { - return 0; - } - - public function readStructBegin(&$name) { - $name = ''; - return 0; - } - - public function readStructEnd() { - return 0; - } - - public function readFieldBegin(&$name, &$fieldType, &$fieldId) { - $result = $this->readByte($fieldType); - if ($fieldType == TType::STOP) { - $fieldId = 0; - return $result; - } - $result += $this->readI16($fieldId); - return $result; - } - - public function readFieldEnd() { - return 0; - } - - public function readMapBegin(&$keyType, &$valType, &$size) { - return - $this->readByte($keyType) + - $this->readByte($valType) + - $this->readI32($size); - } - - public function readMapEnd() { - return 0; - } - - public function readListBegin(&$elemType, &$size) { - return - $this->readByte($elemType) + - $this->readI32($size); - } - - public function readListEnd() { - return 0; - } - - public function readSetBegin(&$elemType, &$size) { - return - $this->readByte($elemType) + - $this->readI32($size); - } - - public function readSetEnd() { - return 0; - } - - public function readBool(&$value) { - $data = $this->trans_->readAll(1); - $arr = unpack('c', $data); - $value = $arr[1] == 1; - return 1; - } - - public function readByte(&$value) { - $data = $this->trans_->readAll(1); - $arr = unpack('c', $data); - $value = $arr[1]; - return 1; - } - - public function readI16(&$value) { - $data = $this->trans_->readAll(2); - $arr = unpack('n', $data); - $value = $arr[1]; - if ($value > 0x7fff) { - $value = 0 - (($value - 1) ^ 0xffff); - } - return 2; - } - - public function readI32(&$value) { - $data = $this->trans_->readAll(4); - $arr = unpack('N', $data); - $value = $arr[1]; - if ($value > 0x7fffffff) { - $value = 0 - (($value - 1) ^ 0xffffffff); - } - return 4; - } - - public function readI64(&$value) { - $data = $this->trans_->readAll(8); - - $arr = unpack('N2', $data); - - // If we are on a 32bit architecture we have to explicitly deal with - // 64-bit twos-complement arithmetic since PHP wants to treat all ints - // as signed and any int over 2^31 - 1 as a float - if (PHP_INT_SIZE == 4) { - - $hi = $arr[1]; - $lo = $arr[2]; - $isNeg = $hi < 0; - - // Check for a negative - if ($isNeg) { - $hi = ~$hi & (int)0xffffffff; - $lo = ~$lo & (int)0xffffffff; - - if ($lo == (int)0xffffffff) { - $hi++; - $lo = 0; - } else { - $lo++; - } - } - - // Force 32bit words in excess of 2G to pe positive - we deal wigh sign - // explicitly below - - if ($hi & (int)0x80000000) { - $hi &= (int)0x7fffffff; - $hi += 0x80000000; - } - - if ($lo & (int)0x80000000) { - $lo &= (int)0x7fffffff; - $lo += 0x80000000; - } - - $value = $hi * 4294967296 + $lo; - - if ($isNeg) { - $value = 0 - $value; - } - } else { - - // Upcast negatives in LSB bit - if ($arr[2] & 0x80000000) { - $arr[2] = $arr[2] & 0xffffffff; - } - - // Check for a negative - if ($arr[1] & 0x80000000) { - $arr[1] = $arr[1] & 0xffffffff; - $arr[1] = $arr[1] ^ 0xffffffff; - $arr[2] = $arr[2] ^ 0xffffffff; - $value = 0 - $arr[1]*4294967296 - $arr[2] - 1; - } else { - $value = $arr[1]*4294967296 + $arr[2]; - } - } - - return 8; - } - - public function readDouble(&$value) { - $data = strrev($this->trans_->readAll(8)); - $arr = unpack('d', $data); - $value = $arr[1]; - return 8; - } - - public function readString(&$value) { - $result = $this->readI32($len); - if ($len) { - $value = $this->trans_->readAll($len); - } else { - $value = ''; - } - return $result + $len; - } -} - -/** - * Binary Protocol Factory - */ -class TBinaryProtocolFactory implements TProtocolFactory { - private $strictRead_ = false; - private $strictWrite_ = false; - - public function __construct($strictRead=false, $strictWrite=false) { - $this->strictRead_ = $strictRead; - $this->strictWrite_ = $strictWrite; - } - - public function getProtocol($trans) { - return new TBinaryProtocol($trans, $this->strictRead_, $this->strictWrite_); - } -} - -/** - * Accelerated binary protocol: used in conjunction with the thrift_protocol - * extension for faster deserialization - */ -class TBinaryProtocolAccelerated extends TBinaryProtocol { - public function __construct($trans, $strictRead=false, $strictWrite=true) { - // If the transport doesn't implement putBack, wrap it in a - // TBufferedTransport (which does) - if (!method_exists($trans, 'putBack')) { - $trans = new TBufferedTransport($trans); - } - parent::__construct($trans, $strictRead, $strictWrite); - } - public function isStrictRead() { - return $this->strictRead_; - } - public function isStrictWrite() { - return $this->strictWrite_; - } -} diff --git a/lib/php/src/protocol/TBinarySerializer.php b/lib/php/src/protocol/TBinarySerializer.php index 542233b9..e69de29b 100644 --- a/lib/php/src/protocol/TBinarySerializer.php +++ b/lib/php/src/protocol/TBinarySerializer.php @@ -1,70 +0,0 @@ -getName(), - TMessageType::REPLY, $object, - 0, $protocol->isStrictWrite()); - - $protocol->readMessageBegin($unused_name, $unused_type, - $unused_seqid); - } else { - $object->write($protocol); - } - $protocol->getTransport()->flush(); - return $transport->getBuffer(); - } - - public static function deserialize($string_object, $class_name) { - $transport = new TMemoryBuffer(); - $protocol = new TBinaryProtocolAccelerated($transport); - if (function_exists('thrift_protocol_read_binary')) { - $protocol->writeMessageBegin('', TMessageType::REPLY, 0); - $transport->write($string_object); - return thrift_protocol_read_binary($protocol, $class_name, - $protocol->isStrictRead()); - } else { - $transport->write($string_object); - $object = new $class_name(); - $object->read($protocol); - return $object; - } - } -} diff --git a/lib/php/src/protocol/TCompactProtocol.php b/lib/php/src/protocol/TCompactProtocol.php index 9f0d4076..e69de29b 100644 --- a/lib/php/src/protocol/TCompactProtocol.php +++ b/lib/php/src/protocol/TCompactProtocol.php @@ -1,678 +0,0 @@ - TCompactProtocol::COMPACT_STOP, - TType::BOOL => TCompactProtocol::COMPACT_TRUE, // used for collection - TType::BYTE => TCompactProtocol::COMPACT_BYTE, - TType::I16 => TCompactProtocol::COMPACT_I16, - TType::I32 => TCompactProtocol::COMPACT_I32, - TType::I64 => TCompactProtocol::COMPACT_I64, - TType::DOUBLE => TCompactProtocol::COMPACT_DOUBLE, - TType::STRING => TCompactProtocol::COMPACT_BINARY, - TType::STRUCT => TCompactProtocol::COMPACT_STRUCT, - TType::LST => TCompactProtocol::COMPACT_LIST, - TType::SET => TCompactProtocol::COMPACT_SET, - TType::MAP => TCompactProtocol::COMPACT_MAP, - ); - - protected static $ttypes = array( - TCompactProtocol::COMPACT_STOP => TType::STOP , - TCompactProtocol::COMPACT_TRUE => TType::BOOL, // used for collection - TCompactProtocol::COMPACT_FALSE => TType::BOOL, - TCompactProtocol::COMPACT_BYTE => TType::BYTE, - TCompactProtocol::COMPACT_I16 => TType::I16, - TCompactProtocol::COMPACT_I32 => TType::I32, - TCompactProtocol::COMPACT_I64 => TType::I64, - TCompactProtocol::COMPACT_DOUBLE => TType::DOUBLE, - TCompactProtocol::COMPACT_BINARY => TType::STRING, - TCompactProtocol::COMPACT_STRUCT => TType::STRUCT, - TCompactProtocol::COMPACT_LIST => TType::LST, - TCompactProtocol::COMPACT_SET => TType::SET, - TCompactProtocol::COMPACT_MAP => TType::MAP, - ); - - protected $state = TCompactProtocol::STATE_CLEAR; - protected $lastFid = 0; - protected $boolFid = null; - protected $boolValue = null; - protected $structs = array(); - protected $containers = array(); - - // Some varint / zigzag helper methods - public function toZigZag($n, $bits) { - return ($n << 1) ^ ($n >> ($bits - 1)); - } - - public function fromZigZag($n) { - return ($n >> 1) ^ -($n & 1); - } - - public function getVarint($data) { - $out = ""; - while (true) { - if (($data & ~0x7f) === 0) { - $out .= chr($data); - break; - } else { - $out .= chr(($data & 0xff) | 0x80); - $data = $data >> 7; - } - } - return $out; - } - - public function writeVarint($data) { - $out = $this->getVarint($data); - $result = TStringFuncFactory::create()->strlen($out); - $this->trans_->write($out, $result); - return $result; - } - - public function readVarint(&$result) { - $idx = 0; - $shift = 0; - $result = 0; - while (true) { - $x = $this->trans_->readAll(1); - $arr = unpack('C', $x); - $byte = $arr[1]; - $idx += 1; - $result |= ($byte & 0x7f) << $shift; - if (($byte >> 7) === 0) { - return $idx; - } - $shift += 7; - } - - return $idx; - } - - public function __construct($trans) { - parent::__construct($trans); - } - - public function writeMessageBegin($name, $type, $seqid) { - $written = - $this->writeUByte(TCompactProtocol::PROTOCOL_ID) + - $this->writeUByte(TCompactProtocol::VERSION | - ($type << TCompactProtocol::TYPE_SHIFT_AMOUNT)) + - $this->writeVarint($seqid) + - $this->writeString($name); - $this->state = TCompactProtocol::STATE_VALUE_WRITE; - return $written; - } - - public function writeMessageEnd() { - $this->state = TCompactProtocol::STATE_CLEAR; - return 0; - } - - public function writeStructBegin($name) { - $this->structs[] = array($this->state, $this->lastFid); - $this->state = TCompactProtocol::STATE_FIELD_WRITE; - $this->lastFid = 0; - return 0; - } - - public function writeStructEnd() { - $old_values = array_pop($this->structs); - $this->state = $old_values[0]; - $this->lastFid = $old_values[1]; - return 0; - } - - public function writeFieldStop() { - return $this->writeByte(0); - } - - public function writeFieldHeader($type, $fid) { - $written = 0; - $delta = $fid - $this->lastFid; - if (0 < $delta && $delta <= 15) { - $written = $this->writeUByte(($delta << 4) | $type); - } else { - $written = $this->writeByte($type) + - $this->writeI16($fid); - } - $this->lastFid = $fid; - return $written; - } - - public function writeFieldBegin($field_name, $field_type, $field_id) { - if ($field_type == TTYPE::BOOL) { - $this->state = TCompactProtocol::STATE_BOOL_WRITE; - $this->boolFid = $field_id; - return 0; - } else { - $this->state = TCompactProtocol::STATE_VALUE_WRITE; - return $this->writeFieldHeader(self::$ctypes[$field_type], $field_id); - } - } - - public function writeFieldEnd() { - $this->state = TCompactProtocol::STATE_FIELD_WRITE; - return 0; - } - - public function writeCollectionBegin($etype, $size) { - $written = 0; - if ($size <= 14) { - $written = $this->writeUByte($size << 4 | - self::$ctypes[$etype]); - } else { - $written = $this->writeUByte(0xf0 | - self::$ctypes[$etype]) + - $this->writeVarint($size); - } - $this->containers[] = $this->state; - $this->state = TCompactProtocol::STATE_CONTAINER_WRITE; - - return $written; - } - - public function writeMapBegin($key_type, $val_type, $size) { - $written = 0; - if ($size == 0) { - $written = $this->writeByte(0); - } else { - $written = $this->writeVarint($size) + - $this->writeUByte(self::$ctypes[$key_type] << 4 | - self::$ctypes[$val_type]); - } - $this->containers[] = $this->state; - return $written; - } - - public function writeCollectionEnd() { - $this->state = array_pop($this->containers); - return 0; - } - - public function writeMapEnd() { - return $this->writeCollectionEnd(); - } - - public function writeListBegin($elem_type, $size) { - return $this->writeCollectionBegin($elem_type, $size); - } - - public function writeListEnd() { - return $this->writeCollectionEnd(); - } - - public function writeSetBegin($elem_type, $size) { - return $this->writeCollectionBegin($elem_type, $size); - } - - public function writeSetEnd() { - return $this->writeCollectionEnd(); - } - - public function writeBool($value) { - if ($this->state == TCompactProtocol::STATE_BOOL_WRITE) { - $ctype = TCompactProtocol::COMPACT_FALSE; - if ($value) { - $ctype = TCompactProtocol::COMPACT_TRUE; - } - return $this->writeFieldHeader($ctype, $this->boolFid); - } else if ($this->state == TCompactProtocol::STATE_CONTAINER_WRITE) { - return $this->writeByte($value ? 1 : 0); - } else { - throw new TProtocolException('Invalid state in compact protocol'); - } - } - - public function writeByte($value) { - $data = pack('c', $value); - $this->trans_->write($data, 1); - return 1; - } - - public function writeUByte($byte) { - $this->trans_->write(pack('C', $byte), 1); - return 1; - } - - public function writeI16($value) { - $thing = $this->toZigZag($value, 16); - return $this->writeVarint($thing); - } - - public function writeI32($value) { - $thing = $this->toZigZag($value, 32); - return $this->writeVarint($thing); - } - - public function writeDouble($value) { - $data = pack('d', $value); - $this->trans_->write(strrev($data), 8); - return 8; - } - - public function writeString($value) { - $len = TStringFuncFactory::create()->strlen($value); - $result = $this->writeVarint($len); - if ($len) { - $this->trans_->write($value, $len); - } - return $result + $len; - } - - public function readFieldBegin(&$name, &$field_type, &$field_id) { - $result = $this->readUByte($field_type); - - if (($field_type & 0x0f) == TType::STOP) { - $field_id = 0; - return $result; - } - $delta = $field_type >> 4; - if ($delta == 0) { - $result += $this->readI16($field_id); - } else { - $field_id = $this->lastFid + $delta; - } - $this->lastFid = $field_id; - $field_type = $this->getTType($field_type & 0x0f); - if ($field_type == TCompactProtocol::COMPACT_TRUE) { - $this->state = TCompactProtocol::STATE_BOOL_READ; - $this->boolValue = true; - } else if ($field_type == TCompactProtocol::COMPACT_FALSE) { - $this->state = TCompactProtocol::STATE_BOOL_READ; - $this->boolValue = false; - } else { - $this->state = TCompactProtocol::STATE_VALUE_READ; - } - return $result; - } - - public function readFieldEnd() { - $this->state = TCompactProtocol::STATE_FIELD_READ; - return 0; - } - - public function readUByte(&$value) { - $data = $this->trans_->readAll(1); - $arr = unpack('C', $data); - $value = $arr[1]; - return 1; - } - - public function readByte(&$value) { - $data = $this->trans_->readAll(1); - $arr = unpack('c', $data); - $value = $arr[1]; - return 1; - } - - public function readZigZag(&$value) { - $result = $this->readVarint($value); - $value = $this->fromZigZag($value); - return $result; - } - - public function readMessageBegin(&$name, &$type, &$seqid) { - $protoId = 0; - $result = $this->readUByte($protoId); - if ($protoId != TCompactProtocol::PROTOCOL_ID) { - throw new TProtocolException('Bad protocol id in TCompact message'); - } - $verType = 0; - $result += $this->readUByte($verType); - $type = ($verType & TCompactProtocol::TYPE_MASK) >> - TCompactProtocol::TYPE_SHIFT_AMOUNT; - $version = $verType & TCompactProtocol::VERSION_MASK; - if ($version != TCompactProtocol::VERSION) { - throw new TProtocolException('Bad version in TCompact message'); - } - $result += $this->readVarint($seqId); - $name += $this->readString($name); - - return $result; - } - - public function readMessageEnd() { - return 0; - } - - public function readStructBegin(&$name) { - $name = ''; // unused - $this->structs[] = array($this->state, $this->lastFid); - $this->state = TCompactProtocol::STATE_FIELD_READ; - $this->lastFid = 0; - return 0; - } - - public function readStructEnd() { - $last = array_pop($this->structs); - $this->state = $last[0]; - $this->lastFid = $last[1]; - return 0; - } - - public function readCollectionBegin(&$type, &$size) { - $sizeType = 0; - $result = $this->readUByte($sizeType); - $size = $sizeType >> 4; - $type = $this->getTType($sizeType); - if ($size == 15) { - $result += $this->readVarint($size); - } - $this->containers[] = $this->state; - $this->state = TCompactProtocol::STATE_CONTAINER_READ; - - return $result; - } - - public function readMapBegin(&$key_type, &$val_type, &$size) { - $result = $this->readVarint($size); - $types = 0; - if ($size > 0) { - $result += $this->readUByte($types); - } - $val_type = $this->getTType($types); - $key_type = $this->getTType($types >> 4); - $this->containers[] = $this->state; - $this->state = TCompactProtocol::STATE_CONTAINER_READ; - - return $result; - } - - public function readCollectionEnd() { - $this->state = array_pop($this->containers); - return 0; - } - - public function readMapEnd() { - return $this->readCollectionEnd(); - } - - public function readListBegin(&$elem_type, &$size) { - return $this->readCollectionBegin($elem_type, $size); - } - - public function readListEnd() { - return $this->readCollectionEnd(); - } - - public function readSetBegin(&$elem_type, &$size) { - return $this->readCollectionBegin($elem_type, $size); - } - - public function readSetEnd() { - return $this->readCollectionEnd(); - } - - public function readBool(&$value) { - if ($this->state == TCompactProtocol::STATE_BOOL_READ) { - $value = $this->boolValue; - return 0; - } else if ($this->state == TCompactProtocol::STATE_CONTAINER_READ) { - return $this->readByte($value); - } else { - throw new TProtocolException('Invalid state in compact protocol'); - } - } - - public function readI16(&$value) { - return $this->readZigZag($value); - } - - public function readI32(&$value) { - return $this->readZigZag($value); - } - - public function readDouble(&$value) { - $data = strrev($this->trans_->readAll(8)); - $arr = unpack('d', $data); - $value = $arr[1]; - return 8; - } - - public function readString(&$value) { - $result = $this->readVarint($len); - if ($len) { - $value = $this->trans_->readAll($len); - } else { - $value = ''; - } - return $result + $len; - } - - public function getTType($byte) { - return self::$ttypes[$byte & 0x0f]; - } - - // If we are on a 32bit architecture we have to explicitly deal with - // 64-bit twos-complement arithmetic since PHP wants to treat all ints - // as signed and any int over 2^31 - 1 as a float - - // Read and write I64 as two 32 bit numbers $hi and $lo - - public function readI64(&$value) { - // Read varint from wire - $hi = 0; - $lo = 0; - - $idx = 0; - $shift = 0; - - while (true) { - $x = $this->trans_->readAll(1); - $arr = unpack('C', $x); - $byte = $arr[1]; - $idx += 1; - if ($shift < 32) { - $lo |= (($byte & 0x7f) << $shift) & - 0x00000000ffffffff; - } - // Shift hi and lo together. - if ($shift >= 32) { - $hi |= (($byte & 0x7f) << ($shift - 32)); - } else if ($shift > 25) { - $hi |= (($byte & 0x7f) >> ($shift - 25)); - } - if (($byte >> 7) === 0) { - break; - } - $shift += 7; - } - - // Now, unzig it. - $xorer = 0; - if ($lo & 1) { - $xorer = 0xffffffff; - } - $lo = ($lo >> 1) & 0x7fffffff; - $lo = $lo | (($hi & 1) << 31); - $hi = ($hi >> 1) ^ $xorer; - $lo = $lo ^ $xorer; - - // Now put $hi and $lo back together - if (true) { - $isNeg = $hi < 0; - - // Check for a negative - if ($isNeg) { - $hi = ~$hi & (int)0xffffffff; - $lo = ~$lo & (int)0xffffffff; - - if ($lo == (int)0xffffffff) { - $hi++; - $lo = 0; - } else { - $lo++; - } - } - - // Force 32bit words in excess of 2G to be positive - we deal with sign - // explicitly below - - if ($hi & (int)0x80000000) { - $hi &= (int)0x7fffffff; - $hi += 0x80000000; - } - - if ($lo & (int)0x80000000) { - $lo &= (int)0x7fffffff; - $lo += 0x80000000; - } - - $value = $hi * 4294967296 + $lo; - - if ($isNeg) { - $value = 0 - $value; - } - } else { - - // Upcast negatives in LSB bit - if ($arr[2] & 0x80000000) { - $arr[2] = $arr[2] & 0xffffffff; - } - - // Check for a negative - if ($arr[1] & 0x80000000) { - $arr[1] = $arr[1] & 0xffffffff; - $arr[1] = $arr[1] ^ 0xffffffff; - $arr[2] = $arr[2] ^ 0xffffffff; - $value = 0 - $arr[1] * 4294967296 - $arr[2] - 1; - } else { - $value = $arr[1] * 4294967296 + $arr[2]; - } - } - - return $idx; - } - - public function writeI64($value) { - // If we are in an I32 range, use the easy method below. - if (($value > 4294967296) || ($value < -4294967296)) { - // Convert $value to $hi and $lo - $neg = $value < 0; - - if ($neg) { - $value *= -1; - } - - $hi = (int)$value >> 32; - $lo = (int)$value & 0xffffffff; - - if ($neg) { - $hi = ~$hi; - $lo = ~$lo; - if (($lo & (int)0xffffffff) == (int)0xffffffff) { - $lo = 0; - $hi++; - } else { - $lo++; - } - } - - // Now do the zigging and zagging. - $xorer = 0; - if ($neg) { - $xorer = 0xffffffff; - } - $lowbit = ($lo >> 31) & 1; - $hi = ($hi << 1) | $lowbit; - $lo = ($lo << 1); - $lo = ($lo ^ $xorer) & 0xffffffff; - $hi = ($hi ^ $xorer) & 0xffffffff; - - // now write out the varint, ensuring we shift both hi and lo - $out = ""; - while (true) { - if (($lo & ~0x7f) === 0 && - $hi === 0) { - $out .= chr($lo); - break; - } else { - $out .= chr(($lo & 0xff) | 0x80); - $lo = $lo >> 7; - $lo = $lo | ($hi << 25); - $hi = $hi >> 7; - // Right shift carries sign, but we don't want it to. - $hi = $hi & (127 << 25); - } - } - - $ret = TStringFuncFactory::create()->strlen($out); - $this->trans_->write($out, $ret); - - return $ret; - } else { - return $this->writeVarint($this->toZigZag($value, 64)); - } - } -} - -/** - * Compact Protocol Factory - */ -class TCompcatProtocolFactory implements TProtocolFactory { - - public function __construct() { - } - - public function getProtocol($trans) { - return new TCompactProtocol($trans); - } -} - diff --git a/lib/php/src/protocol/TJSONProtocol.php b/lib/php/src/protocol/TJSONProtocol.php index b7eaa07f..e69de29b 100755 --- a/lib/php/src/protocol/TJSONProtocol.php +++ b/lib/php/src/protocol/TJSONProtocol.php @@ -1,808 +0,0 @@ - 1) { - switch (substr($name, 0, 1)) { - case 'd': - $result = TType::DOUBLE; - break; - case 'i': - switch (substr($name, 1, 1)) { - case '8': - $result = TType::BYTE; - break; - case '1': - $result = TType::I16; - break; - case '3': - $result = TType::I32; - break; - case '6': - $result = TType::I64; - break; - } - break; - case 'l': - $result = TType::LST; - break; - case 'm': - $result = TType::MAP; - break; - case 'r': - $result = TType::STRUCT; - break; - case 's': - if (substr($name, 1, 1) == 't') { - $result = TType::STRING; - } - else if (substr($name, 1, 1) == 'e') { - $result = TType::SET; - } - break; - case 't': - $result = TType::BOOL; - break; - } - } - if ($result == TType::STOP) { - throw new TProtocolException("Unrecognized type", TProtocolException::INVALID_DATA); - } - return $result; - } - - public $contextStack_ = array(); - public $context_; - public $reader_; - - private function pushContext($c) { - array_push($this->contextStack_, $this->context_); - $this->context_ = $c; - } - - private function popContext() { - $this->context_ = array_pop($this->contextStack_); - } - - public function __construct($trans) { - parent::__construct($trans); - $this->context_ = new TJSONProtocol_JSONBaseContext(); - $this->reader_ = new TJSONProtocol_LookaheadReader($this); - } - - public function reset() { - $this->contextStack_ = array(); - $this->context_ = new TJSONProtocol_JSONBaseContext(); - $this->reader_ = new TJSONProtocol_LookaheadReader($this); - } - - private $tmpbuf_ = array(4); - - public function readJSONSyntaxChar($b) { - $ch = $this->reader_->read(); - - if (substr($ch, 0, 1) != $b) { - throw new TProtocolException("Unexpected character: " . $ch, TProtocolException::INVALID_DATA); - } - } - - private function hexVal($s) { - for ($i = 0; $i < strlen($s); $i++) { - $ch = substr($s, $i, 1); - - if (!($ch >= "a" && $ch <= "f") && !($ch >= "0" && $ch <= "9")) { - throw new TProtocolException("Expected hex character " . $ch, TProtocolException::INVALID_DATA); - } - } - - return hexdec($s); - } - - private function hexChar($val) { - return dechex($val); - } - - private function writeJSONString($b) { - $this->context_->write(); - - if (is_numeric($b) && $this->context_->escapeNum()) { - $this->trans_->write(self::QUOTE); - } - - $this->trans_->write(json_encode($b)); - - if (is_numeric($b) && $this->context_->escapeNum()) { - $this->trans_->write(self::QUOTE); - } - } - - private function writeJSONInteger($num) { - $this->context_->write(); - - if ($this->context_->escapeNum()) { - $this->trans_->write(self::QUOTE); - } - - $this->trans_->write($num); - - if ($this->context_->escapeNum()) { - $this->trans_->write(self::QUOTE); - } - } - - private function writeJSONDouble($num) { - $this->context_->write(); - - if ($this->context_->escapeNum()) { - $this->trans_->write(self::QUOTE); - } - - $this->trans_->write(json_encode($num)); - - if ($this->context_->escapeNum()) { - $this->trans_->write(self::QUOTE); - } - } - - private function writeJSONBase64($data) { - $this->context_->write(); - $this->trans_->write(self::QUOTE); - $this->trans_->write(json_encode(base64_encode($data))); - $this->trans_->write(self::QUOTE); - } - - private function writeJSONObjectStart() { - $this->context_->write(); - $this->trans_->write(self::LBRACE); - $this->pushContext(new TJSONProtocol_JSONPairContext($this)); - } - - private function writeJSONObjectEnd() { - $this->popContext(); - $this->trans_->write(self::RBRACE); - } - - private function writeJSONArrayStart() { - $this->context_->write(); - $this->trans_->write(self::LBRACKET); - $this->pushContext(new TJSONProtocol_JSONListContext($this)); - } - - private function writeJSONArrayEnd() { - $this->popContext(); - $this->trans_->write(self::RBRACKET); - } - - private function readJSONString($skipContext) { - if (!$skipContext) { - $this->context_->read(); - } - - $jsonString = ''; - $lastChar = NULL; - while (true) { - $ch = $this->reader_->read(); - $jsonString .= $ch; - if ($ch == self::QUOTE && - $lastChar !== NULL && - $lastChar !== self::ESCSEQ) { - break; - } - if ($ch == self::ESCSEQ && $lastChar == self::ESCSEQ) { - $lastChar = self::DOUBLEESC; - } else { - $lastChar = $ch; - } - } - return json_decode($jsonString); - } - - private function isJSONNumeric($b) { - switch ($b) { - case '+': - case '-': - case '.': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case 'E': - case 'e': - return true; - } - return false; - } - - private function readJSONNumericChars() { - $strbld = array(); - - while (true) { - $ch = $this->reader_->peek(); - - if (!$this->isJSONNumeric($ch)) { - break; - } - - $strbld[] = $this->reader_->read(); - } - - return implode("", $strbld); - } - - private function readJSONInteger() { - $this->context_->read(); - - if ($this->context_->escapeNum()) { - $this->readJSONSyntaxChar(self::QUOTE); - } - - $str = $this->readJSONNumericChars(); - - if ($this->context_->escapeNum()) { - $this->readJSONSyntaxChar(self::QUOTE); - } - - if (!is_numeric($str)) { - throw new TProtocolException("Invalid data in numeric: " . $str, TProtocolException::INVALID_DATA); - } - - return intval($str); - } - - /** - * Identical to readJSONInteger but without the final cast. - * Needed for proper handling of i64 on 32 bit machines. Why a - * separate function? So we don't have to force the rest of the - * use cases through the extra conditional. - */ - private function readJSONIntegerAsString() { - $this->context_->read(); - - if ($this->context_->escapeNum()) { - $this->readJSONSyntaxChar(self::QUOTE); - } - - $str = $this->readJSONNumericChars(); - - if ($this->context_->escapeNum()) { - $this->readJSONSyntaxChar(self::QUOTE); - } - - if (!is_numeric($str)) { - throw new TProtocolException("Invalid data in numeric: " . $str, TProtocolException::INVALID_DATA); - } - - return $str; - } - - private function readJSONDouble() { - $this->context_->read(); - - if (substr($this->reader_->peek(), 0, 1) == self::QUOTE) { - $arr = $this->readJSONString(true); - - if ($arr == "NaN") { - return NAN; - } else if ($arr == "Infinity") { - return INF; - } else if (!$this->context_->escapeNum()) { - throw new TProtocolException("Numeric data unexpectedly quoted " . $arr, - TProtocolException::INVALID_DATA); - } - - return floatval($arr); - } else { - if ($this->context_->escapeNum()) { - $this->readJSONSyntaxChar(self::QUOTE); - } - - return floatval($this->readJSONNumericChars()); - } - } - - private function readJSONBase64() { - $arr = $this->readJSONString(false); - $data = base64_decode($arr, true); - - if ($data === false) { - throw new TProtocolException("Invalid base64 data " . $arr, TProtocolException::INVALID_DATA); - } - - return $data; - } - - private function readJSONObjectStart() { - $this->context_->read(); - $this->readJSONSyntaxChar(self::LBRACE); - $this->pushContext(new TJSONProtocol_JSONPairContext($this)); - } - - private function readJSONObjectEnd() { - $this->readJSONSyntaxChar(self::RBRACE); - $this->popContext(); - } - - private function readJSONArrayStart() - { - $this->context_->read(); - $this->readJSONSyntaxChar(self::LBRACKET); - $this->pushContext(new TJSONProtocol_JSONListContext($this)); - } - - private function readJSONArrayEnd() { - $this->readJSONSyntaxChar(self::RBRACKET); - $this->popContext(); - } - - /** - * Writes the message header - * - * @param string $name Function name - * @param int $type message type TMessageType::CALL or TMessageType::REPLY - * @param int $seqid The sequence id of this message - */ - public function writeMessageBegin($name, $type, $seqid) { - $this->writeJSONArrayStart(); - $this->writeJSONInteger(self::VERSION); - $this->writeJSONString($name); - $this->writeJSONInteger($type); - $this->writeJSONInteger($seqid); - } - - /** - * Close the message - */ - public function writeMessageEnd() { - $this->writeJSONArrayEnd(); - } - - /** - * Writes a struct header. - * - * @param string $name Struct name - * @throws TException on write error - * @return int How many bytes written - */ - public function writeStructBegin($name) { - $this->writeJSONObjectStart(); - } - - /** - * Close a struct. - * - * @throws TException on write error - * @return int How many bytes written - */ - public function writeStructEnd() { - $this->writeJSONObjectEnd(); - } - - public function writeFieldBegin($fieldName, $fieldType, $fieldId) { - $this->writeJSONInteger($fieldId); - $this->writeJSONObjectStart(); - $this->writeJSONString($this->getTypeNameForTypeID($fieldType)); - } - - public function writeFieldEnd() { - $this->writeJsonObjectEnd(); - } - - public function writeFieldStop() { - } - - public function writeMapBegin($keyType, $valType, $size) { - $this->writeJSONArrayStart(); - $this->writeJSONString($this->getTypeNameForTypeID($keyType)); - $this->writeJSONString($this->getTypeNameForTypeID($valType)); - $this->writeJSONInteger($size); - $this->writeJSONObjectStart(); - } - - public function writeMapEnd() { - $this->writeJSONObjectEnd(); - $this->writeJSONArrayEnd(); - } - - public function writeListBegin($elemType, $size) { - $this->writeJSONArrayStart(); - $this->writeJSONString($this->getTypeNameForTypeID($elemType)); - $this->writeJSONInteger($size); - } - - public function writeListEnd() { - $this->writeJSONArrayEnd(); - } - - public function writeSetBegin($elemType, $size) { - $this->writeJSONArrayStart(); - $this->writeJSONString($this->getTypeNameForTypeID($elemType)); - $this->writeJSONInteger($size); - } - - public function writeSetEnd() { - $this->writeJSONArrayEnd(); - } - - public function writeBool($bool) { - $this->writeJSONInteger($bool ? 1 : 0); - } - - public function writeByte($byte) { - $this->writeJSONInteger($byte); - } - - public function writeI16($i16) { - $this->writeJSONInteger($i16); - } - - public function writeI32($i32) { - $this->writeJSONInteger($i32); - } - - public function writeI64($i64) { - $this->writeJSONInteger($i64); - } - - public function writeDouble($dub) { - $this->writeJSONDouble($dub); - } - - public function writeString($str) { - $this->writeJSONString($str); - } - - /** - * Reads the message header - * - * @param string $name Function name - * @param int $type message type TMessageType::CALL or TMessageType::REPLY - * @parem int $seqid The sequence id of this message - */ - public function readMessageBegin(&$name, &$type, &$seqid) { - $this->readJSONArrayStart(); - - if ($this->readJSONInteger() != self::VERSION) { - throw new TProtocolException("Message contained bad version", TProtocolException::BAD_VERSION); - } - - $name = $this->readJSONString(false); - $type = $this->readJSONInteger(); - $seqid = $this->readJSONInteger(); - - return true; - } - - /** - * Read the close of message - */ - public function readMessageEnd() { - $this->readJSONArrayEnd(); - } - - public function readStructBegin(&$name) { - $this->readJSONObjectStart(); - return 0; - } - - public function readStructEnd() { - $this->readJSONObjectEnd(); - } - - public function readFieldBegin(&$name, &$fieldType, &$fieldId) { - $ch = $this->reader_->peek(); - $name = ""; - - if (substr($ch, 0, 1) == self::RBRACE) { - $fieldType = TType::STOP; - } else { - $fieldId = $this->readJSONInteger(); - $this->readJSONObjectStart(); - $fieldType = $this->getTypeIDForTypeName($this->readJSONString(false)); - } - } - - public function readFieldEnd() { - $this->readJSONObjectEnd(); - } - - public function readMapBegin(&$keyType, &$valType, &$size) { - $this->readJSONArrayStart(); - $keyType = $this->getTypeIDForTypeName($this->readJSONString(false)); - $valType = $this->getTypeIDForTypeName($this->readJSONString(false)); - $size = $this->readJSONInteger(); - $this->readJSONObjectStart(); - } - - public function readMapEnd() { - $this->readJSONObjectEnd(); - $this->readJSONArrayEnd(); - } - - public function readListBegin(&$elemType, &$size) { - $this->readJSONArrayStart(); - $elemType = $this->getTypeIDForTypeName($this->readJSONString(false)); - $size = $this->readJSONInteger(); - return true; - } - - public function readListEnd() { - $this->readJSONArrayEnd(); - } - - public function readSetBegin(&$elemType, &$size) { - $this->readJSONArrayStart(); - $elemType = $this->getTypeIDForTypeName($this->readJSONString(false)); - $size = $this->readJSONInteger(); - return true; - } - - public function readSetEnd() { - $this->readJSONArrayEnd(); - } - - public function readBool(&$bool) { - $bool = $this->readJSONInteger() == 0 ? false : true; - return true; - } - - public function readByte(&$byte) { - $byte = $this->readJSONInteger(); - return true; - } - - public function readI16(&$i16) { - $i16 = $this->readJSONInteger(); - return true; - } - - public function readI32(&$i32) { - $i32 = $this->readJSONInteger(); - return true; - } - - public function readI64(&$i64) { - if ( PHP_INT_SIZE === 4 ) { - $i64 = $this->readJSONIntegerAsString(); - } else { - $i64 = $this->readJSONInteger(); - } - return true; - } - - public function readDouble(&$dub) { - $dub = $this->readJSONDouble(); - return true; - } - - public function readString(&$str) { - $str = $this->readJSONString(false); - return true; - } -} - -/** - * JSON Protocol Factory - */ -class TJSONProtocolFactory implements TProtocolFactory -{ - public function __construct() - { - } - - public function getProtocol($trans) - { - return new TJSONProtocol($trans); - } -} - -class TJSONProtocol_JSONBaseContext -{ - function escapeNum() - { - return false; - } - - function write() - { - } - - function read() - { - } -} - -class TJSONProtocol_JSONListContext extends TJSONProtocol_JSONBaseContext -{ - private $first_ = true; - private $p_; - - public function __construct($p) { - $this->p_ = $p; - } - - public function write() { - if ($this->first_) { - $this->first_ = false; - } else { - $this->p_->getTransport()->write(TJSONProtocol::COMMA); - } - } - - public function read() { - if ($this->first_) { - $this->first_ = false; - } else { - $this->p_->readJSONSyntaxChar(TJSONProtocol::COMMA); - } - } -} - -class TJSONProtocol_JSONPairContext extends TJSONProtocol_JSONBaseContext { - private $first_ = true; - private $colon_ = true; - private $p_ = null; - - public function __construct($p) { - $this->p_ = $p; - } - - public function write() { - if ($this->first_) { - $this->first_ = false; - $this->colon_ = true; - } else { - $this->p_->getTransport()->write($this->colon_ ? TJSONProtocol::COLON : TJSONProtocol::COMMA); - $this->colon_ = !$this->colon_; - } - } - - public function read() { - if ($this->first_) { - $this->first_ = false; - $this->colon_ = true; - } else { - $this->p_->readJSONSyntaxChar($this->colon_ ? TJSONProtocol::COLON : TJSONProtocol::COMMA); - $this->colon_ = !$this->colon_; - } - } - - public function escapeNum() { - return $this->colon_; - } -} - -class TJSONProtocol_LookaheadReader -{ - private $hasData_ = false; - private $data_ = array(); - private $p_; - - public function __construct($p) - { - $this->p_ = $p; - } - - public function read() { - if ($this->hasData_) { - $this->hasData_ = false; - } else { - $this->data_ = $this->p_->getTransport()->readAll(1); - } - - return substr($this->data_, 0, 1); - } - - public function peek() { - if (!$this->hasData_) { - $this->data_ = $this->p_->getTransport()->readAll(1); - } - - $this->hasData_ = true; - return substr($this->data_, 0, 1); - } -} - - diff --git a/lib/php/src/protocol/TProtocol.php b/lib/php/src/protocol/TProtocol.php index 239648e7..e69de29b 100644 --- a/lib/php/src/protocol/TProtocol.php +++ b/lib/php/src/protocol/TProtocol.php @@ -1,376 +0,0 @@ -trans_ = $trans; - } - - /** - * Accessor for transport - * - * @return TTransport - */ - public function getTransport() { - return $this->trans_; - } - - /** - * Writes the message header - * - * @param string $name Function name - * @param int $type message type TMessageType::CALL or TMessageType::REPLY - * @param int $seqid The sequence id of this message - */ - public abstract function writeMessageBegin($name, $type, $seqid); - - /** - * Close the message - */ - public abstract function writeMessageEnd(); - - /** - * Writes a struct header. - * - * @param string $name Struct name - * @throws TException on write error - * @return int How many bytes written - */ - public abstract function writeStructBegin($name); - - /** - * Close a struct. - * - * @throws TException on write error - * @return int How many bytes written - */ - public abstract function writeStructEnd(); - - /* - * Starts a field. - * - * @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($fieldName, $fieldType, $fieldId); - - public abstract function writeFieldEnd(); - - public abstract function writeFieldStop(); - - public abstract function writeMapBegin($keyType, $valType, $size); - - public abstract function writeMapEnd(); - - public abstract function writeListBegin($elemType, $size); - - public abstract function writeListEnd(); - - public abstract function writeSetBegin($elemType, $size); - - public abstract function writeSetEnd(); - - public abstract function writeBool($bool); - - public abstract function writeByte($byte); - - public abstract function writeI16($i16); - - public abstract function writeI32($i32); - - public abstract function writeI64($i64); - - public abstract function writeDouble($dub); - - public abstract function writeString($str); - - /** - * Reads the message header - * - * @param string $name Function name - * @param int $type message type TMessageType::CALL or TMessageType::REPLY - * @parem int $seqid The sequence id of this message - */ - public abstract function readMessageBegin(&$name, &$type, &$seqid); - - /** - * Read the close of message - */ - public abstract function readMessageEnd(); - - public abstract function readStructBegin(&$name); - - public abstract function readStructEnd(); - - public abstract function readFieldBegin(&$name, &$fieldType, &$fieldId); - - public abstract function readFieldEnd(); - - public abstract function readMapBegin(&$keyType, &$valType, &$size); - - public abstract function readMapEnd(); - - public abstract function readListBegin(&$elemType, &$size); - - public abstract function readListEnd(); - - public abstract function readSetBegin(&$elemType, &$size); - - public abstract function readSetEnd(); - - public abstract function readBool(&$bool); - - public abstract function readByte(&$byte); - - public abstract function readI16(&$i16); - - public abstract function readI32(&$i32); - - public abstract function readI64(&$i64); - - public abstract function readDouble(&$dub); - - public abstract function readString(&$str); - - /** - * The skip function is a utility to parse over unrecognized date without - * causing corruption. - * - * @param TType $type What type is it - */ - public function skip($type) { - switch ($type) { - case TType::BOOL: - return $this->readBool($bool); - case TType::BYTE: - return $this->readByte($byte); - case TType::I16: - return $this->readI16($i16); - case TType::I32: - return $this->readI32($i32); - case TType::I64: - return $this->readI64($i64); - case TType::DOUBLE: - return $this->readDouble($dub); - case TType::STRING: - return $this->readString($str); - case TType::STRUCT: - { - $result = $this->readStructBegin($name); - while (true) { - $result += $this->readFieldBegin($name, $ftype, $fid); - if ($ftype == TType::STOP) { - break; - } - $result += $this->skip($ftype); - $result += $this->readFieldEnd(); - } - $result += $this->readStructEnd(); - return $result; - } - case TType::MAP: - { - $result = $this->readMapBegin($keyType, $valType, $size); - for ($i = 0; $i < $size; $i++) { - $result += $this->skip($keyType); - $result += $this->skip($valType); - } - $result += $this->readMapEnd(); - return $result; - } - case TType::SET: - { - $result = $this->readSetBegin($elemType, $size); - for ($i = 0; $i < $size; $i++) { - $result += $this->skip($elemType); - } - $result += $this->readSetEnd(); - return $result; - } - case TType::LST: - { - $result = $this->readListBegin($elemType, $size); - for ($i = 0; $i < $size; $i++) { - $result += $this->skip($elemType); - } - $result += $this->readListEnd(); - return $result; - } - default: - throw new TProtocolException('Unknown field type: '.$type, - TProtocolException::INVALID_DATA); - } - } - - /** - * Utility for skipping binary data - * - * @param TTransport $itrans TTransport object - * @param int $type Field type - */ - public static function skipBinary($itrans, $type) { - switch ($type) { - case TType::BOOL: - return $itrans->readAll(1); - case TType::BYTE: - return $itrans->readAll(1); - case TType::I16: - return $itrans->readAll(2); - case TType::I32: - return $itrans->readAll(4); - case TType::I64: - return $itrans->readAll(8); - case TType::DOUBLE: - return $itrans->readAll(8); - case TType::STRING: - $len = unpack('N', $itrans->readAll(4)); - $len = $len[1]; - if ($len > 0x7fffffff) { - $len = 0 - (($len - 1) ^ 0xffffffff); - } - return 4 + $itrans->readAll($len); - case TType::STRUCT: - { - $result = 0; - while (true) { - $ftype = 0; - $fid = 0; - $data = $itrans->readAll(1); - $arr = unpack('c', $data); - $ftype = $arr[1]; - if ($ftype == TType::STOP) { - break; - } - // I16 field id - $result += $itrans->readAll(2); - $result += self::skipBinary($itrans, $ftype); - } - return $result; - } - case TType::MAP: - { - // Ktype - $data = $itrans->readAll(1); - $arr = unpack('c', $data); - $ktype = $arr[1]; - // Vtype - $data = $itrans->readAll(1); - $arr = unpack('c', $data); - $vtype = $arr[1]; - // Size - $data = $itrans->readAll(4); - $arr = unpack('N', $data); - $size = $arr[1]; - if ($size > 0x7fffffff) { - $size = 0 - (($size - 1) ^ 0xffffffff); - } - $result = 6; - for ($i = 0; $i < $size; $i++) { - $result += self::skipBinary($itrans, $ktype); - $result += self::skipBinary($itrans, $vtype); - } - return $result; - } - case TType::SET: - case TType::LST: - { - // Vtype - $data = $itrans->readAll(1); - $arr = unpack('c', $data); - $vtype = $arr[1]; - // Size - $data = $itrans->readAll(4); - $arr = unpack('N', $data); - $size = $arr[1]; - if ($size > 0x7fffffff) { - $size = 0 - (($size - 1) ^ 0xffffffff); - } - $result = 5; - for ($i = 0; $i < $size; $i++) { - $result += self::skipBinary($itrans, $vtype); - } - return $result; - } - default: - throw new TProtocolException('Unknown field type: '.$type, - TProtocolException::INVALID_DATA); - } - } -} - -/** - * Protocol factory creates protocol objects from transports - */ -interface TProtocolFactory { - /** - * Build a protocol from the base transport - * - * @return TProtocol protocol - */ - public function getProtocol($trans); -} diff --git a/lib/php/src/server/TForkingServer.php b/lib/php/src/server/TForkingServer.php index e2370cb0..e69de29b 100644 --- a/lib/php/src/server/TForkingServer.php +++ b/lib/php/src/server/TForkingServer.php @@ -1,114 +0,0 @@ -transport_->listen(); - - while (!$this->stop_) { - try { - $transport = $this->transport_->accept(); - - if ($transport != null) { - $pid = pcntl_fork(); - - if ($pid > 0) { - $this->handleParent($transport, $pid); - } - else if ($pid === 0) { - $this->handleChild($transport); - } - else { - throw new TException('Failed to fork'); - } - } - } - catch (TTransportException $e) { } - - $this->collectChildren(); - } - } - - /** - * Code run by the parent - * - * @param TTransport $transport - * @param int $pid - * @return void - */ - private function handleParent(TTransport $transport, $pid) { - $this->children_[$pid] = $transport; - } - - /** - * Code run by the child. - * - * @param TTransport $transport - * @return void - */ - private function handleChild(TTransport $transport) { - try { - $inputTransport = $this->inputTransportFactory_->getTransport($transport); - $outputTransport = $this->outputTransportFactory_->getTransport($transport); - $inputProtocol = $this->inputProtocolFactory_->getProtocol($inputTransport); - $outputProtocol = $this->outputProtocolFactory_->getProtocol($outputTransport); - while ($this->processor_->process($inputProtocol, $outputProtocol)) { } - @$transport->close(); - } - catch (TTransportException $e) { } - - exit(0); - } - - /** - * Collects any children we may have - * - * @return void - */ - private function collectChildren() { - foreach ($this->children_ as $pid => $transport) { - if (pcntl_waitpid($pid, $status, WNOHANG) > 0) { - unset($this->children_[$pid]); - if ($transport) @$transport->close(); - } - } - } - - /** - * Stops the server running. Kills the transport - * and then stops the main serving loop - * - * @return void - */ - public function stop() { - $this->transport_->close(); - $this->stop_ = true; - } -} diff --git a/lib/php/src/server/TServer.php b/lib/php/src/server/TServer.php index b25a9ee2..e69de29b 100644 --- a/lib/php/src/server/TServer.php +++ b/lib/php/src/server/TServer.php @@ -1,95 +0,0 @@ -processor_ = $processor; - $this->transport_ = $transport; - $this->inputTransportFactory_ = $inputTransportFactory; - $this->outputTransportFactory_ = $outputTransportFactory; - $this->inputProtocolFactory_ = $inputProtocolFactory; - $this->outputProtocolFactory_ = $outputProtocolFactory; - } - - /** - * Serves the server. This should never return - * unless a problem permits it to do so or it - * is interrupted intentionally - * - * @abstract - * @return void - */ - abstract public function serve(); - - /** - * Stops the server serving - * - * @abstract - * @return void - */ - abstract public function stop(); -} diff --git a/lib/php/src/server/TSimpleServer.php b/lib/php/src/server/TSimpleServer.php index f7d0a328..e69de29b 100644 --- a/lib/php/src/server/TSimpleServer.php +++ b/lib/php/src/server/TSimpleServer.php @@ -1,54 +0,0 @@ -transport_->listen(); - - while (!$this->stop_) { - try { - $transport = $this->transport_->accept(); - - if ($transport != null) { - $inputTransport = $this->inputTransportFactory_->getTransport($transport); - $outputTransport = $this->outputTransportFactory_->getTransport($transport); - $inputProtocol = $this->inputProtocolFactory_->getProtocol($inputTransport); - $outputProtocol = $this->outputProtocolFactory_->getProtocol($outputTransport); - while ($this->processor_->process($inputProtocol, $outputProtocol)) { } - } - } - catch (TTransportException $e) { } - } - } - - /** - * Stops the server running. Kills the transport - * and then stops the main serving loop - * - * @return void - */ - public function stop() { - $this->transport_->close(); - $this->stop_ = true; - } -} diff --git a/lib/php/src/transport/TBufferedTransport.php b/lib/php/src/transport/TBufferedTransport.php index 9b27d023..e69de29b 100644 --- a/lib/php/src/transport/TBufferedTransport.php +++ b/lib/php/src/transport/TBufferedTransport.php @@ -1,161 +0,0 @@ -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 putBack($data) { - if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) { - $this->rBuf_ = $data; - } else { - $this->rBuf_ = ($data . $this->rBuf_); - } - } - - /** - * The reason that we customize readAll here is that the majority of PHP - * streams are already internally buffered by PHP. The socket stream, for - * example, buffers internally and blocks if you call read with $len greater - * than the amount of data available, unlike recv() in C. - * - * Therefore, use the readAll method of the wrapped transport inside - * the buffered readAll. - */ - public function readAll($len) { - $have = TStringFuncFactory::create()->strlen($this->rBuf_); - if ($have == 0) { - $data = $this->transport_->readAll($len); - } else if ($have < $len) { - $data = $this->rBuf_; - $this->rBuf_ = ''; - $data .= $this->transport_->readAll($len - $have); - } else if ($have == $len) { - $data = $this->rBuf_; - $this->rBuf_ = ''; - } else if ($have > $len) { - $data = TStringFuncFactory::create()->substr($this->rBuf_, 0, $len); - $this->rBuf_ = TStringFuncFactory::create()->substr($this->rBuf_, $len); - } - return $data; - } - - public function read($len) { - if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) { - $this->rBuf_ = $this->transport_->read($this->rBufSize_); - } - - if (TStringFuncFactory::create()->strlen($this->rBuf_) <= $len) { - $ret = $this->rBuf_; - $this->rBuf_ = ''; - return $ret; - } - - $ret = TStringFuncFactory::create()->substr($this->rBuf_, 0, $len); - $this->rBuf_ = TStringFuncFactory::create()->substr($this->rBuf_, $len); - return $ret; - } - - public function write($buf) { - $this->wBuf_ .= $buf; - if (TStringFuncFactory::create()->strlen($this->wBuf_) >= $this->wBufSize_) { - $out = $this->wBuf_; - - // Note that we clear the internal wBuf_ prior to the underlying write - // to ensure we're in a sane state (i.e. internal buffer cleaned) - // if the underlying write throws up an exception - $this->wBuf_ = ''; - $this->transport_->write($out); - } - } - - public function flush() { - if (TStringFuncFactory::create()->strlen($this->wBuf_) > 0) { - $this->transport_->write($this->wBuf_); - $this->wBuf_ = ''; - } - $this->transport_->flush(); - } - -} diff --git a/lib/php/src/transport/TFramedTransport.php b/lib/php/src/transport/TFramedTransport.php index bc759dea..e69de29b 100644 --- a/lib/php/src/transport/TFramedTransport.php +++ b/lib/php/src/transport/TFramedTransport.php @@ -1,179 +0,0 @@ -transport_ = $transport; - $this->read_ = $read; - $this->write_ = $write; - } - - public function isOpen() { - return $this->transport_->isOpen(); - } - - public function open() { - $this->transport_->open(); - } - - public function close() { - $this->transport_->close(); - } - - /** - * 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) { - if (!$this->read_) { - return $this->transport_->read($len); - } - - if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) { - $this->readFrame(); - } - - // Just return full buff - if ($len >= TStringFuncFactory::create()->strlen($this->rBuf_)) { - $out = $this->rBuf_; - $this->rBuf_ = null; - return $out; - } - - // Return TStringFuncFactory::create()->substr - $out = TStringFuncFactory::create()->substr($this->rBuf_, 0, $len); - $this->rBuf_ = TStringFuncFactory::create()->substr($this->rBuf_, $len); - return $out; - } - - /** - * Put previously read data back into the buffer - * - * @param string $data data to return - */ - public function putBack($data) { - if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) { - $this->rBuf_ = $data; - } else { - $this->rBuf_ = ($data . $this->rBuf_); - } - } - - /** - * Reads a chunk of data into the internal read buffer. - */ - private function readFrame() { - $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 (!$this->write_) { - return $this->transport_->write($buf, $len); - } - - if ($len !== null && $len < TStringFuncFactory::create()->strlen($buf)) { - $buf = TStringFuncFactory::create()->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() { - if (!$this->write_ || TStringFuncFactory::create()->strlen($this->wBuf_) == 0) { - return $this->transport_->flush(); - } - - $out = pack('N', TStringFuncFactory::create()->strlen($this->wBuf_)); - $out .= $this->wBuf_; - - // Note that we clear the internal wBuf_ prior to the underlying write - // to ensure we're in a sane state (i.e. internal buffer cleaned) - // if the underlying write throws up an exception - $this->wBuf_ = ''; - $this->transport_->write($out); - $this->transport_->flush(); - } - -} diff --git a/lib/php/src/transport/THttpClient.php b/lib/php/src/transport/THttpClient.php index 118f0d3e..e69de29b 100644 --- a/lib/php/src/transport/THttpClient.php +++ b/lib/php/src/transport/THttpClient.php @@ -1,200 +0,0 @@ -strlen($uri) > 0) && ($uri{0} != '/')) { - $uri = '/'.$uri; - } - $this->scheme_ = $scheme; - $this->host_ = $host; - $this->port_ = $port; - $this->uri_ = $uri; - $this->buf_ = ''; - $this->handle_ = null; - $this->timeout_ = null; - } - - /** - * Set read timeout - * - * @param float $timeout - */ - public function setTimeoutSecs($timeout) { - $this->timeout_ = $timeout; - } - - /** - * Whether this transport is open. - * - * @return boolean true if open - */ - public function isOpen() { - return true; - } - - /** - * Open the transport for reading/writing - * - * @throws TTransportException if cannot open - */ - public function open() {} - - /** - * Close the transport. - */ - public function close() { - if ($this->handle_) { - @fclose($this->handle_); - $this->handle_ = null; - } - } - - /** - * 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 function read($len) { - $data = @fread($this->handle_, $len); - if ($data === FALSE || $data === '') { - $md = stream_get_meta_data($this->handle_); - if ($md['timed_out']) { - throw new TTransportException('THttpClient: timed out reading '.$len.' bytes from '.$this->host_.':'.$this->port_.$this->uri_, TTransportException::TIMED_OUT); - } else { - throw new TTransportException('THttpClient: Could not read '.$len.' bytes from '.$this->host_.':'.$this->port_.$this->uri_, TTransportException::UNKNOWN); - } - } - return $data; - } - - /** - * Writes some data into the pending buffer - * - * @param string $buf The data to write - * @throws TTransportException if writing fails - */ - public function write($buf) { - $this->buf_ .= $buf; - } - - /** - * Opens and sends the actual request over the HTTP connection - * - * @throws TTransportException if a writing error occurs - */ - public function flush() { - // God, PHP really has some esoteric ways of doing simple things. - $host = $this->host_.($this->port_ != 80 ? ':'.$this->port_ : ''); - - $headers = array('Host: '.$host, - 'Accept: application/x-thrift', - 'User-Agent: PHP/THttpClient', - 'Content-Type: application/x-thrift', - 'Content-Length: '.TStringFuncFactory::create()->strlen($this->buf_)); - - $options = array('method' => 'POST', - 'header' => implode("\r\n", $headers), - 'max_redirects' => 1, - 'content' => $this->buf_); - if ($this->timeout_ > 0) { - $options['timeout'] = $this->timeout_; - } - $this->buf_ = ''; - - $contextid = stream_context_create(array('http' => $options)); - $this->handle_ = @fopen($this->scheme_.'://'.$host.$this->uri_, 'r', false, $contextid); - - // Connect failed? - if ($this->handle_ === FALSE) { - $this->handle_ = null; - $error = 'THttpClient: Could not connect to '.$host.$this->uri_; - throw new TTransportException($error, TTransportException::NOT_OPEN); - } - } - -} diff --git a/lib/php/src/transport/TMemoryBuffer.php b/lib/php/src/transport/TMemoryBuffer.php index 13aa9e54..e69de29b 100644 --- a/lib/php/src/transport/TMemoryBuffer.php +++ b/lib/php/src/transport/TMemoryBuffer.php @@ -1,82 +0,0 @@ -buf_ = $buf; - } - - protected $buf_ = ''; - - public function isOpen() { - return true; - } - - public function open() {} - - public function close() {} - - public function write($buf) { - $this->buf_ .= $buf; - } - - public function read($len) { - if (TStringFuncFactory::create()->strlen($this->buf_) === 0) { - throw new TTransportException('TMemoryBuffer: Could not read ' . - $len . ' bytes from buffer.', - TTransportException::UNKNOWN); - } - - if (TStringFuncFactory::create()->strlen($this->buf_) <= $len) { - $ret = $this->buf_; - $this->buf_ = ''; - return $ret; - } - - $ret = TStringFuncFactory::create()->substr($this->buf_, 0, $len); - $this->buf_ = TStringFuncFactory::create()->substr($this->buf_, $len); - - return $ret; - } - - function getBuffer() { - return $this->buf_; - } - - public function available() { - return TStringFuncFactory::create()->strlen($this->buf_); - } -} diff --git a/lib/php/src/transport/TNullTransport.php b/lib/php/src/transport/TNullTransport.php index 6e5340d3..e69de29b 100644 --- a/lib/php/src/transport/TNullTransport.php +++ b/lib/php/src/transport/TNullTransport.php @@ -1,46 +0,0 @@ -read_ = $mode & self::MODE_R; - $this->write_ = $mode & self::MODE_W; - } - - public function open() { - if ($this->read_) { - $this->inStream_ = @fopen(self::inStreamName(), 'r'); - if (!is_resource($this->inStream_)) { - throw new TException('TPhpStream: Could not open php://input'); - } - } - if ($this->write_) { - $this->outStream_ = @fopen('php://output', 'w'); - if (!is_resource($this->outStream_)) { - throw new TException('TPhpStream: Could not open php://output'); - } - } - } - - public function close() { - if ($this->read_) { - @fclose($this->inStream_); - $this->inStream_ = null; - } - if ($this->write_) { - @fclose($this->outStream_); - $this->outStream_ = null; - } - } - - public function isOpen() { - return - (!$this->read_ || is_resource($this->inStream_)) && - (!$this->write_ || is_resource($this->outStream_)); - } - - public function read($len) { - $data = @fread($this->inStream_, $len); - if ($data === FALSE || $data === '') { - throw new TException('TPhpStream: Could not read '.$len.' bytes'); - } - return $data; - } - - public function write($buf) { - while (TStringFuncFactory::create()->strlen($buf) > 0) { - $got = @fwrite($this->outStream_, $buf); - if ($got === 0 || $got === FALSE) { - throw new TException('TPhpStream: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes'); - } - $buf = TStringFuncFactory::create()->substr($buf, $got); - } - } - - public function flush() { - @fflush($this->outStream_); - } - - private static function inStreamName() { - if (php_sapi_name() == 'cli') { - return 'php://stdin'; - } - return 'php://input'; - } - -} diff --git a/lib/php/src/transport/TServerSocket.php b/lib/php/src/transport/TServerSocket.php index cac12ab7..e69de29b 100644 --- a/lib/php/src/transport/TServerSocket.php +++ b/lib/php/src/transport/TServerSocket.php @@ -1,96 +0,0 @@ -host_ = $host; - $this->port_ = $port; - } - - /** - * Sets the accept timeout - * - * @param int $acceptTimeout - * @return void - */ - public function setAcceptTimeout($acceptTimeout) { - $this->acceptTimeout_ = $acceptTimeout; - } - - /** - * Opens a new socket server handle - * - * @return void - */ - public function listen() { - $this->listener_ = stream_socket_server('tcp://' . $this->host_ . ':' . $this->port_); - } - - /** - * Closes the socket server handle - * - * @return void - */ - public function close() { - @fclose($this->listener_); - $this->listener_ = null; - } - - /** - * Implementation of accept. If not client is accepted in the given time - * - * @return TSocket - */ - protected function acceptImpl() { - $handle = @stream_socket_accept($this->listener_, $this->acceptTimeout_ / 1000.0); - if(!$handle) return null; - - $socket = new TSocket(); - $socket->setHandle($handle); - - return $socket; - } -} diff --git a/lib/php/src/transport/TServerTransport.php b/lib/php/src/transport/TServerTransport.php index e92ca77c..e69de29b 100644 --- a/lib/php/src/transport/TServerTransport.php +++ b/lib/php/src/transport/TServerTransport.php @@ -1,50 +0,0 @@ -acceptImpl(); - - if ($transport == null) { - throw new TTransportException("accept() may not return NULL"); - } - - return $transport; - } -} diff --git a/lib/php/src/transport/TSocket.php b/lib/php/src/transport/TSocket.php index 02033846..e69de29b 100644 --- a/lib/php/src/transport/TSocket.php +++ b/lib/php/src/transport/TSocket.php @@ -1,320 +0,0 @@ -host_ = $host; - $this->port_ = $port; - $this->persist_ = $persist; - $this->debugHandler_ = $debugHandler ? $debugHandler : 'error_log'; - } - - /** - * @param resource $handle - * @return void - */ - public function setHandle($handle) { - $this->handle_ = $handle; - } - - /** - * Sets the send timeout. - * - * @param int $timeout Timeout in milliseconds. - */ - public function setSendTimeout($timeout) { - $this->sendTimeoutSec_ = floor($timeout / 1000); - $this->sendTimeoutUsec_ = - ($timeout - ($this->sendTimeoutSec_ * 1000)) * 1000; - } - - /** - * Sets the receive timeout. - * - * @param int $timeout Timeout in milliseconds. - */ - public function setRecvTimeout($timeout) { - $this->recvTimeoutSec_ = floor($timeout / 1000); - $this->recvTimeoutUsec_ = - ($timeout - ($this->recvTimeoutSec_ * 1000)) * 1000; - } - - /** - * Sets debugging output on or off - * - * @param bool $debug - */ - public function setDebug($debug) { - $this->debug_ = $debug; - } - - /** - * Get the host that this socket is connected to - * - * @return string host - */ - public function getHost() { - return $this->host_; - } - - /** - * Get the remote port that this socket is connected to - * - * @return int port - */ - public function getPort() { - return $this->port_; - } - - /** - * 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->isOpen()) { - throw new TTransportException('Socket already connected', TTransportException::ALREADY_OPEN); - } - - if (empty($this->host_)) { - throw new TTransportException('Cannot open null host', TTransportException::NOT_OPEN); - } - - if ($this->port_ <= 0) { - throw new TTransportException('Cannot open without port', TTransportException::NOT_OPEN); - } - - if ($this->persist_) { - $this->handle_ = @pfsockopen($this->host_, - $this->port_, - $errno, - $errstr, - $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000)); - } else { - $this->handle_ = @fsockopen($this->host_, - $this->port_, - $errno, - $errstr, - $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000)); - } - - // Connect failed? - if ($this->handle_ === FALSE) { - $error = 'TSocket: Could not connect to '.$this->host_.':'.$this->port_.' ('.$errstr.' ['.$errno.'])'; - if ($this->debug_) { - call_user_func($this->debugHandler_, $error); - } - throw new TException($error); - } - } - - /** - * Closes the socket. - */ - public function close() { - if (!$this->persist_) { - @fclose($this->handle_); - $this->handle_ = null; - } - } - - /** - * Read from the socket at most $len bytes. - * - * This method will not wait for all the requested data, it will return as - * soon as any data is received. - * - * @param int $len Maximum number of bytes to read. - * @return string Binary data - */ - public function read($len) { - $null = null; - $read = array($this->handle_); - $readable = @stream_select($read, $null, $null, $this->recvTimeoutSec_, $this->recvTimeoutUsec_); - - if ($readable > 0) { - $data = @stream_socket_recvfrom($this->handle_, $len); - if ($data === false) { - throw new TTransportException('TSocket: Could not read '.$len.' bytes from '. - $this->host_.':'.$this->port_); - } elseif($data == '' && feof($this->handle_)) { - throw new TTransportException('TSocket read 0 bytes'); - } - - return $data; - } else if ($readable === 0) { - throw new TTransportException('TSocket: timed out reading '.$len.' bytes from '. - $this->host_.':'.$this->port_); - } else { - throw new TTransportException('TSocket: Could not read '.$len.' bytes from '. - $this->host_.':'.$this->port_); - } - } - - /** - * Write to the socket. - * - * @param string $buf The data to write - */ - public function write($buf) { - $null = null; - $write = array($this->handle_); - - // keep writing until all the data has been written - while (TStringFuncFactory::create()->strlen($buf) > 0) { - // wait for stream to become available for writing - $writable = @stream_select($null, $write, $null, $this->sendTimeoutSec_, $this->sendTimeoutUsec_); - if ($writable > 0) { - // write buffer to stream - $written = @stream_socket_sendto($this->handle_, $buf); - if ($written === -1 || $written === false) { - throw new TTransportException('TSocket: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes '. - $this->host_.':'.$this->port_); - } - // determine how much of the buffer is left to write - $buf = TStringFuncFactory::create()->substr($buf, $written); - } else if ($writable === 0) { - throw new TTransportException('TSocket: timed out writing '.TStringFuncFactory::create()->strlen($buf).' bytes from '. - $this->host_.':'.$this->port_); - } else { - throw new TTransportException('TSocket: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes '. - $this->host_.':'.$this->port_); - } - } - } - - /** - * Flush output to the socket. - * - * Since read(), readAll() and write() operate on the sockets directly, - * this is a no-op - * - * If you wish to have flushable buffering behaviour, wrap this TSocket - * in a TBufferedTransport. - */ - public function flush() { - // no-op - } - } diff --git a/lib/php/src/transport/TSocketPool.php b/lib/php/src/transport/TSocketPool.php index ac46c360..e69de29b 100644 --- a/lib/php/src/transport/TSocketPool.php +++ b/lib/php/src/transport/TSocketPool.php @@ -1,294 +0,0 @@ - $val) { - $ports[$key] = $port; - } - } - - foreach ($hosts as $key => $host) { - $this->servers_ []= array('host' => $host, - 'port' => $ports[$key]); - } - } - - /** - * Add a server to the pool - * - * This function does not prevent you from adding a duplicate server entry. - * - * @param string $host hostname or IP - * @param int $port port - */ - public function addServer($host, $port) { - $this->servers_[] = array('host' => $host, 'port' => $port); - } - - /** - * Sets how many time to keep retrying a host in the connect function. - * - * @param int $numRetries - */ - public function setNumRetries($numRetries) { - $this->numRetries_ = $numRetries; - } - - /** - * Sets how long to wait until retrying a host if it was marked down - * - * @param int $numRetries - */ - public function setRetryInterval($retryInterval) { - $this->retryInterval_ = $retryInterval; - } - - /** - * Sets how many time to keep retrying a host before marking it as down. - * - * @param int $numRetries - */ - public function setMaxConsecutiveFailures($maxConsecutiveFailures) { - $this->maxConsecutiveFailures_ = $maxConsecutiveFailures; - } - - /** - * Turns randomization in connect order on or off. - * - * @param bool $randomize - */ - public function setRandomize($randomize) { - $this->randomize_ = $randomize; - } - - /** - * Whether to always try the last server. - * - * @param bool $alwaysTryLast - */ - public function setAlwaysTryLast($alwaysTryLast) { - $this->alwaysTryLast_ = $alwaysTryLast; - } - - - /** - * Connects the socket by iterating through all the servers in the pool - * and trying to find one that works. - */ - public function open() { - // Check if we want order randomization - if ($this->randomize_) { - shuffle($this->servers_); - } - - // Count servers to identify the "last" one - $numServers = count($this->servers_); - - for ($i = 0; $i < $numServers; ++$i) { - - // This extracts the $host and $port variables - extract($this->servers_[$i]); - - // Check APC cache for a record of this server being down - $failtimeKey = 'thrift_failtime:'.$host.':'.$port.'~'; - - // Cache miss? Assume it's OK - $lastFailtime = apc_fetch($failtimeKey); - if ($lastFailtime === FALSE) { - $lastFailtime = 0; - } - - $retryIntervalPassed = FALSE; - - // Cache hit...make sure enough the retry interval has elapsed - if ($lastFailtime > 0) { - $elapsed = time() - $lastFailtime; - if ($elapsed > $this->retryInterval_) { - $retryIntervalPassed = TRUE; - if ($this->debug_) { - call_user_func($this->debugHandler_, - 'TSocketPool: retryInterval '. - '('.$this->retryInterval_.') '. - 'has passed for host '.$host.':'.$port); - } - } - } - - // Only connect if not in the middle of a fail interval, OR if this - // is the LAST server we are trying, just hammer away on it - $isLastServer = FALSE; - if ($this->alwaysTryLast_) { - $isLastServer = ($i == ($numServers - 1)); - } - - if (($lastFailtime === 0) || - ($isLastServer) || - ($lastFailtime > 0 && $retryIntervalPassed)) { - - // Set underlying TSocket params to this one - $this->host_ = $host; - $this->port_ = $port; - - // Try up to numRetries_ connections per server - for ($attempt = 0; $attempt < $this->numRetries_; $attempt++) { - try { - // Use the underlying TSocket open function - parent::open(); - - // Only clear the failure counts if required to do so - if ($lastFailtime > 0) { - apc_store($failtimeKey, 0); - } - - // Successful connection, return now - return; - - } catch (TException $tx) { - // Connection failed - } - } - - // Mark failure of this host in the cache - $consecfailsKey = 'thrift_consecfails:'.$host.':'.$port.'~'; - - // Ignore cache misses - $consecfails = apc_fetch($consecfailsKey); - if ($consecfails === FALSE) { - $consecfails = 0; - } - - // Increment by one - $consecfails++; - - // Log and cache this failure - if ($consecfails >= $this->maxConsecutiveFailures_) { - if ($this->debug_) { - call_user_func($this->debugHandler_, - 'TSocketPool: marking '.$host.':'.$port. - ' as down for '.$this->retryInterval_.' secs '. - 'after '.$consecfails.' failed attempts.'); - } - // Store the failure time - apc_store($failtimeKey, time()); - - // Clear the count of consecutive failures - apc_store($consecfailsKey, 0); - } else { - apc_store($consecfailsKey, $consecfails); - } - } - } - - // Oh no; we failed them all. The system is totally ill! - $error = 'TSocketPool: All hosts in pool are down. '; - $hosts = array(); - foreach ($this->servers_ as $server) { - $hosts []= $server['host'].':'.$server['port']; - } - $hostlist = implode(',', $hosts); - $error .= '('.$hostlist.')'; - if ($this->debug_) { - call_user_func($this->debugHandler_, $error); - } - throw new TException($error); - } -} diff --git a/lib/php/src/transport/TTransport.php b/lib/php/src/transport/TTransport.php index f8c8d86d..e69de29b 100644 --- a/lib/php/src/transport/TTransport.php +++ b/lib/php/src/transport/TTransport.php @@ -1,106 +0,0 @@ -read($len); - - $data = ''; - $got = 0; - while (($got = TStringFuncFactory::create()->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/lib/php/src/transport/TTransportFactory.php b/lib/php/src/transport/TTransportFactory.php index ac0a65ac..e69de29b 100644 --- a/lib/php/src/transport/TTransportFactory.php +++ b/lib/php/src/transport/TTransportFactory.php @@ -1,12 +0,0 @@ -<><"; + + self::$testArgs['testString3'] = + "string that ends in double-backslash \\\\"; + + self::$testArgs['testDouble'] = 3.1415926535898; + + self::$testArgs['testByte'] = 0x01; + + self::$testArgs['testI32'] = pow( 2, 30 ); + + if ( PHP_INT_SIZE == 8 ) + { + self::$testArgs['testI64'] = pow( 2, 60 ); + } + else + { + self::$testArgs['testI64'] = "1152921504606847000"; + } + + self::$testArgs['testStruct'] = + new Xtruct( + array( + 'string_thing' => 'worked', + 'byte_thing' => 0x01, + 'i32_thing' => pow( 2, 30 ), + 'i64_thing' => self::$testArgs['testI64'] + ) + ); + + self::$testArgs['testNestNested'] = + new Xtruct( + array( + 'string_thing' => 'worked', + 'byte_thing' => 0x01, + 'i32_thing' => pow( 2, 30 ), + 'i64_thing' => self::$testArgs['testI64'] + ) + ); + + self::$testArgs['testNest'] = + new Xtruct2( + array( + 'byte_thing' => 0x01, + 'struct_thing' => self::$testArgs['testNestNested'], + 'i32_thing' => pow( 2, 15 ) + ) + ); + + self::$testArgs['testMap'] = + array( + 7 => 77, + 8 => 88, + 9 => 99 + ); + + self::$testArgs['testStringMap'] = + array( + "a" => "123", + "a b" => "with spaces ", + "same" => "same", + "0" => "numeric key", + "longValue" => self::$testArgs['testString1'], + self::$testArgs['testString1'] => "long key" + ); + + self::$testArgs['testSet'] = array( 1 => true, 5 => true, 6 => true ); + + self::$testArgs['testList'] = array( 1, 2, 3 ); + + self::$testArgs['testEnum'] = Numberz::ONE; + + self::$testArgs['testTypedef'] = 69; + + self::$testArgs['testMapMapExpectedResult'] = + array( + 4 => array( + 1 => 1, + 2 => 2, + 3 => 3, + 4 => 4, + ), + -4 => array( + -4 => -4, + -3 => -3, + -2 => -2, + -1 => -1 + ) + ); + + // testInsanity ... takes a few steps to set up! + + $xtruct1 = + new Xtruct( + array( + 'string_thing' => 'Goodbye4', + 'byte_thing' => 4, + 'i32_thing' => 4, + 'i64_thing' => 4 + ) + ); + + $xtruct2 = + new Xtruct( + array( + 'string_thing' => 'Hello2', + 'byte_thing' =>2, + 'i32_thing' => 2, + 'i64_thing' => 2 + ) + ); + + $userMap = + array( + Numberz::FIVE => 5, + Numberz::EIGHT => 8 + ); + + $insanity2 = + new Insanity( + array( + 'userMap' => $userMap, + 'xtructs' => array($xtruct1,$xtruct2) + ) + ); + + $insanity3 = $insanity2; + + $insanity6 = + new Insanity( + array( + 'userMap' => null, + 'xtructs' => null + ) + ); + + self::$testArgs['testInsanityExpectedResult'] = + array( + "1" => array( + Numberz::TWO => $insanity2, + Numberz::THREE => $insanity3 + ), + "2" => array( + Numberz::SIX => $insanity6 + ) + ); + + } +} diff --git a/lib/php/test/Test/Thrift/Protocol/TestTJSONProtocol.php b/lib/php/test/Test/Thrift/Protocol/TestTJSONProtocol.php new file mode 100644 index 00000000..06c6ab24 --- /dev/null +++ b/lib/php/test/Test/Thrift/Protocol/TestTJSONProtocol.php @@ -0,0 +1,561 @@ +registerNamespace('Thrift', __DIR__ . '/../../../../lib'); +$loader->registerNamespace('Test', __DIR__ . '/../../..'); +$loader->registerDefinition('ThriftTest', __DIR__ . '/../../../packages'); +$loader->register(); + +/*** + * This test suite depends on running the compiler against the + * standard ThriftTest.thrift file: + * + * lib/php/test$ ../../../compiler/cpp/thrift --gen php -r \ + * --out ./packages ../../../test/ThriftTest.thrift + */ + +class TestTJSONProtocol extends \PHPUnit_Framework_TestCase +{ + private $transport; + private $protocol; + + public static function setUpBeforeClass() + { + Fixtures::populateTestArgs(); + TestTJSONProtocol_Fixtures::populateTestArgsJSON(); + } + + public function setUp() + { + $this->transport = new TMemoryBuffer(); + $this->protocol = new TJSONProtocol($this->transport); + $this->transport->open(); + } + + /*** + * WRITE TESTS + */ + + public function testVoid_Write() + { + $args = new \ThriftTest\ThriftTest_testVoid_args(); + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testVoid']; + + $this->assertEquals( $expected, $actual ); + } + + public function testString1_Write() + { + $args = new \ThriftTest\ThriftTest_testString_args(); + $args->thing = Fixtures::$testArgs['testString1']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testString1']; + + #$this->assertEquals( $expected, $actual ); + } + + public function testString2_Write() + { + $args = new \ThriftTest\ThriftTest_testString_args(); + $args->thing = Fixtures::$testArgs['testString2']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testString2']; + + $this->assertEquals( $expected, $actual ); + } + + public function testDouble_Write() + { + $args = new \ThriftTest\ThriftTest_testDouble_args(); + $args->thing = Fixtures::$testArgs['testDouble']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testDouble']; + + $this->assertEquals( $expected, $actual ); + } + + public function testByte_Write() + { + $args = new \ThriftTest\ThriftTest_testByte_args(); + $args->thing = Fixtures::$testArgs['testByte']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testByte']; + + $this->assertEquals( $expected, $actual ); + } + + public function testI32_Write() + { + $args = new \ThriftTest\ThriftTest_testI32_args(); + $args->thing = Fixtures::$testArgs['testI32']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testI32']; + + $this->assertEquals( $expected, $actual ); + } + + public function testI64_Write() + { + $args = new \ThriftTest\ThriftTest_testI64_args(); + $args->thing = Fixtures::$testArgs['testI64']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testI64']; + + $this->assertEquals( $expected, $actual ); + } + + public function testStruct_Write() + { + $args = new \ThriftTest\ThriftTest_testStruct_args(); + $args->thing = Fixtures::$testArgs['testStruct']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testStruct']; + + $this->assertEquals( $expected, $actual ); + } + + public function testNest_Write() + { + $args = new \ThriftTest\ThriftTest_testNest_args(); + $args->thing = Fixtures::$testArgs['testNest']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testNest']; + + $this->assertEquals( $expected, $actual ); + } + + public function testMap_Write() + { + $args = new \ThriftTest\ThriftTest_testMap_args(); + $args->thing = Fixtures::$testArgs['testMap']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testMap']; + + $this->assertEquals( $expected, $actual ); + } + + public function testStringMap_Write() + { + $args = new \ThriftTest\ThriftTest_testStringMap_args(); + $args->thing = Fixtures::$testArgs['testStringMap']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testStringMap']; + + $this->assertEquals( $expected, $actual ); + } + + public function testSet_Write() + { + $args = new \ThriftTest\ThriftTest_testSet_args(); + $args->thing = Fixtures::$testArgs['testSet']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testSet']; + + $this->assertEquals( $expected, $actual ); + } + + public function testList_Write() + { + $args = new \ThriftTest\ThriftTest_testList_args(); + $args->thing = Fixtures::$testArgs['testList']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testList']; + + $this->assertEquals( $expected, $actual ); + } + + public function testEnum_Write() + { + $args = new \ThriftTest\ThriftTest_testEnum_args(); + $args->thing = Fixtures::$testArgs['testEnum']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testEnum']; + + $this->assertEquals( $expected, $actual ); + } + + public function testTypedef_Write() + { + $args = new \ThriftTest\ThriftTest_testTypedef_args(); + $args->thing = Fixtures::$testArgs['testTypedef']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testTypedef']; + + $this->assertEquals( $expected, $actual ); + } + + /*** + * READ TESTS + */ + + public function testVoid_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testVoid'] + ); + $args = new \ThriftTest\ThriftTest_testVoid_args(); + $args->read( $this->protocol ); + } + + public function testString1_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testString1'] + ); + $args = new \ThriftTest\ThriftTest_testString_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testString1']; + + $this->assertEquals( $expected, $actual ); + } + + public function testString2_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testString2'] + ); + $args = new \ThriftTest\ThriftTest_testString_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testString2']; + + $this->assertEquals( $expected, $actual ); + } + + public function testString3_Write() + { + $args = new \ThriftTest\ThriftTest_testString_args(); + $args->thing = Fixtures::$testArgs['testString3']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testString3']; + + $this->assertEquals( $expected, $actual ); + } + + public function testDouble_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testDouble'] + ); + $args = new \ThriftTest\ThriftTest_testDouble_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testDouble']; + + $this->assertEquals( $expected, $actual ); + } + + public function testByte_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testByte'] + ); + $args = new \ThriftTest\ThriftTest_testByte_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testByte']; + + $this->assertEquals( $expected, $actual ); + } + + public function testI32_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testI32'] + ); + $args = new \ThriftTest\ThriftTest_testI32_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testI32']; + + $this->assertEquals( $expected, $actual ); + } + + public function testI64_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testI64'] + ); + $args = new \ThriftTest\ThriftTest_testI64_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testI64']; + + $this->assertEquals( $expected, $actual ); + + } + + public function testStruct_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testStruct'] + ); + $args = new \ThriftTest\ThriftTest_testStruct_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testStruct']; + + $this->assertEquals( $expected, $actual ); + + } + + public function testNest_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testNest'] + ); + $args = new \ThriftTest\ThriftTest_testNest_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testNest']; + + $this->assertEquals( $expected, $actual ); + + } + + public function testMap_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testMap'] + ); + $args = new \ThriftTest\ThriftTest_testMap_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testMap']; + + $this->assertEquals( $expected, $actual ); + + } + + public function testStringMap_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testStringMap'] + ); + $args = new \ThriftTest\ThriftTest_testStringMap_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testStringMap']; + + $this->assertEquals( $expected, $actual ); + + } + + public function testSet_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testSet'] + ); + $args = new \ThriftTest\ThriftTest_testSet_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testSet']; + + $this->assertEquals( $expected, $actual ); + + } + + public function testList_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testList'] + ); + $args = new \ThriftTest\ThriftTest_testList_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testList']; + + $this->assertEquals( $expected, $actual ); + + } + + public function testEnum_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testEnum'] + ); + $args = new \ThriftTest\ThriftTest_testEnum_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testEnum']; + + $this->assertEquals( $expected, $actual ); + + } + + public function testTypedef_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testTypedef'] + ); + $args = new \ThriftTest\ThriftTest_testTypedef_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testTypedef']; + + $this->assertEquals( $expected, $actual ); + } + + public function testMapMap_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testMapMap'] + ); + $result = new \ThriftTest\ThriftTest_testMapMap_result(); + $result->read( $this->protocol ); + + $actual = $result->success; + $expected = Fixtures::$testArgs['testMapMapExpectedResult']; + + $this->assertEquals( $expected, $actual ); + } + + public function testInsanity_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testInsanity'] + ); + $result = new \ThriftTest\ThriftTest_testInsanity_result(); + $result->read( $this->protocol ); + + $actual = $result->success; + $expected = Fixtures::$testArgs['testInsanityExpectedResult']; + + $this->assertEquals( $expected, $actual ); + } + +} + +class TestTJSONProtocol_Fixtures +{ + public static $testArgsJSON = array(); + + public static function populateTestArgsJSON() + { + self::$testArgsJSON['testVoid'] = '{}'; + + self::$testArgsJSON['testString1'] = '{"1":{"str":"Afrikaans, Alemannisch, Aragon\u00e9s, \u0627\u0644\u0639\u0631\u0628\u064a\u0629, \u0645\u0635\u0631\u0649, Asturianu, Aymar aru, Az\u0259rbaycan, \u0411\u0430\u0448\u04a1\u043e\u0440\u0442, Boarisch, \u017demait\u0117\u0161ka, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430), \u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438, Bamanankan, \u09ac\u09be\u0982\u09b2\u09be, Brezhoneg, Bosanski, Catal\u00e0, M\u00ecng-d\u0115\u0324ng-ng\u1e73\u0304, \u041d\u043e\u0445\u0447\u0438\u0439\u043d, Cebuano, \u13e3\u13b3\u13a9, \u010cesky, \u0421\u043b\u043e\u0432\u0463\u0301\u043d\u044c\u0441\u043a\u044a \/ \u2c14\u2c0e\u2c11\u2c02\u2c21\u2c10\u2c20\u2c14\u2c0d\u2c1f, \u0427\u04d1\u0432\u0430\u0448\u043b\u0430, Cymraeg, Dansk, Zazaki, \u078b\u07a8\u0788\u07ac\u0780\u07a8\u0784\u07a6\u0790\u07b0, \u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac, Emili\u00e0n e rumagn\u00f2l, English, Esperanto, Espa\u00f1ol, Eesti, Euskara, \u0641\u0627\u0631\u0633\u06cc, Suomi, V\u00f5ro, F\u00f8royskt, Fran\u00e7ais, Arpetan, Furlan, Frysk, Gaeilge, \u8d1b\u8a9e, G\u00e0idhlig, Galego, Ava\u00f1e\'\u1ebd, \u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0, Gaelg, \u05e2\u05d1\u05e8\u05d9\u05ea, \u0939\u093f\u0928\u094d\u0926\u0940, Fiji Hindi, Hrvatski, Krey\u00f2l ayisyen, Magyar, \u0540\u0561\u0575\u0565\u0580\u0565\u0576, Interlingua, Bahasa Indonesia, Ilokano, Ido, \u00cdslenska, Italiano, \u65e5\u672c\u8a9e, Lojban, Basa Jawa, \u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8, Kongo, Kalaallisut, \u0c95\u0ca8\u0ccd\u0ca8\u0ca1, \ud55c\uad6d\uc5b4, \u041a\u044a\u0430\u0440\u0430\u0447\u0430\u0439-\u041c\u0430\u043b\u043a\u044a\u0430\u0440, Ripoarisch, Kurd\u00ee, \u041a\u043e\u043c\u0438, Kernewek, \u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430, Latina, Ladino, L\u00ebtzebuergesch, Limburgs, Ling\u00e1la, \u0ea5\u0eb2\u0ea7, Lietuvi\u0173, Latvie\u0161u, Basa Banyumasan, Malagasy, \u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438, \u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02, \u092e\u0930\u093e\u0920\u0940, Bahasa Melayu, \u0645\u0627\u0632\u0650\u0631\u0648\u0646\u06cc, Nnapulitano, Nedersaksisch, \u0928\u0947\u092a\u093e\u0932 \u092d\u093e\u0937\u093e, Nederlands, \u202aNorsk (nynorsk)\u202c, \u202aNorsk (bokm\u00e5l)\u202c, Nouormand, Din\u00e9 bizaad, Occitan, \u0418\u0440\u043e\u043d\u0430\u0443, Papiamentu, Deitsch, Norfuk \/ Pitkern, Polski, \u067e\u0646\u062c\u0627\u0628\u06cc, \u067e\u069a\u062a\u0648, Portugu\u00eas, Runa Simi, Rumantsch, Romani, Rom\u00e2n\u0103, \u0420\u0443\u0441\u0441\u043a\u0438\u0439, \u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430, Sardu, Sicilianu, Scots, S\u00e1megiella, Simple English, Sloven\u010dina, Sloven\u0161\u010dina, \u0421\u0440\u043f\u0441\u043a\u0438 \/ Srpski, Seeltersk, Svenska, Kiswahili, \u0ba4\u0bae\u0bbf\u0bb4\u0bcd, \u0c24\u0c46\u0c32\u0c41\u0c17\u0c41, \u0422\u043e\u04b7\u0438\u043a\u04e3, \u0e44\u0e17\u0e22, T\u00fcrkmen\u00e7e, Tagalog, T\u00fcrk\u00e7e, \u0422\u0430\u0442\u0430\u0440\u0447\u0430\/Tatar\u00e7a, \u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430, \u0627\u0631\u062f\u0648, Ti\u1ebfng Vi\u1ec7t, Volap\u00fck, Walon, Winaray, \u5434\u8bed, isiXhosa, \u05d9\u05d9\u05b4\u05d3\u05d9\u05e9, Yor\u00f9b\u00e1, Ze\u00eauws, \u4e2d\u6587, B\u00e2n-l\u00e2m-g\u00fa, \u7cb5\u8a9e"}}'; + + self::$testArgsJSON['testString2'] = '{"1":{"str":"quote: \\\\\" backslash: forwardslash-escaped: \\\\\/ backspace: \\\\b formfeed: \f newline: \n return: \r tab: now-all-of-them-together: \"\\\\\\\\\/\\\\b\n\r\t now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><"}}'; + + self::$testArgsJSON['testString3'] = '{"1":{"str":"string that ends in double-backslash \\\\\\\\"}}'; + + self::$testArgsJSON['testDouble'] = '{"1":{"dbl":3.1415926535898}}'; + + self::$testArgsJSON['testByte'] = '{"1":{"i8":1}}'; + + self::$testArgsJSON['testI32'] = '{"1":{"i32":1073741824}}'; + + self::$testArgsJSON['testI64'] = '{"1":{"i64":1152921504606847000}}'; + + self::$testArgsJSON['testStruct'] = '{"1":{"rec":{"1":{"str":"worked"},"4":{"i8":1},"9":{"i32":1073741824},"11":{"i64":1152921504606847000}}}}'; + + self::$testArgsJSON['testNest'] = '{"1":{"rec":{"1":{"i8":1},"2":{"rec":{"1":{"str":"worked"},"4":{"i8":1},"9":{"i32":1073741824},"11":{"i64":1152921504606847000}}},"3":{"i32":32768}}}}'; + + self::$testArgsJSON['testMap'] = '{"1":{"map":["i32","i32",3,{"7":77,"8":88,"9":99}]}}'; + + self::$testArgsJSON['testStringMap'] = '{"1":{"map":["str","str",6,{"a":"123","a b":"with spaces ","same":"same","0":"numeric key","longValue":"Afrikaans, Alemannisch, Aragon\u00e9s, \u0627\u0644\u0639\u0631\u0628\u064a\u0629, \u0645\u0635\u0631\u0649, Asturianu, Aymar aru, Az\u0259rbaycan, \u0411\u0430\u0448\u04a1\u043e\u0440\u0442, Boarisch, \u017demait\u0117\u0161ka, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430), \u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438, Bamanankan, \u09ac\u09be\u0982\u09b2\u09be, Brezhoneg, Bosanski, Catal\u00e0, M\u00ecng-d\u0115\u0324ng-ng\u1e73\u0304, \u041d\u043e\u0445\u0447\u0438\u0439\u043d, Cebuano, \u13e3\u13b3\u13a9, \u010cesky, \u0421\u043b\u043e\u0432\u0463\u0301\u043d\u044c\u0441\u043a\u044a \/ \u2c14\u2c0e\u2c11\u2c02\u2c21\u2c10\u2c20\u2c14\u2c0d\u2c1f, \u0427\u04d1\u0432\u0430\u0448\u043b\u0430, Cymraeg, Dansk, Zazaki, \u078b\u07a8\u0788\u07ac\u0780\u07a8\u0784\u07a6\u0790\u07b0, \u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac, Emili\u00e0n e rumagn\u00f2l, English, Esperanto, Espa\u00f1ol, Eesti, Euskara, \u0641\u0627\u0631\u0633\u06cc, Suomi, V\u00f5ro, F\u00f8royskt, Fran\u00e7ais, Arpetan, Furlan, Frysk, Gaeilge, \u8d1b\u8a9e, G\u00e0idhlig, Galego, Ava\u00f1e\'\u1ebd, \u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0, Gaelg, \u05e2\u05d1\u05e8\u05d9\u05ea, \u0939\u093f\u0928\u094d\u0926\u0940, Fiji Hindi, Hrvatski, Krey\u00f2l ayisyen, Magyar, \u0540\u0561\u0575\u0565\u0580\u0565\u0576, Interlingua, Bahasa Indonesia, Ilokano, Ido, \u00cdslenska, Italiano, \u65e5\u672c\u8a9e, Lojban, Basa Jawa, \u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8, Kongo, Kalaallisut, \u0c95\u0ca8\u0ccd\u0ca8\u0ca1, \ud55c\uad6d\uc5b4, \u041a\u044a\u0430\u0440\u0430\u0447\u0430\u0439-\u041c\u0430\u043b\u043a\u044a\u0430\u0440, Ripoarisch, Kurd\u00ee, \u041a\u043e\u043c\u0438, Kernewek, \u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430, Latina, Ladino, L\u00ebtzebuergesch, Limburgs, Ling\u00e1la, \u0ea5\u0eb2\u0ea7, Lietuvi\u0173, Latvie\u0161u, Basa Banyumasan, Malagasy, \u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438, \u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02, \u092e\u0930\u093e\u0920\u0940, Bahasa Melayu, \u0645\u0627\u0632\u0650\u0631\u0648\u0646\u06cc, Nnapulitano, Nedersaksisch, \u0928\u0947\u092a\u093e\u0932 \u092d\u093e\u0937\u093e, Nederlands, \u202aNorsk (nynorsk)\u202c, \u202aNorsk (bokm\u00e5l)\u202c, Nouormand, Din\u00e9 bizaad, Occitan, \u0418\u0440\u043e\u043d\u0430\u0443, Papiamentu, Deitsch, Norfuk \/ Pitkern, Polski, \u067e\u0646\u062c\u0627\u0628\u06cc, \u067e\u069a\u062a\u0648, Portugu\u00eas, Runa Simi, Rumantsch, Romani, Rom\u00e2n\u0103, \u0420\u0443\u0441\u0441\u043a\u0438\u0439, \u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430, Sardu, Sicilianu, Scots, S\u00e1megiella, Simple English, Sloven\u010dina, Sloven\u0161\u010dina, \u0421\u0440\u043f\u0441\u043a\u0438 \/ Srpski, Seeltersk, Svenska, Kiswahili, \u0ba4\u0bae\u0bbf\u0bb4\u0bcd, \u0c24\u0c46\u0c32\u0c41\u0c17\u0c41, \u0422\u043e\u04b7\u0438\u043a\u04e3, \u0e44\u0e17\u0e22, T\u00fcrkmen\u00e7e, Tagalog, T\u00fcrk\u00e7e, \u0422\u0430\u0442\u0430\u0440\u0447\u0430\/Tatar\u00e7a, \u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430, \u0627\u0631\u062f\u0648, Ti\u1ebfng Vi\u1ec7t, Volap\u00fck, Walon, Winaray, \u5434\u8bed, isiXhosa, \u05d9\u05d9\u05b4\u05d3\u05d9\u05e9, Yor\u00f9b\u00e1, Ze\u00eauws, \u4e2d\u6587, B\u00e2n-l\u00e2m-g\u00fa, \u7cb5\u8a9e","Afrikaans, Alemannisch, Aragon\u00e9s, \u0627\u0644\u0639\u0631\u0628\u064a\u0629, \u0645\u0635\u0631\u0649, Asturianu, Aymar aru, Az\u0259rbaycan, \u0411\u0430\u0448\u04a1\u043e\u0440\u0442, Boarisch, \u017demait\u0117\u0161ka, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430), \u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438, Bamanankan, \u09ac\u09be\u0982\u09b2\u09be, Brezhoneg, Bosanski, Catal\u00e0, M\u00ecng-d\u0115\u0324ng-ng\u1e73\u0304, \u041d\u043e\u0445\u0447\u0438\u0439\u043d, Cebuano, \u13e3\u13b3\u13a9, \u010cesky, \u0421\u043b\u043e\u0432\u0463\u0301\u043d\u044c\u0441\u043a\u044a \/ \u2c14\u2c0e\u2c11\u2c02\u2c21\u2c10\u2c20\u2c14\u2c0d\u2c1f, \u0427\u04d1\u0432\u0430\u0448\u043b\u0430, Cymraeg, Dansk, Zazaki, \u078b\u07a8\u0788\u07ac\u0780\u07a8\u0784\u07a6\u0790\u07b0, \u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac, Emili\u00e0n e rumagn\u00f2l, English, Esperanto, Espa\u00f1ol, Eesti, Euskara, \u0641\u0627\u0631\u0633\u06cc, Suomi, V\u00f5ro, F\u00f8royskt, Fran\u00e7ais, Arpetan, Furlan, Frysk, Gaeilge, \u8d1b\u8a9e, G\u00e0idhlig, Galego, Ava\u00f1e\'\u1ebd, \u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0, Gaelg, \u05e2\u05d1\u05e8\u05d9\u05ea, \u0939\u093f\u0928\u094d\u0926\u0940, Fiji Hindi, Hrvatski, Krey\u00f2l ayisyen, Magyar, \u0540\u0561\u0575\u0565\u0580\u0565\u0576, Interlingua, Bahasa Indonesia, Ilokano, Ido, \u00cdslenska, Italiano, \u65e5\u672c\u8a9e, Lojban, Basa Jawa, \u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8, Kongo, Kalaallisut, \u0c95\u0ca8\u0ccd\u0ca8\u0ca1, \ud55c\uad6d\uc5b4, \u041a\u044a\u0430\u0440\u0430\u0447\u0430\u0439-\u041c\u0430\u043b\u043a\u044a\u0430\u0440, Ripoarisch, Kurd\u00ee, \u041a\u043e\u043c\u0438, Kernewek, \u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430, Latina, Ladino, L\u00ebtzebuergesch, Limburgs, Ling\u00e1la, \u0ea5\u0eb2\u0ea7, Lietuvi\u0173, Latvie\u0161u, Basa Banyumasan, Malagasy, \u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438, \u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02, \u092e\u0930\u093e\u0920\u0940, Bahasa Melayu, \u0645\u0627\u0632\u0650\u0631\u0648\u0646\u06cc, Nnapulitano, Nedersaksisch, \u0928\u0947\u092a\u093e\u0932 \u092d\u093e\u0937\u093e, Nederlands, \u202aNorsk (nynorsk)\u202c, \u202aNorsk (bokm\u00e5l)\u202c, Nouormand, Din\u00e9 bizaad, Occitan, \u0418\u0440\u043e\u043d\u0430\u0443, Papiamentu, Deitsch, Norfuk \/ Pitkern, Polski, \u067e\u0646\u062c\u0627\u0628\u06cc, \u067e\u069a\u062a\u0648, Portugu\u00eas, Runa Simi, Rumantsch, Romani, Rom\u00e2n\u0103, \u0420\u0443\u0441\u0441\u043a\u0438\u0439, \u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430, Sardu, Sicilianu, Scots, S\u00e1megiella, Simple English, Sloven\u010dina, Sloven\u0161\u010dina, \u0421\u0440\u043f\u0441\u043a\u0438 \/ Srpski, Seeltersk, Svenska, Kiswahili, \u0ba4\u0bae\u0bbf\u0bb4\u0bcd, \u0c24\u0c46\u0c32\u0c41\u0c17\u0c41, \u0422\u043e\u04b7\u0438\u043a\u04e3, \u0e44\u0e17\u0e22, T\u00fcrkmen\u00e7e, Tagalog, T\u00fcrk\u00e7e, \u0422\u0430\u0442\u0430\u0440\u0447\u0430\/Tatar\u00e7a, \u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430, \u0627\u0631\u062f\u0648, Ti\u1ebfng Vi\u1ec7t, Volap\u00fck, Walon, Winaray, \u5434\u8bed, isiXhosa, \u05d9\u05d9\u05b4\u05d3\u05d9\u05e9, Yor\u00f9b\u00e1, Ze\u00eauws, \u4e2d\u6587, B\u00e2n-l\u00e2m-g\u00fa, \u7cb5\u8a9e":"long key"}]}}'; + + self::$testArgsJSON['testSet'] = '{"1":{"set":["i32",3,1,5,6]}}'; + + self::$testArgsJSON['testList'] = '{"1":{"lst":["i32",3,1,2,3]}}'; + + self::$testArgsJSON['testEnum'] = '{"1":{"i32":1}}'; + + self::$testArgsJSON['testTypedef'] = '{"1":{"i64":69}}'; + + self::$testArgsJSON['testMapMap'] = '{"0":{"map":["i32","map",2,{"4":["i32","i32",4,{"1":1,"2":2,"3":3,"4":4}],"-4":["i32","i32",4,{"-4":-4,"-3":-3,"-2":-2,"-1":-1}]}]}}'; + + self::$testArgsJSON['testInsanity'] = '{"0":{"map":["i64","map",2,{"1":["i32","rec",2,{"2":{"1":{"map":["i32","i64",2,{"5":5,"8":8}]},"2":{"lst":["rec",2,{"1":{"str":"Goodbye4"},"4":{"i8":4},"9":{"i32":4},"11":{"i64":4}},{"1":{"str":"Hello2"},"4":{"i8":2},"9":{"i32":2},"11":{"i64":2}}]}},"3":{"1":{"map":["i32","i64",2,{"5":5,"8":8}]},"2":{"lst":["rec",2,{"1":{"str":"Goodbye4"},"4":{"i8":4},"9":{"i32":4},"11":{"i64":4}},{"1":{"str":"Hello2"},"4":{"i8":2},"9":{"i32":2},"11":{"i64":2}}]}}}],"2":["i32","rec",1,{"6":{}}]}]}}'; + + } +} diff --git a/test/php/TestClient.php b/test/php/TestClient.php index 009137b3..4df498c8 100755 --- a/test/php/TestClient.php +++ b/test/php/TestClient.php @@ -1,4 +1,23 @@ registerNamespace('Thrift', __DIR__ . '/../../lib/php/lib'); +$loader->registerDefinition('ThriftTest', $GEN_DIR); +$loader->register(); + /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,40 +37,17 @@ * under the License. */ -if (!isset($GEN_DIR)) { - $GEN_DIR = 'gen-php'; -} -if (!isset($MODE)) { - $MODE = 'normal'; -} - -/** Set the Thrift root */ -$GLOBALS['THRIFT_ROOT'] = '../../lib/php/src'; - /** Include the Thrift base */ -require_once $GLOBALS['THRIFT_ROOT'].'/Thrift.php'; - /** Include the binary protocol */ -require_once $GLOBALS['THRIFT_ROOT'].'/protocol/TBinaryProtocol.php'; +use Thrift\Protocol\TBinaryProtocol; /** Include the socket layer */ -require_once $GLOBALS['THRIFT_ROOT'].'/transport/TSocketPool.php'; +use Thrift\Transport\TSocket; +use Thrift\Transport\TSocketPool; /** Include the socket layer */ -require_once $GLOBALS['THRIFT_ROOT'].'/transport/TFramedTransport.php'; -require_once $GLOBALS['THRIFT_ROOT'].'/transport/TBufferedTransport.php'; - -echo '==============================='."\n"; -echo ' SAFE TO IGNORE THESE IN TEST'."\n"; -echo '==============================='."\n"; - -/** Include the generated code */ -require_once $GEN_DIR.'/ThriftTest/ThriftTest.php'; -require_once $GEN_DIR.'/ThriftTest/ThriftTest_types.php'; - -echo '==============================='."\n"; -echo ' END OF SAFE ERRORS SECTION'."\n"; -echo '==============================='."\n\n"; +use Thrift\Transport\TFramedTransport; +use Thrift\Transport\TBufferedTransport; $host = 'localhost'; $port = 9090; @@ -72,17 +68,17 @@ $socket->setDebug(TRUE); if ($MODE == 'inline') { $transport = $socket; - $testClient = new ThriftTest_ThriftTestClient($transport); + $testClient = new \ThriftTest\ThriftTestClient($transport); } else if ($MODE == 'framed') { $framedSocket = new TFramedTransport($socket); $transport = $framedSocket; $protocol = new TBinaryProtocol($transport); - $testClient = new ThriftTest_ThriftTestClient($protocol); + $testClient = new \ThriftTest\ThriftTestClient($protocol); } else { $bufferedSocket = new TBufferedTransport($socket, 1024, 1024); $transport = $bufferedSocket; $protocol = new TBinaryProtocol($transport); - $testClient = new ThriftTest_ThriftTestClient($protocol); + $testClient = new \ThriftTest\ThriftTestClient($protocol); } $transport->open(); @@ -135,7 +131,7 @@ print_r(" = $dub\n"); * STRUCT TEST */ print_r("testStruct({\"Zero\", 1, -3, -5})"); -$out = new ThriftTest_Xtruct(); +$out = new \ThriftTest\Xtruct(); $out->string_thing = "Zero"; $out->byte_thing = 1; $out->i32_thing = -3; @@ -150,7 +146,7 @@ print_r(" = {\"".$in->string_thing."\", ". * NESTED STRUCT TEST */ print_r("testNest({1, {\"Zero\", 1, -3, -5}), 5}"); -$out2 = new ThriftTest_Xtruct2(); +$out2 = new \ThriftTest\Xtruct2(); $out2->byte_thing = 1; $out2->struct_thing = $out; $out2->i32_thing = 5; @@ -305,9 +301,9 @@ print_r("}\n"); /** * INSANITY TEST */ -$insane = new ThriftTest_Insanity(); +$insane = new \ThriftTest\Insanity(); $insane->userMap[ThriftTest_Numberz::FIVE] = 5000; -$truck = new ThriftTest_Xtruct(); +$truck = new \ThriftTest\Xtruct(); $truck->string_thing = "Truck"; $truck->byte_thing = 8; $truck->i32_thing = 8; diff --git a/tutorial/php/PhpClient.php b/tutorial/php/PhpClient.php index a6639d5f..d262b8fe 100755 --- a/tutorial/php/PhpClient.php +++ b/tutorial/php/PhpClient.php @@ -1,5 +1,22 @@ #!/usr/bin/env php registerNamespace('Thrift', __DIR__ . '/../../lib/php/lib'); +$loader->registerDefinition('shared', $GEN_DIR); +$loader->registerDefinition('tutorial', $GEN_DIR); +$loader->register(); + /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -19,29 +36,11 @@ * under the License. */ -$GLOBALS['THRIFT_ROOT'] = '../../lib/php/src'; - -require_once $GLOBALS['THRIFT_ROOT'].'/Thrift.php'; -require_once $GLOBALS['THRIFT_ROOT'].'/protocol/TBinaryProtocol.php'; -require_once $GLOBALS['THRIFT_ROOT'].'/transport/TSocket.php'; -require_once $GLOBALS['THRIFT_ROOT'].'/transport/THttpClient.php'; -require_once $GLOBALS['THRIFT_ROOT'].'/transport/TBufferedTransport.php'; - -/** - * Suppress errors in here, which happen because we have not installed into - * $GLOBALS['THRIFT_ROOT'].'/packages/tutorial' like we are supposed to! - * - * Normally we would only have to include Calculator.php which would properly - * include the other files from their packages/ folder locations, but we - * include everything here due to the bogus path setup. - */ -error_reporting(E_NONE); -$GEN_DIR = '../gen-php'; -require_once $GEN_DIR.'/shared/SharedService.php'; -require_once $GEN_DIR.'/shared/shared_types.php'; -require_once $GEN_DIR.'/tutorial/Calculator.php'; -require_once $GEN_DIR.'/tutorial/tutorial_types.php'; -error_reporting(E_ALL); +use Thrift\Protocol\TBinaryProtocol; +use Thrift\Transport\TSocket; +use Thrift\Transport\THttpClient; +use Thrift\Transport\TBufferedTransport; +use Thrift\Exception\TException; try { if (array_search('--http', $argv)) { @@ -51,7 +50,7 @@ try { } $transport = new TBufferedTransport($socket, 1024, 1024); $protocol = new TBinaryProtocol($transport); - $client = new CalculatorClient($protocol); + $client = new \tutorial\CalculatorClient($protocol); $transport->open(); @@ -61,20 +60,20 @@ try { $sum = $client->add(1,1); print "1+1=$sum\n"; - $work = new tutorial_Work(); + $work = new \tutorial\Work(); - $work->op = tutorial_Operation::DIVIDE; + $work->op = \tutorial\Operation::DIVIDE; $work->num1 = 1; $work->num2 = 0; try { $client->calculate(1, $work); print "Whoa! We can divide by zero?\n"; - } catch (tutorial_InvalidOperation $io) { + } catch (\tutorial\InvalidOperation $io) { print "InvalidOperation: $io->why\n"; } - $work->op = tutorial_Operation::SUBTRACT; + $work->op = \tutorial\Operation::SUBTRACT; $work->num1 = 15; $work->num2 = 10; $diff = $client->calculate(1, $work); diff --git a/tutorial/php/PhpServer.php b/tutorial/php/PhpServer.php index 235b15ba..4af70a48 100755 --- a/tutorial/php/PhpServer.php +++ b/tutorial/php/PhpServer.php @@ -1,5 +1,22 @@ #!/usr/bin/env php registerNamespace('Thrift', __DIR__ . '/../../lib/php/lib'); +$loader->registerDefinition('shared', $GEN_DIR); +$loader->registerDefinition('tutorial', $GEN_DIR); +$loader->register(); + /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -31,30 +48,11 @@ if (php_sapi_name() == 'cli') { ini_set("display_errors", "stderr"); } -$GLOBALS['THRIFT_ROOT'] = realpath(dirname(__FILE__).'/../..').'/lib/php/src'; - -require_once $GLOBALS['THRIFT_ROOT'].'/Thrift.php'; -require_once $GLOBALS['THRIFT_ROOT'].'/protocol/TBinaryProtocol.php'; -require_once $GLOBALS['THRIFT_ROOT'].'/transport/TPhpStream.php'; -require_once $GLOBALS['THRIFT_ROOT'].'/transport/TBufferedTransport.php'; - -/** - * Suppress errors in here, which happen because we have not installed into - * $GLOBALS['THRIFT_ROOT'].'/packages/tutorial' like we are supposed to! - * - * Normally we would only have to include Calculator.php which would properly - * include the other files from their packages/ folder locations, but we - * include everything here due to the bogus path setup. - */ -error_reporting(E_NONE); -$GEN_DIR = realpath(dirname(__FILE__).'/..').'/gen-php'; -require_once $GEN_DIR.'/shared/SharedService.php'; -require_once $GEN_DIR.'/shared/shared_types.php'; -require_once $GEN_DIR.'/tutorial/Calculator.php'; -require_once $GEN_DIR.'/tutorial/tutorial_types.php'; -error_reporting(E_ALL); +use Thrift\Protocol\TBinaryProtocol; +use Thrift\Transport\TPhpStream; +use Thrift\Transport\TBufferedTransport; -class CalculatorHandler implements CalculatorIf { +class CalculatorHandler implements \tutorial\CalculatorIf { protected $log = array(); public function ping() { @@ -66,21 +64,21 @@ class CalculatorHandler implements CalculatorIf { return $num1 + $num2; } - public function calculate($logid, $w) { + public function calculate($logid, \tutorial\Work $w) { error_log("calculate({$logid}, {{$w->op}, {$w->num1}, {$w->num2}})"); switch ($w->op) { - case tutorial_Operation::ADD: + case \tutorial\Operation::ADD: $val = $w->num1 + $w->num2; break; - case tutorial_Operation::SUBTRACT: + case \tutorial\Operation::SUBTRACT: $val = $w->num1 - $w->num2; break; - case tutorial_Operation::MULTIPLY: + case \tutorial\Operation::MULTIPLY: $val = $w->num1 * $w->num2; break; - case tutorial_Operation::DIVIDE: + case \tutorial\Operation::DIVIDE: if ($w->num2 == 0) { - $io = new tutorial_InvalidOperation(); + $io = new \tutorial\InvalidOperation(); $io->what = $w->op; $io->why = "Cannot divide by 0"; throw $io; @@ -88,13 +86,13 @@ class CalculatorHandler implements CalculatorIf { $val = $w->num1 / $w->num2; break; default: - $io = new tutorial_InvalidOperation(); + $io = new \tutorial\InvalidOperation(); $io->what = $w->op; $io->why = "Invalid Operation"; throw $io; } - $log = new SharedStruct(); + $log = new \shared\SharedStruct(); $log->key = $logid; $log->value = (string)$val; $this->log[$logid] = $log; @@ -107,7 +105,7 @@ class CalculatorHandler implements CalculatorIf { // This actually doesn't work because the PHP interpreter is // restarted for every request. //return $this->log[$key]; - return new SharedStruct(array("key" => $key, "value" => "PHP is stateless!")); + return new \shared\SharedStruct(array("key" => $key, "value" => "PHP is stateless!")); } public function zip() { @@ -122,7 +120,7 @@ if (php_sapi_name() == 'cli') { } $handler = new CalculatorHandler(); -$processor = new CalculatorProcessor($handler); +$processor = new \tutorial\CalculatorProcessor($handler); $transport = new TBufferedTransport(new TPhpStream(TPhpStream::MODE_R | TPhpStream::MODE_W)); $protocol = new TBinaryProtocol($transport, true, true); -- 2.17.1