summaryrefslogtreecommitdiffstats
path: root/contrib/libs/protoc/src/google/protobuf/compiler/php
diff options
context:
space:
mode:
authorDevtools Arcadia <[email protected]>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <[email protected]>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /contrib/libs/protoc/src/google/protobuf/compiler/php
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'contrib/libs/protoc/src/google/protobuf/compiler/php')
-rw-r--r--contrib/libs/protoc/src/google/protobuf/compiler/php/php_generator.cc2326
-rw-r--r--contrib/libs/protoc/src/google/protobuf/compiler/php/php_generator.h92
2 files changed, 2418 insertions, 0 deletions
diff --git a/contrib/libs/protoc/src/google/protobuf/compiler/php/php_generator.cc b/contrib/libs/protoc/src/google/protobuf/compiler/php/php_generator.cc
new file mode 100644
index 00000000000..696e4ceec86
--- /dev/null
+++ b/contrib/libs/protoc/src/google/protobuf/compiler/php/php_generator.cc
@@ -0,0 +1,2326 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <google/protobuf/compiler/php/php_generator.h>
+
+#include <google/protobuf/compiler/code_generator.h>
+#include <google/protobuf/compiler/plugin.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/stubs/port.h>
+#include <google/protobuf/stubs/strutil.h>
+
+#include <sstream>
+#include <util/string/cast.h>
+
+const TProtoStringType kDescriptorFile = "google/protobuf/descriptor.proto";
+const TProtoStringType kEmptyFile = "google/protobuf/empty.proto";
+const TProtoStringType kEmptyMetadataFile = "GPBMetadata/Google/Protobuf/GPBEmpty.php";
+const TProtoStringType kDescriptorMetadataFile =
+ "GPBMetadata/Google/Protobuf/Internal/Descriptor.php";
+const TProtoStringType kDescriptorDirName = "Google/Protobuf/Internal";
+const TProtoStringType kDescriptorPackageName = "Google\\Protobuf\\Internal";
+const char* const kReservedNames[] = {
+ "abstract", "and", "array", "as", "break",
+ "callable", "case", "catch", "class", "clone",
+ "const", "continue", "declare", "default", "die",
+ "do", "echo", "else", "elseif", "empty",
+ "enddeclare", "endfor", "endforeach", "endif", "endswitch",
+ "endwhile", "eval", "exit", "extends", "final",
+ "for", "foreach", "function", "global", "goto",
+ "if", "implements", "include", "include_once", "instanceof",
+ "insteadof", "interface", "isset", "list", "namespace",
+ "new", "or", "print", "private", "protected",
+ "public", "require", "require_once", "return", "static",
+ "switch", "throw", "trait", "try", "unset",
+ "use", "var", "while", "xor", "int",
+ "float", "bool", "string", "true", "false",
+ "null", "void", "iterable"};
+const char* const kValidConstantNames[] = {
+ "int", "float", "bool", "string", "true",
+ "false", "null", "void", "iterable",
+};
+const int kReservedNamesSize = 73;
+const int kValidConstantNamesSize = 9;
+const int kFieldSetter = 1;
+const int kFieldGetter = 2;
+const int kFieldProperty = 3;
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace php {
+
+struct Options {
+ bool is_descriptor = false;
+ bool aggregate_metadata = false;
+ bool gen_c_wkt = false;
+ std::set<string> aggregate_metadata_prefixes;
+};
+
+namespace {
+
+// Forward decls.
+TProtoStringType PhpName(const TProtoStringType& full_name, const Options& options);
+TProtoStringType IntToString(int32 value);
+TProtoStringType FilenameToClassname(const TProtoStringType& filename);
+TProtoStringType GeneratedMetadataFileName(const FileDescriptor* file,
+ const Options& options);
+TProtoStringType UnderscoresToCamelCase(const TProtoStringType& name,
+ bool cap_first_letter);
+void Indent(io::Printer* printer);
+void Outdent(io::Printer* printer);
+void GenerateAddFilesToPool(const FileDescriptor* file, const Options& options,
+ io::Printer* printer);
+void GenerateMessageDocComment(io::Printer* printer, const Descriptor* message,
+ const Options& options);
+void GenerateMessageConstructorDocComment(io::Printer* printer,
+ const Descriptor* message,
+ const Options& options);
+void GenerateFieldDocComment(io::Printer* printer, const FieldDescriptor* field,
+ const Options& options, int function_type);
+void GenerateWrapperFieldGetterDocComment(io::Printer* printer,
+ const FieldDescriptor* field);
+void GenerateWrapperFieldSetterDocComment(io::Printer* printer,
+ const FieldDescriptor* field);
+void GenerateEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_,
+ const Options& options);
+void GenerateEnumValueDocComment(io::Printer* printer,
+ const EnumValueDescriptor* value);
+void GenerateServiceDocComment(io::Printer* printer,
+ const ServiceDescriptor* service);
+void GenerateServiceMethodDocComment(io::Printer* printer,
+ const MethodDescriptor* method);
+
+TProtoStringType ReservedNamePrefix(const TProtoStringType& classname,
+ const FileDescriptor* file) {
+ bool is_reserved = false;
+
+ TProtoStringType lower = classname;
+ lower.to_lower();
+
+ for (int i = 0; i < kReservedNamesSize; i++) {
+ if (lower == kReservedNames[i]) {
+ is_reserved = true;
+ break;
+ }
+ }
+
+ if (is_reserved) {
+ if (file->package() == "google.protobuf") {
+ return "GPB";
+ } else {
+ return "PB";
+ }
+ }
+
+ return "";
+}
+
+template <typename DescriptorType>
+TProtoStringType DescriptorFullName(const DescriptorType* desc, bool is_internal) {
+ if (is_internal) {
+ return StringReplace(desc->full_name(),
+ "google.protobuf",
+ "google.protobuf.internal", false);
+ } else {
+ return desc->full_name();
+ }
+}
+
+template <typename DescriptorType>
+TProtoStringType ClassNamePrefix(const TProtoStringType& classname,
+ const DescriptorType* desc) {
+ const TProtoStringType& prefix = (desc->file()->options()).php_class_prefix();
+ if (!prefix.empty()) {
+ return prefix;
+ }
+
+ return ReservedNamePrefix(classname, desc->file());
+}
+
+template <typename DescriptorType>
+TProtoStringType GeneratedClassNameImpl(const DescriptorType* desc) {
+ TProtoStringType classname = ClassNamePrefix(desc->name(), desc) + desc->name();
+ const Descriptor* containing = desc->containing_type();
+ while (containing != NULL) {
+ classname = ClassNamePrefix(containing->name(), desc) + containing->name()
+ + '\\' + classname;
+ containing = containing->containing_type();
+ }
+ return classname;
+}
+
+TProtoStringType GeneratedClassNameImpl(const ServiceDescriptor* desc) {
+ TProtoStringType classname = desc->name();
+ return ClassNamePrefix(classname, desc) + classname;
+}
+
+template <typename DescriptorType>
+TProtoStringType LegacyGeneratedClassName(const DescriptorType* desc) {
+ TProtoStringType classname = desc->name();
+ const Descriptor* containing = desc->containing_type();
+ while (containing != NULL) {
+ classname = containing->name() + '_' + classname;
+ containing = containing->containing_type();
+ }
+ return ClassNamePrefix(classname, desc) + classname;
+}
+
+TProtoStringType ClassNamePrefix(const TProtoStringType& classname) {
+ TProtoStringType lower = classname;
+ lower.to_lower();
+
+ for (int i = 0; i < kReservedNamesSize; i++) {
+ if (lower == kReservedNames[i]) {
+ return "PB";
+ }
+ }
+
+ return "";
+}
+
+TProtoStringType ConstantNamePrefix(const TProtoStringType& classname) {
+ bool is_reserved = false;
+
+ TProtoStringType lower = classname;
+ lower.to_lower();
+
+ for (int i = 0; i < kReservedNamesSize; i++) {
+ if (lower == kReservedNames[i]) {
+ is_reserved = true;
+ break;
+ }
+ }
+
+ for (int i = 0; i < kValidConstantNamesSize; i++) {
+ if (lower == kValidConstantNames[i]) {
+ is_reserved = false;
+ break;
+ }
+ }
+
+ if (is_reserved) {
+ return "PB";
+ }
+
+ return "";
+}
+
+template <typename DescriptorType>
+TProtoStringType RootPhpNamespace(const DescriptorType* desc,
+ const Options& options) {
+ if (desc->file()->options().has_php_namespace()) {
+ const TProtoStringType& php_namespace = desc->file()->options().php_namespace();
+ if (!php_namespace.empty()) {
+ return php_namespace;
+ }
+ return "";
+ }
+
+ if (!desc->file()->package().empty()) {
+ return PhpName(desc->file()->package(), options);
+ }
+ return "";
+}
+
+template <typename DescriptorType>
+TProtoStringType FullClassName(const DescriptorType* desc, const Options& options) {
+ TProtoStringType classname = GeneratedClassNameImpl(desc);
+ TProtoStringType php_namespace = RootPhpNamespace(desc, options);
+ if (!php_namespace.empty()) {
+ return php_namespace + "\\" + classname;
+ }
+ return classname;
+}
+
+template <typename DescriptorType>
+TProtoStringType FullClassName(const DescriptorType* desc, bool is_descriptor) {
+ Options options;
+ options.is_descriptor = is_descriptor;
+ return FullClassName(desc, options);
+}
+
+template <typename DescriptorType>
+TProtoStringType LegacyFullClassName(const DescriptorType* desc,
+ const Options& options) {
+ TProtoStringType classname = LegacyGeneratedClassName(desc);
+ TProtoStringType php_namespace = RootPhpNamespace(desc, options);
+ if (!php_namespace.empty()) {
+ return php_namespace + "\\" + classname;
+ }
+ return classname;
+}
+
+TProtoStringType PhpName(const TProtoStringType& full_name, const Options& options) {
+ if (options.is_descriptor) {
+ return kDescriptorPackageName;
+ }
+
+ TProtoStringType segment;
+ TProtoStringType result;
+ bool cap_next_letter = true;
+ for (int i = 0; i < full_name.size(); i++) {
+ if ('a' <= full_name[i] && full_name[i] <= 'z' && cap_next_letter) {
+ segment += full_name[i] + ('A' - 'a');
+ cap_next_letter = false;
+ } else if (full_name[i] == '.') {
+ result += ClassNamePrefix(segment) + segment + '\\';
+ segment = "";
+ cap_next_letter = true;
+ } else {
+ segment += full_name[i];
+ cap_next_letter = false;
+ }
+ }
+ result += ClassNamePrefix(segment) + segment;
+ return result;
+}
+
+TProtoStringType DefaultForField(const FieldDescriptor* field) {
+ switch (field->type()) {
+ case FieldDescriptor::TYPE_INT32:
+ case FieldDescriptor::TYPE_INT64:
+ case FieldDescriptor::TYPE_UINT32:
+ case FieldDescriptor::TYPE_UINT64:
+ case FieldDescriptor::TYPE_SINT32:
+ case FieldDescriptor::TYPE_SINT64:
+ case FieldDescriptor::TYPE_FIXED32:
+ case FieldDescriptor::TYPE_FIXED64:
+ case FieldDescriptor::TYPE_SFIXED32:
+ case FieldDescriptor::TYPE_SFIXED64:
+ case FieldDescriptor::TYPE_ENUM: return "0";
+ case FieldDescriptor::TYPE_DOUBLE:
+ case FieldDescriptor::TYPE_FLOAT: return "0.0";
+ case FieldDescriptor::TYPE_BOOL: return "false";
+ case FieldDescriptor::TYPE_STRING:
+ case FieldDescriptor::TYPE_BYTES: return "''";
+ case FieldDescriptor::TYPE_MESSAGE:
+ case FieldDescriptor::TYPE_GROUP: return "null";
+ default: assert(false); return "";
+ }
+}
+
+TProtoStringType GeneratedMetadataFileName(const FileDescriptor* file,
+ const Options& options) {
+ const TProtoStringType& proto_file = file->name();
+ int start_index = 0;
+ int first_index = proto_file.find_first_of("/", start_index);
+ TProtoStringType result = "";
+ TProtoStringType segment = "";
+
+ if (proto_file == kEmptyFile) {
+ return kEmptyMetadataFile;
+ }
+ if (options.is_descriptor) {
+ return kDescriptorMetadataFile;
+ }
+
+ // Append directory name.
+ TProtoStringType file_no_suffix;
+ int lastindex = proto_file.find_last_of(".");
+ if (proto_file == kEmptyFile) {
+ return kEmptyMetadataFile;
+ } else {
+ file_no_suffix = proto_file.substr(0, lastindex);
+ }
+
+ if (file->options().has_php_metadata_namespace()) {
+ const TProtoStringType& php_metadata_namespace =
+ file->options().php_metadata_namespace();
+ if (!php_metadata_namespace.empty() && php_metadata_namespace != "\\") {
+ result += php_metadata_namespace;
+ std::replace(result.begin(), result.vend(), '\\', '/');
+ if (result.at(result.size() - 1) != '/') {
+ result += "/";
+ }
+ }
+ } else {
+ result += "GPBMetadata/";
+ while (first_index != TProtoStringType::npos) {
+ segment = UnderscoresToCamelCase(
+ file_no_suffix.substr(start_index, first_index - start_index), true);
+ result += ReservedNamePrefix(segment, file) + segment + "/";
+ start_index = first_index + 1;
+ first_index = file_no_suffix.find_first_of("/", start_index);
+ }
+ }
+
+ // Append file name.
+ int file_name_start = file_no_suffix.find_last_of("/");
+ if (file_name_start == TProtoStringType::npos) {
+ file_name_start = 0;
+ } else {
+ file_name_start += 1;
+ }
+ segment = UnderscoresToCamelCase(
+ file_no_suffix.substr(file_name_start, first_index - file_name_start), true);
+
+ return result + ReservedNamePrefix(segment, file) + segment + ".php";
+}
+
+TProtoStringType GeneratedMetadataFileName(const FileDescriptor* file,
+ bool is_descriptor) {
+ Options options;
+ options.is_descriptor = is_descriptor;
+ return GeneratedMetadataFileName(file, options);
+}
+
+template <typename DescriptorType>
+TProtoStringType GeneratedClassFileName(const DescriptorType* desc,
+ const Options& options) {
+ TProtoStringType result = FullClassName(desc, options);
+ for (int i = 0; i < result.size(); i++) {
+ if (result[i] == '\\') {
+ result[i] = '/';
+ }
+ }
+ return result + ".php";
+}
+
+template <typename DescriptorType>
+TProtoStringType LegacyGeneratedClassFileName(const DescriptorType* desc,
+ const Options& options) {
+ TProtoStringType result = LegacyFullClassName(desc, options);
+
+ for (int i = 0; i < result.size(); i++) {
+ if (result[i] == '\\') {
+ result[i] = '/';
+ }
+ }
+ return result + ".php";
+}
+
+TProtoStringType GeneratedServiceFileName(const ServiceDescriptor* service,
+ const Options& options) {
+ TProtoStringType result = FullClassName(service, options) + "Interface";
+ for (int i = 0; i < result.size(); i++) {
+ if (result[i] == '\\') {
+ result[i] = '/';
+ }
+ }
+ return result + ".php";
+}
+
+TProtoStringType IntToString(int32 value) {
+ std::ostringstream os;
+ os << value;
+ return TProtoStringType{os.str()};
+}
+
+TProtoStringType LabelForField(const FieldDescriptor* field) {
+ switch (field->label()) {
+ case FieldDescriptor::LABEL_OPTIONAL: return "optional";
+ case FieldDescriptor::LABEL_REQUIRED: return "required";
+ case FieldDescriptor::LABEL_REPEATED: return "repeated";
+ default: assert(false); return "";
+ }
+}
+
+TProtoStringType PhpSetterTypeName(const FieldDescriptor* field,
+ const Options& options) {
+ if (field->is_map()) {
+ return "array|\\Google\\Protobuf\\Internal\\MapField";
+ }
+ TProtoStringType 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(), options);
+ 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 != TProtoStringType::npos) {
+ type.replace(start_pos, 1, "[]|");
+ }
+ type += "[]|\\Google\\Protobuf\\Internal\\RepeatedField";
+ }
+ return type;
+}
+
+TProtoStringType PhpSetterTypeName(const FieldDescriptor* field,
+ bool is_descriptor) {
+ Options options;
+ options.is_descriptor = is_descriptor;
+ return PhpSetterTypeName(field, options);
+}
+
+TProtoStringType PhpGetterTypeName(const FieldDescriptor* field,
+ const Options& options) {
+ 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(), options);
+ case FieldDescriptor::TYPE_GROUP: return "null";
+ default: assert(false); return "";
+ }
+}
+
+TProtoStringType PhpGetterTypeName(const FieldDescriptor* field,
+ bool is_descriptor) {
+ Options options;
+ options.is_descriptor = is_descriptor;
+ return PhpGetterTypeName(field, options);
+}
+
+TProtoStringType EnumOrMessageSuffix(const FieldDescriptor* field,
+ const Options& options) {
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ return ", '" +
+ DescriptorFullName(field->message_type(), options.is_descriptor) +
+ "'";
+ }
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
+ return ", '" +
+ DescriptorFullName(field->enum_type(), options.is_descriptor) + "'";
+ }
+ return "";
+}
+
+TProtoStringType EnumOrMessageSuffix(const FieldDescriptor* field,
+ bool is_descriptor) {
+ Options options;
+ options.is_descriptor = is_descriptor;
+ return EnumOrMessageSuffix(field, options);
+}
+
+// Converts a name to camel-case. If cap_first_letter is true, capitalize the
+// first letter.
+TProtoStringType UnderscoresToCamelCase(const TProtoStringType& name,
+ bool cap_first_letter) {
+ TProtoStringType result;
+ for (int i = 0; i < name.size(); i++) {
+ if ('a' <= name[i] && name[i] <= 'z') {
+ if (cap_first_letter) {
+ result += name[i] + ('A' - 'a');
+ } else {
+ result += name[i];
+ }
+ cap_first_letter = false;
+ } else if ('A' <= name[i] && name[i] <= 'Z') {
+ if (i == 0 && !cap_first_letter) {
+ // Force first letter to lower-case unless explicitly told to
+ // capitalize it.
+ result += name[i] + ('a' - 'A');
+ } else {
+ // Capital letters after the first are left as-is.
+ result += name[i];
+ }
+ cap_first_letter = false;
+ } else if ('0' <= name[i] && name[i] <= '9') {
+ result += name[i];
+ cap_first_letter = true;
+ } else {
+ cap_first_letter = true;
+ }
+ }
+ // Add a trailing "_" if the name should be altered.
+ if (name[name.size() - 1] == '#') {
+ result += '_';
+ }
+ return result;
+}
+
+void Indent(io::Printer* printer) {
+ printer->Indent();
+ printer->Indent();
+}
+void Outdent(io::Printer* printer) {
+ printer->Outdent();
+ printer->Outdent();
+}
+
+void GenerateField(const FieldDescriptor* field, io::Printer* printer,
+ const Options& options) {
+ if (field->is_repeated()) {
+ GenerateFieldDocComment(printer, field, options, kFieldProperty);
+ printer->Print(
+ "private $^name^;\n",
+ "name", field->name());
+ } else if (field->real_containing_oneof()) {
+ // Oneof fields are handled by GenerateOneofField.
+ return;
+ } else {
+ TProtoStringType initial_value =
+ field->has_presence() ? "null" : DefaultForField(field);
+ GenerateFieldDocComment(printer, field, options, kFieldProperty);
+ printer->Print(
+ "protected $^name^ = ^initial_value^;\n",
+ "name", field->name(),
+ "initial_value", initial_value);
+ }
+}
+
+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",
+ "name", oneof->name());
+}
+
+void GenerateFieldAccessor(const FieldDescriptor* field, const Options& options,
+ io::Printer* printer) {
+ const OneofDescriptor* oneof = field->real_containing_oneof();
+
+ // Generate getter.
+ GenerateFieldDocComment(printer, field, options, kFieldGetter);
+
+ // deprecation
+ TProtoStringType deprecation_trigger = (field->options().deprecated()) ? "@trigger_error('" +
+ field->name() + " is deprecated.', E_USER_DEPRECATED);\n " : "";
+
+ // Emit getter.
+ if (oneof != NULL) {
+ printer->Print(
+ "public function get^camel_name^()\n"
+ "{\n"
+ " ^deprecation_trigger^return $this->readOneof(^number^);\n"
+ "}\n\n",
+ "camel_name", UnderscoresToCamelCase(field->name(), true),
+ "number", IntToString(field->number()),
+ "deprecation_trigger", deprecation_trigger);
+ } else if (field->has_presence() && !field->message_type()) {
+ printer->Print(
+ "public function get^camel_name^()\n"
+ "{\n"
+ " ^deprecation_trigger^return isset($this->^name^) ? $this->^name^ : ^default_value^;\n"
+ "}\n\n",
+ "camel_name", UnderscoresToCamelCase(field->name(), true),
+ "name", field->name(),
+ "default_value", DefaultForField(field),
+ "deprecation_trigger", deprecation_trigger);
+ } else {
+ printer->Print(
+ "public function get^camel_name^()\n"
+ "{\n"
+ " ^deprecation_trigger^return $this->^name^;\n"
+ "}\n\n",
+ "camel_name", UnderscoresToCamelCase(field->name(), true),
+ "name", field->name(),
+ "deprecation_trigger", deprecation_trigger);
+ }
+
+ // Emit hazzers/clear.
+ if (oneof) {
+ printer->Print(
+ "public function has^camel_name^()\n"
+ "{\n"
+ " ^deprecation_trigger^return $this->hasOneof(^number^);\n"
+ "}\n\n",
+ "camel_name", UnderscoresToCamelCase(field->name(), true),
+ "number", IntToString(field->number()),
+ "deprecation_trigger", deprecation_trigger);
+ } else if (field->has_presence()) {
+ printer->Print(
+ "public function has^camel_name^()\n"
+ "{\n"
+ " ^deprecation_trigger^return isset($this->^name^);\n"
+ "}\n\n"
+ "public function clear^camel_name^()\n"
+ "{\n"
+ " ^deprecation_trigger^unset($this->^name^);\n"
+ "}\n\n",
+ "camel_name", UnderscoresToCamelCase(field->name(), true),
+ "name", field->name(),
+ "default_value", DefaultForField(field),
+ "deprecation_trigger", deprecation_trigger);
+ }
+
+ // For wrapper types, generate an additional getXXXUnwrapped getter
+ if (!field->is_map() &&
+ !field->is_repeated() &&
+ field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
+ IsWrapperType(field)) {
+ GenerateWrapperFieldGetterDocComment(printer, field);
+ printer->Print(
+ "public function get^camel_name^Unwrapped()\n"
+ "{\n"
+ " ^deprecation_trigger^return $this->readWrapperValue(\"^field_name^\");\n"
+ "}\n\n",
+ "camel_name", UnderscoresToCamelCase(field->name(), true),
+ "field_name", field->name(),
+ "deprecation_trigger", deprecation_trigger);
+ }
+
+ // Generate setter.
+ GenerateFieldDocComment(printer, field, options, kFieldSetter);
+ printer->Print(
+ "public function set^camel_name^($var)\n"
+ "{\n",
+ "camel_name", UnderscoresToCamelCase(field->name(), true));
+
+ Indent(printer);
+
+ if (field->options().deprecated()) {
+ printer->Print(
+ "^deprecation_trigger^",
+ "deprecation_trigger", deprecation_trigger
+ );
+ }
+
+ // Type check.
+ if (field->is_map()) {
+ const Descriptor* map_entry = field->message_type();
+ const FieldDescriptor* key = map_entry->FindFieldByName("key");
+ const FieldDescriptor* value = map_entry->FindFieldByName("value");
+ printer->Print(
+ "$arr = GPBUtil::checkMapField($var, "
+ "\\Google\\Protobuf\\Internal\\GPBType::^key_type^, "
+ "\\Google\\Protobuf\\Internal\\GPBType::^value_type^",
+ "key_type", ToUpper(key->type_name()),
+ "value_type", ToUpper(value->type_name()));
+ if (value->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ printer->Print(
+ ", \\^class_name^);\n",
+ "class_name",
+ 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");
+ } else {
+ printer->Print(");\n");
+ }
+ } else if (field->is_repeated()) {
+ printer->Print(
+ "$arr = 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",
+ 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");
+ } else {
+ printer->Print(");\n");
+ }
+ } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ printer->Print(
+ "GPBUtil::checkMessage($var, \\^class_name^::class);\n",
+ "class_name", FullClassName(field->message_type(), options));
+ } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
+ printer->Print(
+ "GPBUtil::checkEnum($var, \\^class_name^::class);\n",
+ "class_name", FullClassName(field->enum_type(), options));
+ } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
+ printer->Print(
+ "GPBUtil::checkString($var, ^utf8^);\n",
+ "utf8",
+ field->type() == FieldDescriptor::TYPE_STRING ? "True": "False");
+ } else {
+ printer->Print(
+ "GPBUtil::check^type^($var);\n",
+ "type", UnderscoresToCamelCase(field->cpp_type_name(), true));
+ }
+
+ if (oneof != NULL) {
+ printer->Print(
+ "$this->writeOneof(^number^, $var);\n",
+ "number", IntToString(field->number()));
+ } else if (field->is_repeated()) {
+ printer->Print(
+ "$this->^name^ = $arr;\n",
+ "name", field->name());
+ } else {
+ printer->Print(
+ "$this->^name^ = $var;\n",
+ "name", field->name());
+ }
+
+ printer->Print("\nreturn $this;\n");
+
+ Outdent(printer);
+
+ printer->Print(
+ "}\n\n");
+
+ // For wrapper types, generate an additional setXXXValue getter
+ if (!field->is_map() &&
+ !field->is_repeated() &&
+ field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
+ IsWrapperType(field)) {
+ GenerateWrapperFieldSetterDocComment(printer, field);
+ printer->Print(
+ "public function set^camel_name^Unwrapped($var)\n"
+ "{\n"
+ " $this->writeWrapperValue(\"^field_name^\", $var);\n"
+ " return $this;"
+ "}\n\n",
+ "camel_name", UnderscoresToCamelCase(field->name(), true),
+ "field_name", field->name());
+ }
+}
+
+void GenerateEnumToPool(const EnumDescriptor* en, io::Printer* printer) {
+ printer->Print(
+ "$pool->addEnum('^name^', "
+ "\\Google\\Protobuf\\Internal\\^class_name^::class)\n",
+ "name", DescriptorFullName(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",
+ "name", ConstantNamePrefix(value->name()) + value->name(),
+ "number", IntToString(value->number()));
+ }
+ printer->Print("->finalizeToPool();\n\n");
+ 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 TProtoStringType& 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;
+ }
+ TProtoStringType class_name =
+ (name_prefix.empty() ? "" : name_prefix + "\\") +
+ ReservedNamePrefix(message->name(), message->file()) + message->name();
+
+ printer->Print(
+ "$pool->addMessage('^message^', "
+ "\\Google\\Protobuf\\Internal\\^class_name^::class)\n",
+ "message", DescriptorFullName(message, true),
+ "class_name", class_name);
+
+ Indent(printer);
+
+ for (int i = 0; i < message->field_count(); i++) {
+ const FieldDescriptor* field = message->field(i);
+ if (field->is_map()) {
+ const FieldDescriptor* key =
+ field->message_type()->FindFieldByName("key");
+ const FieldDescriptor* val =
+ field->message_type()->FindFieldByName("value");
+ printer->Print(
+ "->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", StrCat(field->number()),
+ "other", EnumOrMessageSuffix(val, true));
+ } else if (!field->real_containing_oneof()) {
+ printer->Print(
+ "->^label^('^field^', "
+ "\\Google\\Protobuf\\Internal\\GPBType::^type^, ^number^^other^)\n",
+ "field", field->name(),
+ "label", LabelForField(field),
+ "type", ToUpper(field->type_name()),
+ "number", StrCat(field->number()),
+ "other", EnumOrMessageSuffix(field, true));
+ }
+ }
+
+ // oneofs.
+ for (int i = 0; i < message->real_oneof_decl_count(); i++) {
+ const OneofDescriptor* oneof = message->oneof_decl(i);
+ 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^', "
+ "\\Google\\Protobuf\\Internal\\GPBType::^type^, ^number^^other^)\n",
+ "field", field->name(),
+ "type", ToUpper(field->type_name()),
+ "number", StrCat(field->number()),
+ "other", EnumOrMessageSuffix(field, true));
+ }
+ printer->Print("->finish()\n");
+ Outdent(printer);
+ }
+
+ printer->Print(
+ "->finalizeToPool();\n");
+
+ Outdent(printer);
+
+ printer->Print(
+ "\n");
+
+ for (int i = 0; i < message->nested_type_count(); i++) {
+ GenerateMessageToPool(class_name, message->nested_type(i), printer);
+ }
+ for (int i = 0; i < message->enum_type_count(); i++) {
+ GenerateEnumToPool(message->enum_type(i), printer);
+ }
+}
+
+void GenerateAddFileToPool(const FileDescriptor* file, const Options& options,
+ io::Printer* printer) {
+ printer->Print(
+ "public static $is_initialized = false;\n\n"
+ "public static function initOnce() {\n");
+ Indent(printer);
+
+ if (options.aggregate_metadata) {
+ GenerateAddFilesToPool(file, options, printer);
+ } else {
+ printer->Print(
+ "$pool = \\Google\\Protobuf\\Internal\\"
+ "DescriptorPool::getGeneratedPool();\n\n"
+ "if (static::$is_initialized == true) {\n"
+ " return;\n"
+ "}\n");
+
+ if (options.is_descriptor) {
+ for (int i = 0; i < file->message_type_count(); i++) {
+ GenerateMessageToPool("", file->message_type(i), printer);
+ }
+ for (int i = 0; i < file->enum_type_count(); i++) {
+ GenerateEnumToPool(file->enum_type(i), printer);
+ }
+
+ printer->Print(
+ "$pool->finish();\n");
+ } else {
+ for (int i = 0; i < file->dependency_count(); i++) {
+ const TProtoStringType& 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.
+ if (name == kDescriptorFile) {
+ continue;
+ }
+ TProtoStringType dependency_filename =
+ GeneratedMetadataFileName(file->dependency(i), options);
+ 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);
+
+ // Filter out descriptor.proto as it cannot be depended on for now.
+ RepeatedPtrField<TProtoStringType>* dependency =
+ file_proto->mutable_dependency();
+ for (RepeatedPtrField<TProtoStringType>::iterator it = dependency->begin();
+ it != dependency->end(); ++it) {
+ if (*it != kDescriptorFile) {
+ dependency->erase(it);
+ break;
+ }
+ }
+
+ // Filter out all extensions, since we do not support extension yet.
+ file_proto->clear_extension();
+ RepeatedPtrField<DescriptorProto>* message_type =
+ file_proto->mutable_message_type();
+ for (RepeatedPtrField<DescriptorProto>::iterator it = message_type->begin();
+ it != message_type->end(); ++it) {
+ it->clear_extension();
+ }
+
+ TProtoStringType files_data;
+ files.SerializeToString(&files_data);
+
+ printer->Print("$pool->internalAddGeneratedFile(\n");
+ Indent(printer);
+ printer->Print("'");
+
+ for (auto ch : files_data) {
+ switch (ch) {
+ case '\\':
+ printer->Print(R"(\\)");
+ break;
+ case '\'':
+ printer->Print(R"(\')");
+ break;
+ default:
+ printer->Print("^char^", "char", TProtoStringType(1, ch));
+ break;
+ }
+ }
+
+ printer->Print("'\n");
+ Outdent(printer);
+ printer->Print(
+ ", true);\n\n");
+ }
+ printer->Print(
+ "static::$is_initialized = true;\n");
+ }
+
+ Outdent(printer);
+ printer->Print("}\n");
+}
+
+static void AnalyzeDependencyForFile(
+ const FileDescriptor* file,
+ std::set<const FileDescriptor*>* nodes_without_dependency,
+ std::map<const FileDescriptor*, std::set<const FileDescriptor*>>* deps,
+ std::map<const FileDescriptor*, int>* dependency_count) {
+ int count = file->dependency_count();
+ for (int i = 0; i < file->dependency_count(); i++) {
+ const FileDescriptor* dependency = file->dependency(i);
+ if (dependency->name() == kDescriptorFile) {
+ count--;
+ break;
+ }
+ }
+
+ if (count == 0) {
+ nodes_without_dependency->insert(file);
+ } else {
+ (*dependency_count)[file] = count;
+ for (int i = 0; i < file->dependency_count(); i++) {
+ const FileDescriptor* dependency = file->dependency(i);
+ if (dependency->name() == kDescriptorFile) {
+ continue;
+ }
+ if (deps->find(dependency) == deps->end()) {
+ (*deps)[dependency] = std::set<const FileDescriptor*>();
+ }
+ (*deps)[dependency].insert(file);
+ AnalyzeDependencyForFile(
+ dependency, nodes_without_dependency, deps, dependency_count);
+ }
+ }
+}
+
+static bool NeedsUnwrapping(const FileDescriptor* file,
+ const Options& options) {
+ bool has_aggregate_metadata_prefix = false;
+ if (options.aggregate_metadata_prefixes.empty()) {
+ has_aggregate_metadata_prefix = true;
+ } else {
+ for (const auto& prefix : options.aggregate_metadata_prefixes) {
+ if (HasPrefixString(file->package(), prefix)) {
+ has_aggregate_metadata_prefix = true;
+ break;
+ }
+ }
+ }
+
+ return has_aggregate_metadata_prefix;
+}
+
+void GenerateAddFilesToPool(const FileDescriptor* file, const Options& options,
+ io::Printer* printer) {
+ printer->Print(
+ "$pool = \\Google\\Protobuf\\Internal\\"
+ "DescriptorPool::getGeneratedPool();\n"
+ "if (static::$is_initialized == true) {\n"
+ " return;\n"
+ "}\n");
+
+ // Sort files according to dependency
+ std::map<const FileDescriptor*, std::set<const FileDescriptor*>> deps;
+ std::map<const FileDescriptor*, int> dependency_count;
+ std::set<const FileDescriptor*> nodes_without_dependency;
+ FileDescriptorSet sorted_file_set;
+
+ AnalyzeDependencyForFile(
+ file, &nodes_without_dependency, &deps, &dependency_count);
+
+ while (!nodes_without_dependency.empty()) {
+ auto file = *nodes_without_dependency.begin();
+ nodes_without_dependency.erase(file);
+ for (auto dependent : deps[file]) {
+ if (dependency_count[dependent] == 1) {
+ dependency_count.erase(dependent);
+ nodes_without_dependency.insert(dependent);
+ } else {
+ dependency_count[dependent] -= 1;
+ }
+ }
+
+ bool needs_aggregate = NeedsUnwrapping(file, options);
+
+ if (needs_aggregate) {
+ auto file_proto = sorted_file_set.add_file();
+ file->CopyTo(file_proto);
+
+ // Filter out descriptor.proto as it cannot be depended on for now.
+ RepeatedPtrField<TProtoStringType>* dependency =
+ file_proto->mutable_dependency();
+ for (RepeatedPtrField<TProtoStringType>::iterator it = dependency->begin();
+ it != dependency->end(); ++it) {
+ if (*it != kDescriptorFile) {
+ dependency->erase(it);
+ break;
+ }
+ }
+
+ // Filter out all extensions, since we do not support extension yet.
+ file_proto->clear_extension();
+ RepeatedPtrField<DescriptorProto>* message_type =
+ file_proto->mutable_message_type();
+ for (RepeatedPtrField<DescriptorProto>::iterator it = message_type->begin();
+ it != message_type->end(); ++it) {
+ it->clear_extension();
+ }
+ } else {
+ TProtoStringType dependency_filename = GeneratedMetadataFileName(file, false);
+ printer->Print(
+ "\\^name^::initOnce();\n",
+ "name", FilenameToClassname(dependency_filename));
+ }
+ }
+
+ TProtoStringType files_data;
+ sorted_file_set.SerializeToString(&files_data);
+
+ printer->Print("$pool->internalAddGeneratedFile(\n");
+ Indent(printer);
+ printer->Print("'");
+
+ for (auto ch : files_data) {
+ switch (ch) {
+ case '\\':
+ printer->Print(R"(\\)");
+ break;
+ case '\'':
+ printer->Print(R"(\')");
+ break;
+ default:
+ printer->Print("^char^", "char", TProtoStringType(1, ch));
+ break;
+ }
+ }
+
+ printer->Print("'\n");
+ Outdent(printer);
+ printer->Print(
+ ", true);\n");
+
+ printer->Print(
+ "static::$is_initialized = true;\n");
+}
+
+void GenerateUseDeclaration(const Options& options, io::Printer* printer) {
+ if (!options.is_descriptor) {
+ printer->Print(
+ "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\\GPBType;\n"
+ "use Google\\Protobuf\\Internal\\GPBWire;\n"
+ "use Google\\Protobuf\\Internal\\RepeatedField;\n"
+ "use Google\\Protobuf\\Internal\\InputStream;\n"
+ "use Google\\Protobuf\\Internal\\GPBUtil;\n\n");
+ }
+}
+
+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"
+ "\n",
+ "filename", file->name());
+}
+
+TProtoStringType FilenameToClassname(const TProtoStringType& filename) {
+ int lastindex = filename.find_last_of(".");
+ TProtoStringType 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, const Options& options,
+ GeneratorContext* generator_context) {
+ TProtoStringType filename = GeneratedMetadataFileName(file, options);
+ std::unique_ptr<io::ZeroCopyOutputStream> output(
+ generator_context->Open(filename));
+ io::Printer printer(output.get(), '^');
+
+ GenerateHead(file, &printer);
+
+ TProtoStringType fullname = FilenameToClassname(filename);
+ int lastindex = fullname.find_last_of("\\");
+
+ if (lastindex != TProtoStringType::npos) {
+ printer.Print(
+ "namespace ^name^;\n\n",
+ "name", fullname.substr(0, lastindex));
+
+ printer.Print(
+ "class ^name^\n"
+ "{\n",
+ "name", fullname.substr(lastindex + 1));
+ } else {
+ printer.Print(
+ "class ^name^\n"
+ "{\n",
+ "name", fullname);
+ }
+ Indent(&printer);
+
+ GenerateAddFileToPool(file, options, &printer);
+
+ Outdent(&printer);
+ printer.Print("}\n\n");
+}
+
+template <typename DescriptorType>
+void LegacyGenerateClassFile(const FileDescriptor* file,
+ const DescriptorType* desc, const Options& options,
+ GeneratorContext* generator_context) {
+ TProtoStringType filename = LegacyGeneratedClassFileName(desc, options);
+ std::unique_ptr<io::ZeroCopyOutputStream> output(
+ generator_context->Open(filename));
+ io::Printer printer(output.get(), '^');
+
+ GenerateHead(file, &printer);
+
+ TProtoStringType php_namespace = RootPhpNamespace(desc, options);
+ if (!php_namespace.empty()) {
+ printer.Print(
+ "namespace ^name^;\n\n",
+ "name", php_namespace);
+ }
+ TProtoStringType newname = FullClassName(desc, options);
+ printer.Print("if (false) {\n");
+ Indent(&printer);
+ printer.Print("/**\n");
+ printer.Print(" * This class is deprecated. Use ^new^ instead.\n",
+ "new", newname);
+ printer.Print(" * @deprecated\n");
+ printer.Print(" */\n");
+ printer.Print("class ^old^ {}\n",
+ "old", LegacyGeneratedClassName(desc));
+ Outdent(&printer);
+ printer.Print("}\n");
+ printer.Print("class_exists(^new^::class);\n",
+ "new", GeneratedClassNameImpl(desc));
+ printer.Print("@trigger_error('^old^ is deprecated and will be removed in "
+ "the next major release. Use ^fullname^ instead', E_USER_DEPRECATED);\n\n",
+ "old", LegacyFullClassName(desc, options),
+ "fullname", newname);
+}
+
+void GenerateEnumFile(const FileDescriptor* file, const EnumDescriptor* en,
+ const Options& options,
+ GeneratorContext* generator_context) {
+ TProtoStringType filename = GeneratedClassFileName(en, options);
+ std::unique_ptr<io::ZeroCopyOutputStream> output(
+ generator_context->Open(filename));
+ io::Printer printer(output.get(), '^');
+
+ GenerateHead(file, &printer);
+
+ TProtoStringType fullname = FilenameToClassname(filename);
+ int lastindex = fullname.find_last_of("\\");
+
+ if (lastindex != TProtoStringType::npos) {
+ printer.Print(
+ "namespace ^name^;\n\n",
+ "name", fullname.substr(0, lastindex));
+
+ // We only need this 'use' statement if the enum has a namespace.
+ // Otherwise, we get a warning that the use statement has no effect.
+ printer.Print("use UnexpectedValueException;\n\n");
+ }
+
+ GenerateEnumDocComment(&printer, en, options);
+
+ if (lastindex != TProtoStringType::npos) {
+ fullname = fullname.substr(lastindex + 1);
+ }
+
+ 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", ConstantNamePrefix(value->name()) + value->name(),
+ "number", IntToString(value->number()));
+ }
+
+ printer.Print("\nprivate static $valueToName = [\n");
+ Indent(&printer);
+ for (int i = 0; i < en->value_count(); i++) {
+ const EnumValueDescriptor* value = en->value(i);
+ printer.Print("self::^name^ => '^name^',\n",
+ "name", ConstantNamePrefix(value->name()) + value->name());
+ }
+ Outdent(&printer);
+ printer.Print("];\n");
+
+ printer.Print(
+ "\npublic static function name($value)\n"
+ "{\n");
+ Indent(&printer);
+ printer.Print("if (!isset(self::$valueToName[$value])) {\n");
+ Indent(&printer);
+ printer.Print("throw new UnexpectedValueException(sprintf(\n");
+ Indent(&printer);
+ Indent(&printer);
+ printer.Print("'Enum %s has no name defined for value %s', __CLASS__, $value));\n");
+ Outdent(&printer);
+ Outdent(&printer);
+ Outdent(&printer);
+ printer.Print("}\n"
+ "return self::$valueToName[$value];\n");
+ Outdent(&printer);
+ printer.Print("}\n\n");
+
+ printer.Print(
+ "\npublic static function value($name)\n"
+ "{\n");
+ Indent(&printer);
+ printer.Print("$const = __CLASS__ . '::' . strtoupper($name);\n"
+ "if (!defined($const)) {\n");
+ Indent(&printer);
+ printer.Print("throw new UnexpectedValueException(sprintf(\n");
+ Indent(&printer);
+ Indent(&printer);
+ printer.Print("'Enum %s has no value defined for name %s', __CLASS__, $name));\n");
+ Outdent(&printer);
+ Outdent(&printer);
+ Outdent(&printer);
+ printer.Print("}\n"
+ "return constant($const);\n");
+ Outdent(&printer);
+ printer.Print("}\n");
+
+ Outdent(&printer);
+ printer.Print("}\n\n");
+
+ // write legacy file for backwards compatibility with nested messages and enums
+ if (en->containing_type() != NULL) {
+ printer.Print(
+ "// Adding a class alias for backwards compatibility with the previous class name.\n");
+ printer.Print(
+ "class_alias(^new^::class, \\^old^::class);\n\n",
+ "new", fullname,
+ "old", LegacyFullClassName(en, options));
+ LegacyGenerateClassFile(file, en, options, generator_context);
+ }
+}
+
+void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message,
+ const Options& options,
+ 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;
+ }
+
+ TProtoStringType filename = GeneratedClassFileName(message, options);
+ std::unique_ptr<io::ZeroCopyOutputStream> output(
+ generator_context->Open(filename));
+ io::Printer printer(output.get(), '^');
+
+ GenerateHead(file, &printer);
+
+ TProtoStringType fullname = FilenameToClassname(filename);
+ int lastindex = fullname.find_last_of("\\");
+
+ if (lastindex != TProtoStringType::npos) {
+ printer.Print(
+ "namespace ^name^;\n\n",
+ "name", fullname.substr(0, lastindex));
+ }
+
+ GenerateUseDeclaration(options, &printer);
+
+ GenerateMessageDocComment(&printer, message, options);
+ if (lastindex != TProtoStringType::npos) {
+ fullname = fullname.substr(lastindex + 1);
+ }
+
+ TProtoStringType base;
+
+ switch (message->well_known_type()) {
+ case Descriptor::WELLKNOWNTYPE_ANY:
+ base = "\\Google\\Protobuf\\Internal\\AnyBase";
+ break;
+ case Descriptor::WELLKNOWNTYPE_TIMESTAMP:
+ base = "\\Google\\Protobuf\\Internal\\TimestampBase";
+ break;
+ default:
+ base = "\\Google\\Protobuf\\Internal\\Message";
+ break;
+ }
+
+ printer.Print(
+ "class ^name^ extends ^base^\n"
+ "{\n",
+ "base", base,
+ "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, options);
+ }
+ for (int i = 0; i < message->real_oneof_decl_count(); i++) {
+ const OneofDescriptor* oneof = message->oneof_decl(i);
+ GenerateOneofField(oneof, &printer);
+ }
+ printer.Print("\n");
+
+ GenerateMessageConstructorDocComment(&printer, message, options);
+ printer.Print(
+ "public function __construct($data = NULL) {\n");
+ Indent(&printer);
+
+ TProtoStringType metadata_filename = GeneratedMetadataFileName(file, options);
+ TProtoStringType metadata_fullname = FilenameToClassname(metadata_filename);
+ printer.Print(
+ "\\^fullname^::initOnce();\n",
+ "fullname", metadata_fullname);
+
+ printer.Print(
+ "parent::__construct($data);\n");
+
+ 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, options, &printer);
+ }
+ for (int i = 0; i < message->real_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"
+ "}\n\n",
+ "camel_name", UnderscoresToCamelCase(oneof->name(), true), "name",
+ oneof->name());
+ }
+
+ Outdent(&printer);
+ printer.Print("}\n\n");
+
+ // write legacy file for backwards compatibility with nested messages and enums
+ if (message->containing_type() != NULL) {
+ printer.Print(
+ "// Adding a class alias for backwards compatibility with the previous class name.\n");
+ printer.Print(
+ "class_alias(^new^::class, \\^old^::class);\n\n",
+ "new", fullname,
+ "old", LegacyFullClassName(message, options));
+ LegacyGenerateClassFile(file, message, options, generator_context);
+ }
+
+ // Nested messages and enums.
+ for (int i = 0; i < message->nested_type_count(); i++) {
+ GenerateMessageFile(file, message->nested_type(i), options,
+ generator_context);
+ }
+ for (int i = 0; i < message->enum_type_count(); i++) {
+ GenerateEnumFile(file, message->enum_type(i), options, generator_context);
+ }
+}
+
+void GenerateServiceFile(
+ const FileDescriptor* file, const ServiceDescriptor* service,
+ const Options& options, GeneratorContext* generator_context) {
+ TProtoStringType filename = GeneratedServiceFileName(service, options);
+ std::unique_ptr<io::ZeroCopyOutputStream> output(
+ generator_context->Open(filename));
+ io::Printer printer(output.get(), '^');
+
+ GenerateHead(file, &printer);
+
+ TProtoStringType fullname = FilenameToClassname(filename);
+ int lastindex = fullname.find_last_of("\\");
+
+ if (!file->options().php_namespace().empty() ||
+ (!file->options().has_php_namespace() && !file->package().empty()) ||
+ lastindex != TProtoStringType::npos) {
+ printer.Print(
+ "namespace ^name^;\n\n",
+ "name", fullname.substr(0, lastindex));
+ }
+
+ GenerateServiceDocComment(&printer, service);
+
+ if (lastindex != TProtoStringType::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, const Options& options,
+ GeneratorContext* generator_context) {
+ GenerateMetadataFile(file, options, generator_context);
+
+ for (int i = 0; i < file->message_type_count(); i++) {
+ GenerateMessageFile(file, file->message_type(i), options,
+ generator_context);
+ }
+ for (int i = 0; i < file->enum_type_count(); i++) {
+ GenerateEnumFile(file, file->enum_type(i), options, generator_context);
+ }
+ if (file->options().php_generic_services()) {
+ for (int i = 0; i < file->service_count(); i++) {
+ GenerateServiceFile(file, file->service(i), options, generator_context);
+ }
+ }
+}
+
+static TProtoStringType EscapePhpdoc(const TProtoStringType& input) {
+ TProtoStringType result;
+ result.reserve(input.size() * 2);
+
+ char prev = '*';
+
+ for (TProtoStringType::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;
+ default:
+ result.push_back(c);
+ break;
+ }
+
+ prev = c;
+ }
+
+ return result;
+}
+
+static void GenerateDocCommentBodyForLocation(
+ io::Printer* printer, const SourceLocation& location, bool trailingNewline,
+ int indentCount) {
+ TProtoStringType 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 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.
+ comments = EscapePhpdoc(comments);
+
+ std::vector<TProtoStringType> lines = Split(comments, "\n", true);
+ while (!lines.empty() && lines.back().empty()) {
+ lines.pop_back();
+ }
+
+ 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 (indentCount == 0 && !lines[i].empty() && lines[i][0] == '/') {
+ printer->Print(" * ^line^\n", "line", lines[i]);
+ } else {
+ TProtoStringType indent = TProtoStringType(indentCount, ' ');
+ printer->Print(" *^ind^^line^\n", "ind", indent, "line", lines[i]);
+ }
+ }
+ if (trailingNewline) {
+ printer->Print(" *\n");
+ }
+ }
+}
+
+template <typename DescriptorType>
+static void GenerateDocCommentBody(
+ io::Printer* printer, const DescriptorType* descriptor) {
+ SourceLocation location;
+ if (descriptor->GetSourceLocation(&location)) {
+ GenerateDocCommentBodyForLocation(printer, location, true, 0);
+ }
+}
+
+static TProtoStringType FirstLineOf(const TProtoStringType& value) {
+ TProtoStringType result = value;
+
+ TProtoStringType::size_type pos = result.find_first_of('\n');
+ if (pos != TProtoStringType::npos) {
+ result.erase(pos);
+ }
+
+ return result;
+}
+
+void GenerateMessageDocComment(io::Printer* printer, const Descriptor* message,
+ const Options& options) {
+ printer->Print("/**\n");
+ GenerateDocCommentBody(printer, message);
+ printer->Print(
+ " * Generated from protobuf message <code>^messagename^</code>\n"
+ " */\n",
+ "fullname", EscapePhpdoc(FullClassName(message, options)),
+ "messagename", EscapePhpdoc(message->full_name()));
+}
+
+void GenerateMessageConstructorDocComment(io::Printer* printer,
+ const Descriptor* message,
+ const Options& options) {
+ // 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");
+ printer->Print(" * Constructor.\n");
+ printer->Print(" *\n");
+ printer->Print(" * @param array $data {\n");
+ printer->Print(" * Optional. Data for populating the Message object.\n");
+ printer->Print(" *\n");
+ for (int i = 0; i < message->field_count(); i++) {
+ const FieldDescriptor* field = message->field(i);
+ printer->Print(" * @type ^php_type^ $^var^\n",
+ "php_type", PhpSetterTypeName(field, options),
+ "var", field->name());
+ SourceLocation location;
+ if (field->GetSourceLocation(&location)) {
+ GenerateDocCommentBodyForLocation(printer, location, false, 10);
+ }
+ }
+ printer->Print(" * }\n");
+ printer->Print(" */\n");
+}
+
+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(service->full_name()));
+}
+
+void GenerateFieldDocComment(io::Printer* printer, const FieldDescriptor* field,
+ const Options& options, 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.
+
+ // 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(
+ " * 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, options));
+ printer->Print(" * @return $this\n");
+ } else if (function_type == kFieldGetter) {
+ bool can_return_null = field->has_presence() &&
+ field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE;
+ printer->Print(" * @return ^php_type^^maybe_null^\n",
+ "php_type", PhpGetterTypeName(field, options),
+ "maybe_null", can_return_null ? "|null" : "");
+ }
+ if (field->options().deprecated()) {
+ printer->Print(" * @deprecated\n");
+ }
+ printer->Print(" */\n");
+}
+
+void GenerateWrapperFieldGetterDocComment(io::Printer* printer, const FieldDescriptor* field) {
+ // Generate a doc comment for the special getXXXValue methods that are
+ // generated for wrapper types.
+ const FieldDescriptor* primitiveField = field->message_type()->FindFieldByName("value");
+ printer->Print("/**\n");
+ printer->Print(
+ " * Returns the unboxed value from <code>get^camel_name^()</code>\n\n",
+ "camel_name", UnderscoresToCamelCase(field->name(), true));
+ GenerateDocCommentBody(printer, field);
+ printer->Print(
+ " * Generated from protobuf field <code>^def^</code>\n",
+ "def", EscapePhpdoc(FirstLineOf(field->DebugString())));
+ printer->Print(" * @return ^php_type^|null\n",
+ "php_type", PhpGetterTypeName(primitiveField, false));
+ printer->Print(" */\n");
+}
+
+void GenerateWrapperFieldSetterDocComment(io::Printer* printer, const FieldDescriptor* field) {
+ // Generate a doc comment for the special setXXXValue methods that are
+ // generated for wrapper types.
+ const FieldDescriptor* primitiveField = field->message_type()->FindFieldByName("value");
+ printer->Print("/**\n");
+ printer->Print(
+ " * Sets the field by wrapping a primitive type in a ^message_name^ object.\n\n",
+ "message_name", FullClassName(field->message_type(), false));
+ GenerateDocCommentBody(printer, field);
+ printer->Print(
+ " * Generated from protobuf field <code>^def^</code>\n",
+ "def", EscapePhpdoc(FirstLineOf(field->DebugString())));
+ printer->Print(" * @param ^php_type^|null $var\n",
+ "php_type", PhpSetterTypeName(primitiveField, false));
+ printer->Print(" * @return $this\n");
+ printer->Print(" */\n");
+}
+
+void GenerateEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_,
+ const Options& options) {
+ printer->Print("/**\n");
+ GenerateDocCommentBody(printer, enum_);
+ printer->Print(
+ " * Protobuf type <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(
+ " * 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)));
+}
+
+TProtoStringType FilenameCName(const FileDescriptor* file) {
+ TProtoStringType c_name = file->name();
+ c_name = StringReplace(c_name, ".", "_", true);
+ c_name = StringReplace(c_name, "/", "_", true);
+ return c_name;
+}
+
+void GenerateCEnum(const EnumDescriptor* desc, io::Printer* printer) {
+ TProtoStringType c_name = desc->full_name();
+ c_name = StringReplace(c_name, ".", "_", true);
+ TProtoStringType php_name = FullClassName(desc, Options());
+ php_name = StringReplace(php_name, "\\", "\\\\", true);
+ printer->Print(
+ "/* $c_name$ */\n"
+ "\n"
+ "zend_class_entry* $c_name$_ce;\n"
+ "\n"
+ "PHP_METHOD($c_name$, name) {\n"
+ " $file_c_name$_AddDescriptor();\n"
+ " const upb_symtab *symtab = DescriptorPool_GetSymbolTable();\n"
+ " const upb_enumdef *e = upb_symtab_lookupenum(symtab, \"$name$\");\n"
+ " const char *name;\n"
+ " zend_long value;\n"
+ " if (zend_parse_parameters(ZEND_NUM_ARGS(), \"l\", &value) ==\n"
+ " FAILURE) {\n"
+ " return;\n"
+ " }\n"
+ " name = upb_enumdef_iton(e, value);\n"
+ " if (!name) {\n"
+ " zend_throw_exception_ex(NULL, 0,\n"
+ " \"$php_name$ has no name \"\n"
+ " \"defined for value \" ZEND_LONG_FMT \".\",\n"
+ " value);\n"
+ " return;\n"
+ " }\n"
+ " RETURN_STRING(name);\n"
+ "}\n"
+ "\n"
+ "PHP_METHOD($c_name$, value) {\n"
+ " $file_c_name$_AddDescriptor();\n"
+ " const upb_symtab *symtab = DescriptorPool_GetSymbolTable();\n"
+ " const upb_enumdef *e = upb_symtab_lookupenum(symtab, \"$name$\");\n"
+ " char *name = NULL;\n"
+ " size_t name_len;\n"
+ " int32_t num;\n"
+ " if (zend_parse_parameters(ZEND_NUM_ARGS(), \"s\", &name,\n"
+ " &name_len) == FAILURE) {\n"
+ " return;\n"
+ " }\n"
+ " if (!upb_enumdef_ntoi(e, name, name_len, &num)) {\n"
+ " zend_throw_exception_ex(NULL, 0,\n"
+ " \"$php_name$ has no value \"\n"
+ " \"defined for name %s.\",\n"
+ " name);\n"
+ " return;\n"
+ " }\n"
+ " RETURN_LONG(num);\n"
+ "}\n"
+ "\n"
+ "static zend_function_entry $c_name$_phpmethods[] = {\n"
+ " PHP_ME($c_name$, name, arginfo_lookup, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n"
+ " PHP_ME($c_name$, value, arginfo_lookup, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n"
+ " ZEND_FE_END\n"
+ "};\n"
+ "\n"
+ "static void $c_name$_ModuleInit() {\n"
+ " zend_class_entry tmp_ce;\n"
+ "\n"
+ " INIT_CLASS_ENTRY(tmp_ce, \"$php_name$\",\n"
+ " $c_name$_phpmethods);\n"
+ "\n"
+ " $c_name$_ce = zend_register_internal_class(&tmp_ce);\n",
+ "name", desc->full_name(),
+ "file_c_name", FilenameCName(desc->file()),
+ "c_name", c_name,
+ "php_name", php_name);
+
+ for (int i = 0; i < desc->value_count(); i++) {
+ const EnumValueDescriptor* value = desc->value(i);
+ printer->Print(
+ " zend_declare_class_constant_long($c_name$_ce, \"$name$\",\n"
+ " strlen(\"$name$\"), $num$);\n",
+ "c_name", c_name,
+ "name", value->name(),
+ "num", ToString(value->number()));
+ }
+
+ printer->Print(
+ "}\n"
+ "\n");
+}
+
+void GenerateCMessage(const Descriptor* message, io::Printer* printer) {
+ TProtoStringType c_name = message->full_name();
+ c_name = StringReplace(c_name, ".", "_", true);
+ TProtoStringType php_name = FullClassName(message, Options());
+ php_name = StringReplace(php_name, "\\", "\\\\", true);
+ printer->Print(
+ "/* $c_name$ */\n"
+ "\n"
+ "zend_class_entry* $c_name$_ce;\n"
+ "\n"
+ "static PHP_METHOD($c_name$, __construct) {\n"
+ " $file_c_name$_AddDescriptor();\n"
+ " zim_Message___construct(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n"
+ "}\n"
+ "\n",
+ "file_c_name", FilenameCName(message->file()),
+ "c_name", c_name);
+
+ for (int i = 0; i < message->field_count(); i++) {
+ auto field = message->field(i);
+ printer->Print(
+ "static PHP_METHOD($c_name$, get$camel_name$) {\n"
+ " Message* intern = (Message*)Z_OBJ_P(getThis());\n"
+ " const upb_fielddef *f = upb_msgdef_ntofz(intern->desc->msgdef,\n"
+ " \"$name$\");\n"
+ " zval ret;\n"
+ " Message_get(intern, f, &ret);\n"
+ " RETURN_COPY_VALUE(&ret);\n"
+ "}\n"
+ "\n"
+ "static PHP_METHOD($c_name$, set$camel_name$) {\n"
+ " Message* intern = (Message*)Z_OBJ_P(getThis());\n"
+ " const upb_fielddef *f = upb_msgdef_ntofz(intern->desc->msgdef,\n"
+ " \"$name$\");\n"
+ " zval *val;\n"
+ " if (zend_parse_parameters(ZEND_NUM_ARGS(), \"z\", &val)\n"
+ " == FAILURE) {\n"
+ " return;\n"
+ " }\n"
+ " Message_set(intern, f, val);\n"
+ " RETURN_COPY(getThis());\n"
+ "}\n"
+ "\n",
+ "c_name", c_name,
+ "name", field->name(),
+ "camel_name", UnderscoresToCamelCase(field->name(), true));
+ }
+
+ for (int i = 0; i < message->real_oneof_decl_count(); i++) {
+ auto oneof = message->oneof_decl(i);
+ printer->Print(
+ "static PHP_METHOD($c_name$, get$camel_name$) {\n"
+ " Message* intern = (Message*)Z_OBJ_P(getThis());\n"
+ " const upb_oneofdef *oneof = upb_msgdef_ntooz(intern->desc->msgdef,\n"
+ " \"$name$\");\n"
+ " const upb_fielddef *field = upb_msg_whichoneof(intern->msg, oneof);\n"
+ " RETURN_STRING(field ? upb_fielddef_name(field) : \"\");\n"
+ "}\n",
+ "c_name", c_name,
+ "name", oneof->name(),
+ "camel_name", UnderscoresToCamelCase(oneof->name(), true));
+ }
+
+ switch (message->well_known_type()) {
+ case Descriptor::WELLKNOWNTYPE_ANY:
+ printer->Print(
+ "ZEND_BEGIN_ARG_INFO_EX(arginfo_is, 0, 0, 1)\n"
+ " ZEND_ARG_INFO(0, proto)\n"
+ "ZEND_END_ARG_INFO()\n"
+ "\n"
+ );
+ break;
+ case Descriptor::WELLKNOWNTYPE_TIMESTAMP:
+ printer->Print(
+ "ZEND_BEGIN_ARG_INFO_EX(arginfo_timestamp_fromdatetime, 0, 0, 1)\n"
+ " ZEND_ARG_INFO(0, datetime)\n"
+ "ZEND_END_ARG_INFO()\n"
+ "\n"
+ );
+ break;
+ default:
+ break;
+ }
+
+ printer->Print(
+ "static zend_function_entry $c_name$_phpmethods[] = {\n"
+ " PHP_ME($c_name$, __construct, arginfo_construct, ZEND_ACC_PUBLIC)\n",
+ "c_name", c_name);
+
+ for (int i = 0; i < message->field_count(); i++) {
+ auto field = message->field(i);
+ printer->Print(
+ " PHP_ME($c_name$, get$camel_name$, arginfo_void, ZEND_ACC_PUBLIC)\n"
+ " PHP_ME($c_name$, set$camel_name$, arginfo_setter, ZEND_ACC_PUBLIC)\n",
+ "c_name", c_name,
+ "camel_name", UnderscoresToCamelCase(field->name(), true));
+ }
+
+ for (int i = 0; i < message->real_oneof_decl_count(); i++) {
+ auto oneof = message->oneof_decl(i);
+ printer->Print(
+ " PHP_ME($c_name$, get$camel_name$, arginfo_void, ZEND_ACC_PUBLIC)\n",
+ "c_name", c_name,
+ "camel_name", UnderscoresToCamelCase(oneof->name(), true));
+ }
+
+ // Extra hand-written functions added to the well-known types.
+ switch (message->well_known_type()) {
+ case Descriptor::WELLKNOWNTYPE_ANY:
+ printer->Print(
+ " PHP_ME($c_name$, is, arginfo_is, ZEND_ACC_PUBLIC)\n"
+ " PHP_ME($c_name$, pack, arginfo_setter, ZEND_ACC_PUBLIC)\n"
+ " PHP_ME($c_name$, unpack, arginfo_void, ZEND_ACC_PUBLIC)\n",
+ "c_name", c_name);
+ break;
+ case Descriptor::WELLKNOWNTYPE_TIMESTAMP:
+ printer->Print(
+ " PHP_ME($c_name$, fromDateTime, arginfo_timestamp_fromdatetime, ZEND_ACC_PUBLIC)\n"
+ " PHP_ME($c_name$, toDateTime, arginfo_void, ZEND_ACC_PUBLIC)\n",
+ "c_name", c_name);
+ break;
+ default:
+ break;
+ }
+
+ printer->Print(
+ " ZEND_FE_END\n"
+ "};\n"
+ "\n"
+ "static void $c_name$_ModuleInit() {\n"
+ " zend_class_entry tmp_ce;\n"
+ "\n"
+ " INIT_CLASS_ENTRY(tmp_ce, \"$php_name$\",\n"
+ " $c_name$_phpmethods);\n"
+ "\n"
+ " $c_name$_ce = zend_register_internal_class(&tmp_ce);\n"
+ " $c_name$_ce->ce_flags |= ZEND_ACC_FINAL;\n"
+ " $c_name$_ce->create_object = Message_create;\n"
+ " zend_do_inheritance($c_name$_ce, message_ce);\n"
+ "}\n"
+ "\n",
+ "c_name", c_name,
+ "php_name", php_name);
+
+ for (int i = 0; i < message->nested_type_count(); i++) {
+ GenerateCMessage(message->nested_type(i), printer);
+ }
+ for (int i = 0; i < message->enum_type_count(); i++) {
+ GenerateCEnum(message->enum_type(i), printer);
+ }
+}
+
+void GenerateEnumCInit(const EnumDescriptor* desc, io::Printer* printer) {
+ TProtoStringType c_name = desc->full_name();
+ c_name = StringReplace(c_name, ".", "_", true);
+
+ printer->Print(
+ " $c_name$_ModuleInit();\n",
+ "c_name", c_name);
+}
+
+void GenerateCInit(const Descriptor* message, io::Printer* printer) {
+ TProtoStringType c_name = message->full_name();
+ c_name = StringReplace(c_name, ".", "_", true);
+
+ printer->Print(
+ " $c_name$_ModuleInit();\n",
+ "c_name", c_name);
+
+ for (int i = 0; i < message->nested_type_count(); i++) {
+ GenerateCInit(message->nested_type(i), printer);
+ }
+ for (int i = 0; i < message->enum_type_count(); i++) {
+ GenerateEnumCInit(message->enum_type(i), printer);
+ }
+}
+
+void GenerateCWellKnownTypes(const std::vector<const FileDescriptor*>& files,
+ GeneratorContext* context) {
+ std::unique_ptr<io::ZeroCopyOutputStream> output(
+ context->Open("../ext/google/protobuf/wkt.inc"));
+ io::Printer printer(output.get(), '$');
+
+ printer.Print(
+ "// This file is generated from the .proto files for the well-known\n"
+ "// types. Do not edit!\n\n");
+
+ printer.Print(
+ "ZEND_BEGIN_ARG_INFO_EX(arginfo_lookup, 0, 0, 1)\n"
+ " ZEND_ARG_INFO(0, key)\n"
+ "ZEND_END_ARG_INFO()\n"
+ "\n"
+ );
+
+ for (auto file : files) {
+ printer.Print(
+ "static void $c_name$_AddDescriptor();\n",
+ "c_name", FilenameCName(file));
+ }
+
+ for (auto file : files) {
+ TProtoStringType c_name = FilenameCName(file);
+ TProtoStringType metadata_filename = GeneratedMetadataFileName(file, Options());
+ TProtoStringType metadata_classname = FilenameToClassname(metadata_filename);
+ TProtoStringType metadata_c_name =
+ StringReplace(metadata_classname, "\\", "_", true);
+ metadata_classname = StringReplace(metadata_classname, "\\", "\\\\", true);
+ FileDescriptorProto file_proto;
+ file->CopyTo(&file_proto);
+ TProtoStringType serialized;
+ file_proto.SerializeToString(&serialized);
+ printer.Print(
+ "/* $filename$ */\n"
+ "\n"
+ "zend_class_entry* $metadata_c_name$_ce;\n"
+ "\n"
+ "const char $c_name$_descriptor [$size$] = {\n",
+ "filename", file->name(),
+ "c_name", c_name,
+ "metadata_c_name", metadata_c_name,
+ "size", ToString(serialized.size()));
+
+ for (size_t i = 0; i < serialized.size();) {
+ for (size_t j = 0; j < 25 && i < serialized.size(); ++i, ++j) {
+ printer.Print("'$ch$', ", "ch", CEscape(serialized.substr(i, 1)));
+ }
+ printer.Print("\n");
+ }
+
+ printer.Print(
+ "};\n"
+ "\n"
+ "static void $c_name$_AddDescriptor() {\n"
+ " if (DescriptorPool_HasFile(\"$filename$\")) return;\n",
+ "filename", file->name(),
+ "c_name", c_name,
+ "metadata_c_name", metadata_c_name);
+
+ for (int i = 0; i < file->dependency_count(); i++) {
+ TProtoStringType dep_c_name = FilenameCName(file->dependency(i));
+ printer.Print(
+ " $dep_c_name$_AddDescriptor();\n",
+ "dep_c_name", dep_c_name);
+ }
+
+ printer.Print(
+ " DescriptorPool_AddDescriptor(\"$filename$\", $c_name$_descriptor,\n"
+ " sizeof($c_name$_descriptor));\n"
+ "}\n"
+ "\n"
+ "static PHP_METHOD($metadata_c_name$, initOnce) {\n"
+ " $c_name$_AddDescriptor();\n"
+ "}\n"
+ "\n"
+ "static zend_function_entry $metadata_c_name$_methods[] = {\n"
+ " PHP_ME($metadata_c_name$, initOnce, arginfo_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n"
+ " ZEND_FE_END\n"
+ "};\n"
+ "\n"
+ "static void $metadata_c_name$_ModuleInit() {\n"
+ " zend_class_entry tmp_ce;\n"
+ "\n"
+ " INIT_CLASS_ENTRY(tmp_ce, \"$metadata_classname$\",\n"
+ " $metadata_c_name$_methods);\n"
+ "\n"
+ " $metadata_c_name$_ce = zend_register_internal_class(&tmp_ce);\n"
+ "}\n"
+ "\n",
+ "filename", file->name(),
+ "c_name", c_name,
+ "metadata_c_name", metadata_c_name,
+ "metadata_classname", metadata_classname);
+ for (int i = 0; i < file->message_type_count(); i++) {
+ GenerateCMessage(file->message_type(i), &printer);
+ }
+ for (int i = 0; i < file->enum_type_count(); i++) {
+ GenerateCEnum(file->enum_type(i), &printer);
+ }
+ }
+
+ printer.Print(
+ "static void WellKnownTypes_ModuleInit() {\n");
+
+ for (auto file : files) {
+ TProtoStringType metadata_filename = GeneratedMetadataFileName(file, Options());
+ TProtoStringType metadata_classname = FilenameToClassname(metadata_filename);
+ TProtoStringType metadata_c_name =
+ StringReplace(metadata_classname, "\\", "_", true);
+ printer.Print(
+ " $metadata_c_name$_ModuleInit();\n",
+ "metadata_c_name", metadata_c_name);
+ for (int i = 0; i < file->message_type_count(); i++) {
+ GenerateCInit(file->message_type(i), &printer);
+ }
+ for (int i = 0; i < file->enum_type_count(); i++) {
+ GenerateEnumCInit(file->enum_type(i), &printer);
+ }
+ }
+
+ printer.Print(
+ "}\n");
+}
+
+} // namespace
+
+TProtoStringType GeneratedClassName(const Descriptor* desc) {
+ return GeneratedClassNameImpl(desc);
+}
+
+TProtoStringType GeneratedClassName(const EnumDescriptor* desc) {
+ return GeneratedClassNameImpl(desc);
+}
+
+TProtoStringType GeneratedClassName(const ServiceDescriptor* desc) {
+ return GeneratedClassNameImpl(desc);
+}
+
+bool Generator::Generate(const FileDescriptor* file,
+ const TProtoStringType& parameter,
+ GeneratorContext* generator_context,
+ TProtoStringType* error) const {
+ return Generate(file, Options(), generator_context, error);
+}
+
+bool Generator::Generate(const FileDescriptor* file, const Options& options,
+ GeneratorContext* generator_context,
+ TProtoStringType* error) const {
+ if (options.is_descriptor && file->name() != kDescriptorFile) {
+ *error =
+ "Can only generate PHP code for google/protobuf/descriptor.proto.\n";
+ return false;
+ }
+
+ if (!options.is_descriptor && file->syntax() != FileDescriptor::SYNTAX_PROTO3) {
+ *error =
+ "Can only generate PHP code for proto3 .proto files.\n"
+ "Please add 'syntax = \"proto3\";' to the top of your .proto file.\n";
+ return false;
+ }
+
+ GenerateFile(file, options, generator_context);
+
+ return true;
+}
+
+bool Generator::GenerateAll(const std::vector<const FileDescriptor*>& files,
+ const TProtoStringType& parameter,
+ GeneratorContext* generator_context,
+ TProtoStringType* error) const {
+ Options options;
+
+ for (const auto& option : Split(parameter, ",", true)) {
+ const std::vector<TProtoStringType> option_pair = Split(option, "=", true);
+ if (HasPrefixString(option_pair[0], "aggregate_metadata")) {
+ options.aggregate_metadata = true;
+ for (const auto& prefix : Split(option_pair[1], "#", false)) {
+ options.aggregate_metadata_prefixes.emplace(prefix);
+ GOOGLE_LOG(INFO) << prefix;
+ }
+ } else if (option_pair[0] == "internal") {
+ options.is_descriptor = true;
+ } else if (option_pair[0] == "internal_generate_c_wkt") {
+ GenerateCWellKnownTypes(files, generator_context);
+ } else {
+ GOOGLE_LOG(FATAL) << "Unknown codegen option: " << option_pair[0];
+ }
+ }
+
+ for (auto file : files) {
+ if (!Generate(file, options, generator_context, error)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace php
+} // namespace compiler
+} // namespace protobuf
+} // namespace google
diff --git a/contrib/libs/protoc/src/google/protobuf/compiler/php/php_generator.h b/contrib/libs/protoc/src/google/protobuf/compiler/php/php_generator.h
new file mode 100644
index 00000000000..0247afaa560
--- /dev/null
+++ b/contrib/libs/protoc/src/google/protobuf/compiler/php/php_generator.h
@@ -0,0 +1,92 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLE_PROTOBUF_COMPILER_PHP_GENERATOR_H__
+#define GOOGLE_PROTOBUF_COMPILER_PHP_GENERATOR_H__
+
+#include <google/protobuf/compiler/code_generator.h>
+#include <google/protobuf/descriptor.h>
+
+#include <string>
+
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace php {
+
+struct Options;
+
+class PROTOC_EXPORT Generator : public CodeGenerator {
+ public:
+ virtual bool Generate(
+ const FileDescriptor* file,
+ const TProtoStringType& parameter,
+ GeneratorContext* generator_context,
+ TProtoStringType* error) const override;
+
+ bool GenerateAll(const std::vector<const FileDescriptor*>& files,
+ const TProtoStringType& parameter,
+ GeneratorContext* generator_context,
+ TProtoStringType* error) const override;
+
+ uint64_t GetSupportedFeatures() const override {
+ return FEATURE_PROTO3_OPTIONAL;
+ }
+
+ private:
+ bool Generate(
+ const FileDescriptor* file,
+ const Options& options,
+ GeneratorContext* generator_context,
+ TProtoStringType* error) const;
+};
+
+// To skip reserved keywords in php, some generated classname are prefixed.
+// Other code generators may need following API to figure out the actual
+// classname.
+PROTOC_EXPORT TProtoStringType GeneratedClassName(const Descriptor* desc);
+PROTOC_EXPORT TProtoStringType GeneratedClassName(const EnumDescriptor* desc);
+PROTOC_EXPORT TProtoStringType GeneratedClassName(const ServiceDescriptor* desc);
+
+inline bool IsWrapperType(const FieldDescriptor* descriptor) {
+ return descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
+ descriptor->message_type()->file()->name() == "google/protobuf/wrappers.proto";
+}
+
+} // namespace php
+} // namespace compiler
+} // namespace protobuf
+} // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif // GOOGLE_PROTOBUF_COMPILER_PHP_GENERATOR_H__