Thrift php generator in python.
Cleaned up parser and cpp generator
    


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@664765 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/compiler/src/php_generator.py b/compiler/src/php_generator.py
new file mode 100644
index 0000000..e68a4f7
--- /dev/null
+++ b/compiler/src/php_generator.py
@@ -0,0 +1,1015 @@
+""" Thrift IDL PHP client stub generator
+    
+    This generated PHP class definitions and client stubs for a valid thrift IDL definition
+
+    Author(s): Mark Slee(mclee@facebook.com), Marc Kwiatkowski (marc@facebook.com)
+
+    $Id:
+"""
+import time
+
+import os
+import os.path
+from string import Template
+from thrift.parser import *
+from thrift.generator import *
+
+HEADER_COMMENT = """<?php
+/**
+ * Autogenerated by Thrift
+ * ${date}
+ *
+ * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+ */
+ """
+
+PHP_TYPES_HEADER = Template(HEADER_COMMENT+"""
+require_once THRIFT_ROOT.\'/Thrift.php\';
+""")
+
+PHP_TYPES_FOOTER = Template("""
+?>
+""")
+
+PHP_SERVICES_HEADER = Template(HEADER_COMMENT+"""
+
+require_once THRIFT_ROOT.'/Thrift.php';
+require_once dirname(__FILE__).\'/${source}_types.php\';
+
+""")
+
+PHP_SERVICES_FOOTER = Template("""
+?>
+""")
+
+PHP_IMPL_HEADER = Template(HEADER_COMMENT+"""
+
+require_once THRIFT_ROOT.'/Thrift.php';
+require_once dirname(__FILE__).\'/${source}_types.php\';
+
+""")
+
+PHP_IMPL_FOOTER = Template("""
+?>
+""")
+
+def php_debug(arg):
+    print(arg)
+
+class Indenter(object):
+    def __init__(self, margin=4):
+        self.margin = ''.join([' ' for ix in range(margin)])
+        self.level = 0
+
+    def __getitem__(self, level):
+        return ''.join([self.margin for ix in range(level)])
+
+    def __iadd__(self, value):
+        self.level = self.level + value
+        return self
+
+    def __isub__(self, value):
+        self.level = self.level - value
+        return self
+
+    def __call__(self):
+        return self.__getitem__(self.level)
+
+class Variable(object):
+    def __init__(self, ttype, name):
+        self.ttype = ttype
+        self.name = name
+
+    def toDeclaration(self, new=False):
+	if not new:
+	    defaultValue = defaultValueForType(self.ttype)
+	else:
+	    defaultValue = newInstanceForType(self.ttype)
+        return self.name+" = "+defaultValue
+
+class Scope(object):
+    def __init__(self, parent=None):
+        self.index = 0
+        self.vars = {}
+        self.parent = parent
+	self.childIndex = 0
+        if self.parent:
+            self.level = self.parent.level + 1
+	    self.index = self.parent.childIndex 
+	    self.parent.childIndex+= 1
+	    self.suffix = self.parent.suffix+str(self.index)
+        else:
+            self.level = 0
+	    self.index = 0
+	    self.suffix = ""
+
+    def declare(self, ttype, name):
+        if name in self.vars:
+            raise Exception, "Variable \""+name+"\" already declared in this  scope."
+	result = Variable(ttype, "$_"+name+self.suffix)
+        self.vars[name] = result
+        return result
+
+    def get(self, var):
+        if var.name in self.vars:
+            return self.vars[var.name]
+        elif self.parent:
+            return self.parent.get(var)
+        else:
+            raise Exception, "Variable \""+var.name+"\" does not exist in any enclosing scope"
+
+PHP_TYPE_PROTOCOL_MAP = {
+    BOOL_TYPE : "Bool",
+    STRING_TYPE: "String",
+    UTF7_TYPE: "String",
+    UTF8_TYPE: "Wstring",
+    BYTE_TYPE : "Byte",
+    I08_TYPE: "Byte",
+    I16_TYPE: "I16",
+    I32_TYPE: "I32",
+    I64_TYPE: "I64",
+    I16_TYPE: "I16",
+    I32_TYPE: "I32",
+    I64_TYPE: "I64",
+    U08_TYPE: "Byte",
+    U16_TYPE: "U16",
+    U32_TYPE: "U32",
+    U64_TYPE: "U64",
+    U16_TYPE: "U16",
+    U32_TYPE: "U32",
+    U64_TYPE: "U64",
+    FLOAT_TYPE: "Float",
+    DOUBLE_TYPE: "Double"
+   }
+            
+PHP_PRIMITIVE_TYPE_IO_METHOD_SUFFIX_MAP = {
+    "void" :"Void",
+    "bool" : "Bool",
+    "string": "String",
+    "utf7": "String",
+    "utf8": "String", 
+    "utf16": "String",
+    "i08": "Byte",
+    "i16": "I16",
+    "i32": "I32", 
+    "i64": "I64", 
+    "u08": "Byte",
+    "u16": "U16",
+    "u32": "U32",
+    "u64": "U64",
+    "float": "Float",
+    "double": "Double"
+}
+
+class CodeGenerator(object):
+    def __init__(self,
+                 scope=Scope(),
+		 indent = Indenter()):
+        self.scope = scope
+	self.indent = indent
+
+    def newScope(self):
+	self.scope = Scope(self.scope)
+
+    def oldScope(self):
+	if not self.scope.parent:
+	    raise Exception, "Unbalanced scope entry/exit"
+
+	self.scope = self.scope.parent
+
+    def declare(self, type, name, defaultValue=None):
+        tvar = self.scope.declare(type, name)
+        return tvar
+
+    def toDeclaration(self, tvar, new=False):
+	if not new:
+	    defaultValue = defaultValueForType(tvar.ttype)
+	else:
+	    defaultValue = newInstanceForType(tvar.ttype)
+
+        return self.indent()+tvar.name+" = "+defaultValue+";\n"
+
+class Reader(CodeGenerator):
+    def __init__(self,
+                 iprot="$this->_iprot",
+                 itrans="$this->_itrans",
+                 scope=Scope(),
+		 indent = Indenter()):
+	CodeGenerator.__init__(self, scope, indent)
+        self.iprot = iprot
+        self.itrans = itrans
+
+    def toReadMessageBegin(self, messageNameVar, seqidVar):
+        return self.indent()+self.iprot+"->readMessageBegin("+self.itrans+", "+messageNameVar.name+", "+seqidVar.name+");\n"
+
+    def toReadMessageEnd(self):
+        return self.indent()+self.iprot+"->readMessageEnd("+self.itrans+");\n"
+
+    def toReadStructBegin(self, structNameVar):
+        return self.indent()+self.iprot+"->readStructBegin("+self.itrans+", "+structNameVar.name+");\n"
+
+    def toReadStructEnd(self):
+        return self.indent()+self.iprot+"->readStructEnd("+self.itrans+");\n"
+
+    def toReadMapBegin(self, keyTypeVar, valueTypeVar, sizeVar):
+        return self.indent()+self.iprot+"->readMapBegin("+self.itrans+", "+keyTypeVar.name+", "+valueTypeVar.name+", "+sizeVar.name+");\n"
+
+    def toReadMapEnd(self):
+        return self.indent()+self.iprot+"->readMapEnd("+self.itrans+");\n"
+
+    def toReadSetBegin(self, valueTypeVar, sizeVar):
+        return self.indent()+self.iprot+"->readSetBegin("+self.itrans+", "+valueTypeVar.name+", "+sizeVar.name+");\n"
+
+    def toReadSetEnd(self):
+        return self.indent()+self.iprot+"->readSetEnd("+self.itrans+");\n"
+
+    def toReadListBegin(self, valueTypeVar, sizeVar):
+        return self.indent()+self.iprot+"->readListBegin("+self.itrans+", "+valueTypeVar.name+", "+sizeVar.name+");\n"
+
+    def toReadListEnd(self):
+        return self.indent()+self.iprot+"->readListEnd("+self.itrans+");\n"
+
+    def toReadFieldBegin(self, fieldNameVar, fieldTypeVar, fieldIdVar):
+        return self.indent()+self.iprot+"->readFieldBegin("+self.itrans+", "+fieldNameVar.name+", "+fieldTypeVar.name+", "+fieldIdVar.name+");\n"
+
+    def toReadFieldEnd(self):
+        return self.indent()+self.iprot+"->readFieldEnd("+self.itrans+");\n"
+
+    def toSkipField(self, fieldTypeVar):
+        return self.indent()+self.iprot+"->skipField("+self.itrans+", "+fieldTypeVar.name+");\n"
+
+    def toReadPrimitive(self, value, suffix):
+        return self.indent()+value+" = "+self.iprot+"->read"+suffix+"("+self.itrans+");\n"
+
+    def toRead(self, value, ttype):
+
+        if isinstance(ttype, PrimitiveType):
+            return self.toReadPrimitive(value, PHP_TYPE_PROTOCOL_MAP[ttype])
+
+        elif isinstance(ttype, StructType):
+	    self.newScope()
+            result = self.toReadStruct(value, ttype)
+	    self.oldScope()
+	    return result
+
+        elif isinstance(ttype, CollectionType):
+	    self.newScope()
+            result = self.toReadCollection(value, ttype)
+	    self.oldScope()
+	    return result
+
+        elif isinstance(ttype, EnumType):
+	    return self.toReadPrimitive(value, PHP_TYPE_PROTOCOL_MAP[I32_TYPE])
+
+        elif isinstance(ttype, TypedefType):
+            return self.toRead(value, ttype.definitionType)
+
+        else:
+            raise Exception, "Unsupported type "+str(type(ttype))+":"+str(ttype)
+
+    def toReadStruct(self, value, struct):
+
+        nameVar = self.declare(STRING_TYPE, "name")
+        fieldIdVar = self.declare(I16_TYPE, "fieldId")
+        fieldTypeVar = self.declare(U32_TYPE, "fieldType")
+	localVars = [nameVar, fieldTypeVar, fieldIdVar]
+
+        result= string.join([self.toDeclaration(localVar) for localVar in localVars], "")
+
+        result+= self.toReadStructBegin(nameVar)
+
+        result+= self.indent()+"while(true) {\n"
+
+        self.indent += 1
+
+        result+= self.toReadFieldBegin(nameVar, fieldTypeVar, fieldIdVar) 
+
+        result += self.indent()+"if("+fieldTypeVar.name+" == "+PHP_PROTOCOL_TSTOP+") {\n"
+
+        self.indent+= 1
+
+        result+= self.indent()+"break;\n"
+
+        self.indent-= 1
+
+        result+= self.indent()+"}\n"
+
+        result+= self.indent()+"switch("+fieldIdVar.name+") {\n"
+
+        self.indent+= 1
+
+        # Sort field list in order of increasing ids
+
+        fieldList = []
+
+        fieldList+= struct.fieldList
+
+        fieldList.sort(lambda a,b: a.id - b.id)
+
+        for field in fieldList:
+            if isVoidType(field.type):
+                continue
+
+            result+= self.indent()+"case "+str(field.id)+":\n"
+
+            result+= self.toRead(value+"->"+field.name, field.type)
+	    result+= self.indent()+"break;\n"
+
+        result+= self.indent()+"default:\n"
+        result+= self.toSkipField(fieldTypeVar)
+        result+= self.indent()+"break;\n"
+
+        self.indent-= 1
+
+        result+= self.indent()+"}\n"
+
+        self.indent-= 1
+
+        result+= self.indent()+"}\n"
+
+        result+= self.toReadStructEnd()
+
+        result+= self.indent()+"return "+value+";\n"
+
+        return result
+
+    def toReadCollection(self, value, collection):
+
+        isMap = isinstance(collection, MapType)
+
+        sizeVar = self.declare(U32_TYPE, "size")
+        valueTypeVar = self.declare(U32_TYPE, "valueType")
+        elemVar = self.declare(collection.valueType, "elem")
+        localVars = [sizeVar, valueTypeVar, elemVar]
+
+        if isMap:
+            keyTypeVar = self.declare(U32_TYPE, "keyType")
+            key = self.declare(collection.keyType, "key")
+            localVars+= [keyTypeVar, key]
+
+        ix = self.declare(U32_TYPE, "ix")
+
+        result= string.join([self.toDeclaration(localVar) for localVar in localVars], "")
+
+        if isMap:
+            result+= self.toReadMapBegin(keyTypeVar, valueTypeVar, sizeVar)
+        elif isinstance(collection, ListType):
+            result+= self.toReadListBegin(valueTypeVar, sizeVar)
+        elif isinstance(collection, SetType):
+            result+= self.toReadSetBegin(valueTypeVar, sizeVar)
+	else:
+	    raise Exception, "Unknown collection type "+str(collection)
+
+        result+= self.indent()+"for("+ix.name+" = 0; "+ix.name+" < "+sizeVar.name+"; ++"+ix.name+") {\n"
+
+        self.indent+= 1
+
+	if isMap:
+	    result+= self.toRead(key.name, collection.keyType)
+
+	result+= self.toRead(elemVar.name, collection.valueType)
+	
+        if isMap:
+            result+= self.indent()+value+"["+key.name+"] = "+elemVar.name+";\n"
+        else:
+            result+= self.indent()+value+"[] = "+elemVar.name+";\n"
+
+	self.indent-= 1
+
+	result+= self.indent()+"}\n"
+
+        if isMap:
+            result+= self.toReadMapEnd()
+        elif isinstance(collection, ListType):
+            result+= self.toReadListEnd()
+        elif isinstance(collection, SetType):
+            result+= self.toReadSetEnd()
+	else:
+	    raise Exception, "Unknown collection type "+str(collection)
+
+	return result
+
+class Writer(CodeGenerator):
+    def __init__(self,
+                 oprot="$this->_oprot",
+                 otrans="$this->_otrans",
+                 scope=Scope(),
+		 indent=Indenter()):
+	CodeGenerator.__init__(self, scope, indent)
+        self.oprot = oprot
+        self.otrans = otrans
+
+    def toWriteMessageBegin(self, messageName, seqid):
+        return self.indent()+self.oprot+"->writeMessageBegin("+self.otrans+", "+messageName+", "+seqid+");\n"
+
+    def toWriteMessageEnd(self):
+        return self.indent()+self.oprot+"->writeMessageEnd("+self.otrans+");\n"
+
+    def toWriteStructBegin(self, structName):
+        return self.indent()+self.oprot+"->writeStructBegin("+self.otrans+", "+structName+");\n"
+
+    def toWriteStructEnd(self):
+        return self.indent()+self.oprot+"->writeStructEnd("+self.otrans+");\n"
+
+    def toWriteMapBegin(self, keyType, valueType, size):
+        return self.indent()+self.oprot+"->writeMapBegin("+self.otrans+", "+keyType+", "+valueType+", "+size+");\n"
+
+    def toWriteMapEnd(self):
+        return self.indent()+self.oprot+"->writeMapEnd("+self.otrans+");\n"
+
+    def toWriteSetBegin(self, valueType, size):
+        return self.indent()+self.oprot+"->writeSetBegin("+self.otrans+", "+valueType+", "+size+");\n"
+
+    def toWriteSetEnd(self):
+        return self.indent()+self.oprot+"->writeSetEnd("+self.otrans+");\n"
+
+    def toWriteListBegin(self, valueType, size):
+        return self.indent()+self.oprot+"->writeListBegin("+self.otrans+", "+valueType+", "+size+");\n"
+
+    def toWriteListEnd(self):
+        return self.indent()+self.oprot+"->writeListEnd("+self.otrans+");\n"
+
+    def toWriteFieldBegin(self, fieldName, fieldType, fieldId):
+        return self.indent()+self.oprot+"->writeFieldBegin("+self.otrans+", "+fieldName+", "+fieldType+", "+str(fieldId)+");\n"
+
+    def toWriteFieldEnd(self):
+        return self.indent()+self.oprot+"->writeFieldEnd("+self.otrans+");\n"
+
+    def toWriteFieldStop(self):
+        return self.indent()+self.oprot+"->writeFieldStop("+self.otrans+");\n"
+
+    def toSkipField(self, fieldType):
+        return self.indent()+self.oprot+"->skipField("+self.otrans+", "+fieldType+");\n"
+
+    def toWriteFlush(self):
+	return self.indent()+self.otrans+"->flush();\n"
+
+    def toWritePrimitive(self, value, suffix):
+        return self.indent()+self.oprot+"->write"+suffix+"("+self.otrans+", "+value+");\n"
+
+    def toWrite(self, value, ttype):
+
+        if isinstance(ttype, PrimitiveType):
+            return self.toWritePrimitive(value, PHP_TYPE_PROTOCOL_MAP[ttype])
+
+        elif isinstance(ttype, StructType):
+	    self.newScope()
+            result = self.toWriteStruct(value, ttype)
+	    self.oldScope()
+	    return result
+
+        elif isinstance(ttype, CollectionType):
+	    self.newScope()
+            result = self.toWriteCollection(value, ttype)
+	    self.oldScope()
+	    return result
+
+        elif isinstance(ttype, EnumType):
+	    return self.toWritePrimitive(value, PHP_TYPE_PROTOCOL_MAP[I32_TYPE])
+
+        elif isinstance(ttype, TypedefType):
+            return self.toWrite(value, ttype.definitionType)
+
+        else:
+            raise Exception, "Unsupported type "+str(type(ttype))+":"+str(ttype)
+
+    def toWriteStruct(self, value, struct):
+
+	result=self.indent()+"{\n"
+
+	self.indent+= 1
+
+        result+= self.toWriteStructBegin("\""+struct.name+"\"")
+
+        for field in struct.fieldList:
+            if isVoidType(field.type):
+                continue
+
+            result+= self.toWriteFieldBegin("\""+field.name+"\"", toWireType(field.type), field.id)
+	    result+= self.toWrite(value+"->"+field.name,  field.type)
+            result+= self.toWriteFieldEnd()
+
+	result+= self.toWriteFieldStop()
+
+	result+= self.toWriteStructEnd()
+
+	self.indent-= 1
+
+	result+= self.indent()+"}\n"
+
+        return result
+
+    def toWriteCollection(self, value, collection):
+
+	result=self.indent()+"{\n"
+
+	self.indent+= 1
+
+	size = "count("+value+")"
+
+        isMap = isinstance(collection, MapType)
+
+        elemVar = self.declare(VOID_TYPE, "elem")
+
+        if isMap:
+	    keyVar = self.declare(VOID_TYPE, "key")
+            result+= self.toWriteMapBegin(toWireType(collection.keyType), toWireType(collection.valueType), size)
+        elif isinstance(collection, ListType):
+            result+= self.toWriteListBegin(toWireType(collection.valueType), size)
+        elif isinstance(collection, SetType):
+            result+= self.toWriteSetBegin(toWireType(collection.valueType), size)
+	else:
+	    raise Exception, "Unknown collection type "+str(collection)
+
+	if isMap:
+
+	    result+= self.indent()+"forech("+value+" as "+keyVar.name+" => "+elemVar.name+") {\n"
+
+	else:
+
+	    result+= self.indent()+"forech("+value+" as "+elemVar.name+") {\n"
+	    
+        self.indent+= 1
+
+	if isMap:
+	    result+= self.toWrite(keyVar.name, collection.keyType)
+
+	result+= self.toWrite(elemVar.name, collection.valueType)
+	
+	self.indent-= 1
+
+	result+= self.indent()+"}\n"
+
+        if isMap:
+            result+= self.toWriteMapEnd()
+        elif isinstance(collection, ListType):
+            result+= self.toWriteListEnd()
+        elif isinstance(collection, SetType):
+            result+= self.toWriteSetEnd()
+	else:
+	    raise Exception, "Unknown collection type "+str(collection)
+
+	self.indent-= 1
+
+	result+= self.indent()+"}\n"
+
+	return result
+
+class ClientFunctionGenerator(CodeGenerator):
+    def __init__(self,
+		 scope=Scope(),
+		 indent=Indenter()):
+	CodeGenerator.__init__(self, scope, indent)
+	self.reader = Reader(scope=scope, indent=indent)
+	self.writer = Writer(scope=scope, indent=indent)
+
+    def __call__(self, function):
+	
+	result= self.indent()+"public function "+function.name+"("+string.join(["$arg"+str(ix) for ix in  range(len(function.args()))], ", ")+") {\n"
+
+	self.newScope()
+
+	self.indent+= 1
+
+	result+= self.writer.toWriteMessageBegin("\""+function.name+"\"", "$this->seqid")
+	result+= self.indent()+"$this->_seqid++;\n"
+
+	# Send the args struct 
+
+	result+= self.indent()+"{\n"
+
+	self.newScope()
+
+	self.indent+= 1
+
+	result+= self.writer.toWriteStructBegin("\""+function.argsStruct.name+"\"")
+
+        for ix in range(len(function.argsStruct.fieldList)):
+	    field = function.argsStruct.fieldList[ix]
+            if isVoidType(field.type):
+                continue
+
+            result+= self.writer.toWriteFieldBegin("\""+field.name+"\"", toWireType(field.type), field.id)
+	    result+= self.writer.toWrite("$arg"+str(ix),  field.type)
+            result+= self.writer.toWriteFieldEnd()
+
+	result+= self.writer.toWriteFieldStop()
+
+	result+= self.writer.toWriteStructEnd()
+
+	self.indent-= 1
+
+	self.oldScope()
+
+	result+= self.indent()+"}\n"
+
+	result+= self.writer.toWriteMessageEnd()
+
+	result+= self.writer.toWriteFlush();
+
+	resultVar = self.declare(function.resultStruct, "result")
+	nameVar = self.declare(function.resultStruct, "name")
+	seqidVar = self.declare(function.resultStruct, "seqid")
+
+	result+= self.toDeclaration(resultVar, True)
+	result+= self.toDeclaration(nameVar)
+	result+= self.toDeclaration(seqidVar)
+
+	result+= self.reader.toReadMessageBegin(nameVar, seqidVar)
+
+	result+= self.indent()+"{\n"
+
+	self.newScope()
+
+	self.indent+= 1
+
+	result+= self.reader.toRead(resultVar.name, function.resultStruct)
+
+	self.indent-= 1
+
+	self.oldScope()
+
+	result+= self.indent()+"}\n"
+
+	self.indent-= 1
+
+	self.oldScope()
+
+	result+= self.reader.toReadMessageEnd()
+
+	result+= self.indent()+"}\n"
+	
+	return result
+
+class ClientServiceGenerator(CodeGenerator):
+    def __init__(self,
+		 scope=Scope(),
+		 indent=Indenter()):
+	CodeGenerator.__init__(self, scope, indent)
+	self.functionGenerator =  ClientFunctionGenerator(scope, indent)
+
+    def __call__(self, service):
+	
+	result= self.indent()+"class "+service.name+"Client extends "+service.name+"If {\n"
+
+	self.indent+= 1
+
+	result+= self.indent()+"private $_itrans = null;\n"
+	result+= self.indent()+"private $_otrans = null;\n\n"
+
+	result+= self.indent()+"private $_iprot = null;\n"
+	result+= self.indent()+"private $_oprot = null;\n\n"
+	result+= self.indent()+"private $_seqid = 0;\n\n"
+
+	result+= self.indent()+"public function __construct() {\n"
+	self.indent+= 1
+
+	result+= self.indent()+"$argv = func_get_args();\n"
+	result+= self.indent()+"$argc = count($argv);\n"
+	result+= self.indent()+"if ($argc == 2) {;\n"
+	self.indent+= 1
+	result+= self.indent()+"$this->_itrans = $this->_otrans = $argv[0];\n"
+	result+= self.indent()+"$this->_iprot = $this->_oprot = $argv[1];\n"
+	self.indent-= 1
+	result+= self.indent()+"} else if($argc == 4) {\n"
+	self.indent+= 1
+	result+= self.indent()+"$this->_itrans = $argv[0];\n"
+	result+= self.indent()+"$this->_otrans = $argv[1];\n"
+	result+= self.indent()+"$this->_iprot = $argv[2];\n"
+	result+= self.indent()+"$this->_oprot = $argv[3];\n"
+	self.indent-= 1
+	result+= self.indent()+"}\n"
+	self.indent-= 1
+	result+= self.indent()+"}\n\n"
+
+	for function in service.functionList:
+
+	    result+= self.functionGenerator(function)
+	
+	self.indent-= 1
+	result+= self.indent()+"}\n"
+
+	return result
+
+PHP_PRIMITIVE_DEFAULT_MAP = {
+    VOID_TYPE : "",
+    BOOL_TYPE : "False",
+    STRING_TYPE: "\'\'",
+    UTF7_TYPE: "\'\'",
+    UTF8_TYPE: "\'\'",
+    UTF16_TYPE: "\'\'",
+    BYTE_TYPE : "0",
+    I08_TYPE: "0",
+    I16_TYPE: "0",
+    I32_TYPE: "0",
+    I64_TYPE: "0",
+    U08_TYPE: "0",
+    U16_TYPE: "0",
+    U32_TYPE: "0",
+    U64_TYPE: "0",
+    FLOAT_TYPE: "0.0",
+    DOUBLE_TYPE: "0.0"
+}
+
+def toPHPNamespacePrefix(namespace):
+    """No namespaces in PHP"""
+    return ""
+
+def toPHPNamespaceSuffix(namespace):
+    """No namespaces in PHP"""
+    return ""
+
+def defaultValueForType(ttype, value=None):
+    """Returns the default literal value for a given type"""
+
+    if value:
+	return value
+    elif isinstance(ttype, PrimitiveType):
+        return PHP_PRIMITIVE_DEFAULT_MAP[ttype]
+    elif isinstance(ttype, CollectionType):
+        return "array()"
+    elif isinstance(ttype, StructType):
+        return "null"
+    elif isinstance(ttype, EnumType):
+        return "0"
+    elif isinstance(ttype, TypedefType):
+        return defaultValueForType(toCanonicalType(ttype))
+    else:
+        raise Exception, "Unknown type "+str(ttype)
+
+def newInstanceForType(ttype, value=None):
+    """Returns the default new instance for a given type"""
+
+    if value:
+	return value
+    elif isinstance(ttype, StructType):
+        return "new "+ttype.name+"()"
+    else:
+	return defaultValueForType(ttype, value)
+
+def toPHPTypeDeclaration(ttype):
+    """ Converts the thrift IDL type to the corresponding PHP type.  Note that if ttype is FieldType, this function 
+converts to type declaration followed by field name, i.e. \"string string_thing\""""
+
+    if isinstance(ttype, Field):
+        return "$"+ttype.name
+    if isinstance(ttype, Function):
+        return ttype.name+"("+string.join([toPHPTypeDeclaration(arg) for arg in ttype.args()], ", ")+")"
+    else:
+        return ""
+
+def toTypedefDefinition(typedef):
+    """Converts a thrift typedef to a PHP typdef definition."""
+
+    return ""
+
+def toEnumDefinition(enum):
+    """ Converts a thrift enum to a PHP enum """
+
+    result = "$GLOBALS[\'E_"+enum.name+"\'] = array(\n"
+
+    result += string.join(["    \'"+ed.name+"\' => "+str(ed.id)+",\n" for ed in  enum.enumDefs], "")
+
+    result+= ");\n\n"
+
+    result += "final class "+enum.name+" {\n"
+
+    result += string.join(["    const "+ed.name+" = "+str(ed.id)+";\n" for ed in  enum.enumDefs], "")
+
+    result += "}\n"
+
+    return result
+
+def toStructDefinition(struct):
+    """Converts a thrift struct to a PHP class"""
+
+    result = "class "+struct.name+" {\n"
+
+    # Create constructor defaults for primitive types
+
+    # Field declarations
+
+    result+= string.join(["    public $"+field.name+" = "+defaultValueForType(field.type)+";\n" for field in struct.fieldList if not isVoidType(field.type)], "")
+
+    # bring it on home
+
+    result+= "}\n"
+
+    return result
+
+PHP_DEFINITION_MAP = {
+    TypedefType : toTypedefDefinition,
+    EnumType : toEnumDefinition,
+    StructType : toStructDefinition,
+    ExceptionType : toStructDefinition,
+    Service : None
+    }
+    
+def toDefinitions(definitions):
+    """Converts an arbitrary thrift grammatical unit definition to the corresponding PHP definition"""
+
+    result = ""
+
+    for definition in definitions:
+
+        writer = PHP_DEFINITION_MAP[type(definition)]
+
+        if writer:
+            result+= writer(definition)+"\n"
+
+    return result
+
+PHP_THRIFT_NS = "facebook::thrift"
+
+PHP_INTERFACE_FUNCTION_DECLARATION = Template("""    public abstract function ${functionDeclaration};
+""")
+
+PHP_INTERFACE_DECLARATION = Template("""
+abstract class ${service}If {
+${functionDeclarations}};
+""")
+
+def toServiceInterfaceDeclaration(service, debugp=None):
+    """Converts a thrift service definition into a C++ abstract base class"""
+
+    functionDeclarations = string.join([PHP_INTERFACE_FUNCTION_DECLARATION.substitute(service=service.name, functionDeclaration=toPHPTypeDeclaration(function)) for function in service.functionList], "")
+
+    return PHP_INTERFACE_DECLARATION.substitute(service=service.name, functionDeclarations=functionDeclarations)
+
+PHP_EXCEPTION = PHP_THRIFT_NS+"::Exception"
+
+PHP_SP = Template("boost::shared_ptr<${klass}> ")
+
+
+PHP_PROTOCOL_TSTOP = "TType::STOP"
+PHP_PROTOCOL_TTYPE = "TType::"
+PHP_PROTOCOL_CALL = "TMessageType::T_CALL"
+PHP_PROTOCOL_REPLY = "TMessageType::T_REPLY"
+
+PHP_TTYPE_MAP = {
+    STOP_TYPE : PHP_PROTOCOL_TTYPE+"STOP",
+    VOID_TYPE : PHP_PROTOCOL_TTYPE+"VOID",
+    BOOL_TYPE : PHP_PROTOCOL_TTYPE+"BOOL",
+    UTF7_TYPE : PHP_PROTOCOL_TTYPE+"UTF7",
+    UTF7_TYPE : PHP_PROTOCOL_TTYPE+"UTF7",
+    UTF8_TYPE : PHP_PROTOCOL_TTYPE+"UTF8",
+    UTF16_TYPE : PHP_PROTOCOL_TTYPE+"UTF16",
+    U08_TYPE : PHP_PROTOCOL_TTYPE+"U08",
+    I08_TYPE : PHP_PROTOCOL_TTYPE+"I08",
+    I16_TYPE : PHP_PROTOCOL_TTYPE+"I16",
+    I32_TYPE : PHP_PROTOCOL_TTYPE+"I32",
+    I64_TYPE : PHP_PROTOCOL_TTYPE+"I64",
+    U08_TYPE : PHP_PROTOCOL_TTYPE+"U08",
+    U16_TYPE : PHP_PROTOCOL_TTYPE+"U16",
+    U32_TYPE : PHP_PROTOCOL_TTYPE+"U32",
+    U64_TYPE : PHP_PROTOCOL_TTYPE+"U64",
+    FLOAT_TYPE : PHP_PROTOCOL_TTYPE+"FLOAT",
+    DOUBLE_TYPE : PHP_PROTOCOL_TTYPE+"DOUBLE",
+    StructType : PHP_PROTOCOL_TTYPE+"STRUCT",
+    ExceptionType : PHP_PROTOCOL_TTYPE+"STRUCT",
+    ListType : PHP_PROTOCOL_TTYPE+"LIST",
+    MapType : PHP_PROTOCOL_TTYPE+"MAP",
+    SetType : PHP_PROTOCOL_TTYPE+"SET"
+}
+
+
+def toWireType(ttype):
+    """Converts a thrift type to the corresponding wire type.   This differs from toPHPTypeDeclaration in that it reduces typedefs
+to their canonical form and converts enums to signedf 32 bit integers"""
+
+    if isinstance(ttype, PrimitiveType):
+	return PHP_TTYPE_MAP[ttype]
+
+    elif isinstance(ttype, EnumType):
+	return PHP_TTYPE_MAP[I32_TYPE]
+
+    elif isinstance(ttype, TypedefType):
+	return toWireType(toCanonicalType(ttype))
+
+    elif isinstance(ttype, StructType) or isinstance(ttype, CollectionType):
+	return PHP_TTYPE_MAP[type(ttype)]
+
+    else:
+	raise Exception, "No wire type for thrift type: "+str(ttype)
+
+def toGenDir(filename, suffix="php-gen", debugp=None):
+    """creates a generated-code subdirectory for C++ code based on filename of thrift source file and optional suffix"""
+
+    result = os.path.join(os.path.split(filename)[0], suffix)
+
+    if not os.path.exists(result):
+        os.mkdir(result)
+
+    return result
+
+def toBasename(filename, debugp=None):
+    """ Take the filename minus the path and\".thrift\" extension  if present """
+
+    basename = os.path.split(filename)[1]
+
+    tokens = os.path.splitext(basename)
+
+    if tokens[1].lower() == ".thrift":
+        basename = tokens[0]
+
+    if debugp:
+        debugp("toBasename("+str(filename)+") => "+str(basename))
+
+    return basename
+
+def toDefinitionHeaderName(filename, genDir=None, debugp=None):
+    """Creates a file name for the public thrift data types based on filename of thrift source file and optional suffix"""
+
+    if not genDir:
+        genDir = toGenDir(filename)
+
+    basename = toBasename(filename)
+
+    result = os.path.join(genDir, basename+"_types.php")
+
+    if debugp:
+        debugp("toDefinitionHeaderName("+str(filename)+", "+str(genDir)+") => "+str(basename))
+
+    return result
+
+def writeDefinitionHeader(program, filename, genDir=None, debugp=None):
+    """Writes public thrift data types defined in program into a public data types header file.  Uses the name of the original
+thrift source, filename, and the optional generated C++ code directory, genDir, to determine the name and location of header file"""
+
+    definitionHeader = toDefinitionHeaderName(filename, genDir)
+
+    if debugp:
+        debugp("definitionHeader: "+str(definitionHeader))
+
+    phpFile = file(definitionHeader, "w")
+
+    basename = toBasename(filename)
+
+    phpFile.write(PHP_TYPES_HEADER.substitute(source=basename, date=time.ctime(), namespacePrefix=toPHPNamespacePrefix(program.namespace)))
+
+    phpFile.write(toDefinitions(program.definitions))
+
+    phpFile.write(PHP_TYPES_FOOTER.substitute(source=basename, namespaceSuffix=toPHPNamespaceSuffix(program.namespace)))
+
+    phpFile.close()
+
+def toToImplementationSourceName(filename, genDir=None, debugp=None):
+    """Creates a file name for the public thrift data types based on filename of thrift source file and optional suffix"""
+
+    if not genDir:
+        genDir = toGenDir(filename)
+
+    basename = toBasename(filename)
+
+    result = os.path.join(genDir, basename+".php")
+
+    if debugp:
+        debugp("toToImplementationSourceName("+str(filename)+", "+str(genDir)+") => "+str(basename))
+
+    return result
+
+def writeImplementationSource(program, filename, genDir=None, debugp=None):
+    """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
+thrift source, filename, and the optional generated C++ code directory, genDir, to determine the name and location of header file"""
+
+    toImplementationSource = toToImplementationSourceName(filename, genDir)
+
+    if debugp:
+        debugp("toImplementationSource: "+str(toImplementationSource))
+
+    phpFile = file(toImplementationSource, "w")
+
+    basename = toBasename(filename)
+
+    phpFile.write(PHP_SERVICES_HEADER.substitute(source=basename, date=time.ctime(), namespacePrefix=toPHPNamespacePrefix(program.namespace)))
+
+    # Generate classes for function result "structs"
+
+    privateStructs = []
+
+    for service in program.serviceMap.values():
+	
+	phpFile.write(toServiceInterfaceDeclaration(service))
+
+	for function in service.functionList:
+	    privateStructs.append(function.resultStruct)
+
+    phpFile.write(toDefinitions(privateStructs))    
+
+    serviceGenerator = ClientServiceGenerator()
+
+    for service in program.serviceMap.values():
+
+	phpFile.write(serviceGenerator(service))
+	
+    phpFile.write(PHP_SERVICES_FOOTER.substitute(source=basename, namespaceSuffix=toPHPNamespaceSuffix(program.namespace)))
+
+    phpFile.close()
+
+class PHPGenerator(Generator):
+
+    def __call__(self, program, filename, genDir=None, debugp=None):
+
+        writeDefinitionHeader(program, filename, genDir, debugp)
+        
+        writeImplementationSource(program, filename, genDir, debugp)