Add __autoload() support to Thrift-generated PHP code
Summary: Include thrift/autoload.php and use -phpa flag to generated code that works with autoload. Good for services with lots of methods that are typically not all invoked.
Reviewed By: dreiss
Test Plan: Falcon, baby, falcon.
git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665349 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/compiler/cpp/src/generate/t_php_generator.cc b/compiler/cpp/src/generate/t_php_generator.cc
index c0fdd25..8a3bdb7 100644
--- a/compiler/cpp/src/generate/t_php_generator.cc
+++ b/compiler/cpp/src/generate/t_php_generator.cc
@@ -359,6 +359,28 @@
}
+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 = get_out_dir()+f_struct;
+ autoload_out.open(f_struct_name.c_str());
+ autoload_out << "<?php" << endl;
+ _generate_php_struct_definition(autoload_out, tstruct, is_exception);
+ autoload_out << endl << "?>" << endl;
+ autoload_out.close();
+
+ f_types_ <<
+ "$GLOBALS['THRIFT_AUTOLOAD']['" << php_namespace(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
@@ -366,7 +388,7 @@
*
* @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<t_field*>& members = tstruct->get_members();
@@ -1032,12 +1054,32 @@
"}" << 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 = get_out_dir()+f_struct;
+ autoload_out.open(f_struct_name.c_str());
+ autoload_out << "<?php" << endl;
+ _generate_service_client(autoload_out, tservice);
+ autoload_out << endl << "?>" << endl;
+ autoload_out.close();
+
+ f_service_ <<
+ "$GLOBALS['THRIFT_AUTOLOAD']['" << 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(t_service* tservice) {
+void t_php_generator::_generate_service_client(ofstream& out, t_service* tservice) {
string extends = "";
string extends_client = "";
if (tservice->get_extends() != NULL) {
@@ -1045,33 +1087,33 @@
extends_client = " extends " + extends + "Client";
}
- f_service_ <<
+ out <<
"class " << service_name_ << "Client" << extends_client << " implements " << service_name_ << "If {" << endl;
indent_up();
// Private members
if (extends.empty()) {
- f_service_ <<
+ out <<
indent() << "protected $input_ = null;" << endl <<
indent() << "protected $output_ = null;" << endl <<
endl;
- f_service_ <<
+ out <<
indent() << "protected $seqid_ = 0;" << endl <<
endl;
}
// Constructor function
- f_service_ <<
+ out <<
indent() << "public function __construct($input, $output=null) {" << endl;
if (!extends.empty()) {
- f_service_ <<
+ out <<
indent() << " parent::__construct($input, $output);" << endl;
} else {
- f_service_ <<
+ out <<
indent() << " $this->input_ = $input;" << endl <<
indent() << " $this->output_ = $output ? $output : $input;" << endl;
}
- f_service_ <<
+ out <<
indent() << "}" << endl << endl;
// Generate client method implementations
@@ -1084,10 +1126,10 @@
string funname = (*f_iter)->get_name();
// Open function
- indent(f_service_) <<
+ indent(out) <<
"public function " << function_signature(*f_iter) << endl;
- scope_up(f_service_);
- indent(f_service_) <<
+ scope_up(out);
+ indent(out) <<
"$this->send_" << funname << "(";
bool first = true;
@@ -1095,63 +1137,63 @@
if (first) {
first = false;
} else {
- f_service_ << ", ";
+ out << ", ";
}
- f_service_ << "$" << (*fld_iter)->get_name();
+ out << "$" << (*fld_iter)->get_name();
}
- f_service_ << ");" << endl;
+ out << ");" << endl;
if (!(*f_iter)->is_async()) {
- f_service_ << indent();
+ out << indent();
if (!(*f_iter)->get_returntype()->is_void()) {
- f_service_ << "return ";
+ out << "return ";
}
- f_service_ <<
+ out <<
"$this->recv_" << funname << "();" << endl;
}
- scope_down(f_service_);
- f_service_ << endl;
+ scope_down(out);
+ out << endl;
- indent(f_service_) <<
+ indent(out) <<
"public function send_" << function_signature(*f_iter) << endl;
- scope_up(f_service_);
+ scope_up(out);
std::string argsname = php_namespace(tservice->get_program()) + service_name_ + "_" + (*f_iter)->get_name() + "_args";
// Serialize the request header
if (binary_inline_) {
- f_service_ <<
+ out <<
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 {
- f_service_ <<
+ out <<
indent() << "$this->output_->writeMessageBegin('" << (*f_iter)->get_name() << "', TMessageType::CALL, $this->seqid_);" << endl;
}
- f_service_ <<
+ out <<
indent() << "$args = new " << argsname << "();" << endl;
for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
- f_service_ <<
+ out <<
indent() << "$args->" << (*fld_iter)->get_name() << " = $" << (*fld_iter)->get_name() << ";" << endl;
}
// Write to the stream
if (binary_inline_) {
- f_service_ <<
+ out <<
indent() << "$args->write($buff);" << endl <<
indent() << "$this->output_->write($buff);" << endl <<
indent() << "$this->output_->flush();" << endl;
} else {
- f_service_ <<
+ out <<
indent() << "$args->write($this->output_);" << endl <<
indent() << "$this->output_->writeMessageEnd();" << endl <<
indent() << "$this->output_->getTransport()->flush();" << endl;
}
- scope_down(f_service_);
+ scope_down(out);
if (!(*f_iter)->is_async()) {
@@ -1162,12 +1204,12 @@
string("recv_") + (*f_iter)->get_name(),
&noargs);
// Open function
- f_service_ <<
+ out <<
endl <<
indent() << "public function " << function_signature(&recv_function) << endl;
- scope_up(f_service_);
+ scope_up(out);
- f_service_ <<
+ out <<
indent() << "$rseqid = 0;" << endl <<
indent() << "$fname = null;" << endl <<
indent() << "$mtype = 0;" << endl <<
@@ -1176,16 +1218,16 @@
if (binary_inline_) {
t_field ffname(g_type_string, "fname");
t_field fseqid(g_type_i32, "rseqid");
- f_service_ <<
+ out <<
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 TProtocolException('Bad version identifier: '.$ver, TProtocolException::BAD_VERSION);" << endl;
- generate_deserialize_field(f_service_, &ffname, "", true);
- generate_deserialize_field(f_service_, &fseqid, "", true);
+ generate_deserialize_field(out, &ffname, "", true);
+ generate_deserialize_field(out, &fseqid, "", true);
} else {
- f_service_ <<
+ out <<
indent() << "$this->input_->readMessageBegin($fname, $mtype, $rseqid);" << endl <<
indent() << "if ($mtype == TMessageType::EXCEPTION) {" << endl <<
indent() << " $x = new TApplicationException();" << endl <<
@@ -1195,19 +1237,19 @@
indent() << "}" << endl;
}
- f_service_ <<
+ out <<
indent() << "$result = new " << resultname << "();" << endl <<
indent() << "$result->read($this->input_);" << endl;
if (!binary_inline_) {
- f_service_ <<
+ out <<
indent() << "$this->input_->readMessageEnd();" << endl <<
endl;
}
// Careful, only return result if not a void function
if (!(*f_iter)->get_returntype()->is_void()) {
- f_service_ <<
+ out <<
indent() << "if ($result->success !== null) {" << endl <<
indent() << " return $result->success;" << endl <<
indent() << "}" << endl;
@@ -1217,7 +1259,7 @@
const std::vector<t_field*>& xceptions = xs->get_members();
vector<t_field*>::const_iterator x_iter;
for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
- f_service_ <<
+ out <<
indent() << "if ($result->" << (*x_iter)->get_name() << " !== null) {" << endl <<
indent() << " throw $result->" << (*x_iter)->get_name() << ";" << endl <<
indent() << "}" << endl;
@@ -1225,22 +1267,22 @@
// Careful, only return _result if not a void function
if ((*f_iter)->get_returntype()->is_void()) {
- indent(f_service_) <<
+ indent(out) <<
"return;" << endl;
} else {
- f_service_ <<
+ out <<
indent() << "throw new Exception(\"" << (*f_iter)->get_name() << " failed: unknown result\");" << endl;
}
// Close function
- scope_down(f_service_);
- f_service_ << endl;
+ scope_down(out);
+ out << endl;
}
}
indent_down();
- f_service_ <<
+ out <<
"}" << endl << endl;
}
diff --git a/compiler/cpp/src/generate/t_php_generator.h b/compiler/cpp/src/generate/t_php_generator.h
index 5742747..3f85fea 100644
--- a/compiler/cpp/src/generate/t_php_generator.h
+++ b/compiler/cpp/src/generate/t_php_generator.h
@@ -21,11 +21,16 @@
*/
class t_php_generator : public t_oop_generator {
public:
- t_php_generator(t_program* program, bool binary_inline=false, bool rest=false, bool phps=false) :
+ t_php_generator(t_program* program,
+ bool binary_inline=false,
+ bool rest=false,
+ bool phps=false,
+ bool autoload=false) :
t_oop_generator(program),
binary_inline_(binary_inline),
rest_(rest),
- phps_(phps) {
+ phps_(phps),
+ autoload_(autoload) {
out_dir_base_ = (binary_inline_ ? "gen-phpi" : "gen-php");
}
@@ -55,6 +60,7 @@
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);
@@ -70,6 +76,7 @@
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);
@@ -168,6 +175,11 @@
*/
bool phps_;
+ /**
+ * Generate PHP code that uses autoload
+ */
+ bool autoload_;
+
};
#endif
diff --git a/compiler/cpp/src/main.cc b/compiler/cpp/src/main.cc
index c633fac..0b904ea 100644
--- a/compiler/cpp/src/main.cc
+++ b/compiler/cpp/src/main.cc
@@ -140,6 +140,7 @@
bool gen_php = false;
bool gen_phpi = false;
bool gen_phps = true;
+bool gen_phpa = false;
bool gen_rest = false;
bool gen_perl = false;
bool gen_erl = false;
@@ -562,6 +563,7 @@
fprintf(stderr, " -phpi Generate PHP inlined files\n");
fprintf(stderr, " -phps Generate PHP server stubs (with -php)\n");
fprintf(stderr, " -phpl Generate PHP-lite (with -php)\n");
+ fprintf(stderr, " -phpa Generate PHP with autoload (with -php)\n");
fprintf(stderr, " -py Generate Python output files\n");
fprintf(stderr, " -rb Generate Ruby output files\n");
fprintf(stderr, " -xsd Generate XSD output files\n");
@@ -813,7 +815,7 @@
if (gen_php) {
pverbose("Generating PHP\n");
- t_php_generator* php = new t_php_generator(program, false, gen_rest, gen_phps);
+ t_php_generator* php = new t_php_generator(program, false, gen_rest, gen_phps, gen_phpa);
php->generate_program();
delete php;
}
@@ -953,6 +955,12 @@
gen_php = true;
}
gen_phps = false;
+ } else if (strcmp(arg, "-phpa") == 0) {
+ if (!gen_php) {
+ gen_php = true;
+ gen_phps = false;
+ }
+ gen_phpa = true;
} else if (strcmp(arg, "-rest") == 0) {
gen_rest = true;
} else if (strcmp(arg, "-py") == 0) {
diff --git a/lib/php/src/autoload.php b/lib/php/src/autoload.php
new file mode 100644
index 0000000..b5886c5
--- /dev/null
+++ b/lib/php/src/autoload.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * Copyright (c) 2006- Facebook
+ * Distributed under the Thrift Software License
+ *
+ * See accompanying file LICENSE or visit the Thrift site at:
+ * http://developers.facebook.com/thrift/
+ *
+ * @package thrift
+ * @author Mark Slee <mcslee@facebook.com>
+ */
+
+/**
+ * Include this file if you wish to use autoload with your PHP generated Thrift
+ * code. The generated code will *not* include any defined Thrift classes by
+ * default, except for the service interfaces. The generated code will populate
+ * values into $GLOBALS['THRIFT_AUTOLOAD'] which can be used by the autoload
+ * method below. If you have your own autoload system already in place, you
+ * should merge the following functionality into your autoload system.
+ *
+ * Generate this code using the -phpa Thrift generator flag.
+ */
+
+$GLOBALS['THRIFT_AUTOLOAD'] = array();
+
+if (!function_exists('__autoload')) {
+ function __autoload($class) {
+ global $THRIFT_AUTOLOAD;
+ if (isset($THRIFT_AUTOLOAD[$class])) {
+ include_once $GLOBALS['THRIFT_ROOT'].'/lib/packages/'.$THRIFT_AUTOLOAD[$class];
+ }
+ }
+}