Subversion Repositories SmartDukaan

Rev

Rev 30 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
30 ashish 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 "t_oop_generator.h"
29
#include "platform.h"
30
using namespace std;
31
 
32
 
33
/**
34
 * PHP code generator.
35
 *
36
 */
37
class t_php_generator : public t_oop_generator {
38
 public:
39
  t_php_generator(
40
      t_program* program,
41
      const std::map<std::string, std::string>& parsed_options,
42
      const std::string& option_string)
43
    : t_oop_generator(program)
44
  {
45
    std::map<std::string, std::string>::const_iterator iter;
46
 
47
    iter = parsed_options.find("inlined");
48
    binary_inline_ = (iter != parsed_options.end());
49
 
50
    iter = parsed_options.find("rest");
51
    rest_ = (iter != parsed_options.end());
52
 
53
    iter = parsed_options.find("server");
54
    phps_ = (iter != parsed_options.end());
55
 
56
    iter = parsed_options.find("autoload");
57
    autoload_ = (iter != parsed_options.end());
58
 
59
    iter = parsed_options.find("oop");
60
    oop_ = (iter != parsed_options.end());
61
 
62
    if (oop_ && binary_inline_) {
63
      throw "oop and inlined are mutually exclusive.";
64
    }
65
 
66
    out_dir_base_ = (binary_inline_ ? "gen-phpi" : "gen-php");
67
    escape_['$'] = "\\$";
68
  }
69
 
70
  /**
71
   * Init and close methods
72
   */
73
 
74
  void init_generator();
75
  void close_generator();
76
 
77
  /**
78
   * Program-level generation functions
79
   */
80
 
81
  void generate_typedef  (t_typedef*  ttypedef);
82
  void generate_enum     (t_enum*     tenum);
83
  void generate_const    (t_const*    tconst);
84
  void generate_struct   (t_struct*   tstruct);
85
  void generate_xception (t_struct*   txception);
86
  void generate_service  (t_service*  tservice);
87
 
88
  std::string render_const_value(t_type* type, t_const_value* value);
89
 
90
  /**
91
   * Structs!
92
   */
93
 
94
  void generate_php_struct(t_struct* tstruct, bool is_exception);
95
  void generate_php_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception=false);
96
  void _generate_php_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception=false);
97
  void generate_php_struct_reader(std::ofstream& out, t_struct* tstruct);
98
  void generate_php_struct_writer(std::ofstream& out, t_struct* tstruct);
99
  void generate_php_function_helpers(t_function* tfunction);
100
 
101
  void generate_php_type_spec(std::ofstream &out, t_type* t);
102
  void generate_php_struct_spec(std::ofstream &out, t_struct* tstruct);
103
 
104
  /**
105
   * Service-level generation functions
106
   */
107
 
108
  void generate_service_helpers   (t_service* tservice);
109
  void generate_service_interface (t_service* tservice);
110
  void generate_service_rest      (t_service* tservice);
111
  void generate_service_client    (t_service* tservice);
112
  void _generate_service_client   (std::ofstream &out, t_service* tservice);
113
  void generate_service_processor (t_service* tservice);
114
  void generate_process_function  (t_service* tservice, t_function* tfunction);
115
 
116
  /**
117
   * Serialization constructs
118
   */
119
 
120
  void generate_deserialize_field        (std::ofstream &out,
121
                                          t_field*    tfield,
122
                                          std::string prefix="",
123
                                          bool inclass=false);
124
 
125
  void generate_deserialize_struct       (std::ofstream &out,
126
                                          t_struct*   tstruct,
127
                                          std::string prefix="");
128
 
129
  void generate_deserialize_container    (std::ofstream &out,
130
                                          t_type*     ttype,
131
                                          std::string prefix="");
132
 
133
  void generate_deserialize_set_element  (std::ofstream &out,
134
                                          t_set*      tset,
135
                                          std::string prefix="");
136
 
137
  void generate_deserialize_map_element  (std::ofstream &out,
138
                                          t_map*      tmap,
139
                                          std::string prefix="");
140
 
141
  void generate_deserialize_list_element (std::ofstream &out,
142
                                          t_list*     tlist,
143
                                          std::string prefix="");
144
 
145
  void generate_serialize_field          (std::ofstream &out,
146
                                          t_field*    tfield,
147
                                          std::string prefix="");
148
 
149
  void generate_serialize_struct         (std::ofstream &out,
150
                                          t_struct*   tstruct,
151
                                          std::string prefix="");
152
 
153
  void generate_serialize_container      (std::ofstream &out,
154
                                          t_type*     ttype,
155
                                          std::string prefix="");
156
 
157
  void generate_serialize_map_element    (std::ofstream &out,
158
                                          t_map*      tmap,
159
                                          std::string kiter,
160
                                          std::string viter);
161
 
162
  void generate_serialize_set_element    (std::ofstream &out,
163
                                          t_set*      tmap,
164
                                          std::string iter);
165
 
166
  void generate_serialize_list_element   (std::ofstream &out,
167
                                          t_list*     tlist,
168
                                          std::string iter);
169
 
170
  /**
171
   * Helper rendering functions
172
   */
173
 
174
  std::string php_includes();
175
  std::string declare_field(t_field* tfield, bool init=false, bool obj=false);
176
  std::string function_signature(t_function* tfunction, std::string prefix="");
177
  std::string argument_list(t_struct* tstruct);
178
  std::string type_to_cast(t_type* ttype);
179
  std::string type_to_enum(t_type* ttype);
180
 
181
  std::string php_namespace(t_program* p) {
182
    std::string ns = p->get_namespace("php");
183
    return ns.size() ? (ns + "_") : "";
184
  }
185
 
186
 private:
187
 
188
  /**
189
   * File streams
190
   */
191
  std::ofstream f_types_;
192
  std::ofstream f_consts_;
193
  std::ofstream f_helpers_;
194
  std::ofstream f_service_;
195
 
196
  std::string package_dir_;
197
  /**
198
   * Generate protocol-independent template? Or Binary inline code?
199
   */
200
  bool binary_inline_;
201
 
202
  /**
203
   * Generate a REST handler class
204
   */
205
  bool rest_;
206
 
207
  /**
208
   * Generate stubs for a PHP server
209
   */
210
  bool phps_;
211
 
212
  /**
213
   * Generate PHP code that uses autoload
214
   */
215
  bool autoload_;
216
 
217
  /**
218
   * Whether to use OOP base class TBase
219
   */
220
  bool oop_;
221
 
222
};
223
 
224
 
225
/**
226
 * Prepares for file generation by opening up the necessary file output
227
 * streams.
228
 *
229
 * @param tprogram The program to generate
230
 */
231
void t_php_generator::init_generator() {
232
  // Make output directory
233
  MKDIR(get_out_dir().c_str());
234
  package_dir_ = get_out_dir()+"/"+program_name_+"/";
235
  MKDIR(package_dir_.c_str());
236
  // Make output file
237
  string f_types_name = package_dir_+program_name_+"_types.php";
238
  f_types_.open(f_types_name.c_str());
239
 
240
  // Print header
241
  f_types_ <<
242
    "<?php" << endl <<
243
    autogen_comment() <<
244
    php_includes();
245
 
246
  // Include other Thrift includes
247
  const vector<t_program*>& includes = program_->get_includes();
248
  for (size_t i = 0; i < includes.size(); ++i) {
249
    string package = includes[i]->get_name();
250
    f_types_ <<
251
      "include_once $GLOBALS['THRIFT_ROOT'].'/packages/" << package << "/" << package << "_types.php';" << endl;
252
  }
253
  f_types_ << endl;
254
 
255
  // Print header
256
  if (!program_->get_consts().empty()) {
257
    string f_consts_name = package_dir_+program_name_+"_constants.php";
258
    f_consts_.open(f_consts_name.c_str());
259
    f_consts_ <<
260
      "<?php" << endl <<
261
      autogen_comment() <<
262
      "include_once $GLOBALS['THRIFT_ROOT'].'/packages/" + program_name_ + "/" + program_name_ + "_types.php';" << endl <<
263
      endl <<
264
      "$GLOBALS['" << program_name_ << "_CONSTANTS'] = array();" << endl <<
265
      endl;
266
  }
267
}
268
 
269
/**
270
 * Prints standard php includes
271
 */
272
string t_php_generator::php_includes() {
273
  return
274
    string("include_once $GLOBALS['THRIFT_ROOT'].'/Thrift.php';\n\n");
275
}
276
 
277
/**
278
 * Close up (or down) some filez.
279
 */
280
void t_php_generator::close_generator() {
281
  // Close types file
282
  f_types_ << "?>" << endl;
283
  f_types_.close();
284
 
285
  if (!program_->get_consts().empty()) {
286
    f_consts_ << "?>" << endl;
287
    f_consts_.close();
288
  }
289
}
290
 
291
/**
292
 * Generates a typedef. This is not done in PHP, types are all implicit.
293
 *
294
 * @param ttypedef The type definition
295
 */
296
void t_php_generator::generate_typedef(t_typedef* ttypedef) {}
297
 
298
/**
299
 * Generates code for an enumerated type. Since define is expensive to lookup
300
 * in PHP, we use a global array for this.
301
 *
302
 * @param tenum The enumeration
303
 */
304
void t_php_generator::generate_enum(t_enum* tenum) {
305
  f_types_ <<
306
    "$GLOBALS['" << php_namespace(tenum->get_program()) << "E_" << tenum->get_name() << "'] = array(" << endl;
307
 
308
  vector<t_enum_value*> constants = tenum->get_constants();
309
  vector<t_enum_value*>::iterator c_iter;
310
  int value = -1;
311
  for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
312
    if ((*c_iter)->has_value()) {
313
      value = (*c_iter)->get_value();
314
    } else {
315
      ++value;
316
    }
317
 
318
    f_types_ <<
319
      "  '" << (*c_iter)->get_name() << "' => " << value << "," << endl;
320
  }
321
 
322
  f_types_ <<
323
    ");" << endl << endl;
324
 
325
 
326
  // We're also doing it this way to see how it performs. It's more legible
327
  // code but you can't do things like an 'extract' on it, which is a bit of
328
  // a downer.
329
  f_types_ <<
330
    "final class " << php_namespace(tenum->get_program()) << tenum->get_name() << " {" << endl;
331
  indent_up();
332
 
333
  value = -1;
334
  for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
335
    if ((*c_iter)->has_value()) {
336
      value = (*c_iter)->get_value();
337
    } else {
338
      ++value;
339
    }
340
 
341
    indent(f_types_) <<
342
      "const " << (*c_iter)->get_name() << " = " << value << ";" << endl;
343
  }
344
 
345
  indent(f_types_) <<
346
    "static public $__names = array(" << endl;
347
  value = -1;
348
  for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
