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