|
|
|
@ -30,8 +30,10 @@ |
|
|
|
|
|
|
|
|
|
#include "google/protobuf/compiler/php/php_generator.h" |
|
|
|
|
|
|
|
|
|
#include <algorithm> |
|
|
|
|
#include <sstream> |
|
|
|
|
#include <string> |
|
|
|
|
#include <vector> |
|
|
|
|
|
|
|
|
|
#include "google/protobuf/compiler/code_generator.h" |
|
|
|
|
#include "absl/container/flat_hash_map.h" |
|
|
|
@ -39,6 +41,7 @@ |
|
|
|
|
#include "google/protobuf/stubs/logging.h" |
|
|
|
|
#include "absl/strings/ascii.h" |
|
|
|
|
#include "absl/strings/escaping.h" |
|
|
|
|
#include "absl/strings/str_cat.h" |
|
|
|
|
#include "absl/strings/str_replace.h" |
|
|
|
|
#include "absl/strings/str_split.h" |
|
|
|
|
#include "absl/strings/string_view.h" |
|
|
|
@ -79,12 +82,12 @@ struct Options { |
|
|
|
|
namespace { |
|
|
|
|
|
|
|
|
|
// Forward decls.
|
|
|
|
|
std::string PhpName(const std::string& full_name, const Options& options); |
|
|
|
|
std::string PhpName(absl::string_view full_name, const Options& options); |
|
|
|
|
std::string IntToString(int32_t value); |
|
|
|
|
std::string FilenameToClassname(const std::string& filename); |
|
|
|
|
std::string FilenameToClassname(absl::string_view filename); |
|
|
|
|
std::string GeneratedMetadataFileName(const FileDescriptor* file, |
|
|
|
|
const Options& options); |
|
|
|
|
std::string UnderscoresToCamelCase(const std::string& name, |
|
|
|
|
std::string UnderscoresToCamelCase(absl::string_view name, |
|
|
|
|
bool cap_first_letter); |
|
|
|
|
void Indent(io::Printer* printer); |
|
|
|
|
void Outdent(io::Printer* printer); |
|
|
|
@ -136,10 +139,10 @@ std::string LegacyGeneratedClassName(const DescriptorType* desc) { |
|
|
|
|
return ClassNamePrefix(classname, desc) + classname; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::string ConstantNamePrefix(const std::string& classname) { |
|
|
|
|
std::string ConstantNamePrefix(absl::string_view classname) { |
|
|
|
|
bool is_reserved = false; |
|
|
|
|
|
|
|
|
|
std::string lower = classname; |
|
|
|
|
std::string lower(classname); |
|
|
|
|
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); |
|
|
|
|
|
|
|
|
|
is_reserved = IsReservedName(lower); |
|
|
|
@ -162,9 +165,9 @@ template <typename DescriptorType> |
|
|
|
|
std::string RootPhpNamespace(const DescriptorType* desc, |
|
|
|
|
const Options& options) { |
|
|
|
|
if (desc->file()->options().has_php_namespace()) { |
|
|
|
|
const std::string& php_namespace = desc->file()->options().php_namespace(); |
|
|
|
|
absl::string_view php_namespace = desc->file()->options().php_namespace(); |
|
|
|
|
if (!php_namespace.empty()) { |
|
|
|
|
return php_namespace; |
|
|
|
|
return std::string(php_namespace); |
|
|
|
|
} |
|
|
|
|
return ""; |
|
|
|
|
} |
|
|
|
@ -180,7 +183,7 @@ std::string FullClassName(const DescriptorType* desc, const Options& options) { |
|
|
|
|
std::string classname = GeneratedClassName(desc); |
|
|
|
|
std::string php_namespace = RootPhpNamespace(desc, options); |
|
|
|
|
if (!php_namespace.empty()) { |
|
|
|
|
return php_namespace + "\\" + classname; |
|
|
|
|
return absl::StrCat(php_namespace, "\\", classname); |
|
|
|
|
} |
|
|
|
|
return classname; |
|
|
|
|
} |
|
|
|
@ -198,17 +201,17 @@ std::string LegacyFullClassName(const DescriptorType* desc, |
|
|
|
|
std::string classname = LegacyGeneratedClassName(desc); |
|
|
|
|
std::string php_namespace = RootPhpNamespace(desc, options); |
|
|
|
|
if (!php_namespace.empty()) { |
|
|
|
|
return php_namespace + "\\" + classname; |
|
|
|
|
return absl::StrCat(php_namespace, "\\", classname); |
|
|
|
|
} |
|
|
|
|
return classname; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::string PhpNamePrefix(const std::string& classname) { |
|
|
|
|
std::string PhpNamePrefix(absl::string_view classname) { |
|
|
|
|
if (IsReservedName(classname)) return "PB"; |
|
|
|
|
return ""; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::string PhpName(const std::string& full_name, const Options& options) { |
|
|
|
|
std::string PhpName(absl::string_view full_name, const Options& options) { |
|
|
|
|
if (options.is_descriptor) { |
|
|
|
|
return std::string(kDescriptorPackageName); |
|
|
|
|
} |
|
|
|
@ -259,7 +262,7 @@ std::string DefaultForField(const FieldDescriptor* field) { |
|
|
|
|
|
|
|
|
|
std::string GeneratedMetadataFileName(const FileDescriptor* file, |
|
|
|
|
const Options& options) { |
|
|
|
|
const std::string& proto_file = file->name(); |
|
|
|
|
absl::string_view proto_file = file->name(); |
|
|
|
|
int start_index = 0; |
|
|
|
|
int first_index = proto_file.find_first_of('/', start_index); |
|
|
|
|
std::string result = ""; |
|
|
|
@ -273,7 +276,7 @@ std::string GeneratedMetadataFileName(const FileDescriptor* file, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Append directory name.
|
|
|
|
|
std::string file_no_suffix; |
|
|
|
|
absl::string_view file_no_suffix; |
|
|
|
|
int lastindex = proto_file.find_last_of('.'); |
|
|
|
|
if (proto_file == kEmptyFile) { |
|
|
|
|
return std::string(kEmptyMetadataFile); |
|
|
|
@ -282,21 +285,21 @@ std::string GeneratedMetadataFileName(const FileDescriptor* file, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (file->options().has_php_metadata_namespace()) { |
|
|
|
|
const std::string& php_metadata_namespace = |
|
|
|
|
absl::string_view php_metadata_namespace = |
|
|
|
|
file->options().php_metadata_namespace(); |
|
|
|
|
if (!php_metadata_namespace.empty() && php_metadata_namespace != "\\") { |
|
|
|
|
result += php_metadata_namespace; |
|
|
|
|
absl::StrAppend(&result, php_metadata_namespace); |
|
|
|
|
std::replace(result.begin(), result.end(), '\\', '/'); |
|
|
|
|
if (result.at(result.size() - 1) != '/') { |
|
|
|
|
result += "/"; |
|
|
|
|
absl::StrAppend(&result, "/"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
result += "GPBMetadata/"; |
|
|
|
|
absl::StrAppend(&result, "GPBMetadata/"); |
|
|
|
|
while (first_index != std::string::npos) { |
|
|
|
|
segment = UnderscoresToCamelCase( |
|
|
|
|
file_no_suffix.substr(start_index, first_index - start_index), true); |
|
|
|
|
result += ReservedNamePrefix(segment, file) + segment + "/"; |
|
|
|
|
absl::StrAppend(&result, ReservedNamePrefix(segment, file), segment, "/"); |
|
|
|
|
start_index = first_index + 1; |
|
|
|
|
first_index = file_no_suffix.find_first_of('/', start_index); |
|
|
|
|
} |
|
|
|
@ -312,7 +315,8 @@ std::string GeneratedMetadataFileName(const FileDescriptor* file, |
|
|
|
|
segment = UnderscoresToCamelCase( |
|
|
|
|
file_no_suffix.substr(file_name_start, first_index - file_name_start), true); |
|
|
|
|
|
|
|
|
|
return result + ReservedNamePrefix(segment, file) + segment + ".php"; |
|
|
|
|
return absl::StrCat(result, ReservedNamePrefix(segment, file), segment, |
|
|
|
|
".php"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::string GeneratedMetadataFileName(const FileDescriptor* file, |
|
|
|
@ -331,7 +335,7 @@ std::string GeneratedClassFileName(const DescriptorType* desc, |
|
|
|
|
result[i] = '/'; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return result + ".php"; |
|
|
|
|
return absl::StrCat(result, ".php"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <typename DescriptorType> |
|
|
|
@ -344,7 +348,7 @@ std::string LegacyGeneratedClassFileName(const DescriptorType* desc, |
|
|
|
|
result[i] = '/'; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return result + ".php"; |
|
|
|
|
return absl::StrCat(result, ".php"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <typename DescriptorType> |
|
|
|
@ -356,21 +360,21 @@ std::string LegacyReadOnlyGeneratedClassFileName(std::string php_namespace, |
|
|
|
|
php_namespace[i] = '/'; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return php_namespace + "/" + desc->name() + ".php"; |
|
|
|
|
return absl::StrCat(php_namespace, "/", desc->name(), ".php"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return desc->name() + ".php"; |
|
|
|
|
return absl::StrCat(desc->name(), ".php"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::string GeneratedServiceFileName(const ServiceDescriptor* service, |
|
|
|
|
const Options& options) { |
|
|
|
|
std::string result = FullClassName(service, options) + "Interface"; |
|
|
|
|
std::string result = FullClassName(service, options); |
|
|
|
|
for (int i = 0; i < result.size(); i++) { |
|
|
|
|
if (result[i] == '\\') { |
|
|
|
|
result[i] = '/'; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return result + ".php"; |
|
|
|
|
return absl::StrCat(result, "Interface", ".php"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::string IntToString(int32_t value) { |
|
|
|
@ -422,7 +426,7 @@ std::string PhpSetterTypeName(const FieldDescriptor* field, |
|
|
|
|
type = "string"; |
|
|
|
|
break; |
|
|
|
|
case FieldDescriptor::TYPE_MESSAGE: |
|
|
|
|
type = "\\" + FullClassName(field->message_type(), options); |
|
|
|
|
type = absl::StrCat("\\", FullClassName(field->message_type(), options)); |
|
|
|
|
break; |
|
|
|
|
case FieldDescriptor::TYPE_GROUP: |
|
|
|
|
return "null"; |
|
|
|
@ -434,7 +438,8 @@ std::string PhpSetterTypeName(const FieldDescriptor* field, |
|
|
|
|
if (start_pos != std::string::npos) { |
|
|
|
|
type.replace(start_pos, 1, ">|array<"); |
|
|
|
|
} |
|
|
|
|
type = "array<" + type + ">|\\Google\\Protobuf\\Internal\\RepeatedField"; |
|
|
|
|
type = absl::StrCat("array<", type, |
|
|
|
|
">|\\Google\\Protobuf\\Internal\\RepeatedField"); |
|
|
|
|
} |
|
|
|
|
return type; |
|
|
|
|
} |
|
|
|
@ -472,7 +477,7 @@ std::string PhpGetterTypeName(const FieldDescriptor* field, |
|
|
|
|
case FieldDescriptor::TYPE_STRING: |
|
|
|
|
case FieldDescriptor::TYPE_BYTES: return "string"; |
|
|
|
|
case FieldDescriptor::TYPE_MESSAGE: |
|
|
|
|
return "\\" + FullClassName(field->message_type(), options); |
|
|
|
|
return absl::StrCat("\\", FullClassName(field->message_type(), options)); |
|
|
|
|
case FieldDescriptor::TYPE_GROUP: return "null"; |
|
|
|
|
default: assert(false); return ""; |
|
|
|
|
} |
|
|
|
@ -488,13 +493,14 @@ std::string PhpGetterTypeName(const FieldDescriptor* field, |
|
|
|
|
std::string EnumOrMessageSuffix(const FieldDescriptor* field, |
|
|
|
|
const Options& options) { |
|
|
|
|
if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { |
|
|
|
|
return ", '" + |
|
|
|
|
DescriptorFullName(field->message_type(), options.is_descriptor) + |
|
|
|
|
"'"; |
|
|
|
|
return absl::StrCat( |
|
|
|
|
", '", DescriptorFullName(field->message_type(), options.is_descriptor), |
|
|
|
|
"'"); |
|
|
|
|
} |
|
|
|
|
if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { |
|
|
|
|
return ", '" + |
|
|
|
|
DescriptorFullName(field->enum_type(), options.is_descriptor) + "'"; |
|
|
|
|
return absl::StrCat( |
|
|
|
|
", '", DescriptorFullName(field->enum_type(), options.is_descriptor), |
|
|
|
|
"'"); |
|
|
|
|
} |
|
|
|
|
return ""; |
|
|
|
|
} |
|
|
|
@ -508,7 +514,7 @@ std::string EnumOrMessageSuffix(const FieldDescriptor* field, |
|
|
|
|
|
|
|
|
|
// Converts a name to camel-case. If cap_first_letter is true, capitalize the
|
|
|
|
|
// first letter.
|
|
|
|
|
std::string UnderscoresToCamelCase(const std::string& name, |
|
|
|
|
std::string UnderscoresToCamelCase(absl::string_view name, |
|
|
|
|
bool cap_first_letter) { |
|
|
|
|
std::string result; |
|
|
|
|
for (int i = 0; i < name.size(); i++) { |
|
|
|
@ -589,8 +595,11 @@ void GenerateFieldAccessor(const FieldDescriptor* field, const Options& options, |
|
|
|
|
GenerateFieldDocComment(printer, field, options, kFieldGetter); |
|
|
|
|
|
|
|
|
|
// deprecation
|
|
|
|
|
std::string deprecation_trigger = (field->options().deprecated()) ? "@trigger_error('" + |
|
|
|
|
field->name() + " is deprecated.', E_USER_DEPRECATED);\n " : ""; |
|
|
|
|
std::string deprecation_trigger = |
|
|
|
|
(field->options().deprecated()) |
|
|
|
|
? absl::StrCat("@trigger_error('", field->name(), |
|
|
|
|
" is deprecated.', E_USER_DEPRECATED);\n ") |
|
|
|
|
: ""; |
|
|
|
|
|
|
|
|
|
// Emit getter.
|
|
|
|
|
if (oneof != NULL) { |
|
|
|
@ -693,15 +702,13 @@ void GenerateFieldAccessor(const FieldDescriptor* field, const Options& options, |
|
|
|
|
"key_type", absl::AsciiStrToUpper(key->type_name()), |
|
|
|
|
"value_type", absl::AsciiStrToUpper(value->type_name())); |
|
|
|
|
if (value->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { |
|
|
|
|
printer->Print( |
|
|
|
|
", \\^class_name^);\n", |
|
|
|
|
"class_name", |
|
|
|
|
FullClassName(value->message_type(), options) + "::class"); |
|
|
|
|
printer->Print(", \\^class_name^);\n", "class_name", |
|
|
|
|
absl::StrCat(FullClassName(value->message_type(), options), |
|
|
|
|
"::class")); |
|
|
|
|
} else if (value->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { |
|
|
|
|
printer->Print( |
|
|
|
|
", \\^class_name^);\n", |
|
|
|
|
"class_name", |
|
|
|
|
FullClassName(value->enum_type(), options) + "::class"); |
|
|
|
|
", \\^class_name^);\n", "class_name", |
|
|
|
|
absl::StrCat(FullClassName(value->enum_type(), options), "::class")); |
|
|
|
|
} else { |
|
|
|
|
printer->Print(");\n"); |
|
|
|
|
} |
|
|
|
@ -711,15 +718,13 @@ void GenerateFieldAccessor(const FieldDescriptor* field, const Options& options, |
|
|
|
|
"\\Google\\Protobuf\\Internal\\GPBType::^type^", |
|
|
|
|
"type", absl::AsciiStrToUpper(field->type_name())); |
|
|
|
|
if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { |
|
|
|
|
printer->Print( |
|
|
|
|
", \\^class_name^);\n", |
|
|
|
|
"class_name", |
|
|
|
|
FullClassName(field->message_type(), options) + "::class"); |
|
|
|
|
printer->Print(", \\^class_name^);\n", "class_name", |
|
|
|
|
absl::StrCat(FullClassName(field->message_type(), options), |
|
|
|
|
"::class")); |
|
|
|
|
} else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { |
|
|
|
|
printer->Print( |
|
|
|
|
", \\^class_name^);\n", |
|
|
|
|
"class_name", |
|
|
|
|
FullClassName(field->enum_type(), options) + "::class"); |
|
|
|
|
", \\^class_name^);\n", "class_name", |
|
|
|
|
absl::StrCat(FullClassName(field->enum_type(), options), "::class")); |
|
|
|
|
} else { |
|
|
|
|
printer->Print(");\n"); |
|
|
|
|
} |
|
|
|
@ -809,16 +814,16 @@ void GenerateServiceMethod(const MethodDescriptor* method, |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void GenerateMessageToPool(const std::string& name_prefix, |
|
|
|
|
void GenerateMessageToPool(absl::string_view name_prefix, |
|
|
|
|
const Descriptor* message, io::Printer* printer) { |
|
|
|
|
// Don't generate MapEntry messages -- we use the PHP extension's native
|
|
|
|
|
// support for map fields instead.
|
|
|
|
|
if (message->options().map_entry()) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
std::string class_name = |
|
|
|
|
(name_prefix.empty() ? "" : name_prefix + "\\") + |
|
|
|
|
ReservedNamePrefix(message->name(), message->file()) + message->name(); |
|
|
|
|
std::string class_name = absl::StrCat( |
|
|
|
|
name_prefix.empty() ? "" : absl::StrCat(name_prefix, "\\"), |
|
|
|
|
ReservedNamePrefix(message->name(), message->file()), message->name()); |
|
|
|
|
|
|
|
|
|
printer->Print( |
|
|
|
|
"$pool->addMessage('^message^', " |
|
|
|
@ -920,7 +925,7 @@ void GenerateAddFileToPool(const FileDescriptor* file, const Options& options, |
|
|
|
|
"$pool->finish();\n"); |
|
|
|
|
} else { |
|
|
|
|
for (int i = 0; i < file->dependency_count(); i++) { |
|
|
|
|
const std::string& name = file->dependency(i)->name(); |
|
|
|
|
absl::string_view name = file->dependency(i)->name(); |
|
|
|
|
// Currently, descriptor.proto is not ready for external usage. Skip to
|
|
|
|
|
// import it for now, so that its dependencies can still work as long as
|
|
|
|
|
// they don't use protos defined in descriptor.proto.
|
|
|
|
@ -1164,10 +1169,10 @@ void GenerateHead(const FileDescriptor* file, io::Printer* printer) { |
|
|
|
|
"filename", file->name()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::string FilenameToClassname(const std::string& filename) { |
|
|
|
|
int lastindex = filename.find_last_of('.'); |
|
|
|
|
std::string result = filename.substr(0, lastindex); |
|
|
|
|
for (int i = 0; i < result.size(); i++) { |
|
|
|
|
std::string FilenameToClassname(absl::string_view filename) { |
|
|
|
|
size_t lastindex = filename.find_last_of('.'); |
|
|
|
|
std::string result(filename.substr(0, lastindex)); |
|
|
|
|
for (size_t i = 0; i < result.size(); i++) { |
|
|
|
|
if (result[i] == '/') { |
|
|
|
|
result[i] = '\\'; |
|
|
|
|
} |
|
|
|
@ -1622,7 +1627,7 @@ void GenerateFile(const FileDescriptor* file, const Options& options, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static std::string EscapePhpdoc(const std::string& input) { |
|
|
|
|
static std::string EscapePhpdoc(absl::string_view input) { |
|
|
|
|
std::string result; |
|
|
|
|
result.reserve(input.size() * 2); |
|
|
|
|
|
|
|
|
@ -1679,7 +1684,8 @@ static void GenerateDocCommentBodyForLocation( |
|
|
|
|
// HTML-escape them so that they don't accidentally close the doc comment.
|
|
|
|
|
comments = EscapePhpdoc(comments); |
|
|
|
|
|
|
|
|
|
std::vector<std::string> lines = absl::StrSplit(comments, "\n", absl::SkipEmpty()); |
|
|
|
|
std::vector<absl::string_view> lines = |
|
|
|
|
absl::StrSplit(comments, '\n', absl::SkipEmpty()); |
|
|
|
|
while (!lines.empty() && lines.back().empty()) { |
|
|
|
|
lines.pop_back(); |
|
|
|
|
} |
|
|
|
@ -1710,8 +1716,8 @@ static void GenerateDocCommentBody( |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static std::string FirstLineOf(const std::string& value) { |
|
|
|
|
std::string result = value; |
|
|
|
|
static std::string FirstLineOf(absl::string_view value) { |
|
|
|
|
std::string result(value); |
|
|
|
|
|
|
|
|
|
std::string::size_type pos = result.find_first_of('\n'); |
|
|
|
|
if (pos != std::string::npos) { |
|
|
|
|