349
    if ((*c_iter)->has_value()) {
350
      value = (*c_iter)->get_value();
351
    } else {
352
      ++value;
353
    }
354
 
355
    indent(f_types_) <<
356
      "  " << value << " => '" << (*c_iter)->get_name() << "'," << endl;
357
  }
358
  indent(f_types_) <<
359
    ");" << endl;
360
 
361
  indent_down();
362
  f_types_ << "}" << endl << endl;
363
}
364
 
365
/**
366
 * Generate a constant value
367
 */
368
void t_php_generator::generate_const(t_const* tconst) {
369
  t_type* type = tconst->get_type();
370
  string name = tconst->get_name();
371
  t_const_value* value = tconst->get_value();
372
 
373
  f_consts_ << "$GLOBALS['" << program_name_ << "_CONSTANTS']['" << name << "'] = ";
374
  f_consts_ << render_const_value(type, value);
375
  f_consts_ << ";" << endl << endl;
376
}
377
 
378
/**
379
 * Prints the value of a constant with the given type. Note that type checking
380
 * is NOT performed in this function as it is always run beforehand using the
381
 * validate_types method in main.cc
382
 */
383
string t_php_generator::render_const_value(t_type* type, t_const_value* value) {
384
  std::ostringstream out;
385
  type = get_true_type(type);
386
  if (type->is_base_type()) {
387
    t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
388
    switch (tbase) {
389
    case t_base_type::TYPE_STRING:
390
      out << '"' << get_escaped_string(value) << '"';
391
      break;
392
    case t_base_type::TYPE_BOOL:
393
      out << (value->get_integer() > 0 ? "true" : "false");
394
      break;
395
    case t_base_type::TYPE_BYTE:
396
    case t_base_type::TYPE_I16:
397
    case t_base_type::TYPE_I32:
398
    case t_base_type::TYPE_I64:
399
      out << value->get_integer();
400
      break;
401
    case t_base_type::TYPE_DOUBLE:
402
      if (value->get_type() == t_const_value::CV_INTEGER) {
403
        out << value->get_integer();
404
      } else {
405
        out << value->get_double();
406
      }
407
      break;
408
    default:
409
      throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
410
    }
411
  } else if (type->is_enum()) {
412
    indent(out) << value->get_integer();
413
  } else if (type->is_struct() || type->is_xception()) {
414
    out << "new " << php_namespace(type->get_program()) << type->get_name() << "(array(" << endl;
415
    indent_up();
416
    const vector<t_field*>& fields = ((t_struct*)type)->get_members();
417
    vector<t_field*>::const_iterator f_iter;
418
    const map<t_const_value*, t_const_value*>& val = value->get_map();
419
    map<t_const_value*, t_const_value*>::const_iterator v_iter;
420
    for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
421
      t_type* field_type = NULL;
422
      for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
423
        if ((*f_iter)->get_name() == v_iter->first->get_string()) {
424
          field_type = (*f_iter)->get_type();
425
        }
426
      }
427
      if (field_type == NULL) {
428
        throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
429
      }
430
      out << indent();
431
      out << render_const_value(g_type_string, v_iter->first);
432
      out << " => ";
433
      out << render_const_value(field_type, v_iter->second);
434
      out << endl;
435
    }
436
    indent_down();
437
    indent(out) << "))";
438
  } else if (type->is_map()) {
439
    t_type* ktype = ((t_map*)type)->get_key_type();
440
    t_type* vtype = ((t_map*)type)->get_val_type();
441
    out << "array(" << endl;
442
    indent_up();
443
    const map<t_const_value*, t_const_value*>& val = value->get_map();
444
    map<t_const_value*, t_const_value*>::const_iterator v_iter;
445
    for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
446
      out << indent();
447
      out << render_const_value(ktype, v_iter->first);
448
      out << " => ";
449
      out << render_const_value(vtype, v_iter->second);
450
      out << "," << endl;
451
    }
452
    indent_down();
453
    indent(out) << ")";
454
  } else if (type->is_list() || type->is_set()) {
455
    t_type* etype;
456
    if (type->is_list()) {
457
      etype = ((t_list*)type)->get_elem_type();
458
    } else {
459
      etype = ((t_set*)type)->get_elem_type();
460
    }
461
    out << "array(" << endl;
462
    indent_up();
463
    const vector<t_const_value*>& val = value->get_list();
464
    vector<t_const_value*>::const_iterator v_iter;
465
    for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
466
      out << indent();
467
      out << render_const_value(etype, *v_iter);
468
      if (type->is_set()) {
469
        out << " => true";
470
      }
471
      out << "," << endl;
472
    }
473
    indent_down();
474
    indent(out) << ")";
475
  }
476
  return out.str();
477
}
478
 
479
/**
480
 * Make a struct
481
 */
482
void t_php_generator::generate_struct(t_struct* tstruct) {
483
  generate_php_struct(tstruct, false);
484
}
485
 
486
/**
487
 * Generates a struct definition for a thrift exception. Basically the same
488
 * as a struct but extends the Exception class.
489
 *
490
 * @param txception The struct definition
491
 */
492
void t_php_generator::generate_xception(t_struct* txception) {
493
  generate_php_struct(txception, true);
494
}
495
 
496
/**
497
 * Structs can be normal or exceptions.
498
 */
499
void t_php_generator::generate_php_struct(t_struct* tstruct,
500
                                          bool is_exception) {
501
  generate_php_struct_definition(f_types_, tstruct, is_exception);
502
}
503
 
504
void t_php_generator::generate_php_type_spec(ofstream& out,
505
                                             t_type* t) {
506
  t = get_true_type(t);
507
  indent(out) << "'type' => " << type_to_enum(t) << "," << endl;
508
 
509
  if (t->is_base_type() || t->is_enum()) {
510
    // Noop, type is all we need
511
  } else if (t->is_struct() || t->is_xception()) {
512
    indent(out) << "'class' => '" << php_namespace(t->get_program()) << t->get_name() <<"'," << endl;
513
  } else if (t->is_map()) {
514
    t_type* ktype = get_true_type(((t_map*)t)->get_key_type());
515
    t_type* vtype = get_true_type(((t_map*)t)->get_val_type());
516
    indent(out) << "'ktype' => " << type_to_enum(ktype) << "," << endl;
517
    indent(out) << "'vtype' => " << type_to_enum(vtype) << "," << endl;
518
    indent(out) << "'key' => array(" << endl;
519
    indent_up();
520
    generate_php_type_spec(out, ktype);
521
    indent_down();
522
    indent(out) << ")," << endl;
523
    indent(out) << "'val' => array(" << endl;
524
    indent_up();
525
    generate_php_type_spec(out, vtype);
526
    indent(out) << ")," << endl;
527
    indent_down();
528
  } else if (t->is_list() || t->is_set()) {
529
    t_type* etype;
530
    if (t->is_list()) {
531
      etype = get_true_type(((t_list*)t)->get_elem_type());
532
    } else {
533
      etype = get_true_type(((t_set*)t)->get_elem_type());
534
    }
535
    indent(out) << "'etype' => " << type_to_enum(etype) <<"," << endl;
536
    indent(out) << "'elem' => array(" << endl;
537
    indent_up();
538
    generate_php_type_spec(out, etype);
539
    indent(out) << ")," << endl;
540
    indent_down();
541
  } else {
542
    throw "compiler error: no type for php struct spec field";
543
  }
544
 
545
}
546
 
547
/**
548
 * Generates the struct specification structure, which fully qualifies enough
549
 * type information to generalize serialization routines.
550
 */
551
void t_php_generator::generate_php_struct_spec(ofstream& out,
552
                                               t_struct* tstruct) {
553
  indent(out) << "if (!isset(self::$_TSPEC)) {" << endl;
554
  indent_up();
555
 
556
  indent(out) << "self::$_TSPEC = array(" << endl;
557
  indent_up();
558
 
559
  const vector<t_field*>& members = tstruct->get_members();
560
  vector<t_field*>::const_iterator m_iter;
561
  for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
562
    t_type* t = get_true_type((*m_iter)->get_type());
563
    indent(out) << (*m_iter)->get_key() << " => array(" << endl;
564
    indent_up();
565
    out <<
566
      indent() << "'var' => '" << (*m_iter)->get_name() << "'," << endl;
567
    generate_php_type_spec(out, t);
568
    indent(out) << ")," << endl;
569
    indent_down();
570
  }
571
 
572
  indent_down();
573
  indent(out) << "  );" << endl;
574
  indent_down();
575
  indent(out) << "}" << endl;
576
}
577
 
578
 
579
void t_php_generator::generate_php_struct_definition(ofstream& out,
580
                                                     t_struct* tstruct,
581
                                                     bool is_exception) {
582
  if (autoload_) {
583
    // Make output file
584
    ofstream autoload_out;
585
    string f_struct = program_name_+"."+(tstruct->get_name())+".php";
586
    string f_struct_name = package_dir_+f_struct;
587
    autoload_out.open(f_struct_name.c_str());
588
    autoload_out << "<?php" << endl;
589
    _generate_php_struct_definition(autoload_out, tstruct, is_exception);
590
    autoload_out << endl << "?>" << endl;
591
    autoload_out.close();
592
 
593
    f_types_ <<
594
      "$GLOBALS['THRIFT_AUTOLOAD']['" << lowercase(php_namespace(tstruct->get_program()) + tstruct->get_name()) << "'] = '" << program_name_ << "/" << f_struct << "';" << endl;
595
 
596
  } else {
597
    _generate_php_struct_definition(out, tstruct, is_exception);
598
  }
599
}
600
 
601
/**
602
 * Generates a struct definition for a thrift data type. This is nothing in PHP
603
 * where the objects are all just associative arrays (unless of course we
604
 * decide to start using objects for them...)
605
 *
606
 * @param tstruct The struct definition
607
 */
608
void t_php_generator::_generate_php_struct_definition(ofstream& out,
609
                                                     t_struct* tstruct,
610
                                                     bool is_exception) {
611
  const vector<t_field*>& members = tstruct->get_members();
612
  vector<t_field*>::const_iterator m_iter;
613
 
614
  out <<
615
    "class " << php_namespace(tstruct->get_program()) << tstruct->get_name();
616
  if (is_exception) {
617
    out << " extends TException";
618
  } else if (oop_) {
619
    out << " extends TBase";
620
  }
621
  out <<
622
    " {" << endl;
623
  indent_up();
624
 
625
  indent(out) << "static $_TSPEC;" << endl << endl;
626
 
627
  for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
628
    string dval = "null";
629
    t_type* t = get_true_type((*m_iter)->get_type());
630
    if ((*m_iter)->get_value() != NULL && !(t->is_struct() || t->is_xception())) {
631
      dval = render_const_value((*m_iter)->get_type(), (*m_iter)->get_value());
632
    }
633
    indent(out) <<
634
      "public $" << (*m_iter)->get_name() << " = " << dval << ";" << endl;
635
  }
