diff options
Diffstat (limited to 'src/google/protobuf/compiler/php/php_generator.cc')
-rw-r--r-- | src/google/protobuf/compiler/php/php_generator.cc | 331 |
1 files changed, 277 insertions, 54 deletions
diff --git a/src/google/protobuf/compiler/php/php_generator.cc b/src/google/protobuf/compiler/php/php_generator.cc index ea850c0f..60e6fce9 100644 --- a/src/google/protobuf/compiler/php/php_generator.cc +++ b/src/google/protobuf/compiler/php/php_generator.cc @@ -49,8 +49,11 @@ const std::string kDescriptorMetadataFile = "GPBMetadata/Google/Protobuf/Internal/Descriptor.php"; const std::string kDescriptorDirName = "Google/Protobuf/Internal"; const std::string kDescriptorPackageName = "Google\\Protobuf\\Internal"; -const char* const kReservedNames[] = {"Empty", "ECHO"}; -const int kReservedNamesSize = 2; +const char* const kReservedNames[] = {"ARRAY", "Empty", "ECHO"}; +const int kReservedNamesSize = 3; +const int kFieldSetter = 1; +const int kFieldGetter = 2; +const int kFieldProperty = 3; namespace google { namespace protobuf { @@ -71,12 +74,18 @@ std::string EscapeDollor(const string& to_escape); std::string BinaryToHex(const string& binary); void Indent(io::Printer* printer); void Outdent(io::Printer* printer); -void GenerateMessageDocComment(io::Printer* printer, const Descriptor* message); -void GenerateFieldDocComment(io::Printer* printer, - const FieldDescriptor* field); -void GenerateEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_); +void GenerateMessageDocComment(io::Printer* printer, const Descriptor* message, + int is_descriptor); +void GenerateFieldDocComment(io::Printer* printer, const FieldDescriptor* field, + int is_descriptor, int function_type); +void GenerateEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_, + int is_descriptor); void GenerateEnumValueDocComment(io::Printer* printer, const EnumValueDescriptor* value); +void GenerateServiceDocComment(io::Printer* printer, + const ServiceDescriptor* service); +void GenerateServiceMethodDocComment(io::Printer* printer, + const MethodDescriptor* method); std::string RenameEmpty(const std::string& name) { if (name == "Empty") { @@ -134,6 +143,25 @@ std::string ClassNamePrefix(const string& classname, return ""; } +template <typename DescriptorType> +std::string NamespacedName(const string& classname, + const DescriptorType* desc, bool is_descriptor) { + if (desc->file()->options().has_php_namespace()) { + const string& php_namespace = desc->file()->options().php_namespace(); + if (php_namespace != "") { + return php_namespace + '\\' + classname; + } else { + return classname; + } + } + + if (desc->file()->package() == "") { + return classname; + } else { + return PhpName(desc->file()->package(), is_descriptor) + '\\' + + classname; + } +} template <typename DescriptorType> std::string FullClassName(const DescriptorType* desc, bool is_descriptor) { @@ -144,13 +172,13 @@ std::string FullClassName(const DescriptorType* desc, bool is_descriptor) { containing = containing->containing_type(); } classname = ClassNamePrefix(classname, desc) + classname; + return NamespacedName(classname, desc, is_descriptor); +} - if (desc->file()->package() == "") { - return classname; - } else { - return PhpName(desc->file()->package(), is_descriptor) + '\\' + - classname; - } +std::string FullClassName(const ServiceDescriptor* desc, bool is_descriptor) { + string classname = desc->name(); + classname = ClassNamePrefix(classname, desc) + classname; + return NamespacedName(classname, desc, is_descriptor); } std::string PhpName(const std::string& full_name, bool is_descriptor) { @@ -258,6 +286,17 @@ std::string GeneratedEnumFileName(const EnumDescriptor* en, return result + ".php"; } +std::string GeneratedServiceFileName(const ServiceDescriptor* service, + bool is_descriptor) { + std::string result = FullClassName(service, is_descriptor) + "Interface"; + for (int i = 0; i < result.size(); i++) { + if (result[i] == '\\') { + result[i] = '/'; + } + } + return result + ".php"; +} + std::string IntToString(int32 value) { std::ostringstream os; os << value; @@ -297,6 +336,87 @@ std::string TypeName(const FieldDescriptor* field) { } } +std::string PhpSetterTypeName(const FieldDescriptor* field, bool is_descriptor) { + if (field->is_map()) { + return "array|\\Google\\Protobuf\\Internal\\MapField"; + } + string type; + switch (field->type()) { + case FieldDescriptor::TYPE_INT32: + case FieldDescriptor::TYPE_UINT32: + case FieldDescriptor::TYPE_SINT32: + case FieldDescriptor::TYPE_FIXED32: + case FieldDescriptor::TYPE_SFIXED32: + case FieldDescriptor::TYPE_ENUM: + type = "int"; + break; + case FieldDescriptor::TYPE_INT64: + case FieldDescriptor::TYPE_UINT64: + case FieldDescriptor::TYPE_SINT64: + case FieldDescriptor::TYPE_FIXED64: + case FieldDescriptor::TYPE_SFIXED64: + type = "int|string"; + break; + case FieldDescriptor::TYPE_DOUBLE: + case FieldDescriptor::TYPE_FLOAT: + type = "float"; + break; + case FieldDescriptor::TYPE_BOOL: + type = "bool"; + break; + case FieldDescriptor::TYPE_STRING: + case FieldDescriptor::TYPE_BYTES: + type = "string"; + break; + case FieldDescriptor::TYPE_MESSAGE: + type = "\\" + FullClassName(field->message_type(), is_descriptor); + break; + case FieldDescriptor::TYPE_GROUP: + return "null"; + default: assert(false); return ""; + } + if (field->is_repeated()) { + // accommodate for edge case with multiple types. + size_t start_pos = type.find("|"); + if (start_pos != std::string::npos) { + type.replace(start_pos, 1, "[]|"); + } + type += "[]|\\Google\\Protobuf\\Internal\\RepeatedField"; + } + return type; +} + +std::string PhpGetterTypeName(const FieldDescriptor* field, bool is_descriptor) { + if (field->is_map()) { + return "\\Google\\Protobuf\\Internal\\MapField"; + } + if (field->is_repeated()) { + return "\\Google\\Protobuf\\Internal\\RepeatedField"; + } + switch (field->type()) { + case FieldDescriptor::TYPE_INT32: + case FieldDescriptor::TYPE_UINT32: + case FieldDescriptor::TYPE_SINT32: + case FieldDescriptor::TYPE_FIXED32: + case FieldDescriptor::TYPE_SFIXED32: + case FieldDescriptor::TYPE_ENUM: return "int"; + case FieldDescriptor::TYPE_INT64: + case FieldDescriptor::TYPE_UINT64: + case FieldDescriptor::TYPE_SINT64: + case FieldDescriptor::TYPE_FIXED64: + case FieldDescriptor::TYPE_SFIXED64: return "int|string"; + case FieldDescriptor::TYPE_DOUBLE: + case FieldDescriptor::TYPE_FLOAT: return "float"; + case FieldDescriptor::TYPE_BOOL: return "bool"; + case FieldDescriptor::TYPE_STRING: + case FieldDescriptor::TYPE_BYTES: return "string"; + case FieldDescriptor::TYPE_MESSAGE: + return "\\" + FullClassName(field->message_type(), is_descriptor); + case FieldDescriptor::TYPE_GROUP: return "null"; + default: assert(false); return ""; + } +} + std::string EnumOrMessageSuffix( const FieldDescriptor* field, bool is_descriptor) { if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { @@ -381,7 +501,7 @@ void Outdent(io::Printer* printer) { void GenerateField(const FieldDescriptor* field, io::Printer* printer, bool is_descriptor) { if (field->is_repeated()) { - GenerateFieldDocComment(printer, field); + GenerateFieldDocComment(printer, field, is_descriptor, kFieldProperty); printer->Print( "private $^name^;\n", "name", field->name()); @@ -389,7 +509,7 @@ void GenerateField(const FieldDescriptor* field, io::Printer* printer, // Oneof fields are handled by GenerateOneofField. return; } else { - GenerateFieldDocComment(printer, field); + GenerateFieldDocComment(printer, field, is_descriptor, kFieldProperty); printer->Print( "private $^name^ = ^default^;\n", "name", field->name(), @@ -417,7 +537,7 @@ void GenerateFieldAccessor(const FieldDescriptor* field, bool is_descriptor, // Generate getter. if (oneof != NULL) { - GenerateFieldDocComment(printer, field); + GenerateFieldDocComment(printer, field, is_descriptor, kFieldGetter); printer->Print( "public function get^camel_name^()\n" "{\n" @@ -426,7 +546,7 @@ void GenerateFieldAccessor(const FieldDescriptor* field, bool is_descriptor, "camel_name", UnderscoresToCamelCase(field->name(), true), "number", IntToString(field->number())); } else { - GenerateFieldDocComment(printer, field); + GenerateFieldDocComment(printer, field, is_descriptor, kFieldGetter); printer->Print( "public function get^camel_name^()\n" "{\n" @@ -437,14 +557,11 @@ void GenerateFieldAccessor(const FieldDescriptor* field, bool is_descriptor, } // Generate setter. - GenerateFieldDocComment(printer, field); + GenerateFieldDocComment(printer, field, is_descriptor, kFieldSetter); printer->Print( - "public function set^camel_name^(^var^)\n" + "public function set^camel_name^($var)\n" "{\n", - "camel_name", UnderscoresToCamelCase(field->name(), true), - "var", (field->is_repeated() || - field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ? - "&$var": "$var"); + "camel_name", UnderscoresToCamelCase(field->name(), true)); Indent(printer); @@ -530,6 +647,8 @@ void GenerateFieldAccessor(const FieldDescriptor* field, bool is_descriptor, "field_name", field->name()); } + printer->Print("\nreturn $this;\n"); + Outdent(printer); printer->Print( @@ -566,6 +685,16 @@ void GenerateEnumToPool(const EnumDescriptor* en, io::Printer* printer) { Outdent(printer); } +void GenerateServiceMethod(const MethodDescriptor* method, + io::Printer* printer) { + printer->Print( + "public function ^camel_name^(\\^request_name^ $request);\n\n", + "camel_name", UnderscoresToCamelCase(method->name(), false), + "request_name", FullClassName( + method->input_type(), false) + ); +} + void GenerateMessageToPool(const string& name_prefix, const Descriptor* message, io::Printer* printer) { // Don't generate MapEntry messages -- we use the PHP extension's native @@ -747,7 +876,7 @@ void GenerateUseDeclaration(bool is_descriptor, io::Printer* printer) { "use Google\\Protobuf\\Internal\\GPBType;\n" "use Google\\Protobuf\\Internal\\GPBWire;\n" "use Google\\Protobuf\\Internal\\RepeatedField;\n" - "use Google\\Protobuf\\Internal\\InputStream;\n\n" + "use Google\\Protobuf\\Internal\\InputStream;\n" "use Google\\Protobuf\\Internal\\GPBUtil;\n\n"); } } @@ -820,13 +949,20 @@ void GenerateEnumFile(const FileDescriptor* file, const EnumDescriptor* en, std::string fullname = FilenameToClassname(filename); int lastindex = fullname.find_last_of("\\"); - if (!file->package().empty()) { + if (file->options().has_php_namespace()) { + const string& php_namespace = file->options().php_namespace(); + if (!php_namespace.empty()) { + printer.Print( + "namespace ^name^;\n\n", + "name", php_namespace); + } + } else if (!file->package().empty()) { printer.Print( "namespace ^name^;\n\n", "name", fullname.substr(0, lastindex)); } - GenerateEnumDocComment(&printer, en); + GenerateEnumDocComment(&printer, en, is_descriptor); if (lastindex != string::npos) { printer.Print( @@ -872,7 +1008,14 @@ void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message, std::string fullname = FilenameToClassname(filename); int lastindex = fullname.find_last_of("\\"); - if (!file->package().empty()) { + if (file->options().has_php_namespace()) { + const string& php_namespace = file->options().php_namespace(); + if (!php_namespace.empty()) { + printer.Print( + "namespace ^name^;\n\n", + "name", php_namespace); + } + } else if (!file->package().empty()) { printer.Print( "namespace ^name^;\n\n", "name", fullname.substr(0, lastindex)); @@ -880,7 +1023,7 @@ void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message, GenerateUseDeclaration(is_descriptor, &printer); - GenerateMessageDocComment(&printer, message); + GenerateMessageDocComment(&printer, message, is_descriptor); if (lastindex != string::npos) { printer.Print( "class ^name^ extends \\Google\\Protobuf\\Internal\\Message\n" @@ -928,6 +1071,9 @@ void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message, for (int i = 0; i < message->oneof_decl_count(); i++) { const OneofDescriptor* oneof = message->oneof_decl(i); printer.Print( + "/**\n" + " * @return string\n" + " */\n" "public function get^camel_name^()\n" "{\n" " return $this->whichOneof(\"^name^\");\n" @@ -950,6 +1096,58 @@ void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message, } } +void GenerateServiceFile(const FileDescriptor* file, + const ServiceDescriptor* service, bool is_descriptor, + GeneratorContext* generator_context) { + std::string filename = GeneratedServiceFileName(service, is_descriptor); + scoped_ptr<io::ZeroCopyOutputStream> output( + generator_context->Open(filename)); + io::Printer printer(output.get(), '^'); + + GenerateHead(file, &printer); + + std::string fullname = FilenameToClassname(filename); + int lastindex = fullname.find_last_of("\\"); + + if (file->options().has_php_namespace()) { + const string& php_namespace = file->options().php_namespace(); + if (!php_namespace.empty()) { + printer.Print( + "namespace ^name^;\n\n", + "name", php_namespace); + } + } else if (!file->package().empty()) { + printer.Print( + "namespace ^name^;\n\n", + "name", fullname.substr(0, lastindex)); + } + + GenerateServiceDocComment(&printer, service); + + if (lastindex != string::npos) { + printer.Print( + "interface ^name^\n" + "{\n", + "name", fullname.substr(lastindex + 1)); + } else { + printer.Print( + "interface ^name^\n" + "{\n", + "name", fullname); + } + + Indent(&printer); + + for (int i = 0; i < service->method_count(); i++) { + const MethodDescriptor* method = service->method(i); + GenerateServiceMethodDocComment(&printer, method); + GenerateServiceMethod(method, &printer); + } + + Outdent(&printer); + printer.Print("}\n\n"); +} + void GenerateFile(const FileDescriptor* file, bool is_descriptor, GeneratorContext* generator_context) { GenerateMetadataFile(file, is_descriptor, generator_context); @@ -961,6 +1159,12 @@ void GenerateFile(const FileDescriptor* file, bool is_descriptor, GenerateEnumFile(file, file->enum_type(i), is_descriptor, generator_context); } + if (file->options().php_generic_services()) { + for (int i = 0; i < file->service_count(); i++) { + GenerateServiceFile(file, file->service(i), is_descriptor, + generator_context); + } + } } static string EscapePhpdoc(const string& input) { @@ -994,22 +1198,6 @@ static string EscapePhpdoc(const string& input) { // does not have a corresponding @Deprecated annotation. result.append("@"); break; - case '<': - // Avoid interpretation as HTML. - result.append("<"); - break; - case '>': - // Avoid interpretation as HTML. - result.append(">"); - break; - case '&': - // Avoid interpretation as HTML. - result.append("&"); - break; - case '\\': - // Java interprets Unicode escape sequences anywhere! - result.append("\"); - break; default: result.push_back(c); break; @@ -1028,7 +1216,7 @@ static void GenerateDocCommentBodyForLocation( if (!comments.empty()) { // TODO(teboring): Ideally we should parse the comment text as Markdown and // write it back as HTML, but this requires a Markdown parser. For now - // we just use <pre> to get fixed-width text formatting. + // we just use the proto comments unchanged. // If the comment itself contains block comment start or end markers, // HTML-escape them so that they don't accidentally close the doc comment. @@ -1039,7 +1227,6 @@ static void GenerateDocCommentBodyForLocation( lines.pop_back(); } - printer->Print(" * <pre>\n"); for (int i = 0; i < lines.size(); i++) { // Most lines should start with a space. Watch out for lines that start // with a /, since putting that right after the leading asterisk will @@ -1051,7 +1238,6 @@ static void GenerateDocCommentBodyForLocation( } } printer->Print( - " * </pre>\n" " *\n"); } } @@ -1077,17 +1263,28 @@ static string FirstLineOf(const string& value) { } void GenerateMessageDocComment(io::Printer* printer, - const Descriptor* message) { + const Descriptor* message, int is_descriptor) { printer->Print("/**\n"); GenerateDocCommentBody(printer, message); printer->Print( + " * Generated from protobuf message <code>^messagename^</code>\n" + " */\n", + "fullname", EscapePhpdoc(PhpName(message->full_name(), is_descriptor)), + "messagename", EscapePhpdoc(message->full_name())); +} + +void GenerateServiceDocComment(io::Printer* printer, + const ServiceDescriptor* service) { + printer->Print("/**\n"); + GenerateDocCommentBody(printer, service); + printer->Print( " * Protobuf type <code>^fullname^</code>\n" " */\n", - "fullname", EscapePhpdoc(message->full_name())); + "fullname", EscapePhpdoc(service->full_name())); } -void GenerateFieldDocComment(io::Printer* printer, - const FieldDescriptor* field) { +void GenerateFieldDocComment(io::Printer* printer, const FieldDescriptor* field, + int is_descriptor, int function_type) { // In theory we should have slightly different comments for setters, getters, // etc., but in practice everyone already knows the difference between these // so it's redundant information. @@ -1099,18 +1296,27 @@ void GenerateFieldDocComment(io::Printer* printer, printer->Print("/**\n"); GenerateDocCommentBody(printer, field); printer->Print( - " * <code>^def^</code>\n", + " * Generated from protobuf field <code>^def^</code>\n", "def", EscapePhpdoc(FirstLineOf(field->DebugString()))); + if (function_type == kFieldSetter) { + printer->Print(" * @param ^php_type^ $var\n", + "php_type", PhpSetterTypeName(field, is_descriptor)); + printer->Print(" * @return $this\n"); + } else if (function_type == kFieldGetter) { + printer->Print(" * @return ^php_type^\n", + "php_type", PhpGetterTypeName(field, is_descriptor)); + } printer->Print(" */\n"); } -void GenerateEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_) { +void GenerateEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_, + int is_descriptor) { printer->Print("/**\n"); GenerateDocCommentBody(printer, enum_); printer->Print( " * Protobuf enum <code>^fullname^</code>\n" " */\n", - "fullname", EscapePhpdoc(enum_->full_name())); + "fullname", EscapePhpdoc(PhpName(enum_->full_name(), is_descriptor))); } void GenerateEnumValueDocComment(io::Printer* printer, @@ -1118,11 +1324,28 @@ void GenerateEnumValueDocComment(io::Printer* printer, printer->Print("/**\n"); GenerateDocCommentBody(printer, value); printer->Print( - " * <code>^def^</code>\n" + " * Generated from protobuf enum <code>^def^</code>\n" " */\n", "def", EscapePhpdoc(FirstLineOf(value->DebugString()))); } +void GenerateServiceMethodDocComment(io::Printer* printer, + const MethodDescriptor* method) { + printer->Print("/**\n"); + GenerateDocCommentBody(printer, method); + printer->Print( + " * Method <code>^method_name^</code>\n" + " *\n", + "method_name", EscapePhpdoc(UnderscoresToCamelCase(method->name(), false))); + printer->Print( + " * @param \\^input_type^ $request\n", + "input_type", EscapePhpdoc(FullClassName(method->input_type(), false))); + printer->Print( + " * @return \\^return_type^\n" + " */\n", + "return_type", EscapePhpdoc(FullClassName(method->output_type(), false))); +} + bool Generator::Generate(const FileDescriptor* file, const string& parameter, GeneratorContext* generator_context, string* error) const { |