1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include <string>
21 #include <fstream>
22 #include <iostream>
23 #include <vector>
24
25 #include <stdlib.h>
26 #include <sys/stat.h>
27 #include <sstream>
28 #include "thrift/platform.h"
29 #include "thrift/generate/t_oop_generator.h"
30
31 using std::map;
32 using std::ostream;
33 using std::ostringstream;
34 using std::string;
35 using std::stringstream;
36 using std::vector;
37
38 static const string endl = "\n"; // avoid ostream << std::endl flushes
39
40 #define NSGLOBAL (nsglobal_.size() ? nsglobal_ : "")
41 #define NSGLOBAL_A ("\\" + NSGLOBAL)
42 #define NSGLOBAL_B (NSGLOBAL + "\\")
43 #define NSGLOBAL_AB ("\\" + NSGLOBAL + "\\")
44
45 /**
46 * PHP code generator.
47 *
48 */
49 class t_php_generator : public t_oop_generator {
50 public:
t_php_generator(t_program * program,const std::map<std::string,std::string> & parsed_options,const std::string & option_string)51 t_php_generator(t_program* program,
52 const std::map<std::string, std::string>& parsed_options,
53 const std::string& option_string)
54 : t_oop_generator(program) {
55 (void)option_string;
56 std::map<std::string, std::string>::const_iterator iter;
57
58 binary_inline_ = false;
59 rest_ = false;
60 phps_ = false;
61 oop_ = false;
62 validate_ = false;
63 json_serializable_ = false;
64 getters_setters_ = false;
65
66 nsglobal_ = ""; // by default global namespace is empty
67 classmap_ = false;
68 for (iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
69 if (iter->first.compare("inlined") == 0) {
70 binary_inline_ = true;
71 } else if (iter->first.compare("rest") == 0) {
72 rest_ = true;
73 } else if (iter->first.compare("server") == 0) {
74 phps_ = true;
75 } else if (iter->first.compare("oop") == 0) {
76 oop_ = true;
77 } else if (iter->first.compare("validate") == 0) {
78 validate_ = true;
79 } else if (iter->first.compare("json") == 0) {
80 json_serializable_ = true;
81 } else if (iter->first.compare("nsglobal") == 0) {
82 nsglobal_ = iter->second;
83 } else if (iter->first.compare("classmap") == 0) {
84 classmap_ = true;
85 } else if (iter->first.compare("psr4") == 0) {
86 if(classmap_){
87 throw "psr4 and classmap are mutually exclusive.";
88 } else {
89 pwarning(0, "psr4 is default option! needn't add psr4 option!\n");
90 }
91 } else if (iter->first.compare("getters_setters") == 0) {
92 getters_setters_ = true;
93 } else {
94 throw "unknown option php:" + iter->first;
95 }
96 }
97
98 if (oop_ && binary_inline_) {
99 throw "oop and inlined are mutually exclusive.";
100 }
101
102 update_keywords_for_validation();
103 out_dir_base_ = (binary_inline_ ? "gen-phpi" : "gen-php");
104 escape_['$'] = "\\$";
105 }
106
indent_str() const107 std::string indent_str() const override {
108 return " ";
109 }
110
111 static bool is_valid_namespace(const std::string& sub_namespace);
112
113 /**
114 * Init and close methods
115 */
116
117 void init_generator() override;
118 void close_generator() override;
119 std::string display_name() const override;
120
121 /**
122 * Program-level generation functions
123 */
124
125 void generate_typedef(t_typedef* ttypedef) override;
126 void generate_enum(t_enum* tenum) override;
127 void generate_consts(vector<t_const*> consts) override;
128 void generate_struct(t_struct* tstruct) override;
129 void generate_xception(t_struct* txception) override;
130 void generate_service(t_service* tservice) override;
131
132 std::string render_const_value(t_type* type, t_const_value* value);
133 std::set<std::string> lang_keywords_for_validation() const override;
134
135 /**
136 * Structs!
137 */
138
139 void generate_php_struct(t_struct* tstruct, bool is_exception);
140 void generate_php_struct_definition(std::ostream& out,
141 t_struct* tstruct,
142 bool is_xception = false,
143 bool is_result = false);
144 void generate_php_struct_reader(std::ostream& out, t_struct* tstruct, bool is_result);
145 void generate_php_struct_writer(std::ostream& out, t_struct* tstruct, bool is_result);
146 void generate_php_function_helpers(t_service* tservice, t_function* tfunction);
147 void generate_php_struct_required_validator(ostream& out,
148 t_struct* tstruct,
149 std::string method_name,
150 bool write_mode);
151 void generate_php_struct_read_validator(ostream& out, t_struct* tstruct);
152 void generate_php_struct_write_validator(ostream& out, t_struct* tstruct);
153 void generate_php_struct_json_serialize(ostream& out, t_struct* tstruct, bool is_result);
154 bool needs_php_write_validator(t_struct* tstruct, bool is_result);
155 bool needs_php_read_validator(t_struct* tstruct, bool is_result);
156 int get_php_num_required_fields(const vector<t_field*>& fields, bool write_mode);
157
158 void generate_php_type_spec(std::ostream& out, t_type* t);
159 void generate_php_struct_spec(std::ostream& out, t_struct* tstruct);
160 void generate_generic_field_getters_setters(std::ostream& out, t_struct* tstruct);
161
162 void generate_reflection_setters(ostringstream& out, string field_name, string cap_name);
163 void generate_reflection_getters(ostringstream& out, string field_name, string cap_name);
164
165 std::string get_cap_name(std::string name);
166
167
168 /**
169 * Service-level generation functions
170 */
171
172 void generate_service_helpers(t_service* tservice);
173 void generate_service_interface(t_service* tservice);
174 void generate_service_rest(t_service* tservice);
175 void generate_service_client(t_service* tservice);
176 void generate_service_processor(t_service* tservice);
177 void generate_process_function(std::ostream& out, t_service* tservice, t_function* tfunction);
178 void generate_service_header(t_service* tservice, std::ostream& file);
179 void generate_program_header(std::ostream& file);
180
181 /**
182 * Serialization constructs
183 */
184
185 void generate_deserialize_field(std::ostream& out,
186 t_field* tfield,
187 std::string prefix = "",
188 bool inclass = false);
189
190 void generate_deserialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
191
192 void generate_deserialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
193
194 void generate_deserialize_set_element(std::ostream& out, t_set* tset, std::string prefix = "");
195
196 void generate_deserialize_map_element(std::ostream& out, t_map* tmap, std::string prefix = "");
197
198 void generate_deserialize_list_element(std::ostream& out,
199 t_list* tlist,
200 std::string prefix = "");
201
202 void generate_serialize_field(std::ostream& out, t_field* tfield, std::string prefix = "");
203
204 void generate_serialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
205
206 void generate_serialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
207
208 void generate_serialize_map_element(std::ostream& out,
209 t_map* tmap,
210 std::string kiter,
211 std::string viter);
212
213 void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter);
214
215 void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter);
216
217 void generate_php_doc(std::ostream& out, t_doc* tdoc);
218
219 void generate_php_doc(std::ostream& out, t_field* tfield);
220
221 void generate_php_doc(std::ostream& out, t_function* tfunction);
222
223 void generate_php_docstring_comment(std::ostream& out, string contents);
224
225 /**
226 * Helper rendering functions
227 */
228
229 std::string php_includes();
230 std::string declare_field(t_field* tfield, bool init = false, bool obj = false);
231 std::string function_signature(t_function* tfunction, std::string prefix = "");
232 std::string argument_list(t_struct* tstruct, bool addTypeHints = true);
233 std::string type_to_cast(t_type* ttype);
234 std::string type_to_enum(t_type* ttype);
235 std::string type_to_phpdoc(t_type* ttype);
236
php_is_scalar(t_type * ttype)237 bool php_is_scalar(t_type *ttype) {
238 ttype = ttype->get_true_type();
239 if(ttype->is_base_type()) {
240 return true;
241 } else if(ttype->is_enum()) {
242 return true;
243 } else {
244 return false;
245 }
246 }
247
php_namespace_base(const t_program * p)248 std::string php_namespace_base(const t_program* p) {
249 std::string ns = p->get_namespace("php");
250 const char* delimiter = "\\";
251 size_t position = ns.find('.');
252 while (position != string::npos) {
253 ns.replace(position, 1, delimiter);
254 position = ns.find('.', position + 1);
255 }
256 return ns;
257 }
258
259 // general use namespace prefixing: \my\namespace\ or my_namespace_
php_namespace(const t_program * p)260 string php_namespace(const t_program* p) {
261 string ns = php_namespace_base(p);
262 return (nsglobal_.size() ? NSGLOBAL_AB : NSGLOBAL_B) + (ns.size() ? (ns + "\\") : "");
263 }
264
265 // return the namespace of a file:
266 // global\ns\sub\ns or global\ns or sub\ns
php_namespace_suffix(const t_program * p)267 string php_namespace_suffix(const t_program* p) {
268 string ns = php_namespace_base(p);
269
270 return NSGLOBAL
271 + (ns.size() && NSGLOBAL.size() ? "\\" : "")
272 + ns;
273 }
274
275 // add a directory to already existing namespace
php_namespace_directory(string directory,bool end=true)276 string php_namespace_directory(string directory, bool end = true) {
277 (void)directory;
278 if (end) {
279 return ";";
280 } else {
281 return "";
282 }
283 }
284
285 // writing an autload identifier into globa;ls: my\namespace\ or my_namespace_
php_namespace_autoload(const t_program * p)286 string php_namespace_autoload(const t_program* p) {
287 std::string ns = php_namespace_base(p);
288 return (nsglobal_.size() ? NSGLOBAL_B : NSGLOBAL) + (ns.size() ? (ns + "\\") : "");
289 }
290
291 // declaring a type: typename or my_namespace_typename
php_namespace_declaration(t_type * t)292 string php_namespace_declaration(t_type* t) { return t->get_name(); }
293
php_path(t_program * p)294 std::string php_path(t_program* p) {
295 std::string ns = p->get_namespace("php.path");
296 if (ns.empty()) {
297 return p->get_name();
298 }
299
300 // Transform the java-style namespace into a path.
301 for (char & n : ns) {
302 if (n == '.') {
303 n = '/';
304 }
305 }
306
307 return ns + '/';
308 }
309
310 /**
311 * Transform class_method into ClassMethod
312 *
313 * @param str
314 * @return stirng
315 */
classify(string str)316 string classify(string str) {
317 string classe = "";
318
319 vector<string> x = split(str, '_');
320
321 for (const auto & i : x) {
322 classe = classe + capitalize(i);
323 }
324
325 return classe;
326 }
327
328 /**
329 * Split method
330 * @param s
331 * @param delim
332 * @param elems
333 * @return
334 */
split(const string & s,char delim,vector<string> & elems)335 vector<string>& split(const string& s, char delim, vector<string>& elems) {
336 stringstream ss(s);
337 string item;
338
339 while (getline(ss, item, delim)) {
340 elems.push_back(item);
341 }
342
343 return elems;
344 }
345
split(const string & s,char delim)346 vector<string> split(const string& s, char delim) {
347 vector<string> elems;
348
349 return split(s, delim, elems);
350 }
351
352 /**
353 * Capitalize method
354 * @param str
355 * @return
356 */
capitalize(string str)357 string capitalize(string str) {
358 string::iterator it(str.begin());
359
360 if (it != str.end())
361 str[0] = toupper((unsigned char)str[0]);
362
363 // while(++it != str.end())
364 // {
365 // *it = tolower((unsigned char)*it);
366 // }
367 return str;
368 }
369
370 private:
371 /**
372 * File streams
373 */
374 ofstream_with_content_based_conditional_update f_types_;
375 ofstream_with_content_based_conditional_update f_service_;
376
377 std::string package_dir_;
378 /**
379 * Generate protocol-independent template? Or Binary inline code?
380 */
381 bool binary_inline_;
382
383 /**
384 * Generate a REST handler class
385 */
386 bool rest_;
387
388 /**
389 * Generate stubs for a PHP server
390 */
391 bool phps_;
392
393 /**
394 * Whether to use OOP base class TBase
395 */
396 bool oop_;
397
398 /**
399 * Whether to generate old-style PHP file to use classmap autoloading
400 */
401 bool classmap_;
402
403 /**
404 * Whether to generate validator code
405 */
406 bool validate_;
407
408 /**
409 * Whether to generate JsonSerializable classes
410 */
411 bool json_serializable_;
412
413 /**
414 * Global namespace for PHP 5.3
415 */
416 std::string nsglobal_;
417
418 /**
419 * Whether to generate getters and setters
420 */
421 bool getters_setters_;
422 };
423
lang_keywords_for_validation() const424 std::set<std::string> t_php_generator::lang_keywords_for_validation() const {
425 std::string keywords[] = { "BEGIN", "END", "__CLASS__", "__DIR__", "__FILE__", "__FUNCTION__",
426 "__LINE__", "__METHOD__", "__NAMESPACE__", "abstract", "alias", "and", "args", "as",
427 "assert", "begin", "break", "case", "catch", "class", "clone", "continue", "declare",
428 "def", "default", "del", "delete", "do", "dynamic", "elif", "else", "elseif", "elsif",
429 "end", "enddeclare", "endfor", "endforeach", "endif", "endswitch", "endwhile", "ensure",
430 "except", "exec", "finally", "float", "for", "foreach", "from", "function", "global",
431 "goto", "if", "implements", "import", "in", "inline", "instanceof", "interface", "is",
432 "lambda", "module", "native", "new", "next", "nil", "not", "or", "package", "pass",
433 "public", "print", "private", "protected", "raise", "redo", "rescue", "retry", "register",
434 "return", "self", "sizeof", "static", "super", "switch", "synchronized", "then", "this",
435 "throw", "transient", "try", "undef", "unless", "unsigned", "until", "use", "var",
436 "virtual", "volatile", "when", "while", "with", "xor", "yield" };
437 return std::set<std::string>(keywords, keywords + sizeof(keywords)/sizeof(keywords[0]) );
438 }
439
is_valid_namespace(const std::string & sub_namespace)440 bool t_php_generator::is_valid_namespace(const std::string& sub_namespace) {
441 return sub_namespace == "path";
442 }
443
444 /**
445 * Prepares for file generation by opening up the necessary file output
446 * streams.
447 *
448 * @param tprogram The program to generate
449 */
init_generator()450 void t_php_generator::init_generator() {
451 // Make output directory
452 MKDIR(get_out_dir().c_str());
453
454 // Create Real directory Namespaces
455 vector<string> NSx = split(php_namespace_suffix(get_program()), '\\');
456 package_dir_ = get_out_dir();
457
458 for (const auto & i : NSx) {
459 package_dir_ = package_dir_ + "/" + i + "/";
460 MKDIR(package_dir_.c_str());
461 }
462
463 // Prepare output file for all the types in classmap mode
464 if (classmap_) {
465 // Make output file
466 string f_types_name = package_dir_ + "Types.php";
467 f_types_.open(f_types_name.c_str());
468 generate_program_header(f_types_);
469 }
470 }
471
472 /**
473 * Prints standard php includes
474 */
php_includes()475 string t_php_generator::php_includes() {
476 string includes = "use Thrift\\Base\\TBase;\n"
477 "use Thrift\\Type\\TType;\n"
478 "use Thrift\\Type\\TMessageType;\n"
479 "use Thrift\\Exception\\TException;\n"
480 "use Thrift\\Exception\\TProtocolException;\n"
481 "use Thrift\\Protocol\\TProtocol;\n"
482 "use Thrift\\Protocol\\TBinaryProtocolAccelerated;\n"
483 "use Thrift\\Exception\\TApplicationException;\n";
484
485 if (json_serializable_) {
486 includes += "use JsonSerializable;\n"
487 "use stdClass;\n";
488 }
489
490 return includes;
491 }
492
493 /**
494 * Close up (or down) some filez.
495 */
close_generator()496 void t_php_generator::close_generator() {
497 if (classmap_) {
498 // Close types file
499 f_types_.close();
500 }
501 }
502
503 /**
504 * Generates a typedef. This is not done in PHP, types are all implicit.
505 *
506 * @param ttypedef The type definition
507 */
generate_typedef(t_typedef * ttypedef)508 void t_php_generator::generate_typedef(t_typedef* ttypedef) {
509 (void)ttypedef;
510 }
511
512 /**
513 * Generates service header contains namespace suffix and includes inside file specified
514 */
generate_service_header(t_service * tservice,std::ostream & file)515 void t_php_generator::generate_service_header(t_service* tservice, std::ostream& file) {
516 file << "<?php" << endl;
517 if (!php_namespace_suffix(tservice->get_program()).empty()) {
518 file << "namespace " << php_namespace_suffix(tservice->get_program()) << ";" << endl
519 << endl;
520 }
521 file << autogen_comment() << php_includes();
522
523 file << endl;
524 }
525
526 /**
527 * Generates program header contains namespace suffix and includes inside file specified
528 */
generate_program_header(std::ostream & file)529 void t_php_generator::generate_program_header(std::ostream& file) {
530 file << "<?php" << endl;
531 if (!php_namespace_suffix(get_program()).empty()) {
532 file << "namespace " << php_namespace_suffix(get_program()) << ";" << endl
533 << endl;
534 }
535 file << autogen_comment() << php_includes();
536
537 file << endl;
538 }
539
540 /**
541 * Generates code for an enumerated type. Since define is expensive to lookup
542 * in PHP, we use a global array for this.
543 *
544 * @param tenum The enumeration
545 */
generate_enum(t_enum * tenum)546 void t_php_generator::generate_enum(t_enum* tenum) {
547 ofstream_with_content_based_conditional_update& f_enum = f_types_;
548 if (!classmap_) {
549 string f_enum_name = package_dir_ + tenum->get_name() + ".php";
550 f_enum.open(f_enum_name.c_str());
551 generate_program_header(f_enum);
552 }
553
554 vector<t_enum_value*> constants = tenum->get_constants();
555 vector<t_enum_value*>::iterator c_iter;
556
557 // We're also doing it this way to see how it performs. It's more legible
558 // code but you can't do things like an 'extract' on it, which is a bit of
559 // a downer.
560 generate_php_doc(f_enum, tenum);
561 f_enum << "final class " << tenum->get_name() << endl
562 << "{" << endl;
563 indent_up();
564
565 for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
566 int value = (*c_iter)->get_value();
567 generate_php_doc(f_enum, *c_iter);
568 indent(f_enum) << "const " << (*c_iter)->get_name() << " = " << value << ";" << endl
569 << endl;
570 }
571
572 indent(f_enum) << "static public $__names = array(" << endl;
573
574 indent_up();
575 for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
576 int value = (*c_iter)->get_value();
577 indent(f_enum) << value << " => '" << (*c_iter)->get_name() << "'," << endl;
578 }
579 indent_down();
580 indent(f_enum) << ");" << endl;
581
582 indent_down();
583
584 f_enum << "}" << endl << endl;
585 if (!classmap_) {
586 f_enum.close();
587 }
588 }
589
590 /**
591 * Generate constant class
592 *
593 * Override the one from t_generator
594 */
generate_consts(vector<t_const * > consts)595 void t_php_generator::generate_consts(vector<t_const*> consts) {
596 vector<t_const*>::iterator c_iter;
597
598 // Create class only if needed
599 if (consts.size() > 0) {
600
601 ofstream_with_content_based_conditional_update& f_consts = f_types_;
602 if (!classmap_) {
603 string f_consts_name = package_dir_ + "Constant.php";
604 f_consts.open(f_consts_name.c_str());
605 generate_program_header(f_consts);
606 }
607 f_consts << "final class Constant extends \\Thrift\\Type\\TConstant"<< endl
608 << "{" << endl;
609
610 indent_up();
611
612 // Create static property
613 for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
614 string name = (*c_iter)->get_name();
615
616 indent(f_consts) << "static protected $" << name << ";" << endl;
617 }
618
619 // Create init function
620 for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
621 string name = (*c_iter)->get_name();
622
623 f_consts << endl;
624
625 f_consts << indent() << "protected static function init_" << name << "()" <<endl
626 << indent() << "{" << endl;
627 indent_up();
628
629 indent(f_consts) << "return ";
630 generate_php_doc(f_consts, *c_iter);
631 f_consts << render_const_value((*c_iter)->get_type(), (*c_iter)->get_value());
632 f_consts << ";" << endl;
633
634 indent_down();
635 indent(f_consts) << "}" << endl;
636 }
637
638 indent_down();
639 f_consts << "}" << endl;
640 if (!classmap_) {
641 f_consts.close();
642 }
643 }
644 }
645
646 /**
647 * Prints the value of a constant with the given type. Note that type checking
648 * is NOT performed in this function as it is always run beforehand using the
649 * validate_types method in main.cc
650 */
render_const_value(t_type * type,t_const_value * value)651 string t_php_generator::render_const_value(t_type* type, t_const_value* value) {
652 std::ostringstream out;
653 type = get_true_type(type);
654 if (type->is_base_type()) {
655 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
656 switch (tbase) {
657 case t_base_type::TYPE_STRING:
658 out << '"' << get_escaped_string(value) << '"';
659 break;
660 case t_base_type::TYPE_BOOL:
661 out << (value->get_integer() > 0 ? "true" : "false");
662 break;
663 case t_base_type::TYPE_I8:
664 case t_base_type::TYPE_I16:
665 case t_base_type::TYPE_I32:
666 case t_base_type::TYPE_I64:
667 out << value->get_integer();
668 break;
669 case t_base_type::TYPE_DOUBLE:
670 if (value->get_type() == t_const_value::CV_INTEGER) {
671 out << value->get_integer();
672 } else {
673 out << value->get_double();
674 }
675 break;
676 default:
677 throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
678 }
679 } else if (type->is_enum()) {
680 indent(out) << value->get_integer();
681 } else if (type->is_struct() || type->is_xception()) {
682 out << "new " << php_namespace(type->get_program()) << type->get_name() << "(array(" << endl;
683 indent_up();
684 const vector<t_field*>& fields = ((t_struct*)type)->get_members();
685 vector<t_field*>::const_iterator f_iter;
686 const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
687 map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
688 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
689 t_type* field_type = nullptr;
690 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
691 if ((*f_iter)->get_name() == v_iter->first->get_string()) {
692 field_type = (*f_iter)->get_type();
693 }
694 }
695 if (field_type == nullptr) {
696 throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
697 }
698 out << indent();
699 out << render_const_value(g_type_string, v_iter->first);
700 out << " => ";
701 out << render_const_value(field_type, v_iter->second);
702 out << "," << endl;
703 }
704 indent_down();
705 indent(out) << "))";
706 } else if (type->is_map()) {
707 t_type* ktype = ((t_map*)type)->get_key_type();
708 t_type* vtype = ((t_map*)type)->get_val_type();
709 out << "array(" << endl;
710 indent_up();
711 const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
712 map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
713 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
714 out << indent();
715 out << render_const_value(ktype, v_iter->first);
716 out << " => ";
717 out << render_const_value(vtype, v_iter->second);
718 out << "," << endl;
719 }
720 indent_down();
721 indent(out) << ")";
722 } else if (type->is_list() || type->is_set()) {
723 t_type* etype;
724 if (type->is_list()) {
725 etype = ((t_list*)type)->get_elem_type();
726 } else {
727 etype = ((t_set*)type)->get_elem_type();
728 }
729 out << "array(" << endl;
730 indent_up();
731 const vector<t_const_value*>& val = value->get_list();
732 vector<t_const_value*>::const_iterator v_iter;
733 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
734 out << indent();
735 out << render_const_value(etype, *v_iter);
736 if (type->is_set()) {
737 out << " => true";
738 }
739 out << "," << endl;
740 }
741 indent_down();
742 indent(out) << ")";
743 }
744 return out.str();
745 }
746
747 /**
748 * Make a struct
749 */
generate_struct(t_struct * tstruct)750 void t_php_generator::generate_struct(t_struct* tstruct) {
751 generate_php_struct(tstruct, false);
752 }
753
754 /**
755 * Generates a struct definition for a thrift exception. Basically the same
756 * as a struct but extends the Exception class.
757 *
758 * @param txception The struct definition
759 */
generate_xception(t_struct * txception)760 void t_php_generator::generate_xception(t_struct* txception) {
761 generate_php_struct(txception, true);
762 }
763
764 /**
765 * Structs can be normal or exceptions.
766 */
generate_php_struct(t_struct * tstruct,bool is_exception)767 void t_php_generator::generate_php_struct(t_struct* tstruct, bool is_exception) {
768 ofstream_with_content_based_conditional_update& f_struct = f_types_;
769 if (!classmap_) {
770 string f_struct_name = package_dir_ + tstruct->get_name() + ".php";
771 f_struct.open(f_struct_name.c_str());
772 generate_program_header(f_struct);
773 }
774 generate_php_struct_definition(f_struct, tstruct, is_exception);
775 if (!classmap_) {
776 f_struct.close();
777 }
778 }
779
generate_php_type_spec(ostream & out,t_type * t)780 void t_php_generator::generate_php_type_spec(ostream& out, t_type* t) {
781 t = get_true_type(t);
782 indent(out) << "'type' => " << type_to_enum(t) << "," << endl;
783
784 if (t->is_base_type()) {
785 // Noop, type is all we need
786 } else if (t->is_struct() || t->is_xception() || t->is_enum()) {
787 indent(out) << "'class' => '" << php_namespace(t->get_program()) << t->get_name() << "',"
788 << endl;
789 } else if (t->is_map()) {
790 t_type* ktype = get_true_type(((t_map*)t)->get_key_type());
791 t_type* vtype = get_true_type(((t_map*)t)->get_val_type());
792 indent(out) << "'ktype' => " << type_to_enum(ktype) << "," << endl;
793 indent(out) << "'vtype' => " << type_to_enum(vtype) << "," << endl;
794 indent(out) << "'key' => array(" << endl;
795 indent_up();
796 generate_php_type_spec(out, ktype);
797 indent_down();
798 indent(out) << ")," << endl;
799 indent(out) << "'val' => array(" << endl;
800 indent_up();
801 generate_php_type_spec(out, vtype);
802 indent(out) << ")," << endl;
803 indent_down();
804 } else if (t->is_list() || t->is_set()) {
805 t_type* etype;
806 if (t->is_list()) {
807 etype = get_true_type(((t_list*)t)->get_elem_type());
808 } else {
809 etype = get_true_type(((t_set*)t)->get_elem_type());
810 }
811 indent(out) << "'etype' => " << type_to_enum(etype) << "," << endl;
812 indent(out) << "'elem' => array(" << endl;
813 indent_up();
814 generate_php_type_spec(out, etype);
815 indent(out) << ")," << endl;
816 indent_down();
817 } else {
818 throw "compiler error: no type for php struct spec field";
819 }
820 }
821
822 /**
823 * Generates the struct specification structure, which fully qualifies enough
824 * type information to generalize serialization routines.
825 */
generate_php_struct_spec(ostream & out,t_struct * tstruct)826 void t_php_generator::generate_php_struct_spec(ostream& out, t_struct* tstruct) {
827 indent(out) << "static public $_TSPEC = array(" << endl;
828 indent_up();
829
830 const vector<t_field*>& members = tstruct->get_members();
831 vector<t_field*>::const_iterator m_iter;
832 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
833 t_type* t = get_true_type((*m_iter)->get_type());
834 indent(out) << (*m_iter)->get_key() << " => array(" << endl;
835 indent_up();
836 out << indent() << "'var' => '" << (*m_iter)->get_name() << "'," << endl;
837 out << indent() << "'isRequired' => " << ((*m_iter)->get_req() == t_field::T_REQUIRED ? "true" : "false") << "," << endl;
838 generate_php_type_spec(out, t);
839 indent_down();
840 indent(out) << ")," << endl;
841 }
842
843 indent_down();
844 indent(out) << ");" << endl << endl;
845 }
846 /**
847 * Generates necessary accessors and mutators for the fields
848 */
generate_generic_field_getters_setters(std::ostream & out,t_struct * tstruct)849 void t_php_generator::generate_generic_field_getters_setters(std::ostream& out,
850 t_struct* tstruct) {
851 std::ostringstream getter_stream;
852 std::ostringstream setter_stream;
853
854 // build up the bodies of both the getter and setter at once
855 const vector<t_field*>& fields = tstruct->get_members();
856 vector<t_field*>::const_iterator f_iter;
857 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
858 t_field* field = *f_iter;
859 std::string field_name = field->get_name();
860 std::string cap_name = get_cap_name(field_name);
861
862 indent_up();
863 generate_reflection_setters(setter_stream, field_name, cap_name);
864 generate_reflection_getters(getter_stream, field_name, cap_name);
865 indent_down();
866 }
867
868 indent(out) << endl;
869 out << getter_stream.str();
870 out << setter_stream.str();
871 indent(out) << endl;
872 }
873 /**
874 * Generates a getter for the generated private fields
875 */
generate_reflection_getters(ostringstream & out,string field_name,string cap_name)876 void t_php_generator::generate_reflection_getters(ostringstream& out,
877 string field_name,
878 string cap_name) {
879
880
881 out << indent() << "public function " << "get" << cap_name << "()" << endl
882 << indent() << "{" << endl;
883
884 indent_up();
885
886 out << indent() << "return $this->" << field_name << ";" << endl;
887
888 indent_down();
889 out << indent() << "}" << endl;
890 out << endl;
891 }
892 /**
893 * Generates a setter for the generated private fields
894 */
generate_reflection_setters(ostringstream & out,string field_name,string cap_name)895 void t_php_generator::generate_reflection_setters(ostringstream& out,
896 string field_name,
897 string cap_name) {
898
899 out << indent() << "public function set" << cap_name << "(" << "$" << field_name << ")" << endl
900 << indent() << "{" << endl;
901
902 indent_up();
903
904 out << indent() << "$this->" << field_name << " = $" << field_name << ";" << endl;
905
906
907 indent_down();
908 out << indent() << "}" << endl;
909 out << endl;
910 }
911 /**
912 * Gets the first-letter capitalized name for the field
913 *
914 * @param std::string name of the field
915 */
get_cap_name(std::string name)916 std::string t_php_generator::get_cap_name(std::string name) {
917 name[0] = toupper(name[0]);
918 return name;
919 }
920 /**
921 * Generates a struct definition for a thrift data type. This is nothing in PHP
922 * where the objects are all just associative arrays (unless of course we
923 * decide to start using objects for them...)
924 *
925 * @param tstruct The struct definition
926 */
generate_php_struct_definition(ostream & out,t_struct * tstruct,bool is_exception,bool is_result)927 void t_php_generator::generate_php_struct_definition(ostream& out,
928 t_struct* tstruct,
929 bool is_exception,
930 bool is_result) {
931 const vector<t_field*>& members = tstruct->get_members();
932 vector<t_field*>::const_iterator m_iter;
933
934 generate_php_doc(out, tstruct);
935 out << "class " << php_namespace_declaration(tstruct);
936 if (is_exception) {
937 out << " extends "
938 << "TException";
939 } else if (oop_) {
940 out << " extends "
941 << "TBase";
942 }
943 if (json_serializable_) {
944 out << " implements JsonSerializable";
945 }
946 out << endl
947 << "{" << endl;
948 indent_up();
949
950 out << indent() << "static public $isValidate = " << (validate_ ? "true" : "false") << ";" << endl << endl;
951
952 generate_php_struct_spec(out, tstruct);
953
954 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
955 string dval = "null";
956 t_type* t = get_true_type((*m_iter)->get_type());
957 if ((*m_iter)->get_value() != nullptr && !(t->is_struct() || t->is_xception())) {
958 dval = render_const_value((*m_iter)->get_type(), (*m_iter)->get_value());
959 }
960 generate_php_doc(out, *m_iter);
961 string access = (getters_setters_) ? "private" : "public";
962 indent(out) << access << " $" << (*m_iter)->get_name() << " = " << dval << ";" << endl;
963 }
964
965 out << endl;
966
967 // Generate constructor from array
968 string param = (members.size() > 0) ? "$vals = null" : "";
969 out << indent() << "public function __construct(" << param << ")"<< endl
970 << indent() << "{" << endl;
971 indent_up();
972
973 if (members.size() > 0) {
974 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
975 t_type* t = get_true_type((*m_iter)->get_type());
976 if ((*m_iter)->get_value() != nullptr && (t->is_struct() || t->is_xception())) {
977 indent(out) << "$this->" << (*m_iter)->get_name() << " = "
978 << render_const_value(t, (*m_iter)->get_value()) << ";" << endl;
979 }
980 }
981 out << indent() << "if (is_array($vals)) {" << endl;
982 indent_up();
983 if (oop_) {
984 out << indent() << "parent::__construct(self::$_TSPEC, $vals);" << endl;
985 } else {
986 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
987 out << indent() << "if (isset($vals['" << (*m_iter)->get_name() << "'])) {" << endl;
988
989 indent_up();
990 out << indent() << "$this->" << (*m_iter)->get_name() << " = $vals['"
991 << (*m_iter)->get_name() << "'];" << endl;
992
993 indent_down();
994 out << indent() << "}" << endl;
995 }
996 }
997 indent_down();
998 out << indent() << "}" << endl;
999 }
1000 scope_down(out);
1001 out << endl;
1002
1003 out << indent() << "public function getName()" << endl
1004 << indent() << "{" << endl;
1005
1006 indent_up();
1007 out << indent() << "return '" << tstruct->get_name() << "';" << endl;
1008
1009 indent_down();
1010 out << indent() << "}" << endl << endl;
1011
1012 out << endl;
1013 if (getters_setters_) {
1014 generate_generic_field_getters_setters(out, tstruct);
1015 }
1016 generate_php_struct_reader(out, tstruct, is_result);
1017 out << endl;
1018 generate_php_struct_writer(out, tstruct, is_result);
1019 if (needs_php_read_validator(tstruct, is_result)) {
1020 out << endl;
1021 generate_php_struct_read_validator(out, tstruct);
1022 }
1023 if (needs_php_write_validator(tstruct, is_result)) {
1024 out << endl;
1025 generate_php_struct_write_validator(out, tstruct);
1026 }
1027 if (json_serializable_) {
1028 out << endl;
1029 generate_php_struct_json_serialize(out, tstruct, is_result);
1030 }
1031
1032 indent_down();
1033 out << indent() << "}" << endl;
1034 }
1035
1036 /**
1037 * Generates the read() method for a struct
1038 */
generate_php_struct_reader(ostream & out,t_struct * tstruct,bool is_result)1039 void t_php_generator::generate_php_struct_reader(ostream& out, t_struct* tstruct, bool is_result) {
1040 const vector<t_field*>& fields = tstruct->get_members();
1041 vector<t_field*>::const_iterator f_iter;
1042
1043 indent(out) << "public function read($input)" << endl;
1044 scope_up(out);
1045
1046 if (oop_) {
1047 if (needs_php_read_validator(tstruct, is_result)) {
1048 indent(out) << "$tmp = $this->_read('" << tstruct->get_name() << "', self::$_TSPEC, $input);"
1049 << endl;
1050 indent(out) << "$this->_validateForRead();" << endl;
1051 indent(out) << "return $tmp;" << endl;
1052 } else {
1053 indent(out) << "return $this->_read('" << tstruct->get_name() << "', self::$_TSPEC, $input);"
1054 << endl;
1055 }
1056 scope_down(out);
1057 out << endl;
1058 return;
1059 }
1060
1061 out << indent() << "$xfer = 0;" << endl << indent() << "$fname = null;" << endl << indent()
1062 << "$ftype = 0;" << endl << indent() << "$fid = 0;" << endl;
1063
1064 // Declare stack tmp variables
1065 if (!binary_inline_) {
1066 indent(out) << "$xfer += $input->readStructBegin($fname);" << endl;
1067 }
1068
1069 // Loop over reading in fields
1070 indent(out) << "while (true) {" << endl;
1071
1072 indent_up();
1073
1074 // Read beginning field marker
1075 if (binary_inline_) {
1076 t_field fftype(g_type_i8, "ftype");
1077 t_field ffid(g_type_i16, "fid");
1078 generate_deserialize_field(out, &fftype);
1079 out << indent() << "if ($ftype == "
1080 << "TType::STOP) {" << endl << indent() << " break;" << endl << indent() << "}" << endl;
1081 generate_deserialize_field(out, &ffid);
1082 } else {
1083 indent(out) << "$xfer += $input->readFieldBegin($fname, $ftype, $fid);" << endl;
1084 // Check for field STOP marker and break
1085 indent(out) << "if ($ftype == "
1086 << "TType::STOP) {" << endl;
1087 indent_up();
1088 indent(out) << "break;" << endl;
1089 indent_down();
1090 indent(out) << "}" << endl;
1091 }
1092
1093 // Switch statement on the field we are reading
1094 indent(out) << "switch ($fid) {" << endl;
1095
1096 indent_up();
1097
1098 // Generate deserialization code for known cases
1099 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1100 indent(out) << "case " << (*f_iter)->get_key() << ":" << endl;
1101 indent_up();
1102 indent(out) << "if ($ftype == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl;
1103 indent_up();
1104 generate_deserialize_field(out, *f_iter, "this->");
1105 indent_down();
1106 out << indent() << "} else {" << endl;
1107
1108 indent_up();
1109 if (binary_inline_) {
1110 indent(out) << "$xfer += TProtocol::skipBinary($input, $ftype);" << endl;
1111 } else {
1112 indent(out) << "$xfer += $input->skip($ftype);" << endl;
1113 }
1114
1115 indent_down();
1116 out << indent() << "}" << endl << indent() << "break;" << endl;
1117 indent_down();
1118 }
1119
1120 // In the default case we skip the field
1121 indent(out) << "default:" << endl;
1122
1123 indent_up();
1124 if (binary_inline_) {
1125 indent(out) << "$xfer += "
1126 << "TProtocol::skipBinary($input, $ftype);" << endl;
1127 } else {
1128 indent(out) << "$xfer += $input->skip($ftype);" << endl;
1129 }
1130 indent(out) << "break;" << endl;
1131 indent_down();
1132
1133 scope_down(out);
1134
1135 if (!binary_inline_) {
1136 // Read field end marker
1137 indent(out) << "$xfer += $input->readFieldEnd();" << endl;
1138 }
1139
1140 scope_down(out);
1141
1142 if (!binary_inline_) {
1143 indent(out) << "$xfer += $input->readStructEnd();" << endl;
1144 }
1145
1146 if (needs_php_read_validator(tstruct, is_result)) {
1147 indent(out) << "$this->_validateForRead();" << endl;
1148 }
1149
1150 indent(out) << "return $xfer;" << endl;
1151
1152 indent_down();
1153 out << indent() << "}" << endl;
1154 }
1155
1156 /**
1157 * Generates the write() method for a struct
1158 */
generate_php_struct_writer(ostream & out,t_struct * tstruct,bool is_result)1159 void t_php_generator::generate_php_struct_writer(ostream& out, t_struct* tstruct, bool is_result) {
1160 string name = tstruct->get_name();
1161 const vector<t_field*>& fields = tstruct->get_sorted_members();
1162 vector<t_field*>::const_iterator f_iter;
1163
1164 if (binary_inline_) {
1165 indent(out) << "public function write(&$output)" << endl;
1166 } else {
1167 indent(out) << "public function write($output)" << endl;
1168 }
1169 indent(out) << "{" << endl;
1170 indent_up();
1171
1172 if (needs_php_write_validator(tstruct, is_result)) {
1173 indent(out) << "$this->_validateForWrite();" << endl;
1174 }
1175
1176 if (oop_) {
1177 indent(out) << "return $this->_write('" << tstruct->get_name() << "', self::$_TSPEC, $output);"
1178 << endl;
1179 scope_down(out);
1180 out << endl;
1181 return;
1182 }
1183
1184 indent(out) << "$xfer = 0;" << endl;
1185
1186 if (!binary_inline_) {
1187 indent(out) << "$xfer += $output->writeStructBegin('" << name << "');" << endl;
1188 }
1189
1190 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1191 out << indent() << "if ($this->" << (*f_iter)->get_name() << " !== null) {" << endl;
1192 indent_up();
1193
1194 t_type* type = get_true_type((*f_iter)->get_type());
1195 string expect;
1196 if (type->is_container()) {
1197 expect = "array";
1198 } else if (type->is_struct()) {
1199 expect = "object";
1200 }
1201 if (!expect.empty()) {
1202 out << indent() << "if (!is_" << expect << "($this->" << (*f_iter)->get_name() << ")) {"
1203 << endl;
1204 indent_up();
1205 out << indent() << "throw new "
1206 << "TProtocolException('Bad type in structure.', "
1207 << "TProtocolException::INVALID_DATA);" << endl;
1208 scope_down(out);
1209 }
1210
1211 // Write field header
1212 if (binary_inline_) {
1213 out << indent() << "$output .= pack('c', " << type_to_enum((*f_iter)->get_type()) << ");"
1214 << endl << indent() << "$output .= pack('n', " << (*f_iter)->get_key() << ");" << endl;
1215 } else {
1216 indent(out) << "$xfer += $output->writeFieldBegin("
1217 << "'" << (*f_iter)->get_name() << "', " << type_to_enum((*f_iter)->get_type())
1218 << ", " << (*f_iter)->get_key() << ");" << endl;
1219 }
1220
1221 // Write field contents
1222 generate_serialize_field(out, *f_iter, "this->");
1223
1224 // Write field closer
1225 if (!binary_inline_) {
1226 indent(out) << "$xfer += $output->writeFieldEnd();" << endl;
1227 }
1228
1229 indent_down();
1230 indent(out) << "}" << endl;
1231 }
1232
1233 if (binary_inline_) {
1234 out << indent() << "$output .= pack('c', "
1235 << "TType::STOP);" << endl;
1236 } else {
1237 out << indent() << "$xfer += $output->writeFieldStop();" << endl << indent()
1238 << "$xfer += $output->writeStructEnd();" << endl;
1239 }
1240
1241 out << indent() << "return $xfer;" << endl;
1242
1243 indent_down();
1244 out << indent() << "}" << endl;
1245 }
1246
generate_php_struct_read_validator(ostream & out,t_struct * tstruct)1247 void t_php_generator::generate_php_struct_read_validator(ostream& out, t_struct* tstruct) {
1248 generate_php_struct_required_validator(out, tstruct, "_validateForRead", false);
1249 }
1250
generate_php_struct_write_validator(ostream & out,t_struct * tstruct)1251 void t_php_generator::generate_php_struct_write_validator(ostream& out, t_struct* tstruct) {
1252 generate_php_struct_required_validator(out, tstruct, "_validateForWrite", true);
1253 }
1254
generate_php_struct_required_validator(ostream & out,t_struct * tstruct,std::string method_name,bool write_mode)1255 void t_php_generator::generate_php_struct_required_validator(ostream& out,
1256 t_struct* tstruct,
1257 std::string method_name,
1258 bool write_mode) {
1259 indent(out) << "private function " << method_name << "() {" << endl;
1260 indent_up();
1261
1262 const vector<t_field*>& fields = tstruct->get_members();
1263
1264 if (fields.size() > 0) {
1265 vector<t_field*>::const_iterator f_iter;
1266
1267 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1268 t_field* field = (*f_iter);
1269 if (field->get_req() == t_field::T_REQUIRED
1270 || (field->get_req() == t_field::T_OPT_IN_REQ_OUT && write_mode)) {
1271 indent(out) << "if ($this->" << field->get_name() << " === null) {" << endl;
1272 indent_up();
1273 indent(out) << "throw new TProtocolException('Required field " << tstruct->get_name() << "."
1274 << field->get_name() << " is unset!');" << endl;
1275 indent_down();
1276 indent(out) << "}" << endl;
1277 }
1278 }
1279 }
1280
1281 indent_down();
1282 indent(out) << "}" << endl;
1283 }
1284
generate_php_struct_json_serialize(ostream & out,t_struct * tstruct,bool is_result)1285 void t_php_generator::generate_php_struct_json_serialize(ostream& out,
1286 t_struct* tstruct,
1287 bool is_result) {
1288 indent(out) << "public function jsonSerialize() {" << endl;
1289 indent_up();
1290
1291 if (needs_php_write_validator(tstruct, is_result)) {
1292 indent(out) << "$this->_validateForWrite();" << endl;
1293 }
1294
1295 indent(out) << "$json = new stdClass;" << endl;
1296
1297 const vector<t_field*>& fields = tstruct->get_members();
1298
1299 if (fields.size() > 0) {
1300 vector<t_field*>::const_iterator f_iter;
1301 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1302 t_field* field = (*f_iter);
1303 t_type* type = field->get_type();
1304 const string& name = field->get_name();
1305 if (type->is_map()) {
1306 t_type* key_type = ((t_map*)type)->get_key_type();
1307 if (!(key_type->is_base_type() || key_type->is_enum())) {
1308 // JSON object keys must be strings. PHP's json_encode()
1309 // function will convert any scalar key to strings, but
1310 // we skip thrift maps with non-scalar keys.
1311 continue;
1312 }
1313 }
1314 indent(out) << "if ($this->" << name << " !== null) {" << endl;
1315 indent_up();
1316 indent(out) << "$json->" << name << " = ";
1317 if (type->is_map()) {
1318 out << "(object)";
1319 } else {
1320 out << type_to_cast(type);
1321 }
1322 out << "$this->" << name << ";" << endl;
1323 indent_down();
1324 indent(out) << "}" << endl;
1325 }
1326 }
1327
1328 indent(out) << "return $json;" << endl;
1329 indent_down();
1330
1331 indent(out) << "}" << endl;
1332 }
1333
get_php_num_required_fields(const vector<t_field * > & fields,bool write_mode)1334 int t_php_generator::get_php_num_required_fields(const vector<t_field*>& fields, bool write_mode) {
1335 int num_req = 0;
1336
1337 if (fields.size() > 0) {
1338 vector<t_field*>::const_iterator f_iter;
1339 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1340 if ((*f_iter)->get_req() == t_field::T_REQUIRED
1341 || ((*f_iter)->get_req() == t_field::T_OPT_IN_REQ_OUT && write_mode)) {
1342 ++num_req;
1343 }
1344 }
1345 }
1346 return num_req;
1347 }
1348
needs_php_write_validator(t_struct * tstruct,bool is_result)1349 bool t_php_generator::needs_php_write_validator(t_struct* tstruct, bool is_result) {
1350 return (validate_ && !is_result && !tstruct->is_union()
1351 && get_php_num_required_fields(tstruct->get_members(), true) > 0);
1352 }
1353
needs_php_read_validator(t_struct * tstruct,bool is_result)1354 bool t_php_generator::needs_php_read_validator(t_struct* tstruct, bool is_result) {
1355 return (validate_ && !is_result
1356 && (get_php_num_required_fields(tstruct->get_members(), false) > 0));
1357 }
1358
1359 /**
1360 * Generates a thrift service.
1361 *
1362 * @param tservice The service definition
1363 */
generate_service(t_service * tservice)1364 void t_php_generator::generate_service(t_service* tservice) {
1365 if(classmap_) {
1366 string f_service_name = package_dir_ + service_name_ + ".php";
1367 f_service_.open(f_service_name.c_str());
1368 generate_service_header(tservice, f_service_);
1369 }
1370
1371 // Generate the three main parts of the service (well, two for now in PHP)
1372 generate_service_interface(tservice);
1373 if (rest_) {
1374 generate_service_rest(tservice);
1375 }
1376 generate_service_client(tservice);
1377 generate_service_helpers(tservice);
1378 if (phps_) {
1379 generate_service_processor(tservice);
1380 }
1381
1382 if(classmap_) {
1383 // Close service file
1384 f_service_ << endl;
1385 f_service_.close();
1386 }
1387 }
1388
1389 /**
1390 * Generates a service server definition.
1391 *
1392 * @param tservice The service to generate a server for.
1393 */
generate_service_processor(t_service * tservice)1394 void t_php_generator::generate_service_processor(t_service* tservice) {
1395 ofstream_with_content_based_conditional_update& f_service_processor = f_service_;
1396 if (!classmap_) {
1397 string f_service_processor_name = package_dir_ + service_name_ + "Processor.php";
1398 f_service_processor.open(f_service_processor_name.c_str());
1399 generate_service_header(tservice, f_service_processor);
1400 }
1401
1402 // Generate the dispatch methods
1403 vector<t_function*> functions = tservice->get_functions();
1404 vector<t_function*>::iterator f_iter;
1405
1406 string extends = "";
1407 string extends_processor = "";
1408 if (tservice->get_extends() != nullptr) {
1409 extends = tservice->get_extends()->get_name();
1410 extends_processor = " extends " + php_namespace(tservice->get_extends()->get_program())
1411 + extends + "Processor";
1412 }
1413
1414 // Generate the header portion
1415 f_service_processor << "class " << service_name_ << "Processor" << extends_processor << endl
1416 << "{" << endl;
1417 indent_up();
1418
1419 if (extends.empty()) {
1420 f_service_processor << indent() << "protected $handler_ = null;" << endl;
1421 }
1422
1423 f_service_processor << indent() << "public function __construct($handler)"<< endl
1424 << indent() << "{" << endl;
1425
1426 indent_up();
1427 if (extends.empty()) {
1428 f_service_processor << indent() << "$this->handler_ = $handler;" << endl;
1429 } else {
1430 f_service_processor << indent() << "parent::__construct($handler);" << endl;
1431 }
1432
1433 indent_down();
1434 f_service_processor << indent() << "}" << endl << endl;
1435
1436 // Generate the server implementation
1437 f_service_processor << indent() << "public function process($input, $output)" << endl
1438 << indent() << "{" << endl;
1439 indent_up();
1440
1441 f_service_processor << indent() << "$rseqid = 0;" << endl << indent() << "$fname = null;" << endl
1442 << indent() << "$mtype = 0;" << endl << endl;
1443
1444 if (binary_inline_) {
1445 t_field ffname(g_type_string, "fname");
1446 t_field fmtype(g_type_i8, "mtype");
1447 t_field fseqid(g_type_i32, "rseqid");
1448 generate_deserialize_field(f_service_processor, &ffname, "", true);
1449 generate_deserialize_field(f_service_processor, &fmtype, "", true);
1450 generate_deserialize_field(f_service_processor, &fseqid, "", true);
1451 } else {
1452 f_service_processor << indent() << "$input->readMessageBegin($fname, $mtype, $rseqid);" << endl;
1453 }
1454
1455 // HOT: check for method implementation
1456 f_service_processor << indent() << "$methodname = 'process_'.$fname;" << endl
1457 << indent() << "if (!method_exists($this, $methodname)) {" << endl;
1458
1459 indent_up();
1460 if (binary_inline_) {
1461 f_service_processor << indent() << "throw new \\Exception('Function '.$fname.' not implemented.');" << endl;
1462 } else {
1463 f_service_processor << indent() << " $input->skip("
1464 << "TType::STRUCT);" << endl << indent() << " $input->readMessageEnd();" << endl
1465 << indent() << " $x = new "
1466 << "TApplicationException('Function '.$fname.' not implemented.', "
1467 << "TApplicationException::UNKNOWN_METHOD);" << endl << indent()
1468 << " $output->writeMessageBegin($fname, "
1469 << "TMessageType::EXCEPTION, $rseqid);" << endl << indent()
1470 << " $x->write($output);" << endl << indent() << " $output->writeMessageEnd();"
1471 << endl << indent() << " $output->getTransport()->flush();" << endl << indent()
1472 << " return;" << endl;
1473 }
1474
1475 indent_down();
1476 f_service_processor << indent() << "}" << endl
1477 << indent() << "$this->$methodname($rseqid, $input, $output);" << endl
1478 << indent() << "return true;" << endl;
1479
1480 indent_down();
1481 f_service_processor << indent() << "}" << endl << endl;
1482
1483 // Generate the process subfunctions
1484 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1485 generate_process_function(f_service_processor, tservice, *f_iter);
1486 }
1487
1488 indent_down();
1489 f_service_processor << "}" << endl;
1490
1491 if (!classmap_) {
1492 f_service_processor.close();
1493 }
1494 }
1495
1496 /**
1497 * Generates a process function definition.
1498 *
1499 * @param tfunction The function to write a dispatcher for
1500 */
generate_process_function(std::ostream & out,t_service * tservice,t_function * tfunction)1501 void t_php_generator::generate_process_function(std::ostream& out, t_service* tservice, t_function* tfunction) {
1502 // Open function
1503 out << indent() << "protected function process_" << tfunction->get_name() << "($seqid, $input, $output)" << endl
1504 << indent() << "{" << endl;
1505 indent_up();
1506
1507 string argsname = php_namespace(tservice->get_program()) + service_name_ + "_"
1508 + tfunction->get_name() + "_args";
1509 string resultname = php_namespace(tservice->get_program()) + service_name_ + "_"
1510 + tfunction->get_name() + "_result";
1511
1512 out << indent() << "$bin_accel = ($input instanceof "
1513 << "TBinaryProtocolAccelerated) && function_exists('thrift_protocol_read_binary_after_message_begin');"
1514 << endl;
1515 out << indent() << "if ($bin_accel) {" << endl;
1516 indent_up();
1517
1518 out << indent() << "$args = thrift_protocol_read_binary_after_message_begin(" <<endl;
1519
1520 indent_up();
1521 out << indent() << "$input,"<<endl
1522 << indent() << "'" << argsname << "'," << endl
1523 << indent() << "$input->isStrictRead()" <<endl;
1524
1525 indent_down();
1526 out << indent() <<");" << endl;
1527
1528 indent_down();
1529 out << indent() << "} else {" << endl;
1530
1531 indent_up();
1532 out << indent() << "$args = new " << argsname << "();" << endl
1533 << indent() << "$args->read($input);" << endl;
1534
1535 indent_down();
1536 out << indent() << "}" << endl;
1537
1538 if (!binary_inline_) {
1539 out << indent() << "$input->readMessageEnd();" << endl;
1540 }
1541
1542 t_struct* xs = tfunction->get_xceptions();
1543 const std::vector<t_field*>& xceptions = xs->get_members();
1544 vector<t_field*>::const_iterator x_iter;
1545
1546 // Declare result for non oneway function
1547 if (!tfunction->is_oneway()) {
1548 out << indent() << "$result = new " << resultname << "();" << endl;
1549 }
1550
1551 // Try block for a function with exceptions
1552 if (xceptions.size() > 0) {
1553 out << indent() << "try {" << endl;
1554 indent_up();
1555 }
1556
1557 // Generate the function call
1558 t_struct* arg_struct = tfunction->get_arglist();
1559 const std::vector<t_field*>& fields = arg_struct->get_members();
1560 vector<t_field*>::const_iterator f_iter;
1561
1562 out << indent();
1563 if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
1564 out << "$result->success = ";
1565 }
1566 out << "$this->handler_->" << tfunction->get_name() << "(";
1567 bool first = true;
1568 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1569 if (first) {
1570 first = false;
1571 } else {
1572 out << ", ";
1573 }
1574 out << "$args->" << (*f_iter)->get_name();
1575 }
1576 out << ");" << endl;
1577
1578 if (!tfunction->is_oneway() && xceptions.size() > 0) {
1579 indent_down();
1580 for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
1581 out << indent() << "} catch ("
1582 << php_namespace(get_true_type((*x_iter)->get_type())->get_program())
1583 << (*x_iter)->get_type()->get_name() << " $" << (*x_iter)->get_name() << ") {"
1584 << endl;
1585 if (!tfunction->is_oneway()) {
1586 indent_up();
1587 out << indent() << "$result->" << (*x_iter)->get_name() << " = $"
1588 << (*x_iter)->get_name() << ";" << endl;
1589 indent_down();
1590 out << indent();
1591 }
1592 }
1593 out << "}" << endl;
1594 }
1595
1596 // Shortcut out here for oneway functions
1597 if (tfunction->is_oneway()) {
1598 out << indent() << "return;" << endl;
1599 indent_down();
1600 out << indent() << "}" << endl;
1601 return;
1602 }
1603
1604 out << indent() << "$bin_accel = ($output instanceof "
1605 << "TBinaryProtocolAccelerated) && function_exists('thrift_protocol_write_binary');"
1606 << endl;
1607
1608 out << indent() << "if ($bin_accel) {" << endl;
1609 indent_up();
1610
1611 out << indent() << "thrift_protocol_write_binary(" << endl;
1612
1613 indent_up();
1614 out << indent() << "$output,"<<endl
1615 << indent() << "'" << tfunction->get_name()<< "'," <<endl
1616 << indent() << "TMessageType::REPLY,"<< endl
1617 << indent() << "$result," << endl
1618 << indent() << "$seqid," << endl
1619 << indent() << "$output->isStrictWrite()"<<endl;
1620
1621 indent_down();
1622 out << indent() << ");" << endl;
1623
1624 indent_down();
1625 out << indent() << "} else {" << endl;
1626 indent_up();
1627
1628 // Serialize the request header
1629 if (binary_inline_) {
1630 out << indent() << "$buff = pack('N', (0x80010000 | "
1631 << "TMessageType::REPLY)); " << endl << indent() << "$buff .= pack('N', strlen('"
1632 << tfunction->get_name() << "'));" << endl << indent() << "$buff .= '"
1633 << tfunction->get_name() << "';" << endl << indent() << "$buff .= pack('N', $seqid);"
1634 << endl << indent() << "$result->write($buff);" << endl << indent()
1635 << "$output->write($buff);" << endl << indent() << "$output->flush();" << endl;
1636 } else {
1637 out << indent() << "$output->writeMessageBegin('" << tfunction->get_name() << "', "
1638 << "TMessageType::REPLY, $seqid);" << endl << indent() << "$result->write($output);"
1639 << endl << indent() << "$output->writeMessageEnd();" << endl << indent()
1640 << "$output->getTransport()->flush();" << endl;
1641 }
1642
1643 scope_down(out);
1644
1645 // Close function
1646 indent_down();
1647 out << indent() << "}" << endl;
1648 }
1649
1650 /**
1651 * Generates helper functions for a service.
1652 *
1653 * @param tservice The service to generate a header definition for
1654 */
generate_service_helpers(t_service * tservice)1655 void t_php_generator::generate_service_helpers(t_service* tservice) {
1656 vector<t_function*> functions = tservice->get_functions();
1657 vector<t_function*>::iterator f_iter;
1658
1659 ofstream_with_content_based_conditional_update& f_struct_definition = f_service_;
1660 if (classmap_) {
1661 f_struct_definition << "// HELPER FUNCTIONS AND STRUCTURES" << endl << endl;
1662 }
1663
1664 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1665 t_struct* ts = (*f_iter)->get_arglist();
1666 string name = ts->get_name();
1667 ts->set_name(service_name_ + "_" + name);
1668
1669 if (!classmap_) {
1670 string f_struct_definition_name = package_dir_ + service_name_ + "_" + name + ".php";
1671 f_struct_definition.open(f_struct_definition_name.c_str());
1672 generate_service_header(tservice, f_struct_definition);
1673 }
1674
1675 generate_php_struct_definition(f_struct_definition, ts);
1676 if (!classmap_) {
1677 f_struct_definition.close();
1678 }
1679
1680 generate_php_function_helpers(tservice, *f_iter);
1681 ts->set_name(name);
1682 }
1683 }
1684
1685 /**
1686 * Generates a struct and helpers for a function.
1687 *
1688 * @param tfunction The function
1689 */
generate_php_function_helpers(t_service * tservice,t_function * tfunction)1690 void t_php_generator::generate_php_function_helpers(t_service* tservice, t_function* tfunction) {
1691 if (!tfunction->is_oneway()) {
1692 t_struct result(program_, service_name_ + "_" + tfunction->get_name() + "_result");
1693 t_field success(tfunction->get_returntype(), "success", 0);
1694 if (!tfunction->get_returntype()->is_void()) {
1695 result.append(&success);
1696 }
1697
1698 t_struct* xs = tfunction->get_xceptions();
1699 const vector<t_field*>& fields = xs->get_members();
1700 vector<t_field*>::const_iterator f_iter;
1701 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1702 result.append(*f_iter);
1703 }
1704
1705 ofstream_with_content_based_conditional_update& f_struct_helper = f_service_;
1706 if (!classmap_) {
1707 string f_struct_helper_name = package_dir_ + result.get_name() + ".php";
1708 f_struct_helper.open(f_struct_helper_name.c_str());
1709 generate_service_header(tservice, f_struct_helper);
1710 }
1711 generate_php_struct_definition(f_struct_helper, &result, false, true);
1712 if (!classmap_) {
1713 f_struct_helper.close();
1714 }
1715 }
1716 }
1717
1718 /**
1719 * Generates a service interface definition.
1720 *
1721 * @param tservice The service to generate a header definition for
1722 */
generate_service_interface(t_service * tservice)1723 void t_php_generator::generate_service_interface(t_service* tservice) {
1724 ofstream_with_content_based_conditional_update& f_service_interface = f_service_;
1725 if (!classmap_) {
1726 string f_service_interface_name = package_dir_ + service_name_ + "If.php";
1727 f_service_interface.open(f_service_interface_name.c_str());
1728 generate_service_header(tservice, f_service_interface);
1729 }
1730
1731 string extends = "";
1732 string extends_if = "";
1733 if (tservice->get_extends() != nullptr) {
1734 extends = " extends " + php_namespace(tservice->get_extends()->get_program())
1735 + tservice->get_extends()->get_name();
1736 extends_if = " extends " + php_namespace(tservice->get_extends()->get_program())
1737 + tservice->get_extends()->get_name() + "If";
1738 }
1739 generate_php_doc(f_service_interface, tservice);
1740 f_service_interface << "interface " << php_namespace_declaration(tservice) << "If" << extends_if << endl
1741 << "{" << endl;
1742
1743 indent_up();
1744 vector<t_function*> functions = tservice->get_functions();
1745 vector<t_function*>::iterator f_iter;
1746 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1747 generate_php_doc(f_service_interface, *f_iter);
1748 indent(f_service_interface) << "public function " << function_signature(*f_iter) << ";" << endl;
1749 }
1750 indent_down();
1751 f_service_interface << "}" << endl;
1752
1753 // Close service interface file
1754 if (!classmap_) {
1755 f_service_interface.close();
1756 }
1757 }
1758
1759 /**
1760 * Generates a REST interface
1761 */
generate_service_rest(t_service * tservice)1762 void t_php_generator::generate_service_rest(t_service* tservice) {
1763 ofstream_with_content_based_conditional_update& f_service_rest = f_service_;
1764 if (!classmap_) {
1765 string f_service_rest_name = package_dir_ + service_name_ + "Rest.php";
1766 f_service_rest.open(f_service_rest_name.c_str());
1767 generate_service_header(tservice, f_service_rest);
1768 }
1769
1770 string extends = "";
1771 string extends_if = "";
1772 if (tservice->get_extends() != nullptr) {
1773 extends = " extends " + php_namespace(tservice->get_extends()->get_program())
1774 + tservice->get_extends()->get_name();
1775 extends_if = " extends " + php_namespace(tservice->get_extends()->get_program())
1776 + tservice->get_extends()->get_name() + "Rest";
1777 }
1778 f_service_rest << "class " << service_name_ << "Rest" << extends_if << endl
1779 << "{" << endl;
1780 indent_up();
1781
1782 if (extends.empty()) {
1783 f_service_rest << indent() << "protected $impl_;" << endl << endl;
1784 }
1785
1786 f_service_rest << indent() << "public function __construct($impl) {" << endl << indent()
1787 << " $this->impl_ = $impl;" << endl << indent() << "}" << endl << endl;
1788
1789 vector<t_function*> functions = tservice->get_functions();
1790 vector<t_function*>::iterator f_iter;
1791 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1792 indent(f_service_rest) << "public function " << (*f_iter)->get_name() << "($request) {" << endl;
1793 indent_up();
1794 const vector<t_field*>& args = (*f_iter)->get_arglist()->get_members();
1795 vector<t_field*>::const_iterator a_iter;
1796 for (a_iter = args.begin(); a_iter != args.end(); ++a_iter) {
1797 t_type* atype = get_true_type((*a_iter)->get_type());
1798 string cast = type_to_cast(atype);
1799 string req = "$request['" + (*a_iter)->get_name() + "']";
1800 if (atype->is_bool()) {
1801 f_service_rest << indent() << "$" << (*a_iter)->get_name() << " = " << cast << "(!empty(" << req
1802 << ") && (" << req << " !== 'false'));" << endl;
1803 } else {
1804 f_service_rest << indent() << "$" << (*a_iter)->get_name() << " = isset(" << req << ") ? "
1805 << cast << req << " : null;" << endl;
1806 }
1807 /* slist no longer supported
1808 if (atype->is_string() && ((t_base_type*)atype)->is_string_list()) {
1809 f_service_rest << indent() << "$" << (*a_iter)->get_name() << " = explode(',', $"
1810 << (*a_iter)->get_name() << ");" << endl;
1811 } else */
1812 if (atype->is_map() || atype->is_list()) {
1813 f_service_rest << indent() << "$" << (*a_iter)->get_name() << " = json_decode($"
1814 << (*a_iter)->get_name() << ", true);" << endl;
1815 } else if (atype->is_set()) {
1816 f_service_rest << indent() << "$" << (*a_iter)->get_name() << " = array_fill_keys(json_decode($"
1817 << (*a_iter)->get_name() << ", true), 1);" << endl;
1818 } else if (atype->is_struct() || atype->is_xception()) {
1819 f_service_rest << indent() << "if ($" << (*a_iter)->get_name() << " !== null) {" << endl
1820 << indent() << " $" << (*a_iter)->get_name() << " = new "
1821 << php_namespace(atype->get_program()) << atype->get_name() << "(json_decode($"
1822 << (*a_iter)->get_name() << ", true));" << endl << indent() << "}" << endl;
1823 }
1824 }
1825 f_service_rest << indent() << "return $this->impl_->" << (*f_iter)->get_name() << "("
1826 << argument_list((*f_iter)->get_arglist(), false) << ");" << endl;
1827 indent_down();
1828 indent(f_service_rest) << "}" << endl << endl;
1829 }
1830 indent_down();
1831 f_service_rest << "}" << endl << endl;
1832
1833 // Close service rest file
1834 f_service_rest << endl;
1835 if (!classmap_) {
1836 f_service_rest.close();
1837 }
1838 }
1839
1840 /**
1841 * Generates a service client definition.
1842 *
1843 * @param tservice The service to generate a server for.
1844 */
generate_service_client(t_service * tservice)1845 void t_php_generator::generate_service_client(t_service* tservice) {
1846 ofstream_with_content_based_conditional_update& f_service_client = f_service_;
1847 if (!classmap_) {
1848 string f_service_client_name = package_dir_ + service_name_ + "Client.php";
1849 f_service_client.open(f_service_client_name.c_str());
1850 generate_service_header(tservice, f_service_client);
1851 }
1852
1853 string extends = "";
1854 string extends_client = "";
1855 if (tservice->get_extends() != nullptr) {
1856 extends = tservice->get_extends()->get_name();
1857 extends_client = " extends " + php_namespace(tservice->get_extends()->get_program()) + extends
1858 + "Client";
1859 }
1860
1861 f_service_client << "class " << php_namespace_declaration(tservice) << "Client" << extends_client
1862 << " implements " << php_namespace(tservice->get_program()) << service_name_ << "If" << endl
1863 <<"{"<< endl;
1864 indent_up();
1865
1866 // Private members
1867 if (extends.empty()) {
1868 f_service_client << indent() << "protected $input_ = null;" << endl << indent()
1869 << "protected $output_ = null;" << endl << endl;
1870 f_service_client << indent() << "protected $seqid_ = 0;" << endl << endl;
1871 }
1872
1873 // Constructor function
1874 f_service_client << indent() << "public function __construct($input, $output = null)" << endl
1875 << indent() << "{" << endl;
1876
1877 indent_up();
1878 if (!extends.empty()) {
1879 f_service_client << indent() << "parent::__construct($input, $output);" << endl;
1880 } else {
1881 f_service_client << indent() << "$this->input_ = $input;" << endl
1882 << indent() << "$this->output_ = $output ? $output : $input;" << endl;
1883 }
1884
1885 indent_down();
1886 f_service_client << indent() << "}" << endl << endl;
1887
1888 // Generate client method implementations
1889 vector<t_function*> functions = tservice->get_functions();
1890 vector<t_function*>::const_iterator f_iter;
1891 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1892 t_struct* arg_struct = (*f_iter)->get_arglist();
1893 const vector<t_field*>& fields = arg_struct->get_members();
1894 vector<t_field*>::const_iterator fld_iter;
1895 string funname = (*f_iter)->get_name();
1896
1897 f_service_client << endl;
1898
1899 // Open function
1900 indent(f_service_client) << "public function " << function_signature(*f_iter) << endl;
1901 scope_up(f_service_client);
1902 indent(f_service_client) << "$this->send_" << funname << "(";
1903
1904 bool first = true;
1905 for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
1906 if (first) {
1907 first = false;
1908 } else {
1909 f_service_client << ", ";
1910 }
1911 f_service_client << "$" << (*fld_iter)->get_name();
1912 }
1913 f_service_client << ");" << endl;
1914
1915 if (!(*f_iter)->is_oneway()) {
1916 f_service_client << indent();
1917 if (!(*f_iter)->get_returntype()->is_void()) {
1918 f_service_client << "return ";
1919 }
1920 f_service_client << "$this->recv_" << funname << "();" << endl;
1921 }
1922 scope_down(f_service_client);
1923 f_service_client << endl;
1924
1925 indent(f_service_client) << "public function send_" << function_signature(*f_iter) << endl;
1926 scope_up(f_service_client);
1927
1928 std::string argsname = php_namespace(tservice->get_program()) + service_name_ + "_"
1929 + (*f_iter)->get_name() + "_args";
1930
1931 f_service_client << indent() << "$args = new " << argsname << "();" << endl;
1932
1933 for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
1934 f_service_client << indent() << "$args->" << (*fld_iter)->get_name() << " = $"
1935 << (*fld_iter)->get_name() << ";" << endl;
1936 }
1937
1938 f_service_client << indent() << "$bin_accel = ($this->output_ instanceof "
1939 << "TBinaryProtocolAccelerated) && function_exists('thrift_protocol_write_binary');"
1940 << endl;
1941
1942 f_service_client << indent() << "if ($bin_accel) {" << endl;
1943 indent_up();
1944
1945 string messageType = (*f_iter)->is_oneway() ? "TMessageType::ONEWAY" : "TMessageType::CALL";
1946
1947 f_service_client << indent() << "thrift_protocol_write_binary(" << endl;
1948
1949 indent_up();
1950 f_service_client << indent() << "$this->output_," << endl
1951 << indent() << "'" << (*f_iter)->get_name() << "'," << endl
1952 << indent() << messageType << "," << endl
1953 << indent() << "$args," << endl
1954 << indent() << "$this->seqid_," << endl
1955 << indent() << "$this->output_->isStrictWrite()" << endl;
1956
1957 indent_down();
1958 f_service_client << indent() << ");" << endl;
1959
1960 indent_down();
1961 f_service_client << indent() << "} else {" << endl;
1962 indent_up();
1963
1964 // Serialize the request header
1965 if (binary_inline_) {
1966 f_service_client << indent() << "$buff = pack('N', (0x80010000 | " << messageType << "));" << endl
1967 << indent() << "$buff .= pack('N', strlen('" << funname << "'));" << endl
1968 << indent() << "$buff .= '" << funname << "';" << endl << indent()
1969 << "$buff .= pack('N', $this->seqid_);" << endl;
1970 } else {
1971 f_service_client << indent() << "$this->output_->writeMessageBegin('" << (*f_iter)->get_name()
1972 << "', " << messageType << ", $this->seqid_);" << endl;
1973 }
1974
1975 // Write to the stream
1976 if (binary_inline_) {
1977 f_service_client << indent() << "$args->write($buff);" << endl << indent()
1978 << "$this->output_->write($buff);" << endl << indent()
1979 << "$this->output_->flush();" << endl;
1980 } else {
1981 f_service_client << indent() << "$args->write($this->output_);" << endl << indent()
1982 << "$this->output_->writeMessageEnd();" << endl << indent()
1983 << "$this->output_->getTransport()->flush();" << endl;
1984 }
1985
1986 scope_down(f_service_client);
1987
1988 scope_down(f_service_client);
1989
1990 if (!(*f_iter)->is_oneway()) {
1991 std::string resultname = php_namespace(tservice->get_program()) + service_name_ + "_"
1992 + (*f_iter)->get_name() + "_result";
1993 t_struct noargs(program_);
1994
1995 t_function recv_function((*f_iter)->get_returntype(),
1996 string("recv_") + (*f_iter)->get_name(),
1997 &noargs);
1998 // Open function
1999 f_service_client << endl << indent() << "public function " << function_signature(&recv_function)
2000 << endl;
2001 scope_up(f_service_client);
2002
2003 f_service_client << indent() << "$bin_accel = ($this->input_ instanceof "
2004 << "TBinaryProtocolAccelerated)"
2005 << " && function_exists('thrift_protocol_read_binary');" << endl;
2006
2007 f_service_client << indent() << "if ($bin_accel) {" << endl;
2008
2009 indent_up();
2010 f_service_client << indent() << "$result = thrift_protocol_read_binary(" << endl;
2011
2012 indent_up();
2013 f_service_client << indent() << "$this->input_," << endl
2014 << indent() << "'" << resultname << "'," << endl
2015 << indent() << "$this->input_->isStrictRead()" << endl;
2016
2017 indent_down();
2018 f_service_client << indent() << ");" << endl;
2019
2020 indent_down();
2021 f_service_client << indent() << "} else {" << endl;
2022
2023 indent_up();
2024 f_service_client << indent() << "$rseqid = 0;" << endl
2025 << indent() << "$fname = null;" << endl
2026 << indent() << "$mtype = 0;" << endl << endl;
2027
2028 if (binary_inline_) {
2029 t_field ffname(g_type_string, "fname");
2030 t_field fseqid(g_type_i32, "rseqid");
2031 f_service_client << indent() << "$ver = unpack('N', $this->input_->readAll(4));" << endl
2032 << indent() << "$ver = $ver[1];" << endl << indent() << "$mtype = $ver & 0xff;"
2033 << endl << indent() << "$ver = $ver & 0xffff0000;" << endl << indent()
2034 << "if ($ver != 0x80010000) throw new "
2035 << "TProtocolException('Bad version identifier: '.$ver, "
2036 << "TProtocolException::BAD_VERSION);" << endl;
2037 generate_deserialize_field(f_service_client, &ffname, "", true);
2038 generate_deserialize_field(f_service_client, &fseqid, "", true);
2039 } else {
2040 f_service_client << indent() << "$this->input_->readMessageBegin($fname, $mtype, $rseqid);" << endl
2041 << indent() << "if ($mtype == TMessageType::EXCEPTION) {" << endl;
2042
2043 indent_up();
2044 f_service_client << indent() << "$x = new TApplicationException();" << endl
2045 << indent() << "$x->read($this->input_);" << endl
2046 << indent() << "$this->input_->readMessageEnd();" << endl
2047 << indent() << "throw $x;" << endl;
2048 indent_down();
2049 f_service_client << indent() << "}" << endl;
2050 }
2051
2052 f_service_client << indent() << "$result = new " << resultname << "();" << endl
2053 << indent() << "$result->read($this->input_);" << endl;
2054
2055 if (!binary_inline_) {
2056 f_service_client << indent() << "$this->input_->readMessageEnd();" << endl;
2057 }
2058
2059 scope_down(f_service_client);
2060
2061 // Careful, only return result if not a void function
2062 if (!(*f_iter)->get_returntype()->is_void()) {
2063 f_service_client << indent() << "if ($result->success !== null) {" << endl;
2064
2065 indent_up();
2066 f_service_client << indent() << "return $result->success;" << endl;
2067
2068 indent_down();
2069 f_service_client << indent() << "}" << endl;
2070 }
2071
2072 t_struct* xs = (*f_iter)->get_xceptions();
2073 const std::vector<t_field*>& xceptions = xs->get_members();
2074 vector<t_field*>::const_iterator x_iter;
2075 for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
2076 f_service_client << indent() << "if ($result->" << (*x_iter)->get_name() << " !== null) {" << endl;
2077
2078 indent_up();
2079 f_service_client << indent() << "throw $result->" << (*x_iter)->get_name() << ";" << endl;
2080
2081 indent_down();
2082 f_service_client << indent() << "}" << endl;
2083 }
2084
2085 // Careful, only return _result if not a void function
2086 if ((*f_iter)->get_returntype()->is_void()) {
2087 indent(f_service_client) << "return;" << endl;
2088 } else {
2089 f_service_client << indent() << "throw new \\Exception(\"" << (*f_iter)->get_name()
2090 << " failed: unknown result\");" << endl;
2091 }
2092
2093 // Close function
2094 scope_down(f_service_client);
2095 }
2096 }
2097
2098 indent_down();
2099 f_service_client << "}" << endl;
2100
2101 // Close service client file
2102 if (!classmap_) {
2103 f_service_client.close();
2104 }
2105 }
2106
2107 /**
2108 * Deserializes a field of any type.
2109 */
generate_deserialize_field(ostream & out,t_field * tfield,string prefix,bool inclass)2110 void t_php_generator::generate_deserialize_field(ostream& out,
2111 t_field* tfield,
2112 string prefix,
2113 bool inclass) {
2114 t_type* type = get_true_type(tfield->get_type());
2115
2116 if (type->is_void()) {
2117 throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
2118 }
2119
2120 string name = prefix + tfield->get_name();
2121
2122 if (type->is_struct() || type->is_xception()) {
2123 generate_deserialize_struct(out, (t_struct*)type, name);
2124 } else {
2125
2126 if (type->is_container()) {
2127 generate_deserialize_container(out, type, name);
2128 } else if (type->is_base_type() || type->is_enum()) {
2129
2130 if (binary_inline_) {
2131 std::string itrans = (inclass ? "$this->input_" : "$input");
2132
2133 if (type->is_base_type()) {
2134 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2135 switch (tbase) {
2136 case t_base_type::TYPE_VOID:
2137 throw "compiler error: cannot serialize void field in a struct: " + name;
2138 break;
2139 case t_base_type::TYPE_STRING:
2140 out << indent() << "$len = unpack('N', " << itrans << "->readAll(4));" << endl
2141 << indent() << "$len = $len[1];" << endl << indent() << "if ($len > 0x7fffffff) {"
2142 << endl << indent() << " $len = 0 - (($len - 1) ^ 0xffffffff);" << endl << indent()
2143 << "}" << endl << indent() << "$" << name << " = " << itrans << "->readAll($len);"
2144 << endl;
2145 break;
2146 case t_base_type::TYPE_BOOL:
2147 out << indent() << "$" << name << " = unpack('c', " << itrans << "->readAll(1));"
2148 << endl << indent() << "$" << name << " = (bool)$" << name << "[1];" << endl;
2149 break;
2150 case t_base_type::TYPE_I8:
2151 out << indent() << "$" << name << " = unpack('c', " << itrans << "->readAll(1));"
2152 << endl << indent() << "$" << name << " = $" << name << "[1];" << endl;
2153 break;
2154 case t_base_type::TYPE_I16:
2155 out << indent() << "$val = unpack('n', " << itrans << "->readAll(2));" << endl
2156 << indent() << "$val = $val[1];" << endl << indent() << "if ($val > 0x7fff) {"
2157 << endl << indent() << " $val = 0 - (($val - 1) ^ 0xffff);" << endl << indent()
2158 << "}" << endl << indent() << "$" << name << " = $val;" << endl;
2159 break;
2160 case t_base_type::TYPE_I32:
2161 out << indent() << "$val = unpack('N', " << itrans << "->readAll(4));" << endl
2162 << indent() << "$val = $val[1];" << endl << indent() << "if ($val > 0x7fffffff) {"
2163 << endl << indent() << " $val = 0 - (($val - 1) ^ 0xffffffff);" << endl << indent()
2164 << "}" << endl << indent() << "$" << name << " = $val;" << endl;
2165 break;
2166 case t_base_type::TYPE_I64:
2167 out << indent() << "$arr = unpack('N2', " << itrans << "->readAll(8));" << endl
2168 << indent() << "if ($arr[1] & 0x80000000) {" << endl << indent()
2169 << " $arr[1] = $arr[1] ^ 0xFFFFFFFF;" << endl << indent()
2170 << " $arr[2] = $arr[2] ^ 0xFFFFFFFF;" << endl << indent() << " $" << name
2171 << " = 0 - $arr[1]*4294967296 - $arr[2] - 1;" << endl << indent() << "} else {"
2172 << endl << indent() << " $" << name << " = $arr[1]*4294967296 + $arr[2];" << endl
2173 << indent() << "}" << endl;
2174 break;
2175 case t_base_type::TYPE_DOUBLE:
2176 out << indent() << "$arr = unpack('d', strrev(" << itrans << "->readAll(8)));" << endl
2177 << indent() << "$" << name << " = $arr[1];" << endl;
2178 break;
2179 default:
2180 throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase)
2181 + tfield->get_name();
2182 }
2183 } else if (type->is_enum()) {
2184 out << indent() << "$val = unpack('N', " << itrans << "->readAll(4));" << endl << indent()
2185 << "$val = $val[1];" << endl << indent() << "if ($val > 0x7fffffff) {" << endl
2186 << indent() << " $val = 0 - (($val - 1) ^ 0xffffffff);" << endl << indent() << "}"
2187 << endl << indent() << "$" << name << " = $val;" << endl;
2188 }
2189 } else {
2190
2191 indent(out) << "$xfer += $input->";
2192
2193 if (type->is_base_type()) {
2194 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2195 switch (tbase) {
2196 case t_base_type::TYPE_VOID:
2197 throw "compiler error: cannot serialize void field in a struct: " + name;
2198 break;
2199 case t_base_type::TYPE_STRING:
2200 out << "readString($" << name << ");";
2201 break;
2202 case t_base_type::TYPE_BOOL:
2203 out << "readBool($" << name << ");";
2204 break;
2205 case t_base_type::TYPE_I8:
2206 out << "readByte($" << name << ");";
2207 break;
2208 case t_base_type::TYPE_I16:
2209 out << "readI16($" << name << ");";
2210 break;
2211 case t_base_type::TYPE_I32:
2212 out << "readI32($" << name << ");";
2213 break;
2214 case t_base_type::TYPE_I64:
2215 out << "readI64($" << name << ");";
2216 break;
2217 case t_base_type::TYPE_DOUBLE:
2218 out << "readDouble($" << name << ");";
2219 break;
2220 default:
2221 throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase);
2222 }
2223 } else if (type->is_enum()) {
2224 out << "readI32($" << name << ");";
2225 }
2226 out << endl;
2227 }
2228 } else {
2229 printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
2230 tfield->get_name().c_str(),
2231 type->get_name().c_str());
2232 }
2233 }
2234 }
2235
2236 /**
2237 * Generates an unserializer for a variable. This makes two key assumptions,
2238 * first that there is a const char* variable named data that points to the
2239 * buffer for deserialization, and that there is a variable protocol which
2240 * is a reference to a TProtocol serialization object.
2241 */
generate_deserialize_struct(ostream & out,t_struct * tstruct,string prefix)2242 void t_php_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix) {
2243 out << indent() << "$" << prefix << " = new " << php_namespace(tstruct->get_program())
2244 << tstruct->get_name() << "();" << endl << indent() << "$xfer += $" << prefix
2245 << "->read($input);" << endl;
2246 }
2247
generate_deserialize_container(ostream & out,t_type * ttype,string prefix)2248 void t_php_generator::generate_deserialize_container(ostream& out, t_type* ttype, string prefix) {
2249 string size = tmp("_size");
2250 string ktype = tmp("_ktype");
2251 string vtype = tmp("_vtype");
2252 string etype = tmp("_etype");
2253
2254 t_field fsize(g_type_i32, size);
2255 t_field fktype(g_type_i8, ktype);
2256 t_field fvtype(g_type_i8, vtype);
2257 t_field fetype(g_type_i8, etype);
2258
2259 out << indent() << "$" << prefix << " = array();" << endl << indent() << "$" << size << " = 0;"
2260 << endl;
2261
2262 // Declare variables, read header
2263 if (ttype->is_map()) {
2264 out << indent() << "$" << ktype << " = 0;" << endl << indent() << "$" << vtype << " = 0;"
2265 << endl;
2266 if (binary_inline_) {
2267 generate_deserialize_field(out, &fktype);
2268 generate_deserialize_field(out, &fvtype);
2269 generate_deserialize_field(out, &fsize);
2270 } else {
2271 out << indent() << "$xfer += $input->readMapBegin("
2272 << "$" << ktype << ", $" << vtype << ", $" << size << ");" << endl;
2273 }
2274 } else if (ttype->is_set()) {
2275 if (binary_inline_) {
2276 generate_deserialize_field(out, &fetype);
2277 generate_deserialize_field(out, &fsize);
2278 } else {
2279 out << indent() << "$" << etype << " = 0;" << endl << indent()
2280 << "$xfer += $input->readSetBegin("
2281 << "$" << etype << ", $" << size << ");" << endl;
2282 }
2283 } else if (ttype->is_list()) {
2284 if (binary_inline_) {
2285 generate_deserialize_field(out, &fetype);
2286 generate_deserialize_field(out, &fsize);
2287 } else {
2288 out << indent() << "$" << etype << " = 0;" << endl << indent()
2289 << "$xfer += $input->readListBegin("
2290 << "$" << etype << ", $" << size << ");" << endl;
2291 }
2292 }
2293
2294 // For loop iterates over elements
2295 string i = tmp("_i");
2296 indent(out) << "for ($" << i << " = 0; $" << i << " < $" << size << "; ++$" << i << ") {" << endl;
2297
2298 indent_up();
2299
2300 if (ttype->is_map()) {
2301 generate_deserialize_map_element(out, (t_map*)ttype, prefix);
2302 } else if (ttype->is_set()) {
2303 generate_deserialize_set_element(out, (t_set*)ttype, prefix);
2304 } else if (ttype->is_list()) {
2305 generate_deserialize_list_element(out, (t_list*)ttype, prefix);
2306 }
2307
2308 scope_down(out);
2309
2310 if (!binary_inline_) {
2311 // Read container end
2312 if (ttype->is_map()) {
2313 indent(out) << "$xfer += $input->readMapEnd();" << endl;
2314 } else if (ttype->is_set()) {
2315 indent(out) << "$xfer += $input->readSetEnd();" << endl;
2316 } else if (ttype->is_list()) {
2317 indent(out) << "$xfer += $input->readListEnd();" << endl;
2318 }
2319 }
2320 }
2321
2322 /**
2323 * Generates code to deserialize a map
2324 */
generate_deserialize_map_element(ostream & out,t_map * tmap,string prefix)2325 void t_php_generator::generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix) {
2326 string key = tmp("key");
2327 string val = tmp("val");
2328 t_field fkey(tmap->get_key_type(), key);
2329 t_field fval(tmap->get_val_type(), val);
2330
2331 indent(out) << declare_field(&fkey, true, true) << endl;
2332 indent(out) << declare_field(&fval, true, true) << endl;
2333
2334 generate_deserialize_field(out, &fkey);
2335 generate_deserialize_field(out, &fval);
2336
2337 indent(out) << "$" << prefix << "[$" << key << "] = $" << val << ";" << endl;
2338 }
2339
generate_deserialize_set_element(ostream & out,t_set * tset,string prefix)2340 void t_php_generator::generate_deserialize_set_element(ostream& out, t_set* tset, string prefix) {
2341 string elem = tmp("elem");
2342 t_field felem(tset->get_elem_type(), elem);
2343
2344 indent(out) << "$" << elem << " = null;" << endl;
2345
2346 generate_deserialize_field(out, &felem);
2347
2348 t_type* elem_type = tset->get_elem_type();
2349 if(php_is_scalar(elem_type)) {
2350 indent(out) << "$" << prefix << "[$" << elem << "] = true;" << endl;
2351 } else {
2352 indent(out) << "$" << prefix << "[] = $" << elem << ";" << endl;
2353 }
2354 }
2355
generate_deserialize_list_element(ostream & out,t_list * tlist,string prefix)2356 void t_php_generator::generate_deserialize_list_element(ostream& out,
2357 t_list* tlist,
2358 string prefix) {
2359 string elem = tmp("elem");
2360 t_field felem(tlist->get_elem_type(), elem);
2361
2362 indent(out) << "$" << elem << " = null;" << endl;
2363
2364 generate_deserialize_field(out, &felem);
2365
2366 indent(out) << "$" << prefix << " []= $" << elem << ";" << endl;
2367 }
2368
2369 /**
2370 * Serializes a field of any type.
2371 *
2372 * @param tfield The field to serialize
2373 * @param prefix Name to prepend to field name
2374 */
generate_serialize_field(ostream & out,t_field * tfield,string prefix)2375 void t_php_generator::generate_serialize_field(ostream& out, t_field* tfield, string prefix) {
2376 t_type* type = get_true_type(tfield->get_type());
2377
2378 // Do nothing for void types
2379 if (type->is_void()) {
2380 throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
2381 }
2382
2383 if (type->is_struct() || type->is_xception()) {
2384 generate_serialize_struct(out, (t_struct*)type, prefix + tfield->get_name());
2385 } else if (type->is_container()) {
2386 generate_serialize_container(out, type, prefix + tfield->get_name());
2387 } else if (type->is_base_type() || type->is_enum()) {
2388
2389 string name = prefix + tfield->get_name();
2390
2391 if (binary_inline_) {
2392 if (type->is_base_type()) {
2393 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2394 switch (tbase) {
2395 case t_base_type::TYPE_VOID:
2396 throw "compiler error: cannot serialize void field in a struct: " + name;
2397 break;
2398 case t_base_type::TYPE_STRING:
2399 out << indent() << "$output .= pack('N', strlen($" << name << "));" << endl << indent()
2400 << "$output .= $" << name << ";" << endl;
2401 break;
2402 case t_base_type::TYPE_BOOL:
2403 out << indent() << "$output .= pack('c', $" << name << " ? 1 : 0);" << endl;
2404 break;
2405 case t_base_type::TYPE_I8:
2406 out << indent() << "$output .= pack('c', $" << name << ");" << endl;
2407 break;
2408 case t_base_type::TYPE_I16:
2409 out << indent() << "$output .= pack('n', $" << name << ");" << endl;
2410 break;
2411 case t_base_type::TYPE_I32:
2412 out << indent() << "$output .= pack('N', $" << name << ");" << endl;
2413 break;
2414 case t_base_type::TYPE_I64:
2415 out << indent() << "$output .= pack('N2', $" << name << " >> 32, $" << name
2416 << " & 0xFFFFFFFF);" << endl;
2417 break;
2418 case t_base_type::TYPE_DOUBLE:
2419 out << indent() << "$output .= strrev(pack('d', $" << name << "));" << endl;
2420 break;
2421 default:
2422 throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase);
2423 }
2424 } else if (type->is_enum()) {
2425 out << indent() << "$output .= pack('N', $" << name << ");" << endl;
2426 }
2427 } else {
2428
2429 indent(out) << "$xfer += $output->";
2430
2431 if (type->is_base_type()) {
2432 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2433 switch (tbase) {
2434 case t_base_type::TYPE_VOID:
2435 throw "compiler error: cannot serialize void field in a struct: " + name;
2436 break;
2437 case t_base_type::TYPE_STRING:
2438 out << "writeString($" << name << ");";
2439 break;
2440 case t_base_type::TYPE_BOOL:
2441 out << "writeBool($" << name << ");";
2442 break;
2443 case t_base_type::TYPE_I8:
2444 out << "writeByte($" << name << ");";
2445 break;
2446 case t_base_type::TYPE_I16:
2447 out << "writeI16($" << name << ");";
2448 break;
2449 case t_base_type::TYPE_I32:
2450 out << "writeI32($" << name << ");";
2451 break;
2452 case t_base_type::TYPE_I64:
2453 out << "writeI64($" << name << ");";
2454 break;
2455 case t_base_type::TYPE_DOUBLE:
2456 out << "writeDouble($" << name << ");";
2457 break;
2458 default:
2459 throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase);
2460 }
2461 } else if (type->is_enum()) {
2462 out << "writeI32($" << name << ");";
2463 }
2464 out << endl;
2465 }
2466 } else {
2467 printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n",
2468 prefix.c_str(),
2469 tfield->get_name().c_str(),
2470 type->get_name().c_str());
2471 }
2472 }
2473
2474 /**
2475 * Serializes all the members of a struct.
2476 *
2477 * @param tstruct The struct to serialize
2478 * @param prefix String prefix to attach to all fields
2479 */
generate_serialize_struct(ostream & out,t_struct * tstruct,string prefix)2480 void t_php_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) {
2481 (void)tstruct;
2482 indent(out) << "$xfer += $" << prefix << "->write($output);" << endl;
2483 }
2484
2485 /**
2486 * Writes out a container
2487 */
generate_serialize_container(ostream & out,t_type * ttype,string prefix)2488 void t_php_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) {
2489 if (ttype->is_map()) {
2490 if (binary_inline_) {
2491 out << indent() << "$output .= pack('c', " << type_to_enum(((t_map*)ttype)->get_key_type())
2492 << ");" << endl << indent() << "$output .= pack('c', "
2493 << type_to_enum(((t_map*)ttype)->get_val_type()) << ");" << endl << indent()
2494 << "$output .= strrev(pack('l', count($" << prefix << ")));" << endl;
2495 } else {
2496 indent(out) << "$output->writeMapBegin(" << type_to_enum(((t_map*)ttype)->get_key_type())
2497 << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", "
2498 << "count($" << prefix << "));" << endl;
2499 }
2500 } else if (ttype->is_set()) {
2501 if (binary_inline_) {
2502 out << indent() << "$output .= pack('c', " << type_to_enum(((t_set*)ttype)->get_elem_type())
2503 << ");" << endl << indent() << "$output .= strrev(pack('l', count($" << prefix << ")));"
2504 << endl;
2505
2506 } else {
2507 indent(out) << "$output->writeSetBegin(" << type_to_enum(((t_set*)ttype)->get_elem_type())
2508 << ", "
2509 << "count($" << prefix << "));" << endl;
2510 }
2511 } else if (ttype->is_list()) {
2512 if (binary_inline_) {
2513 out << indent() << "$output .= pack('c', " << type_to_enum(((t_list*)ttype)->get_elem_type())
2514 << ");" << endl << indent() << "$output .= strrev(pack('l', count($" << prefix << ")));"
2515 << endl;
2516
2517 } else {
2518 indent(out) << "$output->writeListBegin(" << type_to_enum(((t_list*)ttype)->get_elem_type())
2519 << ", "
2520 << "count($" << prefix << "));" << endl;
2521 }
2522 }
2523
2524 if (ttype->is_map()) {
2525 string kiter = tmp("kiter");
2526 string viter = tmp("viter");
2527 indent(out) << "foreach ($" << prefix << " as "
2528 << "$" << kiter << " => $" << viter << ") {" << endl;
2529 indent_up();
2530 generate_serialize_map_element(out, (t_map*)ttype, kiter, viter);
2531 scope_down(out);
2532 } else if (ttype->is_set()) {
2533 string iter = tmp("iter");
2534 string iter_val = tmp("iter");
2535 indent(out) << "foreach ($" << prefix << " as $" << iter << " => $" << iter_val << ") {" << endl;
2536 indent_up();
2537
2538 t_type* elem_type = ((t_set*)ttype)->get_elem_type();
2539 if(php_is_scalar(elem_type)) {
2540 generate_serialize_set_element(out, (t_set*)ttype, iter);
2541 } else {
2542 generate_serialize_set_element(out, (t_set*)ttype, iter_val);
2543 }
2544 scope_down(out);
2545 } else if (ttype->is_list()) {
2546 string iter = tmp("iter");
2547 indent(out) << "foreach ($" << prefix << " as $" << iter << ") {" << endl;
2548 indent_up();
2549 generate_serialize_list_element(out, (t_list*)ttype, iter);
2550 scope_down(out);
2551 }
2552
2553 if (!binary_inline_) {
2554 if (ttype->is_map()) {
2555 indent(out) << "$output->writeMapEnd();" << endl;
2556 } else if (ttype->is_set()) {
2557 indent(out) << "$output->writeSetEnd();" << endl;
2558 } else if (ttype->is_list()) {
2559 indent(out) << "$output->writeListEnd();" << endl;
2560 }
2561 }
2562 }
2563
2564 /**
2565 * Serializes the members of a map.
2566 *
2567 */
generate_serialize_map_element(ostream & out,t_map * tmap,string kiter,string viter)2568 void t_php_generator::generate_serialize_map_element(ostream& out,
2569 t_map* tmap,
2570 string kiter,
2571 string viter) {
2572 t_field kfield(tmap->get_key_type(), kiter);
2573 generate_serialize_field(out, &kfield, "");
2574
2575 t_field vfield(tmap->get_val_type(), viter);
2576 generate_serialize_field(out, &vfield, "");
2577 }
2578
2579 /**
2580 * Serializes the members of a set.
2581 */
generate_serialize_set_element(ostream & out,t_set * tset,string iter)2582 void t_php_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) {
2583 t_field efield(tset->get_elem_type(), iter);
2584 generate_serialize_field(out, &efield, "");
2585 }
2586
2587 /**
2588 * Serializes the members of a list.
2589 */
generate_serialize_list_element(ostream & out,t_list * tlist,string iter)2590 void t_php_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter) {
2591 t_field efield(tlist->get_elem_type(), iter);
2592 generate_serialize_field(out, &efield, "");
2593 }
2594
2595 /**
2596 * Emits a PHPDoc comment for the given contents
2597 */
generate_php_docstring_comment(ostream & out,string contents)2598 void t_php_generator::generate_php_docstring_comment(ostream& out, string contents) {
2599 generate_docstring_comment(out, "/**\n", " * ", contents, " */\n");
2600 }
2601
2602 /**
2603 * Emits a PHPDoc comment if the provided object has a doc in Thrift
2604 */
generate_php_doc(ostream & out,t_doc * tdoc)2605 void t_php_generator::generate_php_doc(ostream& out, t_doc* tdoc) {
2606 if (tdoc->has_doc()) {
2607 generate_php_docstring_comment(out, tdoc->get_doc());
2608 }
2609 }
2610
2611 /**
2612 * Emits a PHPDoc comment for a field
2613 */
generate_php_doc(ostream & out,t_field * field)2614 void t_php_generator::generate_php_doc(ostream& out, t_field* field) {
2615 stringstream ss;
2616
2617 // prepend free-style doc if available
2618 if (field->has_doc()) {
2619 ss << field->get_doc() << endl;
2620 }
2621
2622 // append @var tag
2623 t_type* type = get_true_type(field->get_type());
2624 ss << "@var " << type_to_phpdoc(type) << endl;
2625
2626 generate_php_docstring_comment(out, ss.str());
2627 }
2628
2629 /**
2630 * Emits a PHPDoc comment for a function
2631 */
generate_php_doc(ostream & out,t_function * function)2632 void t_php_generator::generate_php_doc(ostream& out, t_function* function) {
2633 stringstream ss;
2634 if (function->has_doc()) {
2635 ss << function->get_doc() << endl;
2636 }
2637
2638 // generate parameter types doc
2639 const vector<t_field*>& args = function->get_arglist()->get_members();
2640 vector<t_field*>::const_iterator a_iter;
2641 for (a_iter = args.begin(); a_iter != args.end(); ++a_iter) {
2642 t_field* arg = *a_iter;
2643 ss << "@param " << type_to_phpdoc(arg->get_type()) << " $" << arg->get_name();
2644 if (arg->has_doc()) {
2645 ss << " " << arg->get_doc();
2646 }
2647 ss << endl;
2648 }
2649
2650 // generate return type doc
2651 t_type* ret_type = function->get_returntype();
2652 if (!ret_type->is_void() || ret_type->has_doc()) {
2653 ss << "@return " << type_to_phpdoc(ret_type);
2654 if (ret_type->has_doc()) {
2655 ss << " " << ret_type->get_doc();
2656 }
2657 ss << endl;
2658 }
2659
2660 // generate exceptions doc
2661 const vector<t_field*>& excs = function->get_xceptions()->get_members();
2662 vector<t_field*>::const_iterator e_iter;
2663 for (e_iter = excs.begin(); e_iter != excs.end(); ++e_iter) {
2664 t_field* exc = *e_iter;
2665 ss << "@throws " << type_to_phpdoc(exc->get_type());
2666 if (exc->has_doc()) {
2667 ss << " " << exc->get_doc();
2668 }
2669 ss << endl;
2670 }
2671
2672 generate_docstring_comment(out, "/**\n", " * ", ss.str(), " */\n");
2673 }
2674
2675 /**
2676 * Declares a field, which may include initialization as necessary.
2677 *
2678 * @param ttype The type
2679 */
declare_field(t_field * tfield,bool init,bool obj)2680 string t_php_generator::declare_field(t_field* tfield, bool init, bool obj) {
2681 string result = "$" + tfield->get_name();
2682 if (init) {
2683 t_type* type = get_true_type(tfield->get_type());
2684 if (type->is_base_type()) {
2685 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2686 switch (tbase) {
2687 case t_base_type::TYPE_VOID:
2688 break;
2689 case t_base_type::TYPE_STRING:
2690 result += " = ''";
2691 break;
2692 case t_base_type::TYPE_BOOL:
2693 result += " = false";
2694 break;
2695 case t_base_type::TYPE_I8:
2696 case t_base_type::TYPE_I16:
2697 case t_base_type::TYPE_I32:
2698 case t_base_type::TYPE_I64:
2699 result += " = 0";
2700 break;
2701 case t_base_type::TYPE_DOUBLE:
2702 result += " = 0.0";
2703 break;
2704 default:
2705 throw "compiler error: no PHP initializer for base type " + t_base_type::t_base_name(tbase);
2706 }
2707 } else if (type->is_enum()) {
2708 result += " = 0";
2709 } else if (type->is_container()) {
2710 result += " = array()";
2711 } else if (type->is_struct() || type->is_xception()) {
2712 if (obj) {
2713 result += " = new " + php_namespace(type->get_program()) + type->get_name() + "()";
2714 } else {
2715 result += " = null";
2716 }
2717 }
2718 }
2719 return result + ";";
2720 }
2721
2722 /**
2723 * Renders a function signature of the form 'type name(args)'
2724 *
2725 * @param tfunction Function definition
2726 * @return String of rendered function definition
2727 */
function_signature(t_function * tfunction,string prefix)2728 string t_php_generator::function_signature(t_function* tfunction, string prefix) {
2729 return prefix + tfunction->get_name() + "(" + argument_list(tfunction->get_arglist()) + ")";
2730 }
2731
2732 /**
2733 * Renders a field list
2734 */
argument_list(t_struct * tstruct,bool addTypeHints)2735 string t_php_generator::argument_list(t_struct* tstruct, bool addTypeHints) {
2736 string result = "";
2737
2738 const vector<t_field*>& fields = tstruct->get_members();
2739 vector<t_field*>::const_iterator f_iter;
2740 bool first = true;
2741 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
2742 if (first) {
2743 first = false;
2744 } else {
2745 result += ", ";
2746 }
2747
2748 t_type* type = (*f_iter)->get_type();
2749
2750 // Set type name
2751 if (addTypeHints) {
2752 if (type->is_struct()) {
2753 string className = php_namespace(type->get_program())
2754 + php_namespace_directory("Definition", false)
2755 + classify(type->get_name());
2756
2757 result += className + " ";
2758 } else if (type->is_container()) {
2759 result += "array ";
2760 }
2761 }
2762
2763 result += "$" + (*f_iter)->get_name();
2764 }
2765 return result;
2766 }
2767
2768 /**
2769 * Gets a typecast string for a particular type.
2770 */
type_to_cast(t_type * type)2771 string t_php_generator::type_to_cast(t_type* type) {
2772 if (type->is_base_type()) {
2773 t_base_type* btype = (t_base_type*)type;
2774 switch (btype->get_base()) {
2775 case t_base_type::TYPE_BOOL:
2776 return "(bool)";
2777 case t_base_type::TYPE_I8:
2778 case t_base_type::TYPE_I16:
2779 case t_base_type::TYPE_I32:
2780 case t_base_type::TYPE_I64:
2781 return "(int)";
2782 case t_base_type::TYPE_DOUBLE:
2783 return "(double)";
2784 case t_base_type::TYPE_STRING:
2785 return "(string)";
2786 default:
2787 return "";
2788 }
2789 } else if (type->is_enum()) {
2790 return "(int)";
2791 }
2792 return "";
2793 }
2794
2795 /**
2796 * Converts the parse type to a C++ enum string for the given type.
2797 */
type_to_enum(t_type * type)2798 string t_php_generator::type_to_enum(t_type* type) {
2799 type = get_true_type(type);
2800
2801 if (type->is_base_type()) {
2802 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2803 switch (tbase) {
2804 case t_base_type::TYPE_VOID:
2805 throw "NO T_VOID CONSTRUCT";
2806 case t_base_type::TYPE_STRING:
2807 return "TType::STRING";
2808 case t_base_type::TYPE_BOOL:
2809 return "TType::BOOL";
2810 case t_base_type::TYPE_I8:
2811 return "TType::BYTE";
2812 case t_base_type::TYPE_I16:
2813 return "TType::I16";
2814 case t_base_type::TYPE_I32:
2815 return "TType::I32";
2816 case t_base_type::TYPE_I64:
2817 return "TType::I64";
2818 case t_base_type::TYPE_DOUBLE:
2819 return "TType::DOUBLE";
2820 default:
2821 throw "compiler error: unhandled type";
2822 }
2823 } else if (type->is_enum()) {
2824 return "TType::I32";
2825 } else if (type->is_struct() || type->is_xception()) {
2826 return "TType::STRUCT";
2827 } else if (type->is_map()) {
2828 return "TType::MAP";
2829 } else if (type->is_set()) {
2830 return "TType::SET";
2831 } else if (type->is_list()) {
2832 return "TType::LST";
2833 }
2834
2835 throw "INVALID TYPE IN type_to_enum: " + type->get_name();
2836 }
2837
2838 /**
2839 * Converts the parse type to a PHPDoc string for the given type.
2840 */
type_to_phpdoc(t_type * type)2841 string t_php_generator::type_to_phpdoc(t_type* type) {
2842 type = get_true_type(type);
2843
2844 if (type->is_base_type()) {
2845 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2846 switch (tbase) {
2847 case t_base_type::TYPE_VOID:
2848 return "void";
2849 case t_base_type::TYPE_STRING:
2850 return "string";
2851 case t_base_type::TYPE_BOOL:
2852 return "bool";
2853 case t_base_type::TYPE_I8:
2854 return "int";
2855 case t_base_type::TYPE_I16:
2856 return "int";
2857 case t_base_type::TYPE_I32:
2858 return "int";
2859 case t_base_type::TYPE_I64:
2860 return "int";
2861 case t_base_type::TYPE_DOUBLE:
2862 return "double";
2863 default:
2864 throw "compiler error: unhandled type";
2865 }
2866 } else if (type->is_enum()) {
2867 return "int";
2868 } else if (type->is_struct() || type->is_xception()) {
2869 return php_namespace(type->get_program()) + type->get_name();
2870 } else if (type->is_map()) {
2871 return "array";
2872 } else if (type->is_set()) {
2873 t_set* tset = static_cast<t_set*>(type);
2874 t_type* t_elem = tset->get_elem_type();
2875 if (t_elem->is_container()) {
2876 return "(" + type_to_phpdoc(t_elem) + ")[]";
2877 } else {
2878 return type_to_phpdoc(t_elem) + "[]";
2879 }
2880 } else if (type->is_list()) {
2881 t_list* tlist = static_cast<t_list*>(type);
2882 t_type* t_elem = tlist->get_elem_type();
2883 if (t_elem->is_container()) {
2884 return "(" + type_to_phpdoc(t_elem) + ")[]";
2885 } else {
2886 return type_to_phpdoc(t_elem) + "[]";
2887 }
2888 }
2889
2890 throw "INVALID TYPE IN type_to_enum: " + type->get_name();
2891 }
2892
display_name() const2893 std::string t_php_generator::display_name() const {
2894 return "PHP";
2895 }
2896
2897
2898 THRIFT_REGISTER_GENERATOR(
2899 php,
2900 "PHP",
2901 " inlined: Generate PHP inlined files\n"
2902 " server: Generate PHP server stubs\n"
2903 " oop: Generate PHP with object oriented subclasses\n"
2904 " classmap: Generate old-style PHP files (use classmap autoloading)\n"
2905 " rest: Generate PHP REST processors\n"
2906 " nsglobal=NAME: Set global namespace\n"
2907 " validate: Generate PHP validator methods\n"
2908 " json: Generate JsonSerializable classes (requires PHP >= 5.4)\n"
2909 " getters_setters: Generate Getters and Setters for struct variables\n")
2910