636
 
637
  out << endl;
638
 
639
  // Generate constructor from array
640
  string param = (members.size() > 0) ? "$vals=null" : "";
641
  out <<
642
    indent() << "public function __construct(" << param << ") {" << endl;
643
  indent_up();
644
 
645
  generate_php_struct_spec(out, tstruct);
646
 
647
  if (members.size() > 0) {
648
    for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
649
      t_type* t = get_true_type((*m_iter)->get_type());
650
      if ((*m_iter)->get_value() != NULL && (t->is_struct() || t->is_xception())) {
651
        indent(out) << "$this->" << (*m_iter)->get_name() << " = " << render_const_value(t, (*m_iter)->get_value()) << ";" << endl;
652
      }
653
    }
654
    out <<
655
      indent() << "if (is_array($vals)) {" << endl;
656
    indent_up();
657
    if (oop_) {
658
      out << indent() << "parent::__construct(self::$_TSPEC, $vals);" << endl;
659
    } else {
660
      for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
661
        out <<
662
          indent() << "if (isset($vals['" << (*m_iter)->get_name() << "'])) {" << endl <<
663
          indent() << "  $this->" << (*m_iter)->get_name() << " = $vals['" << (*m_iter)->get_name() << "'];" << endl <<
664
          indent() << "}" << endl;
665
      }
666
    }
667
    indent_down();
668
    out <<
669
      indent() << "}" << endl;
670
  }
671
  scope_down(out);
672
  out << endl;
673
 
674
  out <<
675
    indent() << "public function getName() {" << endl <<
676
    indent() << "  return '" << tstruct->get_name() << "';" << endl <<
677
    indent() << "}" << endl <<
678
    endl;
679
 
680
  generate_php_struct_reader(out, tstruct);
681
  generate_php_struct_writer(out, tstruct);
682
 
683
  indent_down();
684
  out <<
685
    indent() << "}" << endl <<
686
    endl;
687
}
688
 
689
/**
690
 * Generates the read() method for a struct
691
 */
692
void t_php_generator::generate_php_struct_reader(ofstream& out,
693
                                                 t_struct* tstruct) {
694
  const vector<t_field*>& fields = tstruct->get_members();
695
  vector<t_field*>::const_iterator f_iter;
696
 
697
  indent(out) <<
698
    "public function read($input)" << endl;
699
  scope_up(out);
700
 
701
  if (oop_) {
702
    indent(out) << "return $this->_read('" << tstruct->get_name() << "', self::$_TSPEC, $input);" << endl;
703
    scope_down(out);
704
    return;
705
  }
706
 
707
  out <<
708
    indent() << "$xfer = 0;" << endl <<
709
    indent() << "$fname = null;" << endl <<
710
    indent() << "$ftype = 0;" << endl <<
711
    indent() << "$fid = 0;" << endl;
712
 
713
  // Declare stack tmp variables
714
  if (!binary_inline_) {
715
    indent(out) <<
716
      "$xfer += $input->readStructBegin($fname);" << endl;
717
  }
718
 
719
  // Loop over reading in fields
720
  indent(out) <<
721
    "while (true)" << endl;
722
 
723
    scope_up(out);
724
 
725
    // Read beginning field marker
726
    if (binary_inline_) {
727
      t_field fftype(g_type_byte, "ftype");
728
      t_field ffid(g_type_i16, "fid");
729
      generate_deserialize_field(out, &fftype);
730
      out <<
731
        indent() << "if ($ftype == TType::STOP) {" << endl <<
732
        indent() << "  break;" << endl <<
733
        indent() << "}" << endl;
734
      generate_deserialize_field(out, &ffid);
735
    } else {
736
      indent(out) <<
737
        "$xfer += $input->readFieldBegin($fname, $ftype, $fid);" << endl;
738
      // Check for field STOP marker and break
739
      indent(out) <<
740
        "if ($ftype == TType::STOP) {" << endl;
741
      indent_up();
742
      indent(out) <<
743
        "break;" << endl;
744
      indent_down();
745
      indent(out) <<
746
        "}" << endl;
747
    }
748
 
749
    // Switch statement on the field we are reading
750
    indent(out) <<
751
      "switch ($fid)" << endl;
752
 
753
      scope_up(out);
754
 
755
      // Generate deserialization code for known cases
756
      for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
757
        indent(out) <<
758
          "case " << (*f_iter)->get_key() << ":" << endl;
759
        indent_up();
760
        indent(out) << "if ($ftype == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl;
761
        indent_up();
762
        generate_deserialize_field(out, *f_iter, "this->");
763
        indent_down();
764
        out <<
765
          indent() << "} else {" << endl;
766
        if (binary_inline_) {
767
          indent(out) <<  "  $xfer += TProtocol::skipBinary($input, $ftype);" << endl;
768
        } else {
769
          indent(out) <<  "  $xfer += $input->skip($ftype);" << endl;
770
        }
771
        out <<
772
          indent() << "}" << endl <<
773
          indent() << "break;" << endl;
774
        indent_down();
775
      }
776
 
777
      // In the default case we skip the field
778
      indent(out) <<  "default:" << endl;
779
      if (binary_inline_) {
780
        indent(out) <<  "  $xfer += TProtocol::skipBinary($input, $ftype);" << endl;
781
      } else {
782
        indent(out) <<  "  $xfer += $input->skip($ftype);" << endl;
783
      }
784
      indent(out) <<  "  break;" << endl;
785
 
786
      scope_down(out);
787
 
788
    if (!binary_inline_) {
789
      // Read field end marker
790
      indent(out) <<
791
        "$xfer += $input->readFieldEnd();" << endl;
792
    }
793
 
794
    scope_down(out);
795
 
796
  if (!binary_inline_) {
797
    indent(out) <<
798
      "$xfer += $input->readStructEnd();" << endl;
799
  }
800
 
801
  indent(out) <<
802
    "return $xfer;" << endl;
803
 
804
  indent_down();
805
  out <<
806
    indent() << "}" << endl <<
807
    endl;
808
}
809
 
810
/**
811
 * Generates the write() method for a struct
812
 */
813
void t_php_generator::generate_php_struct_writer(ofstream& out,
814
                                                 t_struct* tstruct) {
815
  string name = tstruct->get_name();
816
  const vector<t_field*>& fields = tstruct->get_sorted_members();
817
  vector<t_field*>::const_iterator f_iter;
818
 
819
  if (binary_inline_) {
820
    indent(out) <<
821
      "public function write(&$output) {" << endl;
822
  } else {
823
    indent(out) <<
824
      "public function write($output) {" << endl;
825
  }
826
  indent_up();
827
 
828
  if (oop_) {
829
    indent(out) << "return $this->_write('" << tstruct->get_name() << "', self::$_TSPEC, $output);" << endl;
830
    scope_down(out);
831
    return;
832
  }
833
 
834
  indent(out) <<
835
    "$xfer = 0;" << endl;
836
 
837
  if (!binary_inline_) {
838
    indent(out) <<
839
      "$xfer += $output->writeStructBegin('" << name << "');" << endl;
840
  }
841
 
842
  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
843
    out <<
844
      indent() << "if ($this->" << (*f_iter)->get_name() << " !== null) {" << endl;
845
    indent_up();
846
 
847
    t_type* type = get_true_type((*f_iter)->get_type());
848
    string expect;
849
    if (type->is_container()) {
850
      expect = "array";
851
    } else if (type->is_struct()) {
852
      expect = "object";
853
    }
854
    if (!expect.empty()) {
855
      out <<
856
        indent() << "if (!is_" << expect << "($this->" << (*f_iter)->get_name() << ")) {" << endl;
857
      indent_up();
858
      out <<
859
        indent() << "throw new TProtocolException('Bad type in structure.', TProtocolException::INVALID_DATA);" << endl;
860
      scope_down(out);
861
    }
862
 
863
    // Write field header
864
    if (binary_inline_) {
865
      out <<
866
        indent() << "$output .= pack('c', " << type_to_enum((*f_iter)->get_type()) << ");" << endl <<
867
        indent() << "$output .= pack('n', " << (*f_iter)->get_key() << ");" << endl;
868
    } else {
869
      indent(out) <<
870
        "$xfer += $output->writeFieldBegin(" <<
871
        "'" << (*f_iter)->get_name() << "', " <<
872
        type_to_enum((*f_iter)->get_type()) << ", " <<
873
        (*f_iter)->get_key() << ");" << endl;
874
    }
875
 
876
    // Write field contents
877
    generate_serialize_field(out, *f_iter, "this->");
878
 
879
    // Write field closer
880
    if (!binary_inline_) {
881
      indent(out) <<
882
        "$xfer += $output->writeFieldEnd();" << endl;
883
    }
884
 
885
    indent_down();
886
    indent(out) <<
887
      "}" << endl;
888
  }
889
 
890
  if (binary_inline_) {
891
    out <<
892
      indent() << "$output .= pack('c', TType::STOP);" << endl;
893
  } else {
894
    out <<
895
      indent() << "$xfer += $output->writeFieldStop();" << endl <<
896
      indent() << "$xfer += $output->writeStructEnd();" << endl;
897
  }
898
 
899
  out <<
900
    indent() << "return $xfer;" << endl;
901
 
902
  indent_down();
903
  out <<
904
    indent() << "}" << endl <<
905
    endl;
906
}
907
 
908
/**
909
 * Generates a thrift service.
910
 *
911
 * @param tservice The service definition
912
 */
913
void t_php_generator::generate_service(t_service* tservice) {
914
  string f_service_name = package_dir_+service_name_+".php";
915
  f_service_.open(f_service_name.c_str());
916
 
917
  f_service_ <<
918
    "<?php" << endl <<
919
    autogen_comment() <<
920
    php_includes();
921
 
922
  f_service_ <<
923
    "include_once $GLOBALS['THRIFT_ROOT'].'/packages/" << program_name_ << "/" << program_name_ << "_types.php';" << endl;
924
 
925
  if (tservice->get_extends() != NULL) {
926
    f_service_ <<
927
      "include_once $GLOBALS['THRIFT_ROOT'].'/packages/" << tservice->get_extends()->get_program()->get_name() << "/" << tservice->get_extends()->get_name() << ".php';" << endl;
928
  }
929
 
930
  f_service_ <<
931
    endl;
932
 
933
  // Generate the three main parts of the service (well, two for now in PHP)
934
  generate_service_interface(tservice);
935
  if (rest_) {
936
    generate_service_rest(tservice);
937
  }
938
  generate_service_client(tservice);
939
  generate_service_helpers(tservice);
940
  if (phps_) {
941
    generate_service_processor(tservice);
942
  }
943
 
944
  // Close service file
945
  f_service_ << "?>" << endl;
946
  f_service_.close();
947
}
948
 
