diff options
author | thegeorg <[email protected]> | 2022-02-10 16:45:08 +0300 |
---|---|---|
committer | Daniil Cherednik <[email protected]> | 2022-02-10 16:45:08 +0300 |
commit | 4e839db24a3bbc9f1c610c43d6faaaa99824dcca (patch) | |
tree | 506dac10f5df94fab310584ee51b24fc5a081c22 /contrib/libs/protoc/src/google/protobuf/compiler/php | |
parent | 2d37894b1b037cf24231090eda8589bbb44fb6fc (diff) |
Restoring authorship annotation for <[email protected]>. Commit 1 of 2.
Diffstat (limited to 'contrib/libs/protoc/src/google/protobuf/compiler/php')
-rw-r--r-- | contrib/libs/protoc/src/google/protobuf/compiler/php/php_generator.cc | 4652 | ||||
-rw-r--r-- | contrib/libs/protoc/src/google/protobuf/compiler/php/php_generator.h | 184 |
2 files changed, 2418 insertions, 2418 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 index 696e4ceec86..86da631f5ba 100644 --- a/contrib/libs/protoc/src/google/protobuf/compiler/php/php_generator.cc +++ b/contrib/libs/protoc/src/google/protobuf/compiler/php/php_generator.cc @@ -1,2326 +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("*"); - } else { - result.push_back(c); - } - break; - case '/': - // Avoid "*/". - if (prev == '*') { - result.append("/"); - } 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("@"); - 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 +// 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("*"); + } else { + result.push_back(c); + } + break; + case '/': + // Avoid "*/". + if (prev == '*') { + result.append("/"); + } 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("@"); + 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 index 0247afaa560..cee037483a1 100644 --- a/contrib/libs/protoc/src/google/protobuf/compiler/php/php_generator.h +++ b/contrib/libs/protoc/src/google/protobuf/compiler/php/php_generator.h @@ -1,92 +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__ +// 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__ |