aboutsummaryrefslogtreecommitdiff
path: root/src/google/protobuf/compiler/php/php_generator.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/google/protobuf/compiler/php/php_generator.cc')
-rw-r--r--src/google/protobuf/compiler/php/php_generator.cc786
1 files changed, 547 insertions, 239 deletions
diff --git a/src/google/protobuf/compiler/php/php_generator.cc b/src/google/protobuf/compiler/php/php_generator.cc
index 75ddb405..ec9a2365 100644
--- a/src/google/protobuf/compiler/php/php_generator.cc
+++ b/src/google/protobuf/compiler/php/php_generator.cc
@@ -43,6 +43,11 @@
using google::protobuf::internal::scoped_ptr;
const std::string kDescriptorFile = "google/protobuf/descriptor.proto";
+const std::string kEmptyFile = "google/protobuf/empty.proto";
+const std::string kEmptyMetadataFile = "GPBMetadata/Google/Protobuf/GPBEmpty.php";
+const std::string kDescriptorMetadataFile =
+ "GPBMetadata/Google/Protobuf/Internal/Descriptor.php";
+const std::string kDescriptorDirName = "Google/Protobuf/Internal";
const std::string kDescriptorPackageName = "Google\\Protobuf\\Internal";
namespace google {
@@ -52,38 +57,57 @@ namespace php {
// Forward decls.
std::string PhpName(const std::string& full_name, bool is_descriptor);
-std::string DefaultForField(google::protobuf::FieldDescriptor* field);
+std::string DefaultForField(FieldDescriptor* field);
std::string IntToString(int32 value);
-std::string GeneratedFileName(const std::string& proto_file,
- bool is_descriptor);
-std::string LabelForField(google::protobuf::FieldDescriptor* field);
-std::string TypeName(google::protobuf::FieldDescriptor* field);
+std::string FilenameToClassname(const string& filename);
+std::string GeneratedMetadataFileName(const std::string& proto_file,
+ bool is_descriptor);
+std::string LabelForField(FieldDescriptor* field);
+std::string TypeName(FieldDescriptor* field);
std::string UnderscoresToCamelCase(const string& name, bool cap_first_letter);
std::string EscapeDollor(const string& to_escape);
std::string BinaryToHex(const string& binary);
-void GenerateMessage(const string& name_prefix,
- const google::protobuf::Descriptor* message,
- bool is_descriptor,
- google::protobuf::io::Printer* printer);
-void GenerateEnum(const google::protobuf::EnumDescriptor* en,
- google::protobuf::io::Printer* printer);
-void Indent(google::protobuf::io::Printer* printer);
-void Outdent(google::protobuf::io::Printer* printer);
-
-std::string MessageName(const google::protobuf::Descriptor* message,
- bool is_descriptor) {
+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 GenerateEnumValueDocComment(io::Printer* printer,
+ const EnumValueDescriptor* value);
+
+std::string RenameEmpty(const std::string& name) {
+ if (name == "Empty") {
+ return "GPBEmpty";
+ } else {
+ return name;
+ }
+}
+
+std::string MessagePrefix(const Descriptor* message) {
+ // Empty cannot be php class name.
+ if (message->name() == "Empty" &&
+ message->file()->package() == "google.protobuf") {
+ return "GPB";
+ } else {
+ return "";
+ }
+}
+
+std::string MessageName(const Descriptor* message, bool is_descriptor) {
string message_name = message->name();
- const google::protobuf::Descriptor* descriptor = message->containing_type();
+ const Descriptor* descriptor = message->containing_type();
while (descriptor != NULL) {
message_name = descriptor->name() + '_' + message_name;
descriptor = descriptor->containing_type();
}
+ message_name = MessagePrefix(message) + message_name;
+
return PhpName(message->file()->package(), is_descriptor) + '\\' +
message_name;
}
-std::string MessageFullName(const google::protobuf::Descriptor* message,
- bool is_descriptor) {
+std::string MessageFullName(const Descriptor* message, bool is_descriptor) {
if (is_descriptor) {
return StringReplace(message->full_name(),
"google.protobuf",
@@ -93,8 +117,7 @@ std::string MessageFullName(const google::protobuf::Descriptor* message,
}
}
-std::string EnumFullName(const google::protobuf::EnumDescriptor* envm,
- bool is_descriptor) {
+std::string EnumFullName(const EnumDescriptor* envm, bool is_descriptor) {
if (is_descriptor) {
return StringReplace(envm->full_name(),
"google.protobuf",
@@ -104,9 +127,9 @@ std::string EnumFullName(const google::protobuf::EnumDescriptor* envm,
}
}
-std::string EnumClassName(const google::protobuf::EnumDescriptor* envm) {
+std::string EnumClassName(const EnumDescriptor* envm) {
string enum_class_name = envm->name();
- const google::protobuf::Descriptor* descriptor = envm->containing_type();
+ const Descriptor* descriptor = envm->containing_type();
while (descriptor != NULL) {
enum_class_name = descriptor->name() + '_' + enum_class_name;
descriptor = descriptor->containing_type();
@@ -114,8 +137,7 @@ std::string EnumClassName(const google::protobuf::EnumDescriptor* envm) {
return enum_class_name;
}
-std::string EnumName(const google::protobuf::EnumDescriptor* envm,
- bool is_descriptor) {
+std::string EnumName(const EnumDescriptor* envm, bool is_descriptor) {
string enum_name = EnumClassName(envm);
return PhpName(envm->file()->package(), is_descriptor) + '\\' + enum_name;
}
@@ -142,7 +164,7 @@ std::string PhpName(const std::string& full_name, bool is_descriptor) {
return result;
}
-std::string DefaultForField(const google::protobuf::FieldDescriptor* field) {
+std::string DefaultForField(const FieldDescriptor* field) {
switch (field->type()) {
case FieldDescriptor::TYPE_INT32:
case FieldDescriptor::TYPE_INT64:
@@ -166,14 +188,63 @@ std::string DefaultForField(const google::protobuf::FieldDescriptor* field) {
}
}
-std::string GeneratedFileName(const std::string& proto_file,
- bool is_descriptor) {
+std::string GeneratedMetadataFileName(const std::string& proto_file,
+ bool is_descriptor) {
+ int start_index = 0;
+ int first_index = proto_file.find_first_of("/", start_index);
+ std::string result = "GPBMetadata/";
+
+ if (proto_file == kEmptyFile) {
+ return kEmptyMetadataFile;
+ }
if (is_descriptor) {
- return "descriptor_internal.pb.php";
+ return kDescriptorMetadataFile;
+ }
+
+ // Append directory name.
+ std::string file_no_suffix;
+ int lastindex = proto_file.find_last_of(".");
+ if (proto_file == kEmptyFile) {
+ return kEmptyMetadataFile;
} else {
- int lastindex = proto_file.find_last_of(".");
- return proto_file.substr(0, lastindex) + ".pb.php";
+ file_no_suffix = proto_file.substr(0, lastindex);
+ }
+
+ while (first_index != string::npos) {
+ result += UnderscoresToCamelCase(
+ file_no_suffix.substr(start_index, first_index - start_index), true);
+ result += "/";
+ start_index = first_index + 1;
+ first_index = file_no_suffix.find_first_of("/", start_index);
+ }
+
+ // Append file name.
+ result += RenameEmpty(UnderscoresToCamelCase(
+ file_no_suffix.substr(start_index, first_index - start_index), true));
+
+ return result += ".php";
+}
+
+std::string GeneratedMessageFileName(const Descriptor* message,
+ bool is_descriptor) {
+ std::string result = MessageName(message, is_descriptor);
+ for (int i = 0; i < result.size(); i++) {
+ if (result[i] == '\\') {
+ result[i] = '/';
+ }
}
+ return result + ".php";
+}
+
+std::string GeneratedEnumFileName(const EnumDescriptor* en,
+ bool is_descriptor) {
+ std::string result = EnumName(en, is_descriptor);
+ for (int i = 0; i < result.size(); i++) {
+ if (result[i] == '\\') {
+ result[i] = '/';
+ }
+ }
+ return result + ".php";
}
std::string IntToString(int32 value) {
@@ -182,7 +253,7 @@ std::string IntToString(int32 value) {
return os.str();
}
-std::string LabelForField(const google::protobuf::FieldDescriptor* field) {
+std::string LabelForField(const FieldDescriptor* field) {
switch (field->label()) {
case FieldDescriptor::LABEL_OPTIONAL: return "optional";
case FieldDescriptor::LABEL_REQUIRED: return "required";
@@ -191,7 +262,7 @@ std::string LabelForField(const google::protobuf::FieldDescriptor* field) {
}
}
-std::string TypeName(const google::protobuf::FieldDescriptor* field) {
+std::string TypeName(const FieldDescriptor* field) {
switch (field->type()) {
case FieldDescriptor::TYPE_INT32: return "int32";
case FieldDescriptor::TYPE_INT64: return "int64";
@@ -216,7 +287,7 @@ std::string TypeName(const google::protobuf::FieldDescriptor* field) {
}
std::string EnumOrMessageSuffix(
- const google::protobuf::FieldDescriptor* field, bool is_descriptor) {
+ const FieldDescriptor* field, bool is_descriptor) {
if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
return ", '" + MessageFullName(field->message_type(), is_descriptor) + "'";
}
@@ -287,74 +358,77 @@ std::string BinaryToHex(const string& src) {
return dest;
}
-void Indent(google::protobuf::io::Printer* printer) {
+void Indent(io::Printer* printer) {
printer->Indent();
printer->Indent();
}
-void Outdent(google::protobuf::io::Printer* printer) {
+void Outdent(io::Printer* printer) {
printer->Outdent();
printer->Outdent();
}
-void GenerateField(const google::protobuf::FieldDescriptor* field,
- google::protobuf::io::Printer* printer, bool is_descriptor) {
+void GenerateField(const FieldDescriptor* field, io::Printer* printer,
+ bool is_descriptor) {
if (field->is_repeated()) {
+ GenerateFieldDocComment(printer, field);
printer->Print(
- "private $@name@;\n",
+ "private $^name^;\n",
"name", field->name());
} else if (field->containing_oneof()) {
// Oneof fields are handled by GenerateOneofField.
return;
} else {
+ GenerateFieldDocComment(printer, field);
printer->Print(
- "private $@name@ = @default@;\n",
+ "private $^name^ = ^default^;\n",
"name", field->name(),
"default", DefaultForField(field));
}
if (is_descriptor) {
printer->Print(
- "private $has_@name@ = false;\n",
+ "private $has_^name^ = false;\n",
"name", field->name());
}
}
-void GenerateOneofField(const google::protobuf::OneofDescriptor* oneof,
- google::protobuf::io::Printer* printer) {
+void GenerateOneofField(const OneofDescriptor* oneof, io::Printer* printer) {
// Oneof property needs to be protected in order to be accessed by parent
// class in implementation.
printer->Print(
- "protected $@name@;\n",
+ "protected $^name^;\n",
"name", oneof->name());
}
-void GenerateFieldAccessor(const google::protobuf::FieldDescriptor* field,
- bool is_descriptor,
- google::protobuf::io::Printer* printer) {
+void GenerateFieldAccessor(const FieldDescriptor* field, bool is_descriptor,
+ io::Printer* printer) {
const OneofDescriptor* oneof = field->containing_oneof();
// Generate getter.
if (oneof != NULL) {
+ GenerateFieldDocComment(printer, field);
printer->Print(
- "public function get@camel_name@()\n"
+ "public function get^camel_name^()\n"
"{\n"
- " return $this->readOneof(@number@);\n"
+ " return $this->readOneof(^number^);\n"
"}\n\n",
"camel_name", UnderscoresToCamelCase(field->name(), true),
"number", IntToString(field->number()));
} else {
+ GenerateFieldDocComment(printer, field);
printer->Print(
- "public function get@camel_name@()\n"
+ "public function get^camel_name^()\n"
"{\n"
- " return $this->@name@;\n"
+ " return $this->^name^;\n"
"}\n\n",
"camel_name", UnderscoresToCamelCase(field->name(), true), "name",
field->name());
}
// Generate setter.
+ GenerateFieldDocComment(printer, field);
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() ||
@@ -367,16 +441,17 @@ void GenerateFieldAccessor(const google::protobuf::FieldDescriptor* field,
if (field->is_map()) {
} else if (field->is_repeated()) {
printer->Print(
- "GPBUtil::checkRepeatedField($var, GPBType::@type@",
+ "GPBUtil::checkRepeatedField($var, "
+ "\\Google\\Protobuf\\Internal\\GPBType::^type^",
"type", ToUpper(field->type_name()));
if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
printer->Print(
- ", \\@class_name@);\n",
+ ", \\^class_name^);\n",
"class_name",
MessageName(field->message_type(), is_descriptor) + "::class");
} else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
printer->Print(
- ", @class_name@);\n",
+ ", ^class_name^);\n",
"class_name",
EnumName(field->enum_type(), is_descriptor) + "::class");
} else {
@@ -384,37 +459,37 @@ void GenerateFieldAccessor(const google::protobuf::FieldDescriptor* field,
}
} else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
printer->Print(
- "GPBUtil::checkMessage($var, \\@class_name@::class);\n",
+ "GPBUtil::checkMessage($var, \\^class_name^::class);\n",
"class_name", MessageName(field->message_type(), is_descriptor));
} else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
printer->Print(
- "GPBUtil::checkEnum($var, \\@class_name@::class);\n",
+ "GPBUtil::checkEnum($var, \\^class_name^::class);\n",
"class_name", EnumName(field->enum_type(), is_descriptor));
} else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
printer->Print(
- "GPBUtil::checkString($var, @utf8@);\n",
+ "GPBUtil::checkString($var, ^utf8^);\n",
"utf8",
field->type() == FieldDescriptor::TYPE_STRING ? "True": "False");
} else {
printer->Print(
- "GPBUtil::check@type@($var);\n",
+ "GPBUtil::check^type^($var);\n",
"type", UnderscoresToCamelCase(field->cpp_type_name(), true));
}
if (oneof != NULL) {
printer->Print(
- "$this->writeOneof(@number@, $var);\n",
+ "$this->writeOneof(^number^, $var);\n",
"number", IntToString(field->number()));
} else {
printer->Print(
- "$this->@name@ = $var;\n",
+ "$this->^name^ = $var;\n",
"name", field->name());
}
// Set has bit for proto2 only.
if (is_descriptor) {
printer->Print(
- "$this->has_@field_name@ = true;\n",
+ "$this->has_^field_name^ = true;\n",
"field_name", field->name());
}
@@ -426,125 +501,27 @@ void GenerateFieldAccessor(const google::protobuf::FieldDescriptor* field,
// Generate has method for proto2 only.
if (is_descriptor) {
printer->Print(
- "public function has@camel_name@()\n"
+ "public function has^camel_name^()\n"
"{\n"
- " return $this->has_@field_name@;\n"
+ " return $this->has_^field_name^;\n"
"}\n\n",
"camel_name", UnderscoresToCamelCase(field->name(), true),
"field_name", field->name());
}
}
-void GenerateRepeatedFieldDecode(
- const google::protobuf::FieldDescriptor* field,
- google::protobuf::io::Printer* printer) {
- printer->Print(
- "if ($input->read@cap_wire_type@($var)) return False;\n"
- "$this->get@cap_field_name@() []= $var;\n",
- "cap_field_name", UnderscoresToCamelCase(field->name(), true),
- "cap_wire_type", UnderscoresToCamelCase(field->type_name(), true));
-}
-
-void GeneratePrimitiveFieldDecode(
- const google::protobuf::FieldDescriptor* field,
- google::protobuf::io::Printer* printer) {
- printer->Print(
- "if ($input->read@cap_wire_type@($var)) return False;\n"
- "$this->set@cap_field_name@($var);\n",
- "cap_field_name", UnderscoresToCamelCase(field->name(), true),
- "cap_wire_type", UnderscoresToCamelCase(field->type_name(), true));
-}
-
-void GenerateFieldDecode(const google::protobuf::FieldDescriptor* field,
- google::protobuf::io::Printer* printer) {
- printer->Print(
- "case @number@:\n",
- "number", IntToString(field->number()));
- Indent(printer);
-
- if (field->is_repeated()) {
- GenerateRepeatedFieldDecode(field, printer);
- } else {
- GeneratePrimitiveFieldDecode(field, printer);
- }
-
- printer->Print(
- "break;\n");
- Outdent(printer);
-}
-
-void GenerateMessage(const string& name_prefix,
- const google::protobuf::Descriptor* message,
- bool is_descriptor,
- google::protobuf::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;
- }
-
- string message_name = name_prefix.empty()?
- message->name() : name_prefix + "_" + message->name();
-
+void GenerateEnumToPool(const EnumDescriptor* en, io::Printer* printer) {
printer->Print(
- "class @name@ extends \\Google\\Protobuf\\Internal\\Message\n"
- "{\n",
- "name", message_name);
- Indent(printer);
-
- // Field and oneof definitions.
- for (int i = 0; i < message->field_count(); i++) {
- const FieldDescriptor* field = message->field(i);
- GenerateField(field, printer, is_descriptor);
- }
- for (int i = 0; i < message->oneof_decl_count(); i++) {
- const OneofDescriptor* oneof = message->oneof_decl(i);
- GenerateOneofField(oneof, printer);
- }
- printer->Print("\n");
-
- // Field and oneof accessors.
- for (int i = 0; i < message->field_count(); i++) {
- const FieldDescriptor* field = message->field(i);
- GenerateFieldAccessor(field, is_descriptor, printer);
- }
- for (int i = 0; i < message->oneof_decl_count(); i++) {
- const google::protobuf::OneofDescriptor* oneof = message->oneof_decl(i);
- printer->Print(
- "public function get@camel_name@()\n"
- "{\n"
- " return $this->@name@;\n"
- "}\n\n",
- "camel_name", UnderscoresToCamelCase(oneof->name(), true), "name",
- oneof->name());
- }
-
- Outdent(printer);
- printer->Print("}\n\n");
-
- // Nested messages and enums.
- for (int i = 0; i < message->nested_type_count(); i++) {
- GenerateMessage(message_name, message->nested_type(i), is_descriptor,
- printer);
- }
- for (int i = 0; i < message->enum_type_count(); i++) {
- GenerateEnum(message->enum_type(i), printer);
- }
-}
-
-void GenerateEnumToPool(const google::protobuf::EnumDescriptor* en,
- bool is_descriptor,
- google::protobuf::io::Printer* printer) {
- printer->Print(
- "$pool->addEnum('@name@', @class_name@::class)\n",
- "name", EnumFullName(en, is_descriptor),
+ "$pool->addEnum('^name^', "
+ "\\Google\\Protobuf\\Internal\\^class_name^::class)\n",
+ "name", EnumFullName(en, true),
"class_name", en->name());
Indent(printer);
for (int i = 0; i < en->value_count(); i++) {
const EnumValueDescriptor* value = en->value(i);
printer->Print(
- "->value(\"@name@\", @number@)\n",
+ "->value(\"^name^\", ^number^)\n",
"name", value->name(),
"number", IntToString(value->number()));
}
@@ -552,10 +529,8 @@ void GenerateEnumToPool(const google::protobuf::EnumDescriptor* en,
Outdent(printer);
}
-void GenerateMessageToPool(const string& name_prefix,
- const google::protobuf::Descriptor* message,
- bool is_descriptor,
- google::protobuf::io::Printer* printer) {
+void GenerateMessageToPool(const string& 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()) {
@@ -565,8 +540,9 @@ void GenerateMessageToPool(const string& name_prefix,
message->name() : name_prefix + "_" + message->name();
printer->Print(
- "$pool->addMessage('@message@', @class_name@::class)\n",
- "message", MessageFullName(message, is_descriptor),
+ "$pool->addMessage('^message^', "
+ "\\Google\\Protobuf\\Internal\\^class_name^::class)\n",
+ "message", MessageFullName(message, true),
"class_name", class_name);
Indent(printer);
@@ -579,38 +555,40 @@ void GenerateMessageToPool(const string& name_prefix,
const FieldDescriptor* val =
field->message_type()->FindFieldByName("value");
printer->Print(
- "->map('@field@', GPBType::@key@, "
- "GPBType::@value@, @number@@other@)\n",
+ "->map('^field^', \\Google\\Protobuf\\Internal\\GPBType::^key^, "
+ "\\Google\\Protobuf\\Internal\\GPBType::^value^, ^number^^other^)\n",
"field", field->name(),
"key", ToUpper(key->type_name()),
"value", ToUpper(val->type_name()),
"number", SimpleItoa(field->number()),
- "other", EnumOrMessageSuffix(val, is_descriptor));
+ "other", EnumOrMessageSuffix(val, true));
} else if (!field->containing_oneof()) {
printer->Print(
- "->@label@('@field@', GPBType::@type@, @number@@other@)\n",
+ "->^label^('^field^', "
+ "\\Google\\Protobuf\\Internal\\GPBType::^type^, ^number^^other^)\n",
"field", field->name(),
"label", LabelForField(field),
"type", ToUpper(field->type_name()),
"number", SimpleItoa(field->number()),
- "other", EnumOrMessageSuffix(field, is_descriptor));
+ "other", EnumOrMessageSuffix(field, true));
}
}
// oneofs.
for (int i = 0; i < message->oneof_decl_count(); i++) {
const OneofDescriptor* oneof = message->oneof_decl(i);
- printer->Print("->oneof(@name@)\n",
+ printer->Print("->oneof(^name^)\n",
"name", oneof->name());
Indent(printer);
for (int index = 0; index < oneof->field_count(); index++) {
const FieldDescriptor* field = oneof->field(index);
printer->Print(
- "->value('@field@', GPBType::@type@, @number@@other@)\n",
+ "->value('^field^', "
+ "\\Google\\Protobuf\\Internal\\GPBType::^type^, ^number^^other^)\n",
"field", field->name(),
"type", ToUpper(field->type_name()),
"number", SimpleItoa(field->number()),
- "other", EnumOrMessageSuffix(field, is_descriptor));
+ "other", EnumOrMessageSuffix(field, true));
}
printer->Print("->finish()\n");
Outdent(printer);
@@ -625,33 +603,48 @@ void GenerateMessageToPool(const string& name_prefix,
"\n");
for (int i = 0; i < message->nested_type_count(); i++) {
- GenerateMessageToPool(class_name, message->nested_type(i), is_descriptor,
- printer);
+ GenerateMessageToPool(class_name, message->nested_type(i), printer);
}
for (int i = 0; i < message->enum_type_count(); i++) {
- GenerateEnumToPool(message->enum_type(i), is_descriptor, printer);
+ GenerateEnumToPool(message->enum_type(i), printer);
}
}
-void GenerateAddFileToPool(const google::protobuf::FileDescriptor* file,
- bool is_descriptor,
- google::protobuf::io::Printer* printer) {
- if (is_descriptor) {
- printer->Print("$pool = DescriptorPool::getGeneratedPool();\n\n");
+void GenerateAddFileToPool(const FileDescriptor* file, bool is_descriptor,
+ io::Printer* printer) {
+ printer->Print(
+ "public static $is_initialized = false;\n\n"
+ "public static function initOnce() {\n");
+ Indent(printer);
+
+ printer->Print(
+ "$pool = \\Google\\Protobuf\\Internal\\"
+ "DescriptorPool::getGeneratedPool();\n\n"
+ "if (static::$is_initialized == true) {\n"
+ " return;\n"
+ "}\n");
+ if (is_descriptor) {
for (int i = 0; i < file->message_type_count(); i++) {
- GenerateMessageToPool("", file->message_type(i), is_descriptor, printer);
+ GenerateMessageToPool("", file->message_type(i), printer);
}
for (int i = 0; i < file->enum_type_count(); i++) {
- GenerateEnumToPool(file->enum_type(i), is_descriptor, printer);
+ GenerateEnumToPool(file->enum_type(i), printer);
}
printer->Print(
"$pool->finish();\n");
} else {
- // Add messages and enums to descriptor pool.
- printer->Print("$pool = DescriptorPool::getGeneratedPool();\n\n");
+ for (int i = 0; i < file->dependency_count(); i++) {
+ const std::string& name = file->dependency(i)->name();
+ std::string dependency_filename =
+ GeneratedMetadataFileName(name, is_descriptor);
+ printer->Print(
+ "\\^name^::initOnce();\n",
+ "name", FilenameToClassname(dependency_filename));
+ }
+ // Add messages and enums to descriptor pool.
FileDescriptorSet files;
FileDescriptorProto* file_proto = files.add_file();
file->CopyTo(file_proto);
@@ -665,7 +658,7 @@ void GenerateAddFileToPool(const google::protobuf::FileDescriptor* file,
static const int kBytesPerLine = 30;
for (int i = 0; i < files_data.size(); i += kBytesPerLine) {
printer->Print(
- "\"@data@\"@dot@\n",
+ "\"^data^\"^dot^\n",
"data", BinaryToHex(files_data.substr(i, kBytesPerLine)),
"dot", i + kBytesPerLine < files_data.size() ? " ." : "");
}
@@ -674,38 +667,20 @@ void GenerateAddFileToPool(const google::protobuf::FileDescriptor* file,
printer->Print(
"));\n\n");
}
-
-}
-
-void GenerateEnum(const google::protobuf::EnumDescriptor* en,
- google::protobuf::io::Printer* printer) {
printer->Print(
- "class @name@\n"
- "{\n",
- "name", EnumClassName(en));
- Indent(printer);
-
- for (int i = 0; i < en->value_count(); i++) {
- const EnumValueDescriptor* value = en->value(i);
- printer->Print("const @name@ = @number@;\n",
- "name", value->name(),
- "number", IntToString(value->number()));
- }
+ "static::$is_initialized = true;\n");
Outdent(printer);
- printer->Print("}\n\n");
+ printer->Print("}\n");
}
-void GenerateUseDeclaration(bool is_descriptor,
- google::protobuf::io::Printer* printer) {
+void GenerateUseDeclaration(bool is_descriptor, io::Printer* printer) {
if (!is_descriptor) {
printer->Print(
- "use Google\\Protobuf\\Internal\\DescriptorPool;\n"
"use Google\\Protobuf\\Internal\\GPBType;\n"
"use Google\\Protobuf\\Internal\\RepeatedField;\n"
"use Google\\Protobuf\\Internal\\GPBUtil;\n\n");
} else {
printer->Print(
- "use Google\\Protobuf\\Internal\\DescriptorPool;\n"
"use Google\\Protobuf\\Internal\\GPBType;\n"
"use Google\\Protobuf\\Internal\\GPBWire;\n"
"use Google\\Protobuf\\Internal\\RepeatedField;\n"
@@ -714,42 +689,380 @@ void GenerateUseDeclaration(bool is_descriptor,
}
}
-void GenerateFile(const google::protobuf::FileDescriptor* file,
- bool is_descriptor, google::protobuf::io::Printer* printer) {
+void GenerateHead(const FileDescriptor* file, io::Printer* printer) {
printer->Print(
"<?php\n"
"# Generated by the protocol buffer compiler. DO NOT EDIT!\n"
- "# source: @filename@\n"
+ "# source: ^filename^\n"
"\n",
"filename", file->name());
+}
+
+std::string FilenameToClassname(const string& filename) {
+ int lastindex = filename.find_last_of(".");
+ std::string result = filename.substr(0, lastindex);
+ for (int i = 0; i < result.size(); i++) {
+ if (result[i] == '/') {
+ result[i] = '\\';
+ }
+ }
+ return result;
+}
+
+void GenerateMetadataFile(const FileDescriptor* file,
+ bool is_descriptor,
+ GeneratorContext* generator_context) {
+ std::string filename = GeneratedMetadataFileName(file->name(), 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("\\");
+
+ printer.Print(
+ "namespace ^name^;\n\n",
+ "name", fullname.substr(0, lastindex));
+
+ if (lastindex != string::npos) {
+ printer.Print(
+ "class ^name^\n"
+ "{\n",
+ "name", fullname.substr(lastindex + 1));
+ } else {
+ printer.Print(
+ "class ^name^\n"
+ "{\n",
+ "name", fullname);
+ }
+ Indent(&printer);
+
+ GenerateAddFileToPool(file, is_descriptor, &printer);
+
+ Outdent(&printer);
+ printer.Print("}\n\n");
+}
+
+void GenerateEnumFile(const FileDescriptor* file, const EnumDescriptor* en,
+ bool is_descriptor, GeneratorContext* generator_context) {
+ std::string filename = GeneratedEnumFileName(en, 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->package().empty()) {
- printer->Print("namespace @name@;\n\n",
- "name", PhpName(file->package(), is_descriptor));
+ printer.Print(
+ "namespace ^name^;\n\n",
+ "name", fullname.substr(0, lastindex));
+ }
+
+ GenerateEnumDocComment(&printer, en);
+
+ if (lastindex != string::npos) {
+ printer.Print(
+ "class ^name^\n"
+ "{\n",
+ "name", fullname.substr(lastindex + 1));
+ } else {
+ printer.Print(
+ "class ^name^\n"
+ "{\n",
+ "name", fullname);
+ }
+ Indent(&printer);
+
+ for (int i = 0; i < en->value_count(); i++) {
+ const EnumValueDescriptor* value = en->value(i);
+ GenerateEnumValueDocComment(&printer, value);
+ printer.Print("const ^name^ = ^number^;\n",
+ "name", value->name(),
+ "number", IntToString(value->number()));
}
- for (int i = 0; i < file->dependency_count(); i++) {
- const std::string& name = file->dependency(i)->name();
- printer->Print("require_once('@name@');\n", "name",
- GeneratedFileName(name, is_descriptor));
+ Outdent(&printer);
+ printer.Print("}\n\n");
+}
+
+void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message,
+ bool is_descriptor,
+ GeneratorContext* generator_context) {
+ // 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 filename = GeneratedMessageFileName(message, 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->package().empty()) {
+ printer.Print(
+ "namespace ^name^;\n\n",
+ "name", fullname.substr(0, lastindex));
+ }
+
+ GenerateUseDeclaration(is_descriptor, &printer);
+
+ GenerateMessageDocComment(&printer, message);
+ if (lastindex != string::npos) {
+ printer.Print(
+ "class ^name^ extends \\Google\\Protobuf\\Internal\\Message\n"
+ "{\n",
+ "name", fullname.substr(lastindex + 1));
+ } else {
+ printer.Print(
+ "class ^name^ extends \\Google\\Protobuf\\Internal\\Message\n"
+ "{\n",
+ "name", fullname);
+ }
+ Indent(&printer);
+
+ // Field and oneof definitions.
+ for (int i = 0; i < message->field_count(); i++) {
+ const FieldDescriptor* field = message->field(i);
+ GenerateField(field, &printer, is_descriptor);
+ }
+ for (int i = 0; i < message->oneof_decl_count(); i++) {
+ const OneofDescriptor* oneof = message->oneof_decl(i);
+ GenerateOneofField(oneof, &printer);
+ }
+ printer.Print("\n");
+
+ printer.Print(
+ "public function __construct() {\n");
+ Indent(&printer);
+
+ std::string metadata_filename =
+ GeneratedMetadataFileName(file->name(), is_descriptor);
+ std::string metadata_fullname = FilenameToClassname(metadata_filename);
+ printer.Print(
+ "\\^fullname^::initOnce();\n"
+ "parent::__construct();\n",
+ "fullname", metadata_fullname);
+
+ Outdent(&printer);
+ printer.Print("}\n\n");
+
+ // Field and oneof accessors.
+ for (int i = 0; i < message->field_count(); i++) {
+ const FieldDescriptor* field = message->field(i);
+ GenerateFieldAccessor(field, is_descriptor, &printer);
+ }
+ for (int i = 0; i < message->oneof_decl_count(); i++) {
+ const OneofDescriptor* oneof = message->oneof_decl(i);
+ printer.Print(
+ "public function get^camel_name^()\n"
+ "{\n"
+ " return $this->whichOneof(\"^name^\");\n"
+ "}\n\n",
+ "camel_name", UnderscoresToCamelCase(oneof->name(), true), "name",
+ oneof->name());
}
- GenerateUseDeclaration(is_descriptor, printer);
+ Outdent(&printer);
+ printer.Print("}\n\n");
+
+ // Nested messages and enums.
+ for (int i = 0; i < message->nested_type_count(); i++) {
+ GenerateMessageFile(file, message->nested_type(i), is_descriptor,
+ generator_context);
+ }
+ for (int i = 0; i < message->enum_type_count(); i++) {
+ GenerateEnumFile(file, message->enum_type(i), is_descriptor,
+ generator_context);
+ }
+}
+void GenerateFile(const FileDescriptor* file, bool is_descriptor,
+ GeneratorContext* generator_context) {
+ GenerateMetadataFile(file, is_descriptor, generator_context);
for (int i = 0; i < file->message_type_count(); i++) {
- GenerateMessage("", file->message_type(i), is_descriptor, printer);
+ GenerateMessageFile(file, file->message_type(i), is_descriptor,
+ generator_context);
}
for (int i = 0; i < file->enum_type_count(); i++) {
- GenerateEnum(file->enum_type(i), printer);
+ GenerateEnumFile(file, file->enum_type(i), is_descriptor,
+ generator_context);
+ }
+}
+
+static string EscapePhpdoc(const string& input) {
+ string result;
+ result.reserve(input.size() * 2);
+
+ char prev = '*';
+
+ for (string::size_type i = 0; i < input.size(); i++) {
+ char c = input[i];
+ switch (c) {
+ case '*':
+ // Avoid "/*".
+ if (prev == '/') {
+ result.append("&#42;");
+ } else {
+ result.push_back(c);
+ }
+ break;
+ case '/':
+ // Avoid "*/".
+ if (prev == '*') {
+ result.append("&#47;");
+ } else {
+ result.push_back(c);
+ }
+ break;
+ case '@':
+ // '@' starts phpdoc tags including the @deprecated tag, which will
+ // cause a compile-time error if inserted before a declaration that
+ // does not have a corresponding @Deprecated annotation.
+ result.append("&#64;");
+ break;
+ case '<':
+ // Avoid interpretation as HTML.
+ result.append("&lt;");
+ break;
+ case '>':
+ // Avoid interpretation as HTML.
+ result.append("&gt;");
+ break;
+ case '&':
+ // Avoid interpretation as HTML.
+ result.append("&amp;");
+ break;
+ case '\\':
+ // Java interprets Unicode escape sequences anywhere!
+ result.append("&#92;");
+ break;
+ default:
+ result.push_back(c);
+ break;
+ }
+
+ prev = c;
}
- GenerateAddFileToPool(file, is_descriptor, printer);
+ return result;
}
-bool Generator::Generate(
- const FileDescriptor* file,
- const string& parameter,
- GeneratorContext* generator_context,
- string* error) const {
+static void GenerateDocCommentBodyForLocation(
+ io::Printer* printer, const SourceLocation& location) {
+ string comments = location.leading_comments.empty() ?
+ location.trailing_comments : location.leading_comments;
+ 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.
+
+ // If the comment itself contains block comment start or end markers,
+ // HTML-escape them so that they don't accidentally close the doc comment.
+ comments = EscapePhpdoc(comments);
+
+ vector<string> lines = Split(comments, "\n");
+ while (!lines.empty() && lines.back().empty()) {
+ 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
+ // close the comment.
+ if (!lines[i].empty() && lines[i][0] == '/') {
+ printer->Print(" * ^line^\n", "line", lines[i]);
+ } else {
+ printer->Print(" *^line^\n", "line", lines[i]);
+ }
+ }
+ printer->Print(
+ " * </pre>\n"
+ " *\n");
+ }
+}
+
+template <typename DescriptorType>
+static void GenerateDocCommentBody(
+ io::Printer* printer, const DescriptorType* descriptor) {
+ SourceLocation location;
+ if (descriptor->GetSourceLocation(&location)) {
+ GenerateDocCommentBodyForLocation(printer, location);
+ }
+}
+
+static string FirstLineOf(const string& value) {
+ string result = value;
+
+ string::size_type pos = result.find_first_of('\n');
+ if (pos != string::npos) {
+ result.erase(pos);
+ }
+
+ return result;
+}
+
+void GenerateMessageDocComment(io::Printer* printer,
+ const Descriptor* message) {
+ printer->Print("/**\n");
+ GenerateDocCommentBody(printer, message);
+ printer->Print(
+ " * Protobuf type <code>^fullname^</code>\n"
+ " */\n",
+ "fullname", EscapePhpdoc(message->full_name()));
+}
+
+void GenerateFieldDocComment(io::Printer* printer,
+ const FieldDescriptor* field) {
+ // 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.
+
+ // We start the comment with the main body based on the comments from the
+ // .proto file (if present). We then end with the field declaration, e.g.:
+ // optional string foo = 5;
+ // If the field is a group, the debug string might end with {.
+ printer->Print("/**\n");
+ GenerateDocCommentBody(printer, field);
+ printer->Print(
+ " * <code>^def^</code>\n",
+ "def", EscapePhpdoc(FirstLineOf(field->DebugString())));
+ printer->Print(" */\n");
+}
+
+void GenerateEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_) {
+ printer->Print("/**\n");
+ GenerateDocCommentBody(printer, enum_);
+ printer->Print(
+ " * Protobuf enum <code>^fullname^</code>\n"
+ " */\n",
+ "fullname", EscapePhpdoc(enum_->full_name()));
+}
+
+void GenerateEnumValueDocComment(io::Printer* printer,
+ const EnumValueDescriptor* value) {
+ printer->Print("/**\n");
+ GenerateDocCommentBody(printer, value);
+ printer->Print(
+ " * <code>^def^</code>\n"
+ " */\n",
+ "def", EscapePhpdoc(FirstLineOf(value->DebugString())));
+}
+
+bool Generator::Generate(const FileDescriptor* file, const string& parameter,
+ GeneratorContext* generator_context,
+ string* error) const {
bool is_descriptor = parameter == "internal";
if (is_descriptor && file->name() != kDescriptorFile) {
@@ -765,12 +1078,7 @@ bool Generator::Generate(
return false;
}
- std::string filename = GeneratedFileName(file->name(), is_descriptor);
- scoped_ptr<io::ZeroCopyOutputStream> output(
- generator_context->Open(filename));
- io::Printer printer(output.get(), '@');
-
- GenerateFile(file, is_descriptor, &printer);
+ GenerateFile(file, is_descriptor, generator_context);
return true;
}