949
/**
950
 * Generates a service server definition.
951
 *
952
 * @param tservice The service to generate a server for.
953
 */
954
void t_php_generator::generate_service_processor(t_service* tservice) {
955
  // Generate the dispatch methods
956
  vector<t_function*> functions = tservice->get_functions();
957
  vector<t_function*>::iterator f_iter;
958
 
959
  string extends = "";
960
  string extends_processor = "";
961
  if (tservice->get_extends() != NULL) {
962
    extends = tservice->get_extends()->get_name();
963
    extends_processor = " extends " + extends + "Processor";
964
  }
965
 
966
  // Generate the header portion
967
  f_service_ <<
968
    "class " << service_name_ << "Processor" << extends_processor << " {" << endl;
969
  indent_up();
970
 
971
  if (extends.empty()) {
972
    f_service_ <<
973
      indent() << "protected $handler_ = null;" << endl;
974
  }
975
 
976
  f_service_ <<
977
    indent() << "public function __construct($handler) {" << endl;
978
  if (extends.empty()) {
979
    f_service_ <<
980
      indent() << "  $this->handler_ = $handler;" << endl;
981
  } else {
982
    f_service_ <<
983
      indent() << "  parent::__construct($handler);" << endl;
984
  }
985
  f_service_ <<
986
    indent() << "}" << endl <<
987
    endl;
988
 
989
  // Generate the server implementation
990
  indent(f_service_) <<
991
    "public function process($input, $output) {" << endl;
992
  indent_up();
993
 
994
  f_service_ <<
995
    indent() << "$rseqid = 0;" << endl <<
996
    indent() << "$fname = null;" << endl <<
997
    indent() << "$mtype = 0;" << endl <<
998
    endl;
999
 
1000
  if (binary_inline_) {
1001
    t_field ffname(g_type_string, "fname");
1002
    t_field fmtype(g_type_byte, "mtype");
1003
    t_field fseqid(g_type_i32, "rseqid");
1004
    generate_deserialize_field(f_service_, &ffname, "", true);
1005
    generate_deserialize_field(f_service_, &fmtype, "", true);
1006
    generate_deserialize_field(f_service_, &fseqid, "", true);
1007
  } else {
1008
    f_service_ <<
1009
      indent() << "$input->readMessageBegin($fname, $mtype, $rseqid);" << endl;
1010
  }
1011
 
1012
  // HOT: check for method implementation
1013
  f_service_ <<
1014
    indent() << "$methodname = 'process_'.$fname;" << endl <<
1015
    indent() << "if (!method_exists($this, $methodname)) {" << endl;
1016
  if (binary_inline_) {
1017
    f_service_ <<
1018
      indent() << "  throw new Exception('Function '.$fname.' not implemented.');" << endl;
1019
  } else {
1020
    f_service_ <<
1021
      indent() << "  $input->skip(TType::STRUCT);" << endl <<
1022
      indent() << "  $input->readMessageEnd();" << endl <<
1023
      indent() << "  $x = new TApplicationException('Function '.$fname.' not implemented.', TApplicationException::UNKNOWN_METHOD);" << endl <<
1024
      indent() << "  $output->writeMessageBegin($fname, TMessageType::EXCEPTION, $rseqid);" << endl <<
1025
      indent() << "  $x->write($output);" << endl <<
1026
      indent() << "  $output->writeMessageEnd();" << endl <<
1027
      indent() << "  $output->getTransport()->flush();" << endl <<
1028
      indent() << "  return;" << endl;
1029
  }
1030
  f_service_ <<
1031
    indent() << "}" << endl <<
1032
    indent() << "$this->$methodname($rseqid, $input, $output);" << endl <<
1033
    indent() << "return true;" << endl;
1034
  indent_down();
1035
  f_service_ <<
1036
    indent() << "}" << endl <<
1037
    endl;
1038
 
1039
  // Generate the process subfunctions
1040
  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1041
    generate_process_function(tservice, *f_iter);
1042
  }
1043
 
1044
  indent_down();
1045
  f_service_ << "}" << endl;
1046
}
1047
 
1048
/**
1049
 * Generates a process function definition.
1050
 *
1051
 * @param tfunction The function to write a dispatcher for
1052
 */
1053
void t_php_generator::generate_process_function(t_service* tservice,
1054
                                                t_function* tfunction) {
1055
  // Open function
1056
  indent(f_service_) <<
1057
    "protected function process_" << tfunction->get_name() <<
1058
    "($seqid, $input, $output) {" << endl;
1059
  indent_up();
1060
 
1061
  string argsname = php_namespace(tservice->get_program()) + service_name_ + "_" + tfunction->get_name() + "_args";
1062
  string resultname = php_namespace(tservice->get_program()) + service_name_ + "_" + tfunction->get_name() + "_result";
1063
 
1064
  f_service_ <<
1065
    indent() << "$args = new " << argsname << "();" << endl <<
1066
    indent() << "$args->read($input);" << endl;
1067
  if (!binary_inline_) {
1068
    f_service_ <<
1069
      indent() << "$input->readMessageEnd();" << endl;
1070
  }
1071
 
1072
  t_struct* xs = tfunction->get_xceptions();
1073
  const std::vector<t_field*>& xceptions = xs->get_members();
1074
  vector<t_field*>::const_iterator x_iter;
1075
 
1076
  // Declare result for non oneway function
1077
  if (!tfunction->is_oneway()) {
1078
    f_service_ <<
1079
      indent() << "$result = new " << resultname << "();" << endl;
1080
  }
1081
 
1082
  // Try block for a function with exceptions
1083
  if (xceptions.size() > 0) {
1084
    f_service_ <<
1085
      indent() << "try {" << endl;
1086
    indent_up();
1087
  }
1088
 
1089
  // Generate the function call
1090
  t_struct* arg_struct = tfunction->get_arglist();
1091
  const std::vector<t_field*>& fields = arg_struct->get_members();
1092
  vector<t_field*>::const_iterator f_iter;
1093
 
1094
  f_service_ << indent();
1095
  if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
1096
    f_service_ << "$result->success = ";
1097
  }
1098
  f_service_ <<
1099
    "$this->handler_->" << tfunction->get_name() << "(";
1100
  bool first = true;
1101
  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1102
    if (first) {
1103
      first = false;
1104
    } else {
1105
      f_service_ << ", ";
1106
    }
1107
    f_service_ << "$args->" << (*f_iter)->get_name();
1108
  }
1109
  f_service_ << ");" << endl;
1110
 
1111
  if (!tfunction->is_oneway() && xceptions.size() > 0) {
1112
    indent_down();
1113
    for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
1114
      f_service_ <<
1115
        indent() << "} catch (" << php_namespace((*x_iter)->get_type()->get_program()) << (*x_iter)->get_type()->get_name() << " $" << (*x_iter)->get_name() << ") {" << endl;
1116
      if (!tfunction->is_oneway()) {
1117
        indent_up();
1118
        f_service_ <<
1119
          indent() << "$result->" << (*x_iter)->get_name() << " = $" << (*x_iter)->get_name() << ";" << endl;
1120
        indent_down();
1121
        f_service_ << indent();
1122
      }
1123
    }
1124
    f_service_ << "}" << endl;
1125
  }
1126
 
1127
  // Shortcut out here for oneway functions
1128
  if (tfunction->is_oneway()) {
1129
    f_service_ <<
1130
      indent() << "return;" << endl;
1131
    indent_down();
1132
    f_service_ <<
1133
      indent() << "}" << endl;
1134
    return;
1135
  }
1136
 
1137
  // Serialize the request header
1138
  if (binary_inline_) {
1139
    f_service_ <<
1140
      indent() << "$buff = pack('N', (0x80010000 | TMessageType::REPLY)); " << endl <<
1141
      indent() << "$buff .= pack('N', strlen('" << tfunction->get_name() << "'));" << endl <<
1142
      indent() << "$buff .= '" << tfunction->get_name() << "';" << endl <<
1143
      indent() << "$buff .= pack('N', $seqid);" << endl <<
1144
      indent() << "$result->write($buff);" << endl <<
1145
      indent() << "$output->write($buff);" << endl <<
1146
      indent() << "$output->flush();" << endl;
1147
  } else {
1148
    f_service_ <<
1149
      indent() << "$output->writeMessageBegin('" << tfunction->get_name() << "', TMessageType::REPLY, $seqid);" << endl <<
1150
      indent() << "$result->write($output);" << endl <<
1151
      indent() << "$output->getTransport()->flush();" << endl;
1152
  }
1153
 
1154
  // Close function
1155
  indent_down();
1156
  f_service_ <<
1157
    indent() << "}" << endl;
1158
}
1159
 
1160
/**
1161
 * Generates helper functions for a service.
1162
 *
1163
 * @param tservice The service to generate a header definition for
1164
 */
1165
void t_php_generator::generate_service_helpers(t_service* tservice) {
1166
  vector<t_function*> functions = tservice->get_functions();
1167
  vector<t_function*>::iterator f_iter;
1168
 
1169
  f_service_ <<
1170
    "// HELPER FUNCTIONS AND STRUCTURES" << endl << endl;
1171
 
1172
  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1173
    t_struct* ts = (*f_iter)->get_arglist();
1174
    string name = ts->get_name();
1175
    ts->set_name(service_name_ + "_" + name);
1176
    generate_php_struct_definition(f_service_, ts, false);
1177
    generate_php_function_helpers(*f_iter);
1178
    ts->set_name(name);
1179
  }
1180
}
1181
 
1182
/**
1183
 * Generates a struct and helpers for a function.
1184
 *
1185
 * @param tfunction The function
1186
 */
1187
void t_php_generator::generate_php_function_helpers(t_function* tfunction) {
1188
  if (!tfunction->is_oneway()) {
1189
    t_struct result(program_, service_name_ + "_" + tfunction->get_name() + "_result");
1190
    t_field success(tfunction->get_returntype(), "success", 0);
1191
    if (!tfunction->get_returntype()->is_void()) {
1192
      result.append(&success);
1193
    }
1194
 
1195
    t_struct* xs = tfunction->get_xceptions();
1196
    const vector<t_field*>& fields = xs->get_members();
1197
    vector<t_field*>::const_iterator f_iter;
1198
    for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1199
      result.append(*f_iter);
1200
    }
1201
 
1202
    generate_php_struct_definition(f_service_, &result, false);
1203
  }
1204
}
1205
 
1206
/**
1207
 * Generates a service interface definition.
1208
 *
1209
 * @param tservice The service to generate a header definition for
1210
 */
