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