blob: 4ad990cac44e41917479513c0189f2777363394f [file] [log] [blame]
Marc Slemkoc6936402006-08-23 02:15:31 +00001""" Thrift IDL PHP client stub generator
2
3 This generated PHP class definitions and client stubs for a valid thrift IDL definition
4
5 Author(s): Mark Slee(mclee@facebook.com), Marc Kwiatkowski (marc@facebook.com)
6
7 $Id:
8"""
9import time
10
11import os
12import os.path
13from string import Template
14from thrift.parser import *
15from thrift.generator import *
16
17HEADER_COMMENT = """<?php
18/**
19 * Autogenerated by Thrift
20 * ${date}
21 *
22 * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
23 */
24 """
25
26PHP_TYPES_HEADER = Template(HEADER_COMMENT+"""
Marc Slemkod97eb612006-08-24 23:37:36 +000027require_once ${prefix}.'thrift/Thrift.php\';
Marc Slemkoc6936402006-08-23 02:15:31 +000028""")
29
30PHP_TYPES_FOOTER = Template("""
31?>
32""")
33
34PHP_SERVICES_HEADER = Template(HEADER_COMMENT+"""
35
Marc Slemkoc6936402006-08-23 02:15:31 +000036require_once dirname(__FILE__).\'/${source}_types.php\';
Marc Slemkod97eb612006-08-24 23:37:36 +000037require_once ${prefix}.'thrift/protocol/TType.php';
38require_once ${prefix}.'thrift/protocol/TProtocol.php';
39require_once ${prefix}.'thrift/transport/TTransport.php';
Marc Slemkoc6936402006-08-23 02:15:31 +000040""")
41
42PHP_SERVICES_FOOTER = Template("""
43?>
44""")
45
46PHP_IMPL_HEADER = Template(HEADER_COMMENT+"""
47
Marc Slemkod97eb612006-08-24 23:37:36 +000048require_once ${prefix}.'thrift/Thrift.php';
49require_once ${prefix}.dirname(__FILE__).\'/${source}_types.php\';
Marc Slemkoc6936402006-08-23 02:15:31 +000050
51""")
52
53PHP_IMPL_FOOTER = Template("""
54?>
55""")
56
57def php_debug(arg):
58 print(arg)
59
60class Indenter(object):
61 def __init__(self, margin=4):
62 self.margin = ''.join([' ' for ix in range(margin)])
63 self.level = 0
64
65 def __getitem__(self, level):
66 return ''.join([self.margin for ix in range(level)])
67
68 def __iadd__(self, value):
69 self.level = self.level + value
70 return self
71
72 def __isub__(self, value):
73 self.level = self.level - value
74 return self
75
76 def __call__(self):
77 return self.__getitem__(self.level)
78
79class Variable(object):
80 def __init__(self, ttype, name):
81 self.ttype = ttype
82 self.name = name
83
84 def toDeclaration(self, new=False):
85 if not new:
86 defaultValue = defaultValueForType(self.ttype)
87 else:
88 defaultValue = newInstanceForType(self.ttype)
89 return self.name+" = "+defaultValue
90
91class Scope(object):
92 def __init__(self, parent=None):
93 self.index = 0
94 self.vars = {}
95 self.parent = parent
96 self.childIndex = 0
97 if self.parent:
98 self.level = self.parent.level + 1
99 self.index = self.parent.childIndex
100 self.parent.childIndex+= 1
101 self.suffix = self.parent.suffix+str(self.index)
102 else:
103 self.level = 0
104 self.index = 0
105 self.suffix = ""
106
107 def declare(self, ttype, name):
108 if name in self.vars:
109 raise Exception, "Variable \""+name+"\" already declared in this scope."
110 result = Variable(ttype, "$_"+name+self.suffix)
111 self.vars[name] = result
112 return result
113
114 def get(self, var):
115 if var.name in self.vars:
116 return self.vars[var.name]
117 elif self.parent:
118 return self.parent.get(var)
119 else:
120 raise Exception, "Variable \""+var.name+"\" does not exist in any enclosing scope"
121
122PHP_TYPE_PROTOCOL_MAP = {
123 BOOL_TYPE : "Bool",
124 STRING_TYPE: "String",
125 UTF7_TYPE: "String",
126 UTF8_TYPE: "Wstring",
127 BYTE_TYPE : "Byte",
128 I08_TYPE: "Byte",
129 I16_TYPE: "I16",
130 I32_TYPE: "I32",
131 I64_TYPE: "I64",
132 I16_TYPE: "I16",
133 I32_TYPE: "I32",
134 I64_TYPE: "I64",
135 U08_TYPE: "Byte",
136 U16_TYPE: "U16",
137 U32_TYPE: "U32",
138 U64_TYPE: "U64",
139 U16_TYPE: "U16",
140 U32_TYPE: "U32",
141 U64_TYPE: "U64",
142 FLOAT_TYPE: "Float",
143 DOUBLE_TYPE: "Double"
144 }
145
146PHP_PRIMITIVE_TYPE_IO_METHOD_SUFFIX_MAP = {
147 "void" :"Void",
148 "bool" : "Bool",
149 "string": "String",
150 "utf7": "String",
151 "utf8": "String",
152 "utf16": "String",
153 "i08": "Byte",
154 "i16": "I16",
155 "i32": "I32",
156 "i64": "I64",
157 "u08": "Byte",
158 "u16": "U16",
159 "u32": "U32",
160 "u64": "U64",
161 "float": "Float",
162 "double": "Double"
163}
164
165class CodeGenerator(object):
166 def __init__(self,
167 scope=Scope(),
168 indent = Indenter()):
169 self.scope = scope
170 self.indent = indent
171
172 def newScope(self):
173 self.scope = Scope(self.scope)
174
175 def oldScope(self):
176 if not self.scope.parent:
177 raise Exception, "Unbalanced scope entry/exit"
178
179 self.scope = self.scope.parent
180
181 def declare(self, type, name, defaultValue=None):
182 tvar = self.scope.declare(type, name)
183 return tvar
184
185 def toDeclaration(self, tvar, new=False):
186 if not new:
187 defaultValue = defaultValueForType(tvar.ttype)
188 else:
189 defaultValue = newInstanceForType(tvar.ttype)
190
191 return self.indent()+tvar.name+" = "+defaultValue+";\n"
192
193class Reader(CodeGenerator):
194 def __init__(self,
195 iprot="$this->_iprot",
196 itrans="$this->_itrans",
197 scope=Scope(),
198 indent = Indenter()):
199 CodeGenerator.__init__(self, scope, indent)
200 self.iprot = iprot
201 self.itrans = itrans
202
Marc Slemkod97eb612006-08-24 23:37:36 +0000203 def toReadMessageBegin(self, messageNameVar, messageTypeVar, seqidVar):
204 return self.indent()+self.iprot+"->readMessageBegin("+self.itrans+", "+messageNameVar.name+", "+messageTypeVar.name+", "+seqidVar.name+");\n"
Marc Slemkoc6936402006-08-23 02:15:31 +0000205
206 def toReadMessageEnd(self):
207 return self.indent()+self.iprot+"->readMessageEnd("+self.itrans+");\n"
208
209 def toReadStructBegin(self, structNameVar):
210 return self.indent()+self.iprot+"->readStructBegin("+self.itrans+", "+structNameVar.name+");\n"
211
212 def toReadStructEnd(self):
213 return self.indent()+self.iprot+"->readStructEnd("+self.itrans+");\n"
214
215 def toReadMapBegin(self, keyTypeVar, valueTypeVar, sizeVar):
216 return self.indent()+self.iprot+"->readMapBegin("+self.itrans+", "+keyTypeVar.name+", "+valueTypeVar.name+", "+sizeVar.name+");\n"
217
218 def toReadMapEnd(self):
219 return self.indent()+self.iprot+"->readMapEnd("+self.itrans+");\n"
220
221 def toReadSetBegin(self, valueTypeVar, sizeVar):
222 return self.indent()+self.iprot+"->readSetBegin("+self.itrans+", "+valueTypeVar.name+", "+sizeVar.name+");\n"
223
224 def toReadSetEnd(self):
225 return self.indent()+self.iprot+"->readSetEnd("+self.itrans+");\n"
226
227 def toReadListBegin(self, valueTypeVar, sizeVar):
228 return self.indent()+self.iprot+"->readListBegin("+self.itrans+", "+valueTypeVar.name+", "+sizeVar.name+");\n"
229
230 def toReadListEnd(self):
231 return self.indent()+self.iprot+"->readListEnd("+self.itrans+");\n"
232
233 def toReadFieldBegin(self, fieldNameVar, fieldTypeVar, fieldIdVar):
234 return self.indent()+self.iprot+"->readFieldBegin("+self.itrans+", "+fieldNameVar.name+", "+fieldTypeVar.name+", "+fieldIdVar.name+");\n"
235
236 def toReadFieldEnd(self):
237 return self.indent()+self.iprot+"->readFieldEnd("+self.itrans+");\n"
238
239 def toSkipField(self, fieldTypeVar):
Marc Slemkod97eb612006-08-24 23:37:36 +0000240 return self.indent()+self.iprot+"->skip("+self.itrans+", "+fieldTypeVar.name+");\n"
Marc Slemkoc6936402006-08-23 02:15:31 +0000241
242 def toReadPrimitive(self, value, suffix):
Marc Slemkod97eb612006-08-24 23:37:36 +0000243 return self.indent()+self.iprot+"->read"+suffix+"("+self.itrans+", "+value+");\n"
Marc Slemkoc6936402006-08-23 02:15:31 +0000244
245 def toRead(self, value, ttype):
246
247 if isinstance(ttype, PrimitiveType):
248 return self.toReadPrimitive(value, PHP_TYPE_PROTOCOL_MAP[ttype])
249
250 elif isinstance(ttype, StructType):
251 self.newScope()
252 result = self.toReadStruct(value, ttype)
253 self.oldScope()
254 return result
255
256 elif isinstance(ttype, CollectionType):
257 self.newScope()
258 result = self.toReadCollection(value, ttype)
259 self.oldScope()
260 return result
261
262 elif isinstance(ttype, EnumType):
263 return self.toReadPrimitive(value, PHP_TYPE_PROTOCOL_MAP[I32_TYPE])
264
265 elif isinstance(ttype, TypedefType):
266 return self.toRead(value, ttype.definitionType)
267
268 else:
269 raise Exception, "Unsupported type "+str(type(ttype))+":"+str(ttype)
270
271 def toReadStruct(self, value, struct):
272
273 nameVar = self.declare(STRING_TYPE, "name")
274 fieldIdVar = self.declare(I16_TYPE, "fieldId")
275 fieldTypeVar = self.declare(U32_TYPE, "fieldType")
276 localVars = [nameVar, fieldTypeVar, fieldIdVar]
277
278 result= string.join([self.toDeclaration(localVar) for localVar in localVars], "")
279
280 result+= self.toReadStructBegin(nameVar)
281
282 result+= self.indent()+"while(true) {\n"
283
284 self.indent += 1
285
286 result+= self.toReadFieldBegin(nameVar, fieldTypeVar, fieldIdVar)
287
288 result += self.indent()+"if("+fieldTypeVar.name+" == "+PHP_PROTOCOL_TSTOP+") {\n"
289
290 self.indent+= 1
291
292 result+= self.indent()+"break;\n"
293
294 self.indent-= 1
295
296 result+= self.indent()+"}\n"
297
298 result+= self.indent()+"switch("+fieldIdVar.name+") {\n"
299
300 self.indent+= 1
301
302 # Sort field list in order of increasing ids
303
304 fieldList = []
305
306 fieldList+= struct.fieldList
307
308 fieldList.sort(lambda a,b: a.id - b.id)
309
310 for field in fieldList:
311 if isVoidType(field.type):
312 continue
313
314 result+= self.indent()+"case "+str(field.id)+":\n"
315
316 result+= self.toRead(value+"->"+field.name, field.type)
317 result+= self.indent()+"break;\n"
318
319 result+= self.indent()+"default:\n"
320 result+= self.toSkipField(fieldTypeVar)
321 result+= self.indent()+"break;\n"
322
323 self.indent-= 1
324
325 result+= self.indent()+"}\n"
326
327 self.indent-= 1
328
329 result+= self.indent()+"}\n"
330
331 result+= self.toReadStructEnd()
332
Marc Slemkoc6936402006-08-23 02:15:31 +0000333 return result
334
335 def toReadCollection(self, value, collection):
336
337 isMap = isinstance(collection, MapType)
338
339 sizeVar = self.declare(U32_TYPE, "size")
340 valueTypeVar = self.declare(U32_TYPE, "valueType")
341 elemVar = self.declare(collection.valueType, "elem")
342 localVars = [sizeVar, valueTypeVar, elemVar]
343
344 if isMap:
345 keyTypeVar = self.declare(U32_TYPE, "keyType")
346 key = self.declare(collection.keyType, "key")
347 localVars+= [keyTypeVar, key]
348
349 ix = self.declare(U32_TYPE, "ix")
350
351 result= string.join([self.toDeclaration(localVar) for localVar in localVars], "")
352
353 if isMap:
354 result+= self.toReadMapBegin(keyTypeVar, valueTypeVar, sizeVar)
355 elif isinstance(collection, ListType):
356 result+= self.toReadListBegin(valueTypeVar, sizeVar)
357 elif isinstance(collection, SetType):
358 result+= self.toReadSetBegin(valueTypeVar, sizeVar)
359 else:
360 raise Exception, "Unknown collection type "+str(collection)
361
362 result+= self.indent()+"for("+ix.name+" = 0; "+ix.name+" < "+sizeVar.name+"; ++"+ix.name+") {\n"
363
364 self.indent+= 1
365
366 if isMap:
367 result+= self.toRead(key.name, collection.keyType)
368
369 result+= self.toRead(elemVar.name, collection.valueType)
370
371 if isMap:
372 result+= self.indent()+value+"["+key.name+"] = "+elemVar.name+";\n"
373 else:
374 result+= self.indent()+value+"[] = "+elemVar.name+";\n"
375
376 self.indent-= 1
377
378 result+= self.indent()+"}\n"
379
380 if isMap:
381 result+= self.toReadMapEnd()
382 elif isinstance(collection, ListType):
383 result+= self.toReadListEnd()
384 elif isinstance(collection, SetType):
385 result+= self.toReadSetEnd()
386 else:
387 raise Exception, "Unknown collection type "+str(collection)
388
389 return result
390
391class Writer(CodeGenerator):
392 def __init__(self,
393 oprot="$this->_oprot",
394 otrans="$this->_otrans",
395 scope=Scope(),
396 indent=Indenter()):
397 CodeGenerator.__init__(self, scope, indent)
398 self.oprot = oprot
399 self.otrans = otrans
400
Marc Slemkod97eb612006-08-24 23:37:36 +0000401 def toWriteMessageBegin(self, messageName, type, seqid):
402 return self.indent()+self.oprot+"->writeMessageBegin("+self.otrans+", "+messageName+", "+type+", "+seqid+");\n"
Marc Slemkoc6936402006-08-23 02:15:31 +0000403
404 def toWriteMessageEnd(self):
405 return self.indent()+self.oprot+"->writeMessageEnd("+self.otrans+");\n"
406
407 def toWriteStructBegin(self, structName):
408 return self.indent()+self.oprot+"->writeStructBegin("+self.otrans+", "+structName+");\n"
409
410 def toWriteStructEnd(self):
411 return self.indent()+self.oprot+"->writeStructEnd("+self.otrans+");\n"
412
413 def toWriteMapBegin(self, keyType, valueType, size):
414 return self.indent()+self.oprot+"->writeMapBegin("+self.otrans+", "+keyType+", "+valueType+", "+size+");\n"
415
416 def toWriteMapEnd(self):
417 return self.indent()+self.oprot+"->writeMapEnd("+self.otrans+");\n"
418
419 def toWriteSetBegin(self, valueType, size):
420 return self.indent()+self.oprot+"->writeSetBegin("+self.otrans+", "+valueType+", "+size+");\n"
421
422 def toWriteSetEnd(self):
423 return self.indent()+self.oprot+"->writeSetEnd("+self.otrans+");\n"
424
425 def toWriteListBegin(self, valueType, size):
426 return self.indent()+self.oprot+"->writeListBegin("+self.otrans+", "+valueType+", "+size+");\n"
427
428 def toWriteListEnd(self):
429 return self.indent()+self.oprot+"->writeListEnd("+self.otrans+");\n"
430
431 def toWriteFieldBegin(self, fieldName, fieldType, fieldId):
432 return self.indent()+self.oprot+"->writeFieldBegin("+self.otrans+", "+fieldName+", "+fieldType+", "+str(fieldId)+");\n"
433
434 def toWriteFieldEnd(self):
435 return self.indent()+self.oprot+"->writeFieldEnd("+self.otrans+");\n"
436
437 def toWriteFieldStop(self):
438 return self.indent()+self.oprot+"->writeFieldStop("+self.otrans+");\n"
439
440 def toSkipField(self, fieldType):
441 return self.indent()+self.oprot+"->skipField("+self.otrans+", "+fieldType+");\n"
442
443 def toWriteFlush(self):
444 return self.indent()+self.otrans+"->flush();\n"
445
446 def toWritePrimitive(self, value, suffix):
447 return self.indent()+self.oprot+"->write"+suffix+"("+self.otrans+", "+value+");\n"
448
449 def toWrite(self, value, ttype):
450
451 if isinstance(ttype, PrimitiveType):
452 return self.toWritePrimitive(value, PHP_TYPE_PROTOCOL_MAP[ttype])
453
454 elif isinstance(ttype, StructType):
455 self.newScope()
456 result = self.toWriteStruct(value, ttype)
457 self.oldScope()
458 return result
459
460 elif isinstance(ttype, CollectionType):
461 self.newScope()
462 result = self.toWriteCollection(value, ttype)
463 self.oldScope()
464 return result
465
466 elif isinstance(ttype, EnumType):
467 return self.toWritePrimitive(value, PHP_TYPE_PROTOCOL_MAP[I32_TYPE])
468
469 elif isinstance(ttype, TypedefType):
470 return self.toWrite(value, ttype.definitionType)
471
472 else:
473 raise Exception, "Unsupported type "+str(type(ttype))+":"+str(ttype)
474
475 def toWriteStruct(self, value, struct):
476
477 result=self.indent()+"{\n"
478
479 self.indent+= 1
480
481 result+= self.toWriteStructBegin("\""+struct.name+"\"")
482
483 for field in struct.fieldList:
484 if isVoidType(field.type):
485 continue
486
487 result+= self.toWriteFieldBegin("\""+field.name+"\"", toWireType(field.type), field.id)
488 result+= self.toWrite(value+"->"+field.name, field.type)
489 result+= self.toWriteFieldEnd()
490
491 result+= self.toWriteFieldStop()
492
493 result+= self.toWriteStructEnd()
494
495 self.indent-= 1
496
497 result+= self.indent()+"}\n"
498
499 return result
500
501 def toWriteCollection(self, value, collection):
502
503 result=self.indent()+"{\n"
504
505 self.indent+= 1
506
507 size = "count("+value+")"
508
509 isMap = isinstance(collection, MapType)
510
511 elemVar = self.declare(VOID_TYPE, "elem")
512
513 if isMap:
514 keyVar = self.declare(VOID_TYPE, "key")
515 result+= self.toWriteMapBegin(toWireType(collection.keyType), toWireType(collection.valueType), size)
516 elif isinstance(collection, ListType):
517 result+= self.toWriteListBegin(toWireType(collection.valueType), size)
518 elif isinstance(collection, SetType):
519 result+= self.toWriteSetBegin(toWireType(collection.valueType), size)
520 else:
521 raise Exception, "Unknown collection type "+str(collection)
522
523 if isMap:
524
Marc Slemkod97eb612006-08-24 23:37:36 +0000525 result+= self.indent()+"foreach("+value+" as "+keyVar.name+" => "+elemVar.name+") {\n"
Marc Slemkoc6936402006-08-23 02:15:31 +0000526
527 else:
528
Marc Slemkod97eb612006-08-24 23:37:36 +0000529 result+= self.indent()+"foreach("+value+" as "+elemVar.name+") {\n"
Marc Slemkoc6936402006-08-23 02:15:31 +0000530
531 self.indent+= 1
532
533 if isMap:
534 result+= self.toWrite(keyVar.name, collection.keyType)
535
536 result+= self.toWrite(elemVar.name, collection.valueType)
537
538 self.indent-= 1
539
540 result+= self.indent()+"}\n"
541
542 if isMap:
543 result+= self.toWriteMapEnd()
544 elif isinstance(collection, ListType):
545 result+= self.toWriteListEnd()
546 elif isinstance(collection, SetType):
547 result+= self.toWriteSetEnd()
548 else:
549 raise Exception, "Unknown collection type "+str(collection)
550
551 self.indent-= 1
552
553 result+= self.indent()+"}\n"
554
555 return result
556
557class ClientFunctionGenerator(CodeGenerator):
558 def __init__(self,
559 scope=Scope(),
560 indent=Indenter()):
561 CodeGenerator.__init__(self, scope, indent)
562 self.reader = Reader(scope=scope, indent=indent)
563 self.writer = Writer(scope=scope, indent=indent)
564
565 def __call__(self, function):
566
567 result= self.indent()+"public function "+function.name+"("+string.join(["$arg"+str(ix) for ix in range(len(function.args()))], ", ")+") {\n"
568
569 self.newScope()
570
571 self.indent+= 1
572
Marc Slemkod97eb612006-08-24 23:37:36 +0000573 result+= self.writer.toWriteMessageBegin("\""+function.name+"\"", PHP_PROTOCOL_CALL, "$this->seqid")
Marc Slemkoc6936402006-08-23 02:15:31 +0000574 result+= self.indent()+"$this->_seqid++;\n"
575
576 # Send the args struct
577
578 result+= self.indent()+"{\n"
579
580 self.newScope()
581
582 self.indent+= 1
583
584 result+= self.writer.toWriteStructBegin("\""+function.argsStruct.name+"\"")
585
586 for ix in range(len(function.argsStruct.fieldList)):
587 field = function.argsStruct.fieldList[ix]
588 if isVoidType(field.type):
589 continue
590
591 result+= self.writer.toWriteFieldBegin("\""+field.name+"\"", toWireType(field.type), field.id)
592 result+= self.writer.toWrite("$arg"+str(ix), field.type)
593 result+= self.writer.toWriteFieldEnd()
594
595 result+= self.writer.toWriteFieldStop()
596
597 result+= self.writer.toWriteStructEnd()
598
599 self.indent-= 1
600
601 self.oldScope()
602
603 result+= self.indent()+"}\n"
604
605 result+= self.writer.toWriteMessageEnd()
606
607 result+= self.writer.toWriteFlush();
608
609 resultVar = self.declare(function.resultStruct, "result")
Marc Slemkod97eb612006-08-24 23:37:36 +0000610 nameVar = self.declare(STRING_TYPE, "name")
611 typeVar = self.declare(U32_TYPE, "type")
612 seqidVar = self.declare(U32_TYPE, "seqid")
Marc Slemkoc6936402006-08-23 02:15:31 +0000613
614 result+= self.toDeclaration(resultVar, True)
615 result+= self.toDeclaration(nameVar)
Marc Slemkod97eb612006-08-24 23:37:36 +0000616 result+= self.toDeclaration(typeVar)
Marc Slemkoc6936402006-08-23 02:15:31 +0000617 result+= self.toDeclaration(seqidVar)
618
Marc Slemkod97eb612006-08-24 23:37:36 +0000619 result+= self.reader.toReadMessageBegin(nameVar, typeVar, seqidVar)
Marc Slemkoc6936402006-08-23 02:15:31 +0000620
621 result+= self.indent()+"{\n"
622
623 self.newScope()
624
625 self.indent+= 1
626
627 result+= self.reader.toRead(resultVar.name, function.resultStruct)
628
629 self.indent-= 1
630
631 self.oldScope()
632
633 result+= self.indent()+"}\n"
634
Marc Slemkob09f5882006-08-23 22:03:34 +0000635 result+= self.reader.toReadMessageEnd()
636
637 if not isVoidType(function.returnType()):
638 result+= self.indent()+"return "+resultVar.name+"->success;\n"
639 else:
640 result+= self.indent()+"return;\n"
641
Marc Slemkoc6936402006-08-23 02:15:31 +0000642 self.indent-= 1
643
644 self.oldScope()
645
Marc Slemkoc6936402006-08-23 02:15:31 +0000646 result+= self.indent()+"}\n"
647
648 return result
649
650class ClientServiceGenerator(CodeGenerator):
651 def __init__(self,
652 scope=Scope(),
653 indent=Indenter()):
654 CodeGenerator.__init__(self, scope, indent)
655 self.functionGenerator = ClientFunctionGenerator(scope, indent)
656
657 def __call__(self, service):
658
659 result= self.indent()+"class "+service.name+"Client extends "+service.name+"If {\n"
660
661 self.indent+= 1
662
663 result+= self.indent()+"private $_itrans = null;\n"
664 result+= self.indent()+"private $_otrans = null;\n\n"
665
666 result+= self.indent()+"private $_iprot = null;\n"
667 result+= self.indent()+"private $_oprot = null;\n\n"
668 result+= self.indent()+"private $_seqid = 0;\n\n"
669
670 result+= self.indent()+"public function __construct() {\n"
671 self.indent+= 1
672
673 result+= self.indent()+"$argv = func_get_args();\n"
674 result+= self.indent()+"$argc = count($argv);\n"
675 result+= self.indent()+"if ($argc == 2) {;\n"
676 self.indent+= 1
677 result+= self.indent()+"$this->_itrans = $this->_otrans = $argv[0];\n"
678 result+= self.indent()+"$this->_iprot = $this->_oprot = $argv[1];\n"
679 self.indent-= 1
680 result+= self.indent()+"} else if($argc == 4) {\n"
681 self.indent+= 1
682 result+= self.indent()+"$this->_itrans = $argv[0];\n"
683 result+= self.indent()+"$this->_otrans = $argv[1];\n"
684 result+= self.indent()+"$this->_iprot = $argv[2];\n"
685 result+= self.indent()+"$this->_oprot = $argv[3];\n"
686 self.indent-= 1
687 result+= self.indent()+"}\n"
688 self.indent-= 1
689 result+= self.indent()+"}\n\n"
690
691 for function in service.functionList:
692
693 result+= self.functionGenerator(function)
694
695 self.indent-= 1
696 result+= self.indent()+"}\n"
697
698 return result
699
700PHP_PRIMITIVE_DEFAULT_MAP = {
701 VOID_TYPE : "",
702 BOOL_TYPE : "False",
703 STRING_TYPE: "\'\'",
704 UTF7_TYPE: "\'\'",
705 UTF8_TYPE: "\'\'",
706 UTF16_TYPE: "\'\'",
707 BYTE_TYPE : "0",
708 I08_TYPE: "0",
709 I16_TYPE: "0",
710 I32_TYPE: "0",
711 I64_TYPE: "0",
712 U08_TYPE: "0",
713 U16_TYPE: "0",
714 U32_TYPE: "0",
715 U64_TYPE: "0",
716 FLOAT_TYPE: "0.0",
717 DOUBLE_TYPE: "0.0"
718}
719
720def toPHPNamespacePrefix(namespace):
721 """No namespaces in PHP"""
722 return ""
723
724def toPHPNamespaceSuffix(namespace):
725 """No namespaces in PHP"""
726 return ""
727
728def defaultValueForType(ttype, value=None):
729 """Returns the default literal value for a given type"""
730
731 if value:
732 return value
733 elif isinstance(ttype, PrimitiveType):
734 return PHP_PRIMITIVE_DEFAULT_MAP[ttype]
735 elif isinstance(ttype, CollectionType):
736 return "array()"
737 elif isinstance(ttype, StructType):
738 return "null"
739 elif isinstance(ttype, EnumType):
740 return "0"
741 elif isinstance(ttype, TypedefType):
742 return defaultValueForType(toCanonicalType(ttype))
743 else:
744 raise Exception, "Unknown type "+str(ttype)
745
746def newInstanceForType(ttype, value=None):
747 """Returns the default new instance for a given type"""
748
749 if value:
750 return value
751 elif isinstance(ttype, StructType):
752 return "new "+ttype.name+"()"
753 else:
754 return defaultValueForType(ttype, value)
755
756def toPHPTypeDeclaration(ttype):
757 """ Converts the thrift IDL type to the corresponding PHP type. Note that if ttype is FieldType, this function
758converts to type declaration followed by field name, i.e. \"string string_thing\""""
759
760 if isinstance(ttype, Field):
761 return "$"+ttype.name
762 if isinstance(ttype, Function):
763 return ttype.name+"("+string.join([toPHPTypeDeclaration(arg) for arg in ttype.args()], ", ")+")"
764 else:
765 return ""
766
767def toTypedefDefinition(typedef):
768 """Converts a thrift typedef to a PHP typdef definition."""
769
770 return ""
771
772def toEnumDefinition(enum):
773 """ Converts a thrift enum to a PHP enum """
774
775 result = "$GLOBALS[\'E_"+enum.name+"\'] = array(\n"
776
777 result += string.join([" \'"+ed.name+"\' => "+str(ed.id)+",\n" for ed in enum.enumDefs], "")
778
779 result+= ");\n\n"
780
781 result += "final class "+enum.name+" {\n"
782
783 result += string.join([" const "+ed.name+" = "+str(ed.id)+";\n" for ed in enum.enumDefs], "")
784
785 result += "}\n"
786
787 return result
788
789def toStructDefinition(struct):
790 """Converts a thrift struct to a PHP class"""
791
792 result = "class "+struct.name+" {\n"
793
794 # Create constructor defaults for primitive types
795
796 # Field declarations
797
798 result+= string.join([" public $"+field.name+" = "+defaultValueForType(field.type)+";\n" for field in struct.fieldList if not isVoidType(field.type)], "")
799
800 # bring it on home
801
802 result+= "}\n"
803
804 return result
805
806PHP_DEFINITION_MAP = {
807 TypedefType : toTypedefDefinition,
808 EnumType : toEnumDefinition,
809 StructType : toStructDefinition,
810 ExceptionType : toStructDefinition,
811 Service : None
812 }
813
814def toDefinitions(definitions):
815 """Converts an arbitrary thrift grammatical unit definition to the corresponding PHP definition"""
816
817 result = ""
818
819 for definition in definitions:
820
821 writer = PHP_DEFINITION_MAP[type(definition)]
822
823 if writer:
824 result+= writer(definition)+"\n"
825
826 return result
827
828PHP_THRIFT_NS = "facebook::thrift"
829
830PHP_INTERFACE_FUNCTION_DECLARATION = Template(""" public abstract function ${functionDeclaration};
831""")
832
833PHP_INTERFACE_DECLARATION = Template("""
834abstract class ${service}If {
835${functionDeclarations}};
836""")
837
838def toServiceInterfaceDeclaration(service, debugp=None):
839 """Converts a thrift service definition into a C++ abstract base class"""
840
841 functionDeclarations = string.join([PHP_INTERFACE_FUNCTION_DECLARATION.substitute(service=service.name, functionDeclaration=toPHPTypeDeclaration(function)) for function in service.functionList], "")
842
843 return PHP_INTERFACE_DECLARATION.substitute(service=service.name, functionDeclarations=functionDeclarations)
844
845PHP_EXCEPTION = PHP_THRIFT_NS+"::Exception"
846
847PHP_SP = Template("boost::shared_ptr<${klass}> ")
848
849
850PHP_PROTOCOL_TSTOP = "TType::STOP"
851PHP_PROTOCOL_TTYPE = "TType::"
Marc Slemkod97eb612006-08-24 23:37:36 +0000852PHP_PROTOCOL_CALL = "TMessageType::CALL"
853PHP_PROTOCOL_REPLY = "TMessageType::REPLY"
Marc Slemkoc6936402006-08-23 02:15:31 +0000854
855PHP_TTYPE_MAP = {
856 STOP_TYPE : PHP_PROTOCOL_TTYPE+"STOP",
857 VOID_TYPE : PHP_PROTOCOL_TTYPE+"VOID",
858 BOOL_TYPE : PHP_PROTOCOL_TTYPE+"BOOL",
859 UTF7_TYPE : PHP_PROTOCOL_TTYPE+"UTF7",
860 UTF7_TYPE : PHP_PROTOCOL_TTYPE+"UTF7",
861 UTF8_TYPE : PHP_PROTOCOL_TTYPE+"UTF8",
862 UTF16_TYPE : PHP_PROTOCOL_TTYPE+"UTF16",
863 U08_TYPE : PHP_PROTOCOL_TTYPE+"U08",
864 I08_TYPE : PHP_PROTOCOL_TTYPE+"I08",
865 I16_TYPE : PHP_PROTOCOL_TTYPE+"I16",
866 I32_TYPE : PHP_PROTOCOL_TTYPE+"I32",
867 I64_TYPE : PHP_PROTOCOL_TTYPE+"I64",
868 U08_TYPE : PHP_PROTOCOL_TTYPE+"U08",
869 U16_TYPE : PHP_PROTOCOL_TTYPE+"U16",
870 U32_TYPE : PHP_PROTOCOL_TTYPE+"U32",
871 U64_TYPE : PHP_PROTOCOL_TTYPE+"U64",
872 FLOAT_TYPE : PHP_PROTOCOL_TTYPE+"FLOAT",
873 DOUBLE_TYPE : PHP_PROTOCOL_TTYPE+"DOUBLE",
874 StructType : PHP_PROTOCOL_TTYPE+"STRUCT",
875 ExceptionType : PHP_PROTOCOL_TTYPE+"STRUCT",
Marc Slemkod97eb612006-08-24 23:37:36 +0000876 ListType : PHP_PROTOCOL_TTYPE+"LST",
Marc Slemkoc6936402006-08-23 02:15:31 +0000877 MapType : PHP_PROTOCOL_TTYPE+"MAP",
878 SetType : PHP_PROTOCOL_TTYPE+"SET"
879}
880
881
882def toWireType(ttype):
883 """Converts a thrift type to the corresponding wire type. This differs from toPHPTypeDeclaration in that it reduces typedefs
884to their canonical form and converts enums to signedf 32 bit integers"""
885
886 if isinstance(ttype, PrimitiveType):
887 return PHP_TTYPE_MAP[ttype]
888
889 elif isinstance(ttype, EnumType):
890 return PHP_TTYPE_MAP[I32_TYPE]
891
892 elif isinstance(ttype, TypedefType):
893 return toWireType(toCanonicalType(ttype))
894
895 elif isinstance(ttype, StructType) or isinstance(ttype, CollectionType):
896 return PHP_TTYPE_MAP[type(ttype)]
897
898 else:
899 raise Exception, "No wire type for thrift type: "+str(ttype)
900
Marc Slemkod97eb612006-08-24 23:37:36 +0000901def toGenDir(filename, suffix="gen-php", debugp=None):
Marc Slemkoc6936402006-08-23 02:15:31 +0000902 """creates a generated-code subdirectory for C++ code based on filename of thrift source file and optional suffix"""
903
904 result = os.path.join(os.path.split(filename)[0], suffix)
905
906 if not os.path.exists(result):
907 os.mkdir(result)
908
909 return result
910
911def toBasename(filename, debugp=None):
912 """ Take the filename minus the path and\".thrift\" extension if present """
913
914 basename = os.path.split(filename)[1]
915
916 tokens = os.path.splitext(basename)
917
918 if tokens[1].lower() == ".thrift":
919 basename = tokens[0]
920
921 if debugp:
922 debugp("toBasename("+str(filename)+") => "+str(basename))
923
924 return basename
925
926def toDefinitionHeaderName(filename, genDir=None, debugp=None):
927 """Creates a file name for the public thrift data types based on filename of thrift source file and optional suffix"""
928
929 if not genDir:
930 genDir = toGenDir(filename)
931
932 basename = toBasename(filename)
933
934 result = os.path.join(genDir, basename+"_types.php")
935
936 if debugp:
937 debugp("toDefinitionHeaderName("+str(filename)+", "+str(genDir)+") => "+str(basename))
938
939 return result
940
941def writeDefinitionHeader(program, filename, genDir=None, debugp=None):
942 """Writes public thrift data types defined in program into a public data types header file. Uses the name of the original
943thrift source, filename, and the optional generated C++ code directory, genDir, to determine the name and location of header file"""
944
945 definitionHeader = toDefinitionHeaderName(filename, genDir)
946
947 if debugp:
948 debugp("definitionHeader: "+str(definitionHeader))
949
950 phpFile = file(definitionHeader, "w")
951
952 basename = toBasename(filename)
953
Marc Slemkod97eb612006-08-24 23:37:36 +0000954 phpFile.write(PHP_TYPES_HEADER.substitute(prefix=PREFIX, source=basename, date=time.ctime(), namespacePrefix=toPHPNamespacePrefix(program.namespace)))
Marc Slemkoc6936402006-08-23 02:15:31 +0000955
956 phpFile.write(toDefinitions(program.definitions))
957
958 phpFile.write(PHP_TYPES_FOOTER.substitute(source=basename, namespaceSuffix=toPHPNamespaceSuffix(program.namespace)))
959
960 phpFile.close()
961
962def toToImplementationSourceName(filename, genDir=None, debugp=None):
963 """Creates a file name for the public thrift data types based on filename of thrift source file and optional suffix"""
964
965 if not genDir:
966 genDir = toGenDir(filename)
967
968 basename = toBasename(filename)
969
970 result = os.path.join(genDir, basename+".php")
971
972 if debugp:
973 debugp("toToImplementationSourceName("+str(filename)+", "+str(genDir)+") => "+str(basename))
974
975 return result
976
977def writeImplementationSource(program, filename, genDir=None, debugp=None):
978 """Writes public thrift service interface, client stubs, and private types defined in a thrift program into a php library file. Uses the name of the original
979thrift source, filename, and the optional generated C++ code directory, genDir, to determine the name and location of header file"""
980
981 toImplementationSource = toToImplementationSourceName(filename, genDir)
982
983 if debugp:
984 debugp("toImplementationSource: "+str(toImplementationSource))
985
986 phpFile = file(toImplementationSource, "w")
987
988 basename = toBasename(filename)
989
Marc Slemkod97eb612006-08-24 23:37:36 +0000990 phpFile.write(PHP_SERVICES_HEADER.substitute(prefix=PREFIX, source=basename, date=time.ctime(), namespacePrefix=toPHPNamespacePrefix(program.namespace)))
Marc Slemkoc6936402006-08-23 02:15:31 +0000991
992 # Generate classes for function result "structs"
993
994 privateStructs = []
995
996 for service in program.serviceMap.values():
997
998 phpFile.write(toServiceInterfaceDeclaration(service))
999
1000 for function in service.functionList:
1001 privateStructs.append(function.resultStruct)
1002
1003 phpFile.write(toDefinitions(privateStructs))
1004
1005 serviceGenerator = ClientServiceGenerator()
1006
1007 for service in program.serviceMap.values():
1008
1009 phpFile.write(serviceGenerator(service))
1010
1011 phpFile.write(PHP_SERVICES_FOOTER.substitute(source=basename, namespaceSuffix=toPHPNamespaceSuffix(program.namespace)))
1012
1013 phpFile.close()
1014
1015class PHPGenerator(Generator):
1016
1017 def __call__(self, program, filename, genDir=None, debugp=None):
1018
1019 writeDefinitionHeader(program, filename, genDir, debugp)
1020
1021 writeImplementationSource(program, filename, genDir, debugp)