1211
void t_php_generator::generate_service_interface(t_service* tservice) {
1212
  string extends = "";
1213
  string extends_if = "";
1214
  if (tservice->get_extends() != NULL) {
1215
    extends = " extends " + tservice->get_extends()->get_name();
1216
    extends_if = " extends " + tservice->get_extends()->get_name() + "If";
1217
  }
1218
  f_service_ <<
1219
    "interface " << service_name_ << "If" << extends_if << " {" << endl;
1220
  indent_up();
1221
  vector<t_function*> functions = tservice->get_functions();
1222
  vector<t_function*>::iterator f_iter;
1223
  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1224
    indent(f_service_) <<
1225
      "public function " << function_signature(*f_iter) << ";" << endl;
1226
  }
1227
  indent_down();
1228
  f_service_ <<
1229
    "}" << endl << endl;
1230
}
1231
 
1232
/**
1233
 * Generates a REST interface
1234
 */
1235
void t_php_generator::generate_service_rest(t_service* tservice) {
1236
  string extends = "";
1237
  string extends_if = "";
1238
  if (tservice->get_extends() != NULL) {
1239
    extends = " extends " + tservice->get_extends()->get_name();
1240
    extends_if = " extends " + tservice->get_extends()->get_name() + "Rest";
1241
  }
1242
  f_service_ <<
1243
    "class " << service_name_ << "Rest" << extends_if << " {" << endl;
1244
  indent_up();
1245
 
1246
  if (extends.empty()) {
1247
    f_service_ <<
1248
      indent() << "protected $impl_;" << endl <<
1249
      endl;
1250
  }
1251
 
1252
  f_service_ <<
1253
    indent() << "public function __construct($impl) {" << endl <<
1254
    indent() << "  $this->impl_ = $impl;" << endl <<
1255
    indent() << "}" << endl <<
1256
    endl;
1257
 
1258
  vector<t_function*> functions = tservice->get_functions();
1259
  vector<t_function*>::iterator f_iter;
1260
  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1261
    indent(f_service_) <<
1262
      "public function " << (*f_iter)->get_name() << "($request) {" << endl;
1263
    indent_up();
1264
    const vector<t_field*>& args = (*f_iter)->get_arglist()->get_members();
1265
    vector<t_field*>::const_iterator a_iter;
1266
    for (a_iter = args.begin(); a_iter != args.end(); ++a_iter) {
1267
      t_type* atype = get_true_type((*a_iter)->get_type());
1268
      string cast = type_to_cast(atype);
1269
      string req = "$request['" + (*a_iter)->get_name() + "']";
1270
      if (atype->is_bool()) {
1271
        f_service_ <<
1272
          indent() << "$" << (*a_iter)->get_name() << " = " << cast << "(!empty(" << req << ") && (" << req << " !== 'false'));" << endl;
1273
      } else {
1274
        f_service_ <<
1275
          indent() << "$" << (*a_iter)->get_name() << " = isset(" << req << ") ? " << cast << req << " : null;" << endl;
1276
      }
1277
      if (atype->is_string() &&
1278
          ((t_base_type*)atype)->is_string_list()) {
1279
        f_service_ <<
1280
          indent() << "$" << (*a_iter)->get_name() << " = explode(',', $" << (*a_iter)->get_name() << ");" << endl;
1281
      } else if (atype->is_map() || atype->is_list()) {
1282
        f_service_ <<
1283
          indent() << "$" << (*a_iter)->get_name() << " = json_decode($" << (*a_iter)->get_name() << ", true);" << endl;
1284
      } else if (atype->is_set()) {
1285
        f_service_ <<
1286
          indent() << "$" << (*a_iter)->get_name() << " = array_fill_keys(json_decode($" << (*a_iter)->get_name() << ", true), 1);" << endl;
1287
      } else if (atype->is_struct() || atype->is_xception()) {
1288
        f_service_ <<
1289
          indent() << "if ($" << (*a_iter)->get_name() << " !== null) {" << endl <<
1290
          indent() << "  $" << (*a_iter)->get_name() << " = new " << php_namespace(atype->get_program()) << atype->get_name() << "(json_decode($" << (*a_iter)->get_name() << ", true));" << endl <<
1291
          indent() << "}" << endl;
1292
      }
1293
    }
1294
    f_service_ <<
1295
      indent() << "return $this->impl_->" << (*f_iter)->get_name() << "(" << argument_list((*f_iter)->get_arglist()) << ");" << endl;
1296
    indent_down();
1297
    indent(f_service_) <<
1298
      "}" << endl <<
1299
      endl;
1300
  }
1301
  indent_down();
1302
  f_service_ <<
1303
    "}" << endl << endl;
1304
}
1305
 
1306
void t_php_generator::generate_service_client(t_service* tservice) {
1307
  if (autoload_) {
1308
    // Make output file
1309
    ofstream autoload_out;
1310
    string f_struct = program_name_+"."+(tservice->get_name())+".client.php";
1311
    string f_struct_name = package_dir_+f_struct;
1312
    autoload_out.open(f_struct_name.c_str());
1313
    autoload_out << "<?php" << endl;
1314
    _generate_service_client(autoload_out, tservice);
1315
    autoload_out << endl << "?>" << endl;
1316
    autoload_out.close();
1317
 
1318
    f_service_ <<
1319
      "$GLOBALS['THRIFT_AUTOLOAD']['" << lowercase(service_name_ + "Client") << "'] = '" << program_name_ << "/" << f_struct << "';" << endl;
1320
 
1321
  } else {
1322
    _generate_service_client(f_service_, tservice);
1323
  }
1324
}
1325
 
1326
/**
1327
 * Generates a service client definition.
1328
 *
1329
 * @param tservice The service to generate a server for.
1330
 */
1331
void t_php_generator::_generate_service_client(ofstream& out, t_service* tservice) {
1332
  string extends = "";
1333
  string extends_client = "";
1334
  if (tservice->get_extends() != NULL) {
1335
    extends = tservice->get_extends()->get_name();
1336
    extends_client = " extends " + extends + "Client";
1337
  }
1338
 
1339
  out <<
1340
    "class " << service_name_ << "Client" << extends_client << " implements " << service_name_ << "If {" << endl;
1341
  indent_up();
1342
 
1343
  // Private members
1344
  if (extends.empty()) {
1345
    out <<
1346
      indent() << "protected $input_ = null;" << endl <<
1347
      indent() << "protected $output_ = null;" << endl <<
1348
      endl;
1349
    out <<
1350
      indent() << "protected $seqid_ = 0;" << endl <<
1351
      endl;
1352
  }
1353
 
1354
  // Constructor function
1355
  out <<
1356
    indent() << "public function __construct($input, $output=null) {" << endl;
1357
  if (!extends.empty()) {
1358
    out <<
1359
      indent() << "  parent::__construct($input, $output);" << endl;
1360
  } else {
1361
    out <<
1362
      indent() << "  $this->input_ = $input;" << endl <<
1363
      indent() << "  $this->output_ = $output ? $output : $input;" << endl;
1364
  }
1365
  out <<
1366
    indent() << "}" << endl << endl;
1367
 
1368
  // Generate client method implementations
1369
  vector<t_function*> functions = tservice->get_functions();
1370
  vector<t_function*>::const_iterator f_iter;
1371
  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1372
    t_struct* arg_struct = (*f_iter)->get_arglist();
1373
    const vector<t_field*>& fields = arg_struct->get_members();
1374
    vector<t_field*>::const_iterator fld_iter;
1375
    string funname = (*f_iter)->get_name();
1376
 
1377
    // Open function
1378
    indent(out) <<
1379
      "public function " << function_signature(*f_iter) << endl;
1380
    scope_up(out);
1381
      indent(out) <<
1382
        "$this->send_" << funname << "(";
1383
 
1384
      bool first = true;
1385
      for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
1386
        if (first) {
1387
          first = false;
1388
        } else {
1389
          out << ", ";
1390
        }
1391
        out << "$" << (*fld_iter)->get_name();
1392
      }
1393
      out << ");" << endl;
1394
 
1395
      if (!(*f_iter)->is_oneway()) {
1396
        out << indent();
1397
        if (!(*f_iter)->get_returntype()->is_void()) {
1398
          out << "return ";
1399
        }
1400
        out <<
1401
          "$this->recv_" << funname << "();" << endl;
1402
      }
1403
    scope_down(out);
1404
    out << endl;
1405
 
1406
    indent(out) <<
1407
      "public function send_" << function_signature(*f_iter) << endl;
1408
    scope_up(out);
1409
 
1410
      std::string argsname = php_namespace(tservice->get_program()) + service_name_ + "_" + (*f_iter)->get_name() + "_args";
1411
 
1412
      out <<
1413
        indent() << "$args = new " << argsname << "();" << endl;
1414
 
1415
      for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
1416
        out <<
1417
          indent() << "$args->" << (*fld_iter)->get_name() << " = $" << (*fld_iter)->get_name() << ";" << endl;
1418
      }
1419
 
1420
      out <<
1421
        indent() << "$bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary');" << endl;
1422
 
1423
      out <<
1424
        indent() << "if ($bin_accel)" << endl;
1425
      scope_up(out);
1426
 
1427
      out <<
1428
        indent() << "thrift_protocol_write_binary($this->output_, '" << (*f_iter)->get_name() << "', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite());" << endl;
1429
 
1430
      scope_down(out);
1431
      out <<
1432
        indent() << "else" << endl;
1433
      scope_up(out);
1434
 
1435
      // Serialize the request header
1436
      if (binary_inline_) {
1437
        out <<
1438
          indent() << "$buff = pack('N', (0x80010000 | TMessageType::CALL));" << endl <<
1439
          indent() << "$buff .= pack('N', strlen('" << funname << "'));" << endl <<
1440
          indent() << "$buff .= '" << funname << "';" << endl <<
1441
          indent() << "$buff .= pack('N', $this->seqid_);" << endl;
1442
      } else {
1443
        out <<
1444
          indent() << "$this->output_->writeMessageBegin('" << (*f_iter)->get_name() << "', TMessageType::CALL, $this->seqid_);" << endl;
1445
      }
1446
 
1447
      // Write to the stream
1448
      if (binary_inline_) {
1449
        out <<
1450
          indent() << "$args->write($buff);" << endl <<
1451
          indent() << "$this->output_->write($buff);" << endl <<
1452
          indent() << "$this->output_->flush();" << endl;
1453
      } else {
1454
        out <<
1455
          indent() << "$args->write($this->output_);" << endl <<
1456
          indent() << "$this->output_->writeMessageEnd();" << endl <<
1457
          indent() << "$this->output_->getTransport()->flush();" << endl;
1458
      }
1459
 
1460
    scope_down(out);
1461
 
1462
    scope_down(out);
1463
 
1464
 
