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