1465
    if (!(*f_iter)->is_oneway()) {
1466
      std::string resultname = php_namespace(tservice->get_program()) + service_name_ + "_" + (*f_iter)->get_name() + "_result";
1467
      t_struct noargs(program_);
1468
 
1469
      t_function recv_function((*f_iter)->get_returntype(),
1470
                               string("recv_") + (*f_iter)->get_name(),
1471
                               &noargs);
1472
      // Open function
1473
      out <<
1474
        endl <<
1475
        indent() << "public function " << function_signature(&recv_function) << endl;
1476
      scope_up(out);
1477
 
1478
      out <<
1479
        indent() << "$bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED)"
1480
                 << " && function_exists('thrift_protocol_read_binary');" << endl;
1481
 
1482
      out <<
1483
        indent() << "if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, '" << resultname << "', $this->input_->isStrictRead());" << endl;
1484
      out <<
1485
        indent() << "else" << endl;
1486
      scope_up(out);
1487
 
1488
      out <<
1489
        indent() << "$rseqid = 0;" << endl <<
1490
        indent() << "$fname = null;" << endl <<
1491
        indent() << "$mtype = 0;" << endl <<
1492
        endl;
1493
 
1494
      if (binary_inline_) {
1495
        t_field ffname(g_type_string, "fname");
1496
        t_field fseqid(g_type_i32, "rseqid");
1497
        out <<
1498
          indent() << "$ver = unpack('N', $this->input_->readAll(4));" << endl <<
1499
          indent() << "$ver = $ver[1];" << endl <<
1500
          indent() << "$mtype = $ver & 0xff;" << endl <<
1501
          indent() << "$ver = $ver & 0xffff0000;" << endl <<
1502
          indent() << "if ($ver != 0x80010000) throw new TProtocolException('Bad version identifier: '.$ver, TProtocolException::BAD_VERSION);" << endl;
1503
        generate_deserialize_field(out, &ffname, "", true);
1504
        generate_deserialize_field(out, &fseqid, "", true);
1505
      } else {
1506
        out <<
1507
          indent() << "$this->input_->readMessageBegin($fname, $mtype, $rseqid);" << endl <<
1508
          indent() << "if ($mtype == TMessageType::EXCEPTION) {" << endl <<
1509
          indent() << "  $x = new TApplicationException();" << endl <<
1510
          indent() << "  $x->read($this->input_);" << endl <<
1511
          indent() << "  $this->input_->readMessageEnd();" << endl <<
1512
          indent() << "  throw $x;" << endl <<
1513
          indent() << "}" << endl;
1514
      }
1515
 
1516
      out <<
1517
        indent() << "$result = new " << resultname << "();" << endl <<
1518
        indent() << "$result->read($this->input_);" << endl;
1519
 
1520
      if (!binary_inline_) {
1521
        out <<
1522
          indent() << "$this->input_->readMessageEnd();" << endl;
1523
      }
1524
 
1525
      scope_down(out);
1526
 
1527
      // Careful, only return result if not a void function
1528
      if (!(*f_iter)->get_returntype()->is_void()) {
1529
        out <<
1530
          indent() << "if ($result->success !== null) {" << endl <<
1531
          indent() << "  return $result->success;" << endl <<
1532
          indent() << "}" << endl;
1533
      }
1534
 
1535
      t_struct* xs = (*f_iter)->get_xceptions();
1536
      const std::vector<t_field*>& xceptions = xs->get_members();
1537
      vector<t_field*>::const_iterator x_iter;
1538
      for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
1539
        out <<
1540
          indent() << "if ($result->" << (*x_iter)->get_name() << " !== null) {" << endl <<
1541
          indent() << "  throw $result->" << (*x_iter)->get_name() << ";" << endl <<
1542
          indent() << "}" << endl;
1543
      }
1544
 
1545
      // Careful, only return _result if not a void function
1546
      if ((*f_iter)->get_returntype()->is_void()) {
1547
        indent(out) <<
1548
          "return;" << endl;
1549
      } else {
1550
        out <<
1551
          indent() << "throw new Exception(\"" << (*f_iter)->get_name() << " failed: unknown result\");" << endl;
1552
      }
1553
 
1554
    // Close function
1555
    scope_down(out);
1556
    out << endl;
1557
 
1558
    }
1559
  }
1560
 
1561
  indent_down();
1562
  out <<
1563
    "}" << endl << endl;
1564
}
1565
 
1566
/**
1567
 * Deserializes a field of any type.
1568
 */
1569
void t_php_generator::generate_deserialize_field(ofstream &out,
1570
                                                 t_field* tfield,
1571
                                                 string prefix,
1572
                                                 bool inclass) {
1573
  t_type* type = get_true_type(tfield->get_type());
1574
 
1575
  if (type->is_void()) {
1576
    throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " +
1577
      prefix + tfield->get_name();
1578
  }
1579
 
1580
  string name = prefix + tfield->get_name();
1581
 
1582
  if (type->is_struct() || type->is_xception()) {
1583
    generate_deserialize_struct(out,
1584
                                (t_struct*)type,
1585
                                 name);
1586
  } else {
1587
 
1588
    if (type->is_container()) {
1589
      generate_deserialize_container(out, type, name);
1590
    } else if (type->is_base_type() || type->is_enum()) {
1591
 
1592
      if (binary_inline_) {
1593
        std::string itrans = (inclass ? "$this->input_" : "$input");
1594
 
1595
        if (type->is_base_type()) {
1596
          t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
1597
          switch (tbase) {
1598
          case t_base_type::TYPE_VOID:
1599
            throw "compiler error: cannot serialize void field in a struct: " +
1600
              name;
1601
            break;
1602
          case t_base_type::TYPE_STRING:
1603
            out <<
1604
              indent() << "$len = unpack('N', " << itrans << "->readAll(4));" << endl <<
1605
              indent() << "$len = $len[1];" << endl <<
1606
              indent() << "if ($len > 0x7fffffff) {" << endl <<
1607
              indent() << "  $len = 0 - (($len - 1) ^ 0xffffffff);" << endl <<
1608
              indent() << "}" << endl <<
1609
              indent() << "$" << name << " = " << itrans << "->readAll($len);" << endl;
1610
            break;
1611
          case t_base_type::TYPE_BOOL:
1612
            out <<
1613
              indent() << "$" << name << " = unpack('c', " << itrans << "->readAll(1));" << endl <<
1614
              indent() << "$" << name << " = (bool)$" << name << "[1];" << endl;
1615
            break;
1616
          case t_base_type::TYPE_BYTE:
1617
            out <<
1618
              indent() << "$" << name << " = unpack('c', " << itrans << "->readAll(1));" << endl <<
1619
              indent() << "$" << name << " = $" << name << "[1];" << endl;
1620
            break;
1621
          case t_base_type::TYPE_I16:
1622
            out <<
1623
              indent() << "$val = unpack('n', " << itrans << "->readAll(2));" << endl <<
1624
              indent() << "$val = $val[1];" << endl <<
1625
              indent() << "if ($val > 0x7fff) {" << endl <<
1626
              indent() << "  $val = 0 - (($val - 1) ^ 0xffff);" << endl <<
1627
              indent() << "}" << endl <<
1628
              indent() << "$" << name << " = $val;" << endl;
1629
            break;
1630
          case t_base_type::TYPE_I32:
1631
            out <<
1632
              indent() << "$val = unpack('N', " << itrans << "->readAll(4));" << endl <<
1633
              indent() << "$val = $val[1];" << endl <<
1634
              indent() << "if ($val > 0x7fffffff) {" << endl <<
1635
              indent() << "  $val = 0 - (($val - 1) ^ 0xffffffff);" << endl <<
1636
              indent() << "}" << endl <<
1637
              indent() << "$" << name << " = $val;" << endl;
1638
            break;
1639
          case t_base_type::TYPE_I64:
1640
            out <<
1641
              indent() << "$arr = unpack('N2', " << itrans << "->readAll(8));" << endl <<
1642
              indent() << "if ($arr[1] & 0x80000000) {" << endl <<
1643
              indent() << "  $arr[1] = $arr[1] ^ 0xFFFFFFFF;" << endl <<
1644
              indent() << "  $arr[2] = $arr[2] ^ 0xFFFFFFFF;" << endl <<
1645
              indent() << "  $" << name << " = 0 - $arr[1]*4294967296 - $arr[2] - 1;" << endl <<
1646
              indent() << "} else {" << endl <<
1647
              indent() << "  $" << name << " = $arr[1]*4294967296 + $arr[2];" << endl <<
1648
              indent() << "}" << endl;
1649
            break;
1650
          case t_base_type::TYPE_DOUBLE:
1651
            out <<
1652
              indent() << "$arr = unpack('d', strrev(" << itrans << "->readAll(8)));" << endl <<
1653
              indent() << "$" << name << " = $arr[1];" << endl;
1654
            break;
1655
          default:
1656
            throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase) + tfield->get_name();
1657
          }
1658
        } else if (type->is_enum()) {
1659
            out <<
1660
              indent() << "$val = unpack('N', " << itrans << "->readAll(4));" << endl <<
1661
              indent() << "$val = $val[1];" << endl <<
1662
              indent() << "if ($val > 0x7fffffff) {" << endl <<
1663
              indent() << "  $val = 0 - (($val - 1) ^ 0xffffffff);" << endl <<
1664
              indent() << "}" << endl <<
1665
              indent() << "$" << name << " = $val;" << endl;
1666
        }
1667
      } else {
1668
 
1669
        indent(out) <<
1670
          "$xfer += $input->";
1671
 
1672
        if (type->is_base_type()) {
1673
          t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
1674
          switch (tbase) {
1675
          case t_base_type::TYPE_VOID:
1676
            throw "compiler error: cannot serialize void field in a struct: " +
1677
              name;
1678
            break;
1679
          case t_base_type::TYPE_STRING:
1680
            out << "readString($" << name << ");";
1681
            break;
1682
          case t_base_type::TYPE_BOOL:
1683
            out << "readBool($" << name << ");";
1684
            break;
1685
          case t_base_type::TYPE_BYTE:
1686
            out << "readByte($" << name << ");";
1687
            break;
1688
          case t_base_type::TYPE_I16:
1689
            out << "readI16($" << name << ");";
1690
            break;
1691
          case t_base_type::TYPE_I32:
1692
            out << "readI32($" << name << ");";
1693
            break;
1694
          case t_base_type::TYPE_I64:
1695
            out << "readI64($" << name << ");";
1696
            break;
1697
          case t_base_type::TYPE_DOUBLE:
1698
            out << "readDouble($" << name << ");";
1699
            break;
1700
          default:
1701
            throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase);
1702
          }
1703
        } else if (type->is_enum()) {
1704
          out << "readI32($" << name << ");";
1705
        }
1706
        out << endl;
1707
      }
1708
    } else {
1709
      printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
1710
             tfield->get_name().c_str(), type->get_name().c_str());
1711
    }
1712
  }
1713
}
1714
 
1715
/**
1716
 * Generates an unserializer for a variable. This makes two key assumptions,
1717
 * first that there is a const char* variable named data that points to the
1718
 * buffer for deserialization, and that there is a variable protocol which
1719
 * is a reference to a TProtocol serialization object.
1720
 */
1721
void t_php_generator::generate_deserialize_struct(ofstream &out,
1722
                                                  t_struct* tstruct,
1723
                                                  string prefix) {
1724
  out <<
1725
    indent() << "$" << prefix << " = new " << php_namespace(tstruct->get_program()) << tstruct->get_name() << "();" << endl <<
1726
    indent() << "$xfer += $" << prefix << "->read($input);" << endl;
1727
}
1728
 
1729
void t_php_generator::generate_deserialize_container(ofstream &out,
1730
                                                     t_type* ttype,
1731
                                                     string prefix) {
1732
  string size = tmp("_size");
1733
  string ktype = tmp("_ktype");
1734
  string vtype = tmp("_vtype");
1735
  string etype = tmp("_etype");
1736
 
1737
  t_field fsize(g_type_i32, size);
1738
  t_field fktype(g_type_byte, ktype);
1739
  t_field fvtype(g_type_byte, vtype);
1740
  t_field fetype(g_type_byte, etype);
1741
 
1742
  out <<
1743
    indent() << "$" << prefix << " = array();" << endl <<
1744
    indent() << "$" << size << " = 0;" << endl;
1745
 
1746
  // Declare variables, read header
1747
  if (ttype->is_map()) {
1748
    out <<
1749
      indent() << "$" << ktype << " = 0;" << endl <<
1750
      indent() << "$" << vtype << " = 0;" << endl;
1751
    if (binary_inline_) {
1752
      generate_deserialize_field(out, &fktype);
1753
      generate_deserialize_field(out, &fvtype);
1754
      generate_deserialize_field(out, &fsize);
1755
    } else {
1756
      out <<
1757
        indent() << "$xfer += $input->readMapBegin(" <<
1758
        "$" << ktype << ", $" << vtype << ", $" << size << ");" << endl;
1759
    }
1760
  } else if (ttype->is_set()) {
1761
    if (binary_inline_) {
1762
      generate_deserialize_field(out, &fetype);
1763
      generate_deserialize_field(out, &fsize);
1764
    } else {
1765
      out <<
1766
        indent() << "$" << etype << " = 0;" << endl <<
1767
        indent() << "$xfer += $input->readSetBegin(" <<
1768
        "$" << etype << ", $" << size << ");" << endl;
1769
    }
1770
  } else if (ttype->is_list()) {
1771
    if (binary_inline_) {
1772
      generate_deserialize_field(out, &fetype);
1773
      generate_deserialize_field(out, &fsize);
1774
    } else {
1775
      out <<
1776
        indent() << "$" << etype << " = 0;" << endl <<
1777
        indent() << "$xfer += $input->readListBegin(" <<
1778
        "$" << etype << ", $" << size << ");" << endl;
1779
    }
1780
  }
1781
 
1782
  // For loop iterates over elements
1783
  string i = tmp("_i");
1784
  indent(out) <<
1785
    "for ($" <<
1786
    i << " = 0; $" << i << " < $" << size << "; ++$" << i << ")" << endl;
1787
 
1788
    scope_up(out);
1789
 
1790
    if (ttype->is_map()) {
1791
      generate_deserialize_map_element(out, (t_map*)ttype, prefix);
1792
    } else if (ttype->is_set()) {
1793
      generate_deserialize_set_element(out, (t_set*)ttype, prefix);
1794
    } else if (ttype->is_list()) {
1795
      generate_deserialize_list_element(out, (t_list*)ttype, prefix);
1796
    }
1797
 
1798
    scope_down(out);
1799
 
1800
  if (!binary_inline_) {
1801
    // Read container end
1802
    if (ttype->is_map()) {
1803
      indent(out) << "$xfer += $input->readMapEnd();" << endl;
1804
    } else if (ttype->is_set()) {
1805
      indent(out) << "$xfer += $input->readSetEnd();" << endl;
1806
    } else if (ttype->is_list()) {
1807
      indent(out) << "$xfer += $input->readListEnd();" << endl;
1808
    }
1809
  }
1810
}
1811
 
1812
 
1813
/**
1814
 * Generates code to deserialize a map
1815
 */
1816
void t_php_generator::generate_deserialize_map_element(ofstream &out,
1817
                                                       t_map* tmap,
1818
                                                       string prefix) {
1819
  string key = tmp("key");
1820
  string val = tmp("val");
1821
  t_field fkey(tmap->get_key_type(), key);
1822
  t_field fval(tmap->get_val_type(), val);
1823
 
1824
  indent(out) <<
1825
    declare_field(&fkey, true, true) << endl;
1826
  indent(out) <<
1827
    declare_field(&fval, true, true) << endl;
1828
 
1829
  generate_deserialize_field(out, &fkey);
1830
  generate_deserialize_field(out, &fval);
1831
 
1832
  indent(out) <<
1833
    "$" << prefix << "[$" << key << "] = $" << val << ";" << endl;
1834
}
1835
 
1836
void t_php_generator::generate_deserialize_set_element(ofstream &out,
1837
                                                       t_set* tset,
1838
                                                       string prefix) {
1839
  string elem = tmp("elem");
1840
  t_field felem(tset->get_elem_type(), elem);
1841
 
1842
  indent(out) <<
1843
    "$" << elem << " = null;" << endl;
1844
 
1845
  generate_deserialize_field(out, &felem);
1846
 
1847
  indent(out) <<
1848
    "$" << prefix << "[$" << elem << "] = true;" << endl;
1849
}
1850
 
1851
void t_php_generator::generate_deserialize_list_element(ofstream &out,
1852
                                                        t_list* tlist,
1853
                                                        string prefix) {
1854
  string elem = tmp("elem");
1855
  t_field felem(tlist->get_elem_type(), elem);
1856
 
1857
  indent(out) <<
1858
    "$" << elem << " = null;" << endl;
1859
 
1860
  generate_deserialize_field(out, &felem);
1861
 
1862
  indent(out) <<
1863
    "$" << prefix << " []= $" << elem << ";" << endl;
1864
}
1865
 
1866
 
1867
/**
1868
 * Serializes a field of any type.
1869
 *
1870
 * @param tfield The field to serialize
1871
 * @param prefix Name to prepend to field name
1872
 */
1873
void t_php_generator::generate_serialize_field(ofstream &out,
1874
                                               t_field* tfield,
1875
                                               string prefix) {
1876
  t_type* type = get_true_type(tfield->get_type());
1877
 
1878
  // Do nothing for void types
1879
  if (type->is_void()) {
1880
    throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " +
1881
      prefix + tfield->get_name();
1882
  }
1883
 
1884
  if (type->is_struct() || type->is_xception()) {
1885
    generate_serialize_struct(out,
1886
                              (t_struct*)type,
1887
                              prefix + tfield->get_name());
1888
  } else if (type->is_container()) {
1889
    generate_serialize_container(out,
1890
                                 type,
1891
                                 prefix + tfield->get_name());
1892
  } else if (type->is_base_type() || type->is_enum()) {
1893
 
1894
    string name = prefix + tfield->get_name();
1895
 
1896
    if (binary_inline_) {
1897
      if (type->is_base_type()) {
1898
        t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
1899
        switch (tbase) {
1900
        case t_base_type::TYPE_VOID:
1901
          throw
1902
            "compiler error: cannot serialize void field in a struct: " + name;
1903
          break;
1904
        case t_base_type::TYPE_STRING:
1905
          out <<
1906
            indent() << "$output .= pack('N', strlen($" << name << "));" << endl <<
1907
            indent() << "$output .= $" << name << ";" << endl;
1908
          break;
1909
        case t_base_type::TYPE_BOOL:
1910
          out <<
1911
            indent() << "$output .= pack('c', $" << name << " ? 1 : 0);" << endl;
1912
          break;
1913
        case t_base_type::TYPE_BYTE:
1914
          out <<
1915
            indent() << "$output .= pack('c', $" << name << ");" << endl;
1916
          break;
1917
        case t_base_type::TYPE_I16:
1918
          out <<
1919
            indent() << "$output .= pack('n', $" << name << ");" << endl;
1920
          break;
1921
        case t_base_type::TYPE_I32:
1922
          out <<
1923
            indent() << "$output .= pack('N', $" << name << ");" << endl;
1924
          break;
1925
        case t_base_type::TYPE_I64:
1926
          out <<
1927
            indent() << "$output .= pack('N2', $" << name << " >> 32, $" << name << " & 0xFFFFFFFF);" << endl;
1928
          break;
1929
        case t_base_type::TYPE_DOUBLE:
1930
          out <<
1931
            indent() << "$output .= strrev(pack('d', $" << name << "));" << endl;
1932
          break;
1933
        default:
1934
          throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase);
1935
        }
1936
      } else if (type->is_enum()) {
1937
        out <<
1938
          indent() << "$output .= pack('N', $" << name << ");" << endl;
1939
      }
1940
    } else {
1941
 
1942
      indent(out) <<
1943
        "$xfer += $output->";
1944
 
1945
      if (type->is_base_type()) {
1946
        t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
1947
        switch (tbase) {
1948
        case t_base_type::TYPE_VOID:
1949
          throw
1950
            "compiler error: cannot serialize void field in a struct: " + name;
1951
          break;
1952
        case t_base_type::TYPE_STRING:
1953
          out << "writeString($" << name << ");";
1954
          break;
1955
        case t_base_type::TYPE_BOOL:
1956
          out << "writeBool($" << name << ");";
1957
          break;
1958
        case t_base_type::TYPE_BYTE:
1959
          out << "writeByte($" << name << ");";
1960
          break;
1961
        case t_base_type::TYPE_I16:
1962
          out << "writeI16($" << name << ");";
1963
          break;
1964
        case t_base_type::TYPE_I32:
1965
          out << "writeI32($" << name << ");";
1966
          break;
1967
        case t_base_type::TYPE_I64:
1968
          out << "writeI64($" << name << ");";
1969
          break;
1970
        case t_base_type::TYPE_DOUBLE:
1971
          out << "writeDouble($" << name << ");";
1972
          break;
1973
        default:
1974
          throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase);
1975
        }
1976
      } else if (type->is_enum()) {
1977
        out << "writeI32($" << name << ");";
1978
      }
1979
      out << endl;
1980
    }
1981
  } else {
1982
    printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n",
1983
           prefix.c_str(),
1984
           tfield->get_name().c_str(),
1985
           type->get_name().c_str());
1986
  }
1987
}
1988
 
1989
/**
1990
 * Serializes all the members of a struct.
1991
 *
1992
 * @param tstruct The struct to serialize
1993
 * @param prefix  String prefix to attach to all fields
1994
 */
1995
void t_php_generator::generate_serialize_struct(ofstream &out,
1996
                                                t_struct* tstruct,
1997
                                                string prefix) {
1998
  indent(out) <<
1999
    "$xfer += $" << prefix << "->write($output);" << endl;
2000
}
2001
 
2002
/**
2003
 * Writes out a container
2004
 */
2005
void t_php_generator::generate_serialize_container(ofstream &out,
2006
                                                   t_type* ttype,
2007
                                                   string prefix) {
2008
  scope_up(out);
2009
 
2010
  if (ttype->is_map()) {
2011
    if (binary_inline_) {
2012
      out <<
2013
        indent() << "$output .= pack('c', " << type_to_enum(((t_map*)ttype)->get_key_type()) << ");" << endl <<
2014
        indent() << "$output .= pack('c', " << type_to_enum(((t_map*)ttype)->get_val_type()) << ");" << endl <<
2015
        indent() << "$output .= strrev(pack('l', count($" << prefix << ")));" << endl;
2016
    } else {
2017
      indent(out) <<
2018
        "$output->writeMapBegin(" <<
2019
        type_to_enum(((t_map*)ttype)->get_key_type()) << ", " <<
2020
        type_to_enum(((t_map*)ttype)->get_val_type()) << ", " <<
2021
        "count($" << prefix << "));" << endl;
2022
    }
2023
  } else if (ttype->is_set()) {
2024
    if (binary_inline_) {
2025
      out <<
2026
        indent() << "$output .= pack('c', " << type_to_enum(((t_set*)ttype)->get_elem_type()) << ");" << endl <<
2027
        indent() << "$output .= strrev(pack('l', count($" << prefix << ")));" << endl;
2028
 
2029
    } else {
2030
      indent(out) <<
2031
        "$output->writeSetBegin(" <<
2032
        type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " <<
2033
        "count($" << prefix << "));" << endl;
2034
    }
2035
  } else if (ttype->is_list()) {
2036
    if (binary_inline_) {
2037
      out <<
2038
        indent() << "$output .= pack('c', " << type_to_enum(((t_list*)ttype)->get_elem_type()) << ");" << endl <<
2039
        indent() << "$output .= strrev(pack('l', count($" << prefix << ")));" << endl;
2040
 
2041
    } else {
2042
      indent(out) <<
2043
        "$output->writeListBegin(" <<
2044
        type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " <<
2045
        "count($" << prefix << "));" << endl;
2046
    }
2047
  }
2048
 
2049
  scope_up(out);
2050
 
2051
  if (ttype->is_map()) {
2052
    string kiter = tmp("kiter");
2053
    string viter = tmp("viter");
2054
    indent(out) <<
2055
      "foreach ($" << prefix << " as " <<
2056
      "$" << kiter << " => $" << viter << ")" << endl;
2057
    scope_up(out);
2058
    generate_serialize_map_element(out, (t_map*)ttype, kiter, viter);
2059
    scope_down(out);
2060
  } else if (ttype->is_set()) {
2061
    string iter = tmp("iter");
2062
    indent(out) <<
2063
      "foreach ($" << prefix << " as $" << iter << " => $true)" << endl;
2064
    scope_up(out);
2065
    generate_serialize_set_element(out, (t_set*)ttype, iter);
2066
    scope_down(out);
2067
  } else if (ttype->is_list()) {
2068
    string iter = tmp("iter");
2069
    indent(out) <<
2070
      "foreach ($" << prefix << " as $" << iter << ")" << endl;
2071
    scope_up(out);
2072
    generate_serialize_list_element(out, (t_list*)ttype, iter);
2073
    scope_down(out);
2074
  }
2075
 
2076
  scope_down(out);
2077
 
2078
  if (!binary_inline_) {
2079
    if (ttype->is_map()) {
2080
      indent(out) <<
2081
        "$output->writeMapEnd();" << endl;
2082
    } else if (ttype->is_set()) {
2083
      indent(out) <<
2084
        "$output->writeSetEnd();" << endl;
2085
    } else if (ttype->is_list()) {
2086
      indent(out) <<
2087
        "$output->writeListEnd();" << endl;
2088
    }
2089
  }
2090
 
2091
  scope_down(out);
2092
}
2093
 
2094
/**
2095
 * Serializes the members of a map.
2096
 *
2097
 */
2098
void t_php_generator::generate_serialize_map_element(ofstream &out,
2099
                                                     t_map* tmap,
2100
                                                     string kiter,
2101
                                                     string viter) {
2102
  t_field kfield(tmap->get_key_type(), kiter);
2103
  generate_serialize_field(out, &kfield, "");
2104
 
2105
  t_field vfield(tmap->get_val_type(), viter);
2106
  generate_serialize_field(out, &vfield, "");
2107
}
2108
 
2109
/**
2110
 * Serializes the members of a set.
2111
 */
2112
void t_php_generator::generate_serialize_set_element(ofstream &out,
2113
                                                     t_set* tset,
2114
                                                     string iter) {
2115
  t_field efield(tset->get_elem_type(), iter);
2116
  generate_serialize_field(out, &efield, "");
2117
}
2118
 
2119
/**
2120
 * Serializes the members of a list.
2121
 */
2122
void t_php_generator::generate_serialize_list_element(ofstream &out,
2123
                                                      t_list* tlist,
2124
                                                      string iter) {
2125
  t_field efield(tlist->get_elem_type(), iter);
2126
  generate_serialize_field(out, &efield, "");
2127
}
2128
 
2129
/**
2130
 * Declares a field, which may include initialization as necessary.
2131
 *
2132
 * @param ttype The type
2133
 */
2134
string t_php_generator::declare_field(t_field* tfield, bool init, bool obj) {
2135
  string result = "$" + tfield->get_name();
2136
  if (init) {
2137
    t_type* type = get_true_type(tfield->get_type());
2138
    if (type->is_base_type()) {
2139
      t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2140
      switch (tbase) {
2141
      case t_base_type::TYPE_VOID:
2142
        break;
2143
      case t_base_type::TYPE_STRING:
2144
        result += " = ''";
2145
        break;
2146
      case t_base_type::TYPE_BOOL:
2147
        result += " = false";
2148
        break;
2149
      case t_base_type::TYPE_BYTE:
2150
      case t_base_type::TYPE_I16:
2151
      case t_base_type::TYPE_I32:
2152
      case t_base_type::TYPE_I64:
2153
        result += " = 0";
2154
        break;
2155
      case t_base_type::TYPE_DOUBLE:
2156
        result += " = 0.0";
2157
        break;
2158
      default:
2159
        throw "compiler error: no PHP initializer for base type " + t_base_type::t_base_name(tbase);
2160
      }
2161
    } else if (type->is_enum()) {
2162
      result += " = 0";
2163
    } else if (type->is_container()) {
2164
      result += " = array()";
2165
    } else if (type->is_struct() || type->is_xception()) {
2166
      if (obj) {
2167
        result += " = new " + php_namespace(type->get_program()) + type->get_name() + "()";
2168
      } else {
2169
        result += " = null";
2170
      }
2171
    }
2172
  }
2173
  return result + ";";
2174
}
2175
 
2176
/**
2177
 * Renders a function signature of the form 'type name(args)'
2178
 *
2179
 * @param tfunction Function definition
2180
 * @return String of rendered function definition
2181
 */
2182
string t_php_generator::function_signature(t_function* tfunction,
2183
                                           string prefix) {
2184
  return
2185
    prefix + tfunction->get_name() +
2186
    "(" + argument_list(tfunction->get_arglist()) + ")";
2187
}
2188
 
2189
/**
2190
 * Renders a field list
2191
 */
2192
string t_php_generator::argument_list(t_struct* tstruct) {
2193
  string result = "";
2194
 
2195
  const vector<t_field*>& fields = tstruct->get_members();
2196
  vector<t_field*>::const_iterator f_iter;
2197
  bool first = true;
2198
  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
2199
    if (first) {
2200
      first = false;
2201
    } else {
2202
      result += ", ";
2203
    }
2204
    result += "$" + (*f_iter)->get_name();
2205
  }
2206
  return result;
2207
}
2208
 
2209
/**
2210
 * Gets a typecast string for a particular type.
2211
 */
2212
string t_php_generator::type_to_cast(t_type* type) {
2213
  if (type->is_base_type()) {
2214
    t_base_type* btype = (t_base_type*)type;
2215
    switch (btype->get_base()) {
2216
    case t_base_type::TYPE_BOOL:
2217
      return "(bool)";
2218
    case t_base_type::TYPE_BYTE:
2219
    case t_base_type::TYPE_I16:
2220
    case t_base_type::TYPE_I32:
2221
    case t_base_type::TYPE_I64:
2222
      return "(int)";
2223
    case t_base_type::TYPE_DOUBLE:
2224
      return "(double)";
2225
    case t_base_type::TYPE_STRING:
2226
      return "(string)";
2227
    default:
2228
      return "";
2229
    }
2230
  } else if (type->is_enum()) {
2231
    return "(int)";
2232
  }
2233
  return "";
2234
}
2235
 
2236
/**
2237
 * Converts the parse type to a C++ enum string for the given type.
2238
 */
2239
string t_php_generator ::type_to_enum(t_type* type) {
2240
  type = get_true_type(type);
2241
 
2242
  if (type->is_base_type()) {
2243
    t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2244
    switch (tbase) {
2245
    case t_base_type::TYPE_VOID:
2246
      throw "NO T_VOID CONSTRUCT";
2247
    case t_base_type::TYPE_STRING:
2248
      return "TType::STRING";
2249
    case t_base_type::TYPE_BOOL:
2250
      return "TType::BOOL";
2251
    case t_base_type::TYPE_BYTE:
2252
      return "TType::BYTE";
2253
    case t_base_type::TYPE_I16:
2254
      return "TType::I16";
2255
    case t_base_type::TYPE_I32:
2256
      return "TType::I32";
2257
    case t_base_type::TYPE_I64:
2258
      return "TType::I64";
2259
    case t_base_type::TYPE_DOUBLE:
2260
      return "TType::DOUBLE";
2261
    }
2262
  } else if (type->is_enum()) {
2263
    return "TType::I32";
2264
  } else if (type->is_struct() || type->is_xception()) {
2265
    return "TType::STRUCT";
2266
  } else if (type->is_map()) {
2267
    return "TType::MAP";
2268
  } else if (type->is_set()) {
2269
    return "TType::SET";
2270
  } else if (type->is_list()) {
2271
    return "TType::LST";
2272
  }
2273
 
2274
  throw "INVALID TYPE IN type_to_enum: " + type->get_name();
2275
}
2276
 
2277
THRIFT_REGISTER_GENERATOR(php, "PHP",
2278
"    inlined:         Generate PHP inlined files\n"
2279
"    server:          Generate PHP server stubs\n"
2280
"    autoload:        Generate PHP with autoload\n"
2281
"    oop:             Generate PHP with object oriented subclasses\n"
2282
"    rest:            Generate PHP REST processors\n"
2283
);