xref: /aosp_15_r20/external/cronet/third_party/protobuf/src/google/protobuf/compiler/php/php_generator.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 #include <google/protobuf/compiler/php/php_generator.h>
32 
33 #include <google/protobuf/compiler/code_generator.h>
34 #include <google/protobuf/compiler/plugin.h>
35 #include <google/protobuf/descriptor.h>
36 #include <google/protobuf/descriptor.pb.h>
37 #include <google/protobuf/io/printer.h>
38 #include <google/protobuf/io/zero_copy_stream.h>
39 #include <google/protobuf/stubs/strutil.h>
40 
41 #include <sstream>
42 
43 const std::string kDescriptorFile = "google/protobuf/descriptor.proto";
44 const std::string kEmptyFile = "google/protobuf/empty.proto";
45 const std::string kEmptyMetadataFile = "GPBMetadata/Google/Protobuf/GPBEmpty.php";
46 const std::string kDescriptorMetadataFile =
47     "GPBMetadata/Google/Protobuf/Internal/Descriptor.php";
48 const std::string kDescriptorDirName = "Google/Protobuf/Internal";
49 const std::string kDescriptorPackageName = "Google\\Protobuf\\Internal";
50 const char* const kReservedNames[] = {
51     "abstract",     "and",        "array",        "as",         "break",
52     "callable",     "case",       "catch",        "class",      "clone",
53     "const",        "continue",   "declare",      "default",    "die",
54     "do",           "echo",       "else",         "elseif",     "empty",
55     "enddeclare",   "endfor",     "endforeach",   "endif",      "endswitch",
56     "endwhile",     "eval",       "exit",         "extends",    "final",
57     "finally",      "fn",         "for",          "foreach",    "function",
58     "global",       "goto",       "if",           "implements", "include",
59     "include_once", "instanceof", "insteadof",    "interface",  "isset",
60     "list",         "match",      "namespace",    "new",        "or",
61     "parent",       "print",      "private",      "protected",  "public",
62     "readonly",     "require",    "require_once", "return",     "self",
63     "static",       "switch",     "throw",        "trait",      "try",
64     "unset",        "use",        "var",          "while",      "xor",
65     "yield",        "int",        "float",        "bool",       "string",
66     "true",         "false",      "null",         "void",       "iterable"};
67 const char* const kValidConstantNames[] = {
68     "int",   "float", "bool", "string",   "true",
69     "false", "null",  "void", "iterable", "parent",
70     "self", "readonly"
71 };
72 const int kReservedNamesSize = 80;
73 const int kValidConstantNamesSize = 12;
74 const int kFieldSetter = 1;
75 const int kFieldGetter = 2;
76 const int kFieldProperty = 3;
77 
78 namespace google {
79 namespace protobuf {
80 namespace compiler {
81 namespace php {
82 
83 struct Options {
84   bool is_descriptor = false;
85   bool aggregate_metadata = false;
86   bool gen_c_wkt = false;
87   std::set<std::string> aggregate_metadata_prefixes;
88 };
89 
90 namespace {
91 
92 // Forward decls.
93 std::string PhpName(const std::string& full_name, const Options& options);
94 std::string IntToString(int32_t value);
95 std::string FilenameToClassname(const std::string& filename);
96 std::string GeneratedMetadataFileName(const FileDescriptor* file,
97                                       const Options& options);
98 std::string UnderscoresToCamelCase(const std::string& name,
99                                    bool cap_first_letter);
100 void Indent(io::Printer* printer);
101 void Outdent(io::Printer* printer);
102 void GenerateAddFilesToPool(const FileDescriptor* file, const Options& options,
103                             io::Printer* printer);
104 void GenerateMessageDocComment(io::Printer* printer, const Descriptor* message,
105                                const Options& options);
106 void GenerateMessageConstructorDocComment(io::Printer* printer,
107                                           const Descriptor* message,
108                                           const Options& options);
109 void GenerateFieldDocComment(io::Printer* printer, const FieldDescriptor* field,
110                              const Options& options, int function_type);
111 void GenerateWrapperFieldGetterDocComment(io::Printer* printer,
112                                           const FieldDescriptor* field);
113 void GenerateWrapperFieldSetterDocComment(io::Printer* printer,
114                                           const FieldDescriptor* field);
115 void GenerateEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_,
116                             const Options& options);
117 void GenerateEnumValueDocComment(io::Printer* printer,
118                                  const EnumValueDescriptor* value);
119 void GenerateServiceDocComment(io::Printer* printer,
120                                const ServiceDescriptor* service);
121 void GenerateServiceMethodDocComment(io::Printer* printer,
122                               const MethodDescriptor* method);
123 
ReservedNamePrefix(const std::string & classname,const FileDescriptor * file)124 std::string ReservedNamePrefix(const std::string& classname,
125                                 const FileDescriptor* file) {
126   bool is_reserved = false;
127 
128   std::string lower = classname;
129   std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
130 
131   for (int i = 0; i < kReservedNamesSize; i++) {
132     if (lower == kReservedNames[i]) {
133       is_reserved = true;
134       break;
135     }
136   }
137 
138   if (is_reserved) {
139     if (file->package() == "google.protobuf") {
140       return "GPB";
141     } else {
142       return "PB";
143     }
144   }
145 
146   return "";
147 }
148 
149 template <typename DescriptorType>
DescriptorFullName(const DescriptorType * desc,bool is_internal)150 std::string DescriptorFullName(const DescriptorType* desc, bool is_internal) {
151   if (is_internal) {
152     return StringReplace(desc->full_name(),
153                          "google.protobuf",
154                          "google.protobuf.internal", false);
155   } else {
156     return desc->full_name();
157   }
158 }
159 
160 template <typename DescriptorType>
ClassNamePrefix(const std::string & classname,const DescriptorType * desc)161 std::string ClassNamePrefix(const std::string& classname,
162                             const DescriptorType* desc) {
163   const std::string& prefix = (desc->file()->options()).php_class_prefix();
164   if (!prefix.empty()) {
165     return prefix;
166   }
167 
168   return ReservedNamePrefix(classname, desc->file());
169 }
170 
171 template <typename DescriptorType>
GeneratedClassNameImpl(const DescriptorType * desc)172 std::string GeneratedClassNameImpl(const DescriptorType* desc) {
173   std::string classname = ClassNamePrefix(desc->name(), desc) + desc->name();
174   const Descriptor* containing = desc->containing_type();
175   while (containing != NULL) {
176     classname = ClassNamePrefix(containing->name(), desc) + containing->name()
177        + '\\' + classname;
178     containing = containing->containing_type();
179   }
180   return classname;
181 }
182 
GeneratedClassNameImpl(const ServiceDescriptor * desc)183 std::string GeneratedClassNameImpl(const ServiceDescriptor* desc) {
184   std::string classname = desc->name();
185   return ClassNamePrefix(classname, desc) + classname;
186 }
187 
188 template <typename DescriptorType>
LegacyGeneratedClassName(const DescriptorType * desc)189 std::string LegacyGeneratedClassName(const DescriptorType* desc) {
190   std::string classname = desc->name();
191   const Descriptor* containing = desc->containing_type();
192   while (containing != NULL) {
193     classname = containing->name() + '_' + classname;
194     containing = containing->containing_type();
195   }
196   return ClassNamePrefix(classname, desc) + classname;
197 }
198 
ClassNamePrefix(const std::string & classname)199 std::string ClassNamePrefix(const std::string& classname) {
200   std::string lower = classname;
201   std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
202 
203   for (int i = 0; i < kReservedNamesSize; i++) {
204     if (lower == kReservedNames[i]) {
205       return "PB";
206     }
207   }
208 
209   return "";
210 }
211 
ConstantNamePrefix(const std::string & classname)212 std::string ConstantNamePrefix(const std::string& classname) {
213   bool is_reserved = false;
214 
215   std::string lower = classname;
216   std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
217 
218   for (int i = 0; i < kReservedNamesSize; i++) {
219     if (lower == kReservedNames[i]) {
220       is_reserved = true;
221       break;
222     }
223   }
224 
225   for (int i = 0; i < kValidConstantNamesSize; i++) {
226     if (lower == kValidConstantNames[i]) {
227       is_reserved = false;
228       break;
229     }
230   }
231 
232   if (is_reserved) {
233     return "PB";
234   }
235 
236   return "";
237 }
238 
239 template <typename DescriptorType>
RootPhpNamespace(const DescriptorType * desc,const Options & options)240 std::string RootPhpNamespace(const DescriptorType* desc,
241                              const Options& options) {
242   if (desc->file()->options().has_php_namespace()) {
243     const std::string& php_namespace = desc->file()->options().php_namespace();
244     if (!php_namespace.empty()) {
245       return php_namespace;
246     }
247     return "";
248   }
249 
250   if (!desc->file()->package().empty()) {
251     return PhpName(desc->file()->package(), options);
252   }
253   return "";
254 }
255 
256 template <typename DescriptorType>
FullClassName(const DescriptorType * desc,const Options & options)257 std::string FullClassName(const DescriptorType* desc, const Options& options) {
258   std::string classname = GeneratedClassNameImpl(desc);
259   std::string php_namespace = RootPhpNamespace(desc, options);
260   if (!php_namespace.empty()) {
261     return php_namespace + "\\" + classname;
262   }
263   return classname;
264 }
265 
266 template <typename DescriptorType>
FullClassName(const DescriptorType * desc,bool is_descriptor)267 std::string FullClassName(const DescriptorType* desc, bool is_descriptor) {
268   Options options;
269   options.is_descriptor = is_descriptor;
270   return FullClassName(desc, options);
271 }
272 
273 template <typename DescriptorType>
LegacyFullClassName(const DescriptorType * desc,const Options & options)274 std::string LegacyFullClassName(const DescriptorType* desc,
275                                 const Options& options) {
276   std::string classname = LegacyGeneratedClassName(desc);
277   std::string php_namespace = RootPhpNamespace(desc, options);
278   if (!php_namespace.empty()) {
279     return php_namespace + "\\" + classname;
280   }
281   return classname;
282 }
283 
PhpName(const std::string & full_name,const Options & options)284 std::string PhpName(const std::string& full_name, const Options& options) {
285   if (options.is_descriptor) {
286     return kDescriptorPackageName;
287   }
288 
289   std::string segment;
290   std::string result;
291   bool cap_next_letter = true;
292   for (int i = 0; i < full_name.size(); i++) {
293     if ('a' <= full_name[i] && full_name[i] <= 'z' && cap_next_letter) {
294       segment += full_name[i] + ('A' - 'a');
295       cap_next_letter = false;
296     } else if (full_name[i] == '.') {
297       result += ClassNamePrefix(segment) + segment + '\\';
298       segment = "";
299       cap_next_letter = true;
300     } else {
301       segment += full_name[i];
302       cap_next_letter = false;
303     }
304   }
305   result += ClassNamePrefix(segment) + segment;
306   return result;
307 }
308 
DefaultForField(const FieldDescriptor * field)309 std::string DefaultForField(const FieldDescriptor* field) {
310   switch (field->type()) {
311     case FieldDescriptor::TYPE_INT32:
312     case FieldDescriptor::TYPE_INT64:
313     case FieldDescriptor::TYPE_UINT32:
314     case FieldDescriptor::TYPE_UINT64:
315     case FieldDescriptor::TYPE_SINT32:
316     case FieldDescriptor::TYPE_SINT64:
317     case FieldDescriptor::TYPE_FIXED32:
318     case FieldDescriptor::TYPE_FIXED64:
319     case FieldDescriptor::TYPE_SFIXED32:
320     case FieldDescriptor::TYPE_SFIXED64:
321     case FieldDescriptor::TYPE_ENUM: return "0";
322     case FieldDescriptor::TYPE_DOUBLE:
323     case FieldDescriptor::TYPE_FLOAT: return "0.0";
324     case FieldDescriptor::TYPE_BOOL: return "false";
325     case FieldDescriptor::TYPE_STRING:
326     case FieldDescriptor::TYPE_BYTES: return "''";
327     case FieldDescriptor::TYPE_MESSAGE:
328     case FieldDescriptor::TYPE_GROUP: return "null";
329     default: assert(false); return "";
330   }
331 }
332 
GeneratedMetadataFileName(const FileDescriptor * file,const Options & options)333 std::string GeneratedMetadataFileName(const FileDescriptor* file,
334                                       const Options& options) {
335   const std::string& proto_file = file->name();
336   int start_index = 0;
337   int first_index = proto_file.find_first_of("/", start_index);
338   std::string result = "";
339   std::string segment = "";
340 
341   if (proto_file == kEmptyFile) {
342     return kEmptyMetadataFile;
343   }
344   if (options.is_descriptor) {
345     return kDescriptorMetadataFile;
346   }
347 
348   // Append directory name.
349   std::string file_no_suffix;
350   int lastindex = proto_file.find_last_of(".");
351   if (proto_file == kEmptyFile) {
352     return kEmptyMetadataFile;
353   } else {
354     file_no_suffix = proto_file.substr(0, lastindex);
355   }
356 
357   if (file->options().has_php_metadata_namespace()) {
358     const std::string& php_metadata_namespace =
359         file->options().php_metadata_namespace();
360     if (!php_metadata_namespace.empty() && php_metadata_namespace != "\\") {
361       result += php_metadata_namespace;
362       std::replace(result.begin(), result.end(), '\\', '/');
363       if (result.at(result.size() - 1) != '/') {
364         result += "/";
365       }
366     }
367   } else {
368     result += "GPBMetadata/";
369     while (first_index != std::string::npos) {
370       segment = UnderscoresToCamelCase(
371           file_no_suffix.substr(start_index, first_index - start_index), true);
372       result += ReservedNamePrefix(segment, file) + segment + "/";
373       start_index = first_index + 1;
374       first_index = file_no_suffix.find_first_of("/", start_index);
375     }
376   }
377 
378   // Append file name.
379   int file_name_start = file_no_suffix.find_last_of("/");
380   if (file_name_start == std::string::npos) {
381     file_name_start = 0;
382   } else {
383     file_name_start += 1;
384   }
385   segment = UnderscoresToCamelCase(
386       file_no_suffix.substr(file_name_start, first_index - file_name_start), true);
387 
388   return result + ReservedNamePrefix(segment, file) + segment + ".php";
389 }
390 
GeneratedMetadataFileName(const FileDescriptor * file,bool is_descriptor)391 std::string GeneratedMetadataFileName(const FileDescriptor* file,
392                                       bool is_descriptor) {
393   Options options;
394   options.is_descriptor = is_descriptor;
395   return GeneratedMetadataFileName(file, options);
396 }
397 
398 template <typename DescriptorType>
GeneratedClassFileName(const DescriptorType * desc,const Options & options)399 std::string GeneratedClassFileName(const DescriptorType* desc,
400                                    const Options& options) {
401   std::string result = FullClassName(desc, options);
402   for (int i = 0; i < result.size(); i++) {
403     if (result[i] == '\\') {
404       result[i] = '/';
405     }
406   }
407   return result + ".php";
408 }
409 
410 template <typename DescriptorType>
LegacyGeneratedClassFileName(const DescriptorType * desc,const Options & options)411 std::string LegacyGeneratedClassFileName(const DescriptorType* desc,
412                                          const Options& options) {
413   std::string result = LegacyFullClassName(desc, options);
414 
415   for (int i = 0; i < result.size(); i++) {
416     if (result[i] == '\\') {
417       result[i] = '/';
418     }
419   }
420   return result + ".php";
421 }
422 
423 template <typename DescriptorType>
LegacyReadOnlyGeneratedClassFileName(std::string php_namespace,const DescriptorType * desc)424 std::string LegacyReadOnlyGeneratedClassFileName(std::string php_namespace,
425                                                  const DescriptorType* desc) {
426   if (!php_namespace.empty()) {
427     for (int i = 0; i < php_namespace.size(); i++) {
428       if (php_namespace[i] == '\\') {
429         php_namespace[i] = '/';
430       }
431     }
432     return php_namespace + "/" + desc->name() + ".php";
433   }
434 
435   return desc->name() + ".php";
436 }
437 
GeneratedServiceFileName(const ServiceDescriptor * service,const Options & options)438 std::string GeneratedServiceFileName(const ServiceDescriptor* service,
439                                      const Options& options) {
440   std::string result = FullClassName(service, options) + "Interface";
441   for (int i = 0; i < result.size(); i++) {
442     if (result[i] == '\\') {
443       result[i] = '/';
444     }
445   }
446   return result + ".php";
447 }
448 
IntToString(int32_t value)449 std::string IntToString(int32_t value) {
450   std::ostringstream os;
451   os << value;
452   return os.str();
453 }
454 
LabelForField(const FieldDescriptor * field)455 std::string LabelForField(const FieldDescriptor* field) {
456   switch (field->label()) {
457     case FieldDescriptor::LABEL_OPTIONAL: return "optional";
458     case FieldDescriptor::LABEL_REQUIRED: return "required";
459     case FieldDescriptor::LABEL_REPEATED: return "repeated";
460     default: assert(false); return "";
461   }
462 }
463 
PhpSetterTypeName(const FieldDescriptor * field,const Options & options)464 std::string PhpSetterTypeName(const FieldDescriptor* field,
465                               const Options& options) {
466   if (field->is_map()) {
467     return "array|\\Google\\Protobuf\\Internal\\MapField";
468   }
469   std::string type;
470   switch (field->type()) {
471     case FieldDescriptor::TYPE_INT32:
472     case FieldDescriptor::TYPE_UINT32:
473     case FieldDescriptor::TYPE_SINT32:
474     case FieldDescriptor::TYPE_FIXED32:
475     case FieldDescriptor::TYPE_SFIXED32:
476     case FieldDescriptor::TYPE_ENUM:
477       type = "int";
478       break;
479     case FieldDescriptor::TYPE_INT64:
480     case FieldDescriptor::TYPE_UINT64:
481     case FieldDescriptor::TYPE_SINT64:
482     case FieldDescriptor::TYPE_FIXED64:
483     case FieldDescriptor::TYPE_SFIXED64:
484       type = "int|string";
485       break;
486     case FieldDescriptor::TYPE_DOUBLE:
487     case FieldDescriptor::TYPE_FLOAT:
488       type = "float";
489       break;
490     case FieldDescriptor::TYPE_BOOL:
491       type = "bool";
492       break;
493     case FieldDescriptor::TYPE_STRING:
494     case FieldDescriptor::TYPE_BYTES:
495       type = "string";
496       break;
497     case FieldDescriptor::TYPE_MESSAGE:
498       type = "\\" + FullClassName(field->message_type(), options);
499       break;
500     case FieldDescriptor::TYPE_GROUP:
501       return "null";
502     default: assert(false); return "";
503   }
504   if (field->is_repeated()) {
505     // accommodate for edge case with multiple types.
506     size_t start_pos = type.find("|");
507     if (start_pos != std::string::npos) {
508       type.replace(start_pos, 1, ">|array<");
509     }
510     type = "array<" + type + ">|\\Google\\Protobuf\\Internal\\RepeatedField";
511   }
512   return type;
513 }
514 
PhpSetterTypeName(const FieldDescriptor * field,bool is_descriptor)515 std::string PhpSetterTypeName(const FieldDescriptor* field,
516                               bool is_descriptor) {
517   Options options;
518   options.is_descriptor = is_descriptor;
519   return PhpSetterTypeName(field, options);
520 }
521 
PhpGetterTypeName(const FieldDescriptor * field,const Options & options)522 std::string PhpGetterTypeName(const FieldDescriptor* field,
523                               const Options& options) {
524   if (field->is_map()) {
525     return "\\Google\\Protobuf\\Internal\\MapField";
526   }
527   if (field->is_repeated()) {
528     return "\\Google\\Protobuf\\Internal\\RepeatedField";
529   }
530   switch (field->type()) {
531     case FieldDescriptor::TYPE_INT32:
532     case FieldDescriptor::TYPE_UINT32:
533     case FieldDescriptor::TYPE_SINT32:
534     case FieldDescriptor::TYPE_FIXED32:
535     case FieldDescriptor::TYPE_SFIXED32:
536     case FieldDescriptor::TYPE_ENUM: return "int";
537     case FieldDescriptor::TYPE_INT64:
538     case FieldDescriptor::TYPE_UINT64:
539     case FieldDescriptor::TYPE_SINT64:
540     case FieldDescriptor::TYPE_FIXED64:
541     case FieldDescriptor::TYPE_SFIXED64: return "int|string";
542     case FieldDescriptor::TYPE_DOUBLE:
543     case FieldDescriptor::TYPE_FLOAT: return "float";
544     case FieldDescriptor::TYPE_BOOL: return "bool";
545     case FieldDescriptor::TYPE_STRING:
546     case FieldDescriptor::TYPE_BYTES: return "string";
547     case FieldDescriptor::TYPE_MESSAGE:
548       return "\\" + FullClassName(field->message_type(), options);
549     case FieldDescriptor::TYPE_GROUP: return "null";
550     default: assert(false); return "";
551   }
552 }
553 
PhpGetterTypeName(const FieldDescriptor * field,bool is_descriptor)554 std::string PhpGetterTypeName(const FieldDescriptor* field,
555                               bool is_descriptor) {
556   Options options;
557   options.is_descriptor = is_descriptor;
558   return PhpGetterTypeName(field, options);
559 }
560 
EnumOrMessageSuffix(const FieldDescriptor * field,const Options & options)561 std::string EnumOrMessageSuffix(const FieldDescriptor* field,
562                                 const Options& options) {
563   if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
564     return ", '" +
565            DescriptorFullName(field->message_type(), options.is_descriptor) +
566            "'";
567   }
568   if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
569     return ", '" +
570            DescriptorFullName(field->enum_type(), options.is_descriptor) + "'";
571   }
572   return "";
573 }
574 
EnumOrMessageSuffix(const FieldDescriptor * field,bool is_descriptor)575 std::string EnumOrMessageSuffix(const FieldDescriptor* field,
576                                 bool is_descriptor) {
577   Options options;
578   options.is_descriptor = is_descriptor;
579   return EnumOrMessageSuffix(field, options);
580 }
581 
582 // Converts a name to camel-case. If cap_first_letter is true, capitalize the
583 // first letter.
UnderscoresToCamelCase(const std::string & name,bool cap_first_letter)584 std::string UnderscoresToCamelCase(const std::string& name,
585                                    bool cap_first_letter) {
586   std::string result;
587   for (int i = 0; i < name.size(); i++) {
588     if ('a' <= name[i] && name[i] <= 'z') {
589       if (cap_first_letter) {
590         result += name[i] + ('A' - 'a');
591       } else {
592         result += name[i];
593       }
594       cap_first_letter = false;
595     } else if ('A' <= name[i] && name[i] <= 'Z') {
596       if (i == 0 && !cap_first_letter) {
597         // Force first letter to lower-case unless explicitly told to
598         // capitalize it.
599         result += name[i] + ('a' - 'A');
600       } else {
601         // Capital letters after the first are left as-is.
602         result += name[i];
603       }
604       cap_first_letter = false;
605     } else if ('0' <= name[i] && name[i] <= '9') {
606       result += name[i];
607       cap_first_letter = true;
608     } else {
609       cap_first_letter = true;
610     }
611   }
612   // Add a trailing "_" if the name should be altered.
613   if (name[name.size() - 1] == '#') {
614     result += '_';
615   }
616   return result;
617 }
618 
Indent(io::Printer * printer)619 void Indent(io::Printer* printer) {
620   printer->Indent();
621   printer->Indent();
622 }
Outdent(io::Printer * printer)623 void Outdent(io::Printer* printer) {
624   printer->Outdent();
625   printer->Outdent();
626 }
627 
GenerateField(const FieldDescriptor * field,io::Printer * printer,const Options & options)628 void GenerateField(const FieldDescriptor* field, io::Printer* printer,
629                    const Options& options) {
630   if (field->is_repeated()) {
631     GenerateFieldDocComment(printer, field, options, kFieldProperty);
632     printer->Print(
633         "private $^name^;\n",
634         "name", field->name());
635   } else if (field->real_containing_oneof()) {
636     // Oneof fields are handled by GenerateOneofField.
637     return;
638   } else {
639     std::string initial_value =
640         field->has_presence() ? "null" : DefaultForField(field);
641     GenerateFieldDocComment(printer, field, options, kFieldProperty);
642     printer->Print(
643         "protected $^name^ = ^initial_value^;\n",
644         "name", field->name(),
645         "initial_value", initial_value);
646   }
647 }
648 
GenerateOneofField(const OneofDescriptor * oneof,io::Printer * printer)649 void GenerateOneofField(const OneofDescriptor* oneof, io::Printer* printer) {
650   // Oneof property needs to be protected in order to be accessed by parent
651   // class in implementation.
652   printer->Print(
653       "protected $^name^;\n",
654       "name", oneof->name());
655 }
656 
GenerateFieldAccessor(const FieldDescriptor * field,const Options & options,io::Printer * printer)657 void GenerateFieldAccessor(const FieldDescriptor* field, const Options& options,
658                            io::Printer* printer) {
659   const OneofDescriptor* oneof = field->real_containing_oneof();
660 
661   // Generate getter.
662   GenerateFieldDocComment(printer, field, options, kFieldGetter);
663 
664   // deprecation
665   std::string deprecation_trigger = (field->options().deprecated()) ? "@trigger_error('" +
666       field->name() + " is deprecated.', E_USER_DEPRECATED);\n        " : "";
667 
668   // Emit getter.
669   if (oneof != NULL) {
670     printer->Print(
671         "public function get^camel_name^()\n"
672         "{\n"
673         "    ^deprecation_trigger^return $this->readOneof(^number^);\n"
674         "}\n\n",
675         "camel_name", UnderscoresToCamelCase(field->name(), true),
676         "number", IntToString(field->number()),
677         "deprecation_trigger", deprecation_trigger);
678   } else if (field->has_presence() && !field->message_type()) {
679     printer->Print(
680         "public function get^camel_name^()\n"
681         "{\n"
682         "    ^deprecation_trigger^return isset($this->^name^) ? $this->^name^ : ^default_value^;\n"
683         "}\n\n",
684         "camel_name", UnderscoresToCamelCase(field->name(), true),
685         "name", field->name(),
686         "default_value", DefaultForField(field),
687         "deprecation_trigger", deprecation_trigger);
688   } else {
689     printer->Print(
690         "public function get^camel_name^()\n"
691         "{\n"
692         "    ^deprecation_trigger^return $this->^name^;\n"
693         "}\n\n",
694         "camel_name", UnderscoresToCamelCase(field->name(), true),
695         "name", field->name(),
696         "deprecation_trigger", deprecation_trigger);
697   }
698 
699   // Emit hazzers/clear.
700   if (oneof) {
701     printer->Print(
702         "public function has^camel_name^()\n"
703         "{\n"
704         "    ^deprecation_trigger^return $this->hasOneof(^number^);\n"
705         "}\n\n",
706         "camel_name", UnderscoresToCamelCase(field->name(), true),
707         "number", IntToString(field->number()),
708         "deprecation_trigger", deprecation_trigger);
709   } else if (field->has_presence()) {
710     printer->Print(
711         "public function has^camel_name^()\n"
712         "{\n"
713         "    ^deprecation_trigger^return isset($this->^name^);\n"
714         "}\n\n"
715         "public function clear^camel_name^()\n"
716         "{\n"
717         "    ^deprecation_trigger^unset($this->^name^);\n"
718         "}\n\n",
719         "camel_name", UnderscoresToCamelCase(field->name(), true),
720         "name", field->name(),
721         "default_value", DefaultForField(field),
722         "deprecation_trigger", deprecation_trigger);
723   }
724 
725   // For wrapper types, generate an additional getXXXUnwrapped getter
726   if (!field->is_map() &&
727       !field->is_repeated() &&
728       field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
729       IsWrapperType(field)) {
730     GenerateWrapperFieldGetterDocComment(printer, field);
731     printer->Print(
732         "public function get^camel_name^Unwrapped()\n"
733         "{\n"
734         "    ^deprecation_trigger^return $this->readWrapperValue(\"^field_name^\");\n"
735         "}\n\n",
736         "camel_name", UnderscoresToCamelCase(field->name(), true),
737         "field_name", field->name(),
738         "deprecation_trigger", deprecation_trigger);
739   }
740 
741   // Generate setter.
742   GenerateFieldDocComment(printer, field, options, kFieldSetter);
743   printer->Print(
744       "public function set^camel_name^($var)\n"
745       "{\n",
746       "camel_name", UnderscoresToCamelCase(field->name(), true));
747 
748   Indent(printer);
749 
750   if (field->options().deprecated()) {
751       printer->Print(
752           "^deprecation_trigger^",
753           "deprecation_trigger", deprecation_trigger
754       );
755   }
756 
757   // Type check.
758   if (field->is_map()) {
759     const Descriptor* map_entry = field->message_type();
760     const FieldDescriptor* key = map_entry->map_key();
761     const FieldDescriptor* value = map_entry->map_value();
762     printer->Print(
763         "$arr = GPBUtil::checkMapField($var, "
764         "\\Google\\Protobuf\\Internal\\GPBType::^key_type^, "
765         "\\Google\\Protobuf\\Internal\\GPBType::^value_type^",
766         "key_type", ToUpper(key->type_name()),
767         "value_type", ToUpper(value->type_name()));
768     if (value->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
769       printer->Print(
770           ", \\^class_name^);\n",
771           "class_name",
772           FullClassName(value->message_type(), options) + "::class");
773     } else if (value->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
774       printer->Print(
775           ", \\^class_name^);\n",
776           "class_name",
777           FullClassName(value->enum_type(), options) + "::class");
778     } else {
779       printer->Print(");\n");
780     }
781   } else if (field->is_repeated()) {
782     printer->Print(
783         "$arr = GPBUtil::checkRepeatedField($var, "
784         "\\Google\\Protobuf\\Internal\\GPBType::^type^",
785         "type", ToUpper(field->type_name()));
786     if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
787       printer->Print(
788           ", \\^class_name^);\n",
789           "class_name",
790           FullClassName(field->message_type(), options) + "::class");
791     } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
792       printer->Print(
793           ", \\^class_name^);\n",
794           "class_name",
795           FullClassName(field->enum_type(), options) + "::class");
796     } else {
797       printer->Print(");\n");
798     }
799   } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
800     printer->Print(
801         "GPBUtil::checkMessage($var, \\^class_name^::class);\n",
802         "class_name", FullClassName(field->message_type(), options));
803   } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
804     printer->Print(
805         "GPBUtil::checkEnum($var, \\^class_name^::class);\n",
806         "class_name", FullClassName(field->enum_type(), options));
807   } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
808     printer->Print(
809         "GPBUtil::checkString($var, ^utf8^);\n",
810         "utf8",
811         field->type() == FieldDescriptor::TYPE_STRING ? "True": "False");
812   } else {
813     printer->Print(
814         "GPBUtil::check^type^($var);\n",
815         "type", UnderscoresToCamelCase(field->cpp_type_name(), true));
816   }
817 
818   if (oneof != NULL) {
819     printer->Print(
820         "$this->writeOneof(^number^, $var);\n",
821         "number", IntToString(field->number()));
822   } else if (field->is_repeated()) {
823     printer->Print(
824         "$this->^name^ = $arr;\n",
825         "name", field->name());
826   } else {
827     printer->Print(
828         "$this->^name^ = $var;\n",
829         "name", field->name());
830   }
831 
832   printer->Print("\nreturn $this;\n");
833 
834   Outdent(printer);
835 
836   printer->Print(
837       "}\n\n");
838 
839   // For wrapper types, generate an additional setXXXValue getter
840   if (!field->is_map() &&
841       !field->is_repeated() &&
842       field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
843       IsWrapperType(field)) {
844     GenerateWrapperFieldSetterDocComment(printer, field);
845     printer->Print(
846         "public function set^camel_name^Unwrapped($var)\n"
847         "{\n"
848         "    $this->writeWrapperValue(\"^field_name^\", $var);\n"
849         "    return $this;"
850         "}\n\n",
851         "camel_name", UnderscoresToCamelCase(field->name(), true),
852         "field_name", field->name());
853   }
854 }
855 
GenerateEnumToPool(const EnumDescriptor * en,io::Printer * printer)856 void GenerateEnumToPool(const EnumDescriptor* en, io::Printer* printer) {
857   printer->Print(
858       "$pool->addEnum('^name^', "
859       "\\Google\\Protobuf\\Internal\\^class_name^::class)\n",
860       "name", DescriptorFullName(en, true),
861       "class_name", en->name());
862   Indent(printer);
863 
864   for (int i = 0; i < en->value_count(); i++) {
865     const EnumValueDescriptor* value = en->value(i);
866     printer->Print(
867         "->value(\"^name^\", ^number^)\n",
868         "name", ConstantNamePrefix(value->name()) + value->name(),
869         "number", IntToString(value->number()));
870   }
871   printer->Print("->finalizeToPool();\n\n");
872   Outdent(printer);
873 }
874 
GenerateServiceMethod(const MethodDescriptor * method,io::Printer * printer)875 void GenerateServiceMethod(const MethodDescriptor* method,
876                            io::Printer* printer) {
877   printer->Print(
878         "public function ^camel_name^(\\^request_name^ $request);\n\n",
879         "camel_name", UnderscoresToCamelCase(method->name(), false),
880         "request_name", FullClassName(
881           method->input_type(), false)
882   );
883 }
884 
GenerateMessageToPool(const std::string & name_prefix,const Descriptor * message,io::Printer * printer)885 void GenerateMessageToPool(const std::string& name_prefix,
886                            const Descriptor* message, io::Printer* printer) {
887   // Don't generate MapEntry messages -- we use the PHP extension's native
888   // support for map fields instead.
889   if (message->options().map_entry()) {
890     return;
891   }
892   std::string class_name =
893       (name_prefix.empty() ? "" : name_prefix + "\\") +
894       ReservedNamePrefix(message->name(), message->file()) + message->name();
895 
896   printer->Print(
897       "$pool->addMessage('^message^', "
898       "\\Google\\Protobuf\\Internal\\^class_name^::class)\n",
899       "message", DescriptorFullName(message, true),
900       "class_name", class_name);
901 
902   Indent(printer);
903 
904   for (int i = 0; i < message->field_count(); i++) {
905     const FieldDescriptor* field = message->field(i);
906     if (field->is_map()) {
907       const FieldDescriptor* key =
908           field->message_type()->map_key();
909       const FieldDescriptor* val =
910           field->message_type()->map_value();
911       printer->Print(
912           "->map('^field^', \\Google\\Protobuf\\Internal\\GPBType::^key^, "
913           "\\Google\\Protobuf\\Internal\\GPBType::^value^, ^number^^other^)\n",
914           "field", field->name(),
915           "key", ToUpper(key->type_name()),
916           "value", ToUpper(val->type_name()),
917           "number", StrCat(field->number()),
918           "other", EnumOrMessageSuffix(val, true));
919     } else if (!field->real_containing_oneof()) {
920       printer->Print(
921           "->^label^('^field^', "
922           "\\Google\\Protobuf\\Internal\\GPBType::^type^, ^number^^other^)\n",
923           "field", field->name(),
924           "label", LabelForField(field),
925           "type", ToUpper(field->type_name()),
926           "number", StrCat(field->number()),
927           "other", EnumOrMessageSuffix(field, true));
928     }
929   }
930 
931   // oneofs.
932   for (int i = 0; i < message->real_oneof_decl_count(); i++) {
933     const OneofDescriptor* oneof = message->oneof_decl(i);
934     printer->Print("->oneof(^name^)\n",
935                    "name", oneof->name());
936     Indent(printer);
937     for (int index = 0; index < oneof->field_count(); index++) {
938       const FieldDescriptor* field = oneof->field(index);
939       printer->Print(
940           "->value('^field^', "
941           "\\Google\\Protobuf\\Internal\\GPBType::^type^, ^number^^other^)\n",
942           "field", field->name(),
943           "type", ToUpper(field->type_name()),
944           "number", StrCat(field->number()),
945           "other", EnumOrMessageSuffix(field, true));
946     }
947     printer->Print("->finish()\n");
948     Outdent(printer);
949   }
950 
951   printer->Print(
952       "->finalizeToPool();\n");
953 
954   Outdent(printer);
955 
956   printer->Print(
957       "\n");
958 
959   for (int i = 0; i < message->nested_type_count(); i++) {
960     GenerateMessageToPool(class_name, message->nested_type(i), printer);
961   }
962   for (int i = 0; i < message->enum_type_count(); i++) {
963     GenerateEnumToPool(message->enum_type(i), printer);
964   }
965 }
966 
GenerateAddFileToPool(const FileDescriptor * file,const Options & options,io::Printer * printer)967 void GenerateAddFileToPool(const FileDescriptor* file, const Options& options,
968                            io::Printer* printer) {
969   printer->Print(
970       "public static $is_initialized = false;\n\n"
971       "public static function initOnce() {\n");
972   Indent(printer);
973 
974   if (options.aggregate_metadata) {
975     GenerateAddFilesToPool(file, options, printer);
976   } else {
977     printer->Print(
978         "$pool = \\Google\\Protobuf\\Internal\\"
979         "DescriptorPool::getGeneratedPool();\n\n"
980         "if (static::$is_initialized == true) {\n"
981         "  return;\n"
982         "}\n");
983 
984     if (options.is_descriptor) {
985       for (int i = 0; i < file->message_type_count(); i++) {
986         GenerateMessageToPool("", file->message_type(i), printer);
987       }
988       for (int i = 0; i < file->enum_type_count(); i++) {
989         GenerateEnumToPool(file->enum_type(i), printer);
990       }
991 
992       printer->Print(
993           "$pool->finish();\n");
994     } else {
995       for (int i = 0; i < file->dependency_count(); i++) {
996         const std::string& name = file->dependency(i)->name();
997         // Currently, descriptor.proto is not ready for external usage. Skip to
998         // import it for now, so that its dependencies can still work as long as
999         // they don't use protos defined in descriptor.proto.
1000         if (name == kDescriptorFile) {
1001           continue;
1002         }
1003         std::string dependency_filename =
1004             GeneratedMetadataFileName(file->dependency(i), options);
1005         printer->Print(
1006             "\\^name^::initOnce();\n",
1007             "name", FilenameToClassname(dependency_filename));
1008       }
1009 
1010       // Add messages and enums to descriptor pool.
1011       FileDescriptorSet files;
1012       FileDescriptorProto* file_proto = files.add_file();
1013       file->CopyTo(file_proto);
1014 
1015       // Filter out descriptor.proto as it cannot be depended on for now.
1016       RepeatedPtrField<std::string>* dependency =
1017           file_proto->mutable_dependency();
1018       for (RepeatedPtrField<std::string>::iterator it = dependency->begin();
1019            it != dependency->end(); ++it) {
1020         if (*it != kDescriptorFile) {
1021           dependency->erase(it);
1022           break;
1023         }
1024       }
1025 
1026       // Filter out all extensions, since we do not support extension yet.
1027       file_proto->clear_extension();
1028       RepeatedPtrField<DescriptorProto>* message_type =
1029           file_proto->mutable_message_type();
1030       for (RepeatedPtrField<DescriptorProto>::iterator it = message_type->begin();
1031            it != message_type->end(); ++it) {
1032         it->clear_extension();
1033       }
1034 
1035       std::string files_data;
1036       files.SerializeToString(&files_data);
1037 
1038       printer->Print("$pool->internalAddGeneratedFile(\n");
1039       Indent(printer);
1040       printer->Print("'");
1041 
1042       for (auto ch : files_data) {
1043         switch (ch) {
1044           case '\\':
1045             printer->Print(R"(\\)");
1046             break;
1047           case '\'':
1048             printer->Print(R"(\')");
1049             break;
1050           default:
1051             printer->Print("^char^", "char", std::string(1, ch));
1052             break;
1053         }
1054       }
1055 
1056       printer->Print("'\n");
1057       Outdent(printer);
1058       printer->Print(
1059           ", true);\n\n");
1060     }
1061     printer->Print(
1062         "static::$is_initialized = true;\n");
1063   }
1064 
1065   Outdent(printer);
1066   printer->Print("}\n");
1067 }
1068 
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)1069 static void AnalyzeDependencyForFile(
1070     const FileDescriptor* file,
1071     std::set<const FileDescriptor*>* nodes_without_dependency,
1072     std::map<const FileDescriptor*, std::set<const FileDescriptor*>>* deps,
1073     std::map<const FileDescriptor*, int>* dependency_count) {
1074   int count = file->dependency_count();
1075   for (int i = 0; i < file->dependency_count(); i++) {
1076       const FileDescriptor* dependency = file->dependency(i);
1077       if (dependency->name() == kDescriptorFile) {
1078         count--;
1079         break;
1080       }
1081   }
1082 
1083   if (count == 0) {
1084     nodes_without_dependency->insert(file);
1085   } else {
1086     (*dependency_count)[file] = count;
1087     for (int i = 0; i < file->dependency_count(); i++) {
1088       const FileDescriptor* dependency = file->dependency(i);
1089       if (dependency->name() == kDescriptorFile) {
1090         continue;
1091       }
1092       if (deps->find(dependency) == deps->end()) {
1093         (*deps)[dependency] = std::set<const FileDescriptor*>();
1094       }
1095       (*deps)[dependency].insert(file);
1096       AnalyzeDependencyForFile(
1097           dependency, nodes_without_dependency, deps, dependency_count);
1098     }
1099   }
1100 }
1101 
NeedsUnwrapping(const FileDescriptor * file,const Options & options)1102 static bool NeedsUnwrapping(const FileDescriptor* file,
1103                             const Options& options) {
1104   bool has_aggregate_metadata_prefix = false;
1105   if (options.aggregate_metadata_prefixes.empty()) {
1106     has_aggregate_metadata_prefix = true;
1107   } else {
1108     for (const auto& prefix : options.aggregate_metadata_prefixes) {
1109       if (HasPrefixString(file->package(), prefix)) {
1110         has_aggregate_metadata_prefix = true;
1111         break;
1112       }
1113     }
1114   }
1115 
1116   return has_aggregate_metadata_prefix;
1117 }
1118 
GenerateAddFilesToPool(const FileDescriptor * file,const Options & options,io::Printer * printer)1119 void GenerateAddFilesToPool(const FileDescriptor* file, const Options& options,
1120                             io::Printer* printer) {
1121   printer->Print(
1122       "$pool = \\Google\\Protobuf\\Internal\\"
1123       "DescriptorPool::getGeneratedPool();\n"
1124       "if (static::$is_initialized == true) {\n"
1125       "  return;\n"
1126       "}\n");
1127 
1128   // Sort files according to dependency
1129   std::map<const FileDescriptor*, std::set<const FileDescriptor*>> deps;
1130   std::map<const FileDescriptor*, int> dependency_count;
1131   std::set<const FileDescriptor*> nodes_without_dependency;
1132   FileDescriptorSet sorted_file_set;
1133 
1134   AnalyzeDependencyForFile(
1135       file, &nodes_without_dependency, &deps, &dependency_count);
1136 
1137   while (!nodes_without_dependency.empty()) {
1138     auto file_node = *nodes_without_dependency.begin();
1139     nodes_without_dependency.erase(file_node);
1140     for (auto dependent : deps[file_node]) {
1141       if (dependency_count[dependent] == 1) {
1142         dependency_count.erase(dependent);
1143         nodes_without_dependency.insert(dependent);
1144       } else {
1145         dependency_count[dependent] -= 1;
1146       }
1147     }
1148 
1149     bool needs_aggregate = NeedsUnwrapping(file_node, options);
1150 
1151     if (needs_aggregate) {
1152       auto file_proto = sorted_file_set.add_file();
1153       file_node->CopyTo(file_proto);
1154 
1155       // Filter out descriptor.proto as it cannot be depended on for now.
1156       RepeatedPtrField<std::string>* dependency =
1157           file_proto->mutable_dependency();
1158       for (RepeatedPtrField<std::string>::iterator it = dependency->begin();
1159            it != dependency->end(); ++it) {
1160         if (*it != kDescriptorFile) {
1161           dependency->erase(it);
1162           break;
1163         }
1164       }
1165 
1166       // Filter out all extensions, since we do not support extension yet.
1167       file_proto->clear_extension();
1168       RepeatedPtrField<DescriptorProto>* message_type =
1169           file_proto->mutable_message_type();
1170       for (RepeatedPtrField<DescriptorProto>::iterator it = message_type->begin();
1171            it != message_type->end(); ++it) {
1172         it->clear_extension();
1173       }
1174     } else {
1175       std::string dependency_filename = GeneratedMetadataFileName(file_node, false);
1176       printer->Print(
1177           "\\^name^::initOnce();\n",
1178           "name", FilenameToClassname(dependency_filename));
1179     }
1180   }
1181 
1182   std::string files_data;
1183   sorted_file_set.SerializeToString(&files_data);
1184 
1185   printer->Print("$pool->internalAddGeneratedFile(\n");
1186   Indent(printer);
1187   printer->Print("'");
1188 
1189   for (auto ch : files_data) {
1190     switch (ch) {
1191       case '\\':
1192         printer->Print(R"(\\)");
1193         break;
1194       case '\'':
1195         printer->Print(R"(\')");
1196         break;
1197       default:
1198         printer->Print("^char^", "char", std::string(1, ch));
1199         break;
1200     }
1201   }
1202 
1203   printer->Print("'\n");
1204   Outdent(printer);
1205   printer->Print(
1206       ", true);\n");
1207 
1208   printer->Print(
1209       "static::$is_initialized = true;\n");
1210 }
1211 
GenerateUseDeclaration(const Options & options,io::Printer * printer)1212 void GenerateUseDeclaration(const Options& options, io::Printer* printer) {
1213   if (!options.is_descriptor) {
1214     printer->Print(
1215         "use Google\\Protobuf\\Internal\\GPBType;\n"
1216         "use Google\\Protobuf\\Internal\\RepeatedField;\n"
1217         "use Google\\Protobuf\\Internal\\GPBUtil;\n\n");
1218   } else {
1219     printer->Print(
1220         "use Google\\Protobuf\\Internal\\GPBType;\n"
1221         "use Google\\Protobuf\\Internal\\GPBWire;\n"
1222         "use Google\\Protobuf\\Internal\\RepeatedField;\n"
1223         "use Google\\Protobuf\\Internal\\InputStream;\n"
1224         "use Google\\Protobuf\\Internal\\GPBUtil;\n\n");
1225   }
1226 }
1227 
GenerateHead(const FileDescriptor * file,io::Printer * printer)1228 void GenerateHead(const FileDescriptor* file, io::Printer* printer) {
1229   printer->Print(
1230     "<?php\n"
1231     "# Generated by the protocol buffer compiler.  DO NOT EDIT!\n"
1232     "# source: ^filename^\n"
1233     "\n",
1234     "filename", file->name());
1235 }
1236 
FilenameToClassname(const std::string & filename)1237 std::string FilenameToClassname(const std::string& filename) {
1238   int lastindex = filename.find_last_of(".");
1239   std::string result = filename.substr(0, lastindex);
1240   for (int i = 0; i < result.size(); i++) {
1241     if (result[i] == '/') {
1242       result[i] = '\\';
1243     }
1244   }
1245   return result;
1246 }
1247 
GenerateMetadataFile(const FileDescriptor * file,const Options & options,GeneratorContext * generator_context)1248 void GenerateMetadataFile(const FileDescriptor* file, const Options& options,
1249                           GeneratorContext* generator_context) {
1250   std::string filename = GeneratedMetadataFileName(file, options);
1251   std::unique_ptr<io::ZeroCopyOutputStream> output(
1252       generator_context->Open(filename));
1253   io::Printer printer(output.get(), '^');
1254 
1255   GenerateHead(file, &printer);
1256 
1257   std::string fullname = FilenameToClassname(filename);
1258   int lastindex = fullname.find_last_of("\\");
1259 
1260   if (lastindex != std::string::npos) {
1261     printer.Print(
1262         "namespace ^name^;\n\n",
1263         "name", fullname.substr(0, lastindex));
1264 
1265     printer.Print(
1266         "class ^name^\n"
1267         "{\n",
1268         "name", fullname.substr(lastindex + 1));
1269   } else {
1270     printer.Print(
1271         "class ^name^\n"
1272         "{\n",
1273         "name", fullname);
1274   }
1275   Indent(&printer);
1276 
1277   GenerateAddFileToPool(file, options, &printer);
1278 
1279   Outdent(&printer);
1280   printer.Print("}\n\n");
1281 }
1282 
1283 template <typename DescriptorType>
LegacyGenerateClassFile(const FileDescriptor * file,const DescriptorType * desc,const Options & options,GeneratorContext * generator_context)1284 void LegacyGenerateClassFile(const FileDescriptor* file,
1285                              const DescriptorType* desc, const Options& options,
1286                              GeneratorContext* generator_context) {
1287   std::string filename = LegacyGeneratedClassFileName(desc, options);
1288   std::unique_ptr<io::ZeroCopyOutputStream> output(
1289       generator_context->Open(filename));
1290   io::Printer printer(output.get(), '^');
1291 
1292   GenerateHead(file, &printer);
1293 
1294   std::string php_namespace = RootPhpNamespace(desc, options);
1295   if (!php_namespace.empty()) {
1296     printer.Print(
1297         "namespace ^name^;\n\n",
1298         "name", php_namespace);
1299   }
1300   std::string newname = FullClassName(desc, options);
1301   printer.Print("if (false) {\n");
1302   Indent(&printer);
1303   printer.Print("/**\n");
1304   printer.Print(" * This class is deprecated. Use ^new^ instead.\n",
1305       "new", newname);
1306   printer.Print(" * @deprecated\n");
1307   printer.Print(" */\n");
1308   printer.Print("class ^old^ {}\n",
1309       "old", LegacyGeneratedClassName(desc));
1310   Outdent(&printer);
1311   printer.Print("}\n");
1312   printer.Print("class_exists(^new^::class);\n",
1313       "new", GeneratedClassNameImpl(desc));
1314   printer.Print("@trigger_error('^old^ is deprecated and will be removed in "
1315       "the next major release. Use ^fullname^ instead', E_USER_DEPRECATED);\n\n",
1316       "old", LegacyFullClassName(desc, options),
1317       "fullname", newname);
1318 }
1319 
1320 template <typename DescriptorType>
LegacyReadOnlyGenerateClassFile(const FileDescriptor * file,const DescriptorType * desc,const Options & options,GeneratorContext * generator_context)1321 void LegacyReadOnlyGenerateClassFile(const FileDescriptor* file,
1322                              const DescriptorType* desc, const Options& options,
1323                              GeneratorContext* generator_context) {
1324   std::string fullname = FullClassName(desc, options);
1325   std::string php_namespace;
1326   std::string classname;
1327   int lastindex = fullname.find_last_of("\\");
1328 
1329   if (lastindex != std::string::npos) {
1330     php_namespace = fullname.substr(0, lastindex);
1331     classname = fullname.substr(lastindex + 1);
1332   } else {
1333     php_namespace = "";
1334     classname = fullname;
1335   }
1336 
1337   std::string filename = LegacyReadOnlyGeneratedClassFileName(php_namespace, desc);
1338   std::unique_ptr<io::ZeroCopyOutputStream> output(
1339       generator_context->Open(filename));
1340   io::Printer printer(output.get(), '^');
1341 
1342   GenerateHead(file, &printer);
1343 
1344   if (!php_namespace.empty()) {
1345     printer.Print(
1346         "namespace ^name^;\n\n",
1347         "name", php_namespace);
1348   }
1349 
1350   printer.Print("class_exists(^new^::class); // autoload the new class, which "
1351       "will also create an alias to the deprecated class\n",
1352       "new", classname);
1353   printer.Print("@trigger_error(__NAMESPACE__ . '\\^old^ is deprecated and will be removed in "
1354       "the next major release. Use ^fullname^ instead', E_USER_DEPRECATED);\n\n",
1355       "old", desc->name(),
1356       "fullname", classname);
1357 }
1358 
GenerateEnumFile(const FileDescriptor * file,const EnumDescriptor * en,const Options & options,GeneratorContext * generator_context)1359 void GenerateEnumFile(const FileDescriptor* file, const EnumDescriptor* en,
1360                       const Options& options,
1361                       GeneratorContext* generator_context) {
1362   std::string filename = GeneratedClassFileName(en, options);
1363   std::unique_ptr<io::ZeroCopyOutputStream> output(
1364       generator_context->Open(filename));
1365   io::Printer printer(output.get(), '^');
1366 
1367   GenerateHead(file, &printer);
1368 
1369   std::string fullname = FilenameToClassname(filename);
1370   int lastindex = fullname.find_last_of("\\");
1371 
1372   if (lastindex != std::string::npos) {
1373     printer.Print(
1374         "namespace ^name^;\n\n",
1375         "name", fullname.substr(0, lastindex));
1376 
1377     // We only need this 'use' statement if the enum has a namespace.
1378     // Otherwise, we get a warning that the use statement has no effect.
1379     printer.Print("use UnexpectedValueException;\n\n");
1380   }
1381 
1382   GenerateEnumDocComment(&printer, en, options);
1383 
1384   if (lastindex != std::string::npos) {
1385     fullname = fullname.substr(lastindex + 1);
1386   }
1387 
1388   printer.Print(
1389       "class ^name^\n"
1390       "{\n",
1391       "name", fullname);
1392   Indent(&printer);
1393 
1394   bool hasReserved = false;
1395   for (int i = 0; i < en->value_count(); i++) {
1396     const EnumValueDescriptor* value = en->value(i);
1397     GenerateEnumValueDocComment(&printer, value);
1398 
1399     std::string prefix = ConstantNamePrefix(value->name());
1400     if (!prefix.empty()) {
1401       hasReserved = true;
1402     }
1403 
1404     printer.Print("const ^name^ = ^number^;\n",
1405                   "name", prefix + value->name(),
1406                   "number", IntToString(value->number()));
1407   }
1408 
1409   printer.Print("\nprivate static $valueToName = [\n");
1410   Indent(&printer);
1411   for (int i = 0; i < en->value_count(); i++) {
1412     const EnumValueDescriptor* value = en->value(i);
1413     printer.Print("self::^constant^ => '^name^',\n",
1414                   "constant", ConstantNamePrefix(value->name()) + value->name(),
1415                   "name", value->name());
1416   }
1417   Outdent(&printer);
1418   printer.Print("];\n");
1419 
1420   printer.Print(
1421       "\npublic static function name($value)\n"
1422       "{\n");
1423   Indent(&printer);
1424   printer.Print("if (!isset(self::$valueToName[$value])) {\n");
1425   Indent(&printer);
1426   printer.Print("throw new UnexpectedValueException(sprintf(\n");
1427   Indent(&printer);
1428   Indent(&printer);
1429   printer.Print("'Enum %s has no name defined for value %s', __CLASS__, $value));\n");
1430   Outdent(&printer);
1431   Outdent(&printer);
1432   Outdent(&printer);
1433   printer.Print("}\n"
1434                 "return self::$valueToName[$value];\n");
1435   Outdent(&printer);
1436   printer.Print("}\n\n");
1437 
1438   printer.Print(
1439       "\npublic static function value($name)\n"
1440       "{\n");
1441   Indent(&printer);
1442   printer.Print("$const = __CLASS__ . '::' . strtoupper($name);\n"
1443                 "if (!defined($const)) {\n");
1444   Indent(&printer);
1445   if (hasReserved) {
1446     printer.Print("$pbconst =  __CLASS__. '::PB' . strtoupper($name);\n"
1447                 "if (!defined($pbconst)) {\n");
1448     Indent(&printer);
1449   }
1450   printer.Print("throw new UnexpectedValueException(sprintf(\n");
1451   Indent(&printer);
1452   Indent(&printer);
1453   printer.Print("'Enum %s has no value defined for name %s', __CLASS__, $name));\n");
1454   Outdent(&printer);
1455   Outdent(&printer);
1456   if (hasReserved) {
1457     Outdent(&printer);
1458     printer.Print("}\n"
1459                   "return constant($pbconst);\n");
1460   }
1461   Outdent(&printer);
1462   printer.Print("}\n"
1463                 "return constant($const);\n");
1464   Outdent(&printer);
1465   printer.Print("}\n");
1466 
1467   Outdent(&printer);
1468   printer.Print("}\n\n");
1469 
1470   // write legacy file for backwards compatibility with nested messages and enums
1471   if (en->containing_type() != NULL) {
1472     printer.Print(
1473         "// Adding a class alias for backwards compatibility with the previous class name.\n");
1474     printer.Print(
1475         "class_alias(^new^::class, \\^old^::class);\n\n",
1476         "new", fullname,
1477         "old", LegacyFullClassName(en, options));
1478     LegacyGenerateClassFile(file, en, options, generator_context);
1479   }
1480 
1481   // Write legacy file for backwards compatibility with "readonly" keywword
1482   std::string lower = en->name();
1483   std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
1484   if (lower == "readonly") {
1485     printer.Print(
1486         "// Adding a class alias for backwards compatibility with the \"readonly\" keyword.\n");
1487     printer.Print(
1488         "class_alias(^new^::class, __NAMESPACE__ . '\\^old^');\n\n",
1489         "new", fullname,
1490         "old", en->name());
1491     LegacyReadOnlyGenerateClassFile(file, en, options, generator_context);
1492   }
1493 }
1494 
GenerateMessageFile(const FileDescriptor * file,const Descriptor * message,const Options & options,GeneratorContext * generator_context)1495 void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message,
1496                          const Options& options,
1497                          GeneratorContext* generator_context) {
1498   // Don't generate MapEntry messages -- we use the PHP extension's native
1499   // support for map fields instead.
1500   if (message->options().map_entry()) {
1501     return;
1502   }
1503 
1504   std::string filename = GeneratedClassFileName(message, options);
1505   std::unique_ptr<io::ZeroCopyOutputStream> output(
1506       generator_context->Open(filename));
1507   io::Printer printer(output.get(), '^');
1508 
1509   GenerateHead(file, &printer);
1510 
1511   std::string fullname = FilenameToClassname(filename);
1512   int lastindex = fullname.find_last_of("\\");
1513 
1514   if (lastindex != std::string::npos) {
1515     printer.Print(
1516         "namespace ^name^;\n\n",
1517         "name", fullname.substr(0, lastindex));
1518   }
1519 
1520   GenerateUseDeclaration(options, &printer);
1521 
1522   GenerateMessageDocComment(&printer, message, options);
1523   if (lastindex != std::string::npos) {
1524     fullname = fullname.substr(lastindex + 1);
1525   }
1526 
1527   std::string base;
1528 
1529   switch (message->well_known_type()) {
1530     case Descriptor::WELLKNOWNTYPE_ANY:
1531       base = "\\Google\\Protobuf\\Internal\\AnyBase";
1532       break;
1533     case Descriptor::WELLKNOWNTYPE_TIMESTAMP:
1534       base = "\\Google\\Protobuf\\Internal\\TimestampBase";
1535       break;
1536     default:
1537       base = "\\Google\\Protobuf\\Internal\\Message";
1538       break;
1539   }
1540 
1541   printer.Print(
1542       "class ^name^ extends ^base^\n"
1543       "{\n",
1544       "base", base,
1545       "name", fullname);
1546   Indent(&printer);
1547 
1548   // Field and oneof definitions.
1549   for (int i = 0; i < message->field_count(); i++) {
1550     const FieldDescriptor* field = message->field(i);
1551     GenerateField(field, &printer, options);
1552   }
1553   for (int i = 0; i < message->real_oneof_decl_count(); i++) {
1554     const OneofDescriptor* oneof = message->oneof_decl(i);
1555     GenerateOneofField(oneof, &printer);
1556   }
1557   printer.Print("\n");
1558 
1559   GenerateMessageConstructorDocComment(&printer, message, options);
1560   printer.Print(
1561       "public function __construct($data = NULL) {\n");
1562   Indent(&printer);
1563 
1564   std::string metadata_filename = GeneratedMetadataFileName(file, options);
1565   std::string metadata_fullname = FilenameToClassname(metadata_filename);
1566   printer.Print(
1567       "\\^fullname^::initOnce();\n",
1568       "fullname", metadata_fullname);
1569 
1570   printer.Print(
1571       "parent::__construct($data);\n");
1572 
1573   Outdent(&printer);
1574   printer.Print("}\n\n");
1575 
1576   // Field and oneof accessors.
1577   for (int i = 0; i < message->field_count(); i++) {
1578     const FieldDescriptor* field = message->field(i);
1579     GenerateFieldAccessor(field, options, &printer);
1580   }
1581   for (int i = 0; i < message->real_oneof_decl_count(); i++) {
1582     const OneofDescriptor* oneof = message->oneof_decl(i);
1583     printer.Print(
1584       "/**\n"
1585       " * @return string\n"
1586       " */\n"
1587       "public function get^camel_name^()\n"
1588       "{\n"
1589       "    return $this->whichOneof(\"^name^\");\n"
1590       "}\n\n",
1591       "camel_name", UnderscoresToCamelCase(oneof->name(), true), "name",
1592       oneof->name());
1593   }
1594 
1595   Outdent(&printer);
1596   printer.Print("}\n\n");
1597 
1598   // write legacy file for backwards compatibility with nested messages and enums
1599   if (message->containing_type() != NULL) {
1600     printer.Print(
1601         "// Adding a class alias for backwards compatibility with the previous class name.\n");
1602     printer.Print(
1603         "class_alias(^new^::class, \\^old^::class);\n\n",
1604         "new", fullname,
1605         "old", LegacyFullClassName(message, options));
1606     LegacyGenerateClassFile(file, message, options, generator_context);
1607   }
1608 
1609   // Write legacy file for backwards compatibility with "readonly" keywword
1610   std::string lower = message->name();
1611   std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
1612   if (lower == "readonly") {
1613     printer.Print(
1614         "// Adding a class alias for backwards compatibility with the \"readonly\" keyword.\n");
1615     printer.Print(
1616         "class_alias(^new^::class, __NAMESPACE__ . '\\^old^');\n\n",
1617         "new", fullname,
1618         "old", message->name());
1619     LegacyReadOnlyGenerateClassFile(file, message, options, generator_context);
1620   }
1621 
1622   // Nested messages and enums.
1623   for (int i = 0; i < message->nested_type_count(); i++) {
1624     GenerateMessageFile(file, message->nested_type(i), options,
1625                         generator_context);
1626   }
1627   for (int i = 0; i < message->enum_type_count(); i++) {
1628     GenerateEnumFile(file, message->enum_type(i), options, generator_context);
1629   }
1630 }
1631 
GenerateServiceFile(const FileDescriptor * file,const ServiceDescriptor * service,const Options & options,GeneratorContext * generator_context)1632 void GenerateServiceFile(
1633     const FileDescriptor* file, const ServiceDescriptor* service,
1634     const Options& options, GeneratorContext* generator_context) {
1635   std::string filename = GeneratedServiceFileName(service, options);
1636   std::unique_ptr<io::ZeroCopyOutputStream> output(
1637       generator_context->Open(filename));
1638   io::Printer printer(output.get(), '^');
1639 
1640   GenerateHead(file, &printer);
1641 
1642   std::string fullname = FilenameToClassname(filename);
1643   int lastindex = fullname.find_last_of("\\");
1644 
1645   if (!file->options().php_namespace().empty() ||
1646       (!file->options().has_php_namespace() && !file->package().empty()) ||
1647       lastindex != std::string::npos) {
1648     printer.Print(
1649         "namespace ^name^;\n\n",
1650         "name", fullname.substr(0, lastindex));
1651   }
1652 
1653   GenerateServiceDocComment(&printer, service);
1654 
1655   if (lastindex != std::string::npos) {
1656     printer.Print(
1657         "interface ^name^\n"
1658         "{\n",
1659         "name", fullname.substr(lastindex + 1));
1660   } else {
1661     printer.Print(
1662         "interface ^name^\n"
1663         "{\n",
1664         "name", fullname);
1665   }
1666 
1667   Indent(&printer);
1668 
1669   for (int i = 0; i < service->method_count(); i++) {
1670     const MethodDescriptor* method = service->method(i);
1671     GenerateServiceMethodDocComment(&printer, method);
1672     GenerateServiceMethod(method, &printer);
1673   }
1674 
1675   Outdent(&printer);
1676   printer.Print("}\n\n");
1677 }
1678 
GenerateFile(const FileDescriptor * file,const Options & options,GeneratorContext * generator_context)1679 void GenerateFile(const FileDescriptor* file, const Options& options,
1680                   GeneratorContext* generator_context) {
1681   GenerateMetadataFile(file, options, generator_context);
1682 
1683   for (int i = 0; i < file->message_type_count(); i++) {
1684     GenerateMessageFile(file, file->message_type(i), options,
1685                         generator_context);
1686   }
1687   for (int i = 0; i < file->enum_type_count(); i++) {
1688     GenerateEnumFile(file, file->enum_type(i), options, generator_context);
1689   }
1690   if (file->options().php_generic_services()) {
1691     for (int i = 0; i < file->service_count(); i++) {
1692       GenerateServiceFile(file, file->service(i), options, generator_context);
1693     }
1694   }
1695 }
1696 
EscapePhpdoc(const std::string & input)1697 static std::string EscapePhpdoc(const std::string& input) {
1698   std::string result;
1699   result.reserve(input.size() * 2);
1700 
1701   char prev = '*';
1702 
1703   for (std::string::size_type i = 0; i < input.size(); i++) {
1704     char c = input[i];
1705     switch (c) {
1706       case '*':
1707         // Avoid "/*".
1708         if (prev == '/') {
1709           result.append("&#42;");
1710         } else {
1711           result.push_back(c);
1712         }
1713         break;
1714       case '/':
1715         // Avoid "*/".
1716         if (prev == '*') {
1717           result.append("&#47;");
1718         } else {
1719           result.push_back(c);
1720         }
1721         break;
1722       case '@':
1723         // '@' starts phpdoc tags including the @deprecated tag, which will
1724         // cause a compile-time error if inserted before a declaration that
1725         // does not have a corresponding @Deprecated annotation.
1726         result.append("&#64;");
1727         break;
1728       default:
1729         result.push_back(c);
1730         break;
1731     }
1732 
1733     prev = c;
1734   }
1735 
1736   return result;
1737 }
1738 
GenerateDocCommentBodyForLocation(io::Printer * printer,const SourceLocation & location,bool trailingNewline,int indentCount)1739 static void GenerateDocCommentBodyForLocation(
1740     io::Printer* printer, const SourceLocation& location, bool trailingNewline,
1741     int indentCount) {
1742   std::string comments = location.leading_comments.empty()
1743                              ? location.trailing_comments
1744                              : location.leading_comments;
1745   if (!comments.empty()) {
1746     // TODO(teboring):  Ideally we should parse the comment text as Markdown and
1747     //   write it back as HTML, but this requires a Markdown parser.  For now
1748     //   we just use the proto comments unchanged.
1749 
1750     // If the comment itself contains block comment start or end markers,
1751     // HTML-escape them so that they don't accidentally close the doc comment.
1752     comments = EscapePhpdoc(comments);
1753 
1754     std::vector<std::string> lines = Split(comments, "\n", true);
1755     while (!lines.empty() && lines.back().empty()) {
1756       lines.pop_back();
1757     }
1758 
1759     for (int i = 0; i < lines.size(); i++) {
1760       // Most lines should start with a space.  Watch out for lines that start
1761       // with a /, since putting that right after the leading asterisk will
1762       // close the comment.
1763       if (indentCount == 0 && !lines[i].empty() && lines[i][0] == '/') {
1764         printer->Print(" * ^line^\n", "line", lines[i]);
1765       } else {
1766         std::string indent = std::string(indentCount, ' ');
1767         printer->Print(" *^ind^^line^\n", "ind", indent, "line", lines[i]);
1768       }
1769     }
1770     if (trailingNewline) {
1771       printer->Print(" *\n");
1772     }
1773   }
1774 }
1775 
1776 template <typename DescriptorType>
GenerateDocCommentBody(io::Printer * printer,const DescriptorType * descriptor)1777 static void GenerateDocCommentBody(
1778     io::Printer* printer, const DescriptorType* descriptor) {
1779   SourceLocation location;
1780   if (descriptor->GetSourceLocation(&location)) {
1781     GenerateDocCommentBodyForLocation(printer, location, true, 0);
1782   }
1783 }
1784 
FirstLineOf(const std::string & value)1785 static std::string FirstLineOf(const std::string& value) {
1786   std::string result = value;
1787 
1788   std::string::size_type pos = result.find_first_of('\n');
1789   if (pos != std::string::npos) {
1790     result.erase(pos);
1791   }
1792 
1793   return result;
1794 }
1795 
GenerateMessageDocComment(io::Printer * printer,const Descriptor * message,const Options & options)1796 void GenerateMessageDocComment(io::Printer* printer, const Descriptor* message,
1797                                const Options& options) {
1798   printer->Print("/**\n");
1799   GenerateDocCommentBody(printer, message);
1800   printer->Print(
1801     " * Generated from protobuf message <code>^messagename^</code>\n"
1802     " */\n",
1803     "fullname", EscapePhpdoc(FullClassName(message, options)),
1804     "messagename", EscapePhpdoc(message->full_name()));
1805 }
1806 
GenerateMessageConstructorDocComment(io::Printer * printer,const Descriptor * message,const Options & options)1807 void GenerateMessageConstructorDocComment(io::Printer* printer,
1808                                           const Descriptor* message,
1809                                           const Options& options) {
1810   // In theory we should have slightly different comments for setters, getters,
1811   // etc., but in practice everyone already knows the difference between these
1812   // so it's redundant information.
1813 
1814   // We start the comment with the main body based on the comments from the
1815   // .proto file (if present). We then end with the field declaration, e.g.:
1816   //   optional string foo = 5;
1817   // If the field is a group, the debug string might end with {.
1818   printer->Print("/**\n");
1819   printer->Print(" * Constructor.\n");
1820   printer->Print(" *\n");
1821   printer->Print(" * @param array $data {\n");
1822   printer->Print(" *     Optional. Data for populating the Message object.\n");
1823   printer->Print(" *\n");
1824   for (int i = 0; i < message->field_count(); i++) {
1825     const FieldDescriptor* field = message->field(i);
1826     printer->Print(" *     @type ^php_type^ $^var^\n",
1827       "php_type", PhpSetterTypeName(field, options),
1828       "var", field->name());
1829     SourceLocation location;
1830     if (field->GetSourceLocation(&location)) {
1831       GenerateDocCommentBodyForLocation(printer, location, false, 10);
1832     }
1833   }
1834   printer->Print(" * }\n");
1835   printer->Print(" */\n");
1836 }
1837 
GenerateServiceDocComment(io::Printer * printer,const ServiceDescriptor * service)1838 void GenerateServiceDocComment(io::Printer* printer,
1839                                const ServiceDescriptor* service) {
1840   printer->Print("/**\n");
1841   GenerateDocCommentBody(printer, service);
1842   printer->Print(
1843     " * Protobuf type <code>^fullname^</code>\n"
1844     " */\n",
1845     "fullname", EscapePhpdoc(service->full_name()));
1846 }
1847 
GenerateFieldDocComment(io::Printer * printer,const FieldDescriptor * field,const Options & options,int function_type)1848 void GenerateFieldDocComment(io::Printer* printer, const FieldDescriptor* field,
1849                              const Options& options, int function_type) {
1850   // In theory we should have slightly different comments for setters, getters,
1851   // etc., but in practice everyone already knows the difference between these
1852   // so it's redundant information.
1853 
1854   // We start the comment with the main body based on the comments from the
1855   // .proto file (if present). We then end with the field declaration, e.g.:
1856   //   optional string foo = 5;
1857   // If the field is a group, the debug string might end with {.
1858   printer->Print("/**\n");
1859   GenerateDocCommentBody(printer, field);
1860   printer->Print(
1861     " * Generated from protobuf field <code>^def^</code>\n",
1862     "def", EscapePhpdoc(FirstLineOf(field->DebugString())));
1863   if (function_type == kFieldSetter) {
1864     printer->Print(" * @param ^php_type^ $var\n",
1865       "php_type", PhpSetterTypeName(field, options));
1866     printer->Print(" * @return $this\n");
1867   } else if (function_type == kFieldGetter) {
1868     bool can_return_null = field->has_presence() &&
1869                            field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE;
1870     printer->Print(" * @return ^php_type^^maybe_null^\n",
1871       "php_type", PhpGetterTypeName(field, options),
1872       "maybe_null", can_return_null ? "|null" : "");
1873   }
1874   if (field->options().deprecated()) {
1875     printer->Print(" * @deprecated\n");
1876   }
1877   printer->Print(" */\n");
1878 }
1879 
GenerateWrapperFieldGetterDocComment(io::Printer * printer,const FieldDescriptor * field)1880 void GenerateWrapperFieldGetterDocComment(io::Printer* printer, const FieldDescriptor* field) {
1881   // Generate a doc comment for the special getXXXValue methods that are
1882   // generated for wrapper types.
1883   const FieldDescriptor* primitiveField = field->message_type()->FindFieldByName("value");
1884   printer->Print("/**\n");
1885   printer->Print(
1886       " * Returns the unboxed value from <code>get^camel_name^()</code>\n\n",
1887       "camel_name", UnderscoresToCamelCase(field->name(), true));
1888   GenerateDocCommentBody(printer, field);
1889   printer->Print(
1890     " * Generated from protobuf field <code>^def^</code>\n",
1891     "def", EscapePhpdoc(FirstLineOf(field->DebugString())));
1892   printer->Print(" * @return ^php_type^|null\n",
1893         "php_type", PhpGetterTypeName(primitiveField, false));
1894   printer->Print(" */\n");
1895 }
1896 
GenerateWrapperFieldSetterDocComment(io::Printer * printer,const FieldDescriptor * field)1897 void GenerateWrapperFieldSetterDocComment(io::Printer* printer, const FieldDescriptor* field) {
1898   // Generate a doc comment for the special setXXXValue methods that are
1899   // generated for wrapper types.
1900   const FieldDescriptor* primitiveField = field->message_type()->FindFieldByName("value");
1901   printer->Print("/**\n");
1902   printer->Print(
1903       " * Sets the field by wrapping a primitive type in a ^message_name^ object.\n\n",
1904       "message_name", FullClassName(field->message_type(), false));
1905   GenerateDocCommentBody(printer, field);
1906   printer->Print(
1907     " * Generated from protobuf field <code>^def^</code>\n",
1908     "def", EscapePhpdoc(FirstLineOf(field->DebugString())));
1909   printer->Print(" * @param ^php_type^|null $var\n",
1910         "php_type", PhpSetterTypeName(primitiveField, false));
1911   printer->Print(" * @return $this\n");
1912   printer->Print(" */\n");
1913 }
1914 
GenerateEnumDocComment(io::Printer * printer,const EnumDescriptor * enum_,const Options & options)1915 void GenerateEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_,
1916                             const Options& options) {
1917   printer->Print("/**\n");
1918   GenerateDocCommentBody(printer, enum_);
1919   printer->Print(
1920     " * Protobuf type <code>^fullname^</code>\n"
1921     " */\n",
1922     "fullname", EscapePhpdoc(enum_->full_name()));
1923 }
1924 
GenerateEnumValueDocComment(io::Printer * printer,const EnumValueDescriptor * value)1925 void GenerateEnumValueDocComment(io::Printer* printer,
1926                                  const EnumValueDescriptor* value) {
1927   printer->Print("/**\n");
1928   GenerateDocCommentBody(printer, value);
1929   printer->Print(
1930     " * Generated from protobuf enum <code>^def^</code>\n"
1931     " */\n",
1932     "def", EscapePhpdoc(FirstLineOf(value->DebugString())));
1933 }
1934 
GenerateServiceMethodDocComment(io::Printer * printer,const MethodDescriptor * method)1935 void GenerateServiceMethodDocComment(io::Printer* printer,
1936                                      const MethodDescriptor* method) {
1937   printer->Print("/**\n");
1938   GenerateDocCommentBody(printer, method);
1939   printer->Print(
1940     " * Method <code>^method_name^</code>\n"
1941     " *\n",
1942     "method_name", EscapePhpdoc(UnderscoresToCamelCase(method->name(), false)));
1943   printer->Print(
1944     " * @param \\^input_type^ $request\n",
1945     "input_type", EscapePhpdoc(FullClassName(method->input_type(), false)));
1946   printer->Print(
1947     " * @return \\^return_type^\n"
1948     " */\n",
1949     "return_type", EscapePhpdoc(FullClassName(method->output_type(), false)));
1950 }
1951 
FilenameCName(const FileDescriptor * file)1952 std::string FilenameCName(const FileDescriptor* file) {
1953   std::string c_name = file->name();
1954   c_name = StringReplace(c_name, ".", "_", true);
1955   c_name = StringReplace(c_name, "/", "_", true);
1956   return c_name;
1957 }
1958 
GenerateCEnum(const EnumDescriptor * desc,io::Printer * printer)1959 void GenerateCEnum(const EnumDescriptor* desc, io::Printer* printer) {
1960   std::string c_name = desc->full_name();
1961   c_name = StringReplace(c_name, ".", "_", true);
1962   std::string php_name = FullClassName(desc, Options());
1963   php_name = StringReplace(php_name, "\\", "\\\\", true);
1964   printer->Print(
1965       "/* $c_name$ */\n"
1966       "\n"
1967       "zend_class_entry* $c_name$_ce;\n"
1968       "\n"
1969       "PHP_METHOD($c_name$, name) {\n"
1970       "  $file_c_name$_AddDescriptor();\n"
1971       "  const upb_DefPool *symtab = DescriptorPool_GetSymbolTable();\n"
1972       "  const upb_EnumDef *e = upb_DefPool_FindEnumByName(symtab, \"$name$\");\n"
1973       "  zend_long value;\n"
1974       "  if (zend_parse_parameters(ZEND_NUM_ARGS(), \"l\", &value) ==\n"
1975       "      FAILURE) {\n"
1976       "    return;\n"
1977       "  }\n"
1978       "  const upb_EnumValueDef* ev =\n"
1979       "      upb_EnumDef_FindValueByNumber(e, value);\n"
1980       "  if (!ev) {\n"
1981       "    zend_throw_exception_ex(NULL, 0,\n"
1982       "                            \"$php_name$ has no name \"\n"
1983       "                            \"defined for value \" ZEND_LONG_FMT \".\",\n"
1984       "                            value);\n"
1985       "    return;\n"
1986       "  }\n"
1987       "  RETURN_STRING(upb_EnumValueDef_Name(ev));\n"
1988       "}\n"
1989       "\n"
1990       "PHP_METHOD($c_name$, value) {\n"
1991       "  $file_c_name$_AddDescriptor();\n"
1992       "  const upb_DefPool *symtab = DescriptorPool_GetSymbolTable();\n"
1993       "  const upb_EnumDef *e = upb_DefPool_FindEnumByName(symtab, \"$name$\");\n"
1994       "  char *name = NULL;\n"
1995       "  size_t name_len;\n"
1996       "  if (zend_parse_parameters(ZEND_NUM_ARGS(), \"s\", &name,\n"
1997       "                            &name_len) == FAILURE) {\n"
1998       "    return;\n"
1999       "  }\n"
2000       "  const upb_EnumValueDef* ev = upb_EnumDef_FindValueByNameWithSize(\n"
2001       "      e, name, name_len);\n"
2002       "  if (!ev) {\n"
2003       "    zend_throw_exception_ex(NULL, 0,\n"
2004       "                            \"$php_name$ has no value \"\n"
2005       "                            \"defined for name %s.\",\n"
2006       "                            name);\n"
2007       "    return;\n"
2008       "  }\n"
2009       "  RETURN_LONG(upb_EnumValueDef_Number(ev));\n"
2010       "}\n"
2011       "\n"
2012       "static zend_function_entry $c_name$_phpmethods[] = {\n"
2013       "  PHP_ME($c_name$, name, arginfo_lookup, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n"
2014       "  PHP_ME($c_name$, value, arginfo_lookup, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n"
2015       "  ZEND_FE_END\n"
2016       "};\n"
2017       "\n"
2018       "static void $c_name$_ModuleInit() {\n"
2019       "  zend_class_entry tmp_ce;\n"
2020       "\n"
2021       "  INIT_CLASS_ENTRY(tmp_ce, \"$php_name$\",\n"
2022       "                   $c_name$_phpmethods);\n"
2023       "\n"
2024       "  $c_name$_ce = zend_register_internal_class(&tmp_ce);\n",
2025       "name", desc->full_name(),
2026       "file_c_name", FilenameCName(desc->file()),
2027       "c_name", c_name,
2028       "php_name", php_name);
2029 
2030   for (int i = 0; i < desc->value_count(); i++) {
2031     const EnumValueDescriptor* value = desc->value(i);
2032     printer->Print(
2033         "  zend_declare_class_constant_long($c_name$_ce, \"$name$\",\n"
2034         "                                   strlen(\"$name$\"), $num$);\n",
2035         "c_name", c_name,
2036         "name", value->name(),
2037         "num", std::to_string(value->number()));
2038   }
2039 
2040   printer->Print(
2041       "}\n"
2042       "\n");
2043 }
2044 
GenerateCMessage(const Descriptor * message,io::Printer * printer)2045 void GenerateCMessage(const Descriptor* message, io::Printer* printer) {
2046   std::string c_name = message->full_name();
2047   c_name = StringReplace(c_name, ".", "_", true);
2048   std::string php_name = FullClassName(message, Options());
2049   php_name = StringReplace(php_name, "\\", "\\\\", true);
2050   printer->Print(
2051       "/* $c_name$ */\n"
2052       "\n"
2053       "zend_class_entry* $c_name$_ce;\n"
2054       "\n"
2055       "static PHP_METHOD($c_name$, __construct) {\n"
2056       "  $file_c_name$_AddDescriptor();\n"
2057       "  zim_Message___construct(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n"
2058       "}\n"
2059       "\n",
2060       "file_c_name", FilenameCName(message->file()),
2061       "c_name", c_name);
2062 
2063   for (int i = 0; i < message->field_count(); i++) {
2064     auto field = message->field(i);
2065     printer->Print(
2066       "static PHP_METHOD($c_name$, get$camel_name$) {\n"
2067       "  Message* intern = (Message*)Z_OBJ_P(getThis());\n"
2068       "  const upb_FieldDef *f = upb_MessageDef_FindFieldByName(\n"
2069       "      intern->desc->msgdef, \"$name$\");\n"
2070       "  zval ret;\n"
2071       "  Message_get(intern, f, &ret);\n"
2072       "  RETURN_COPY_VALUE(&ret);\n"
2073       "}\n"
2074       "\n"
2075       "static PHP_METHOD($c_name$, set$camel_name$) {\n"
2076       "  Message* intern = (Message*)Z_OBJ_P(getThis());\n"
2077       "  const upb_FieldDef *f = upb_MessageDef_FindFieldByName(\n"
2078       "      intern->desc->msgdef, \"$name$\");\n"
2079       "  zval *val;\n"
2080       "  if (zend_parse_parameters(ZEND_NUM_ARGS(), \"z\", &val)\n"
2081       "      == FAILURE) {\n"
2082       "    return;\n"
2083       "  }\n"
2084       "  Message_set(intern, f, val);\n"
2085       "  RETURN_COPY(getThis());\n"
2086       "}\n"
2087       "\n",
2088       "c_name", c_name,
2089       "name", field->name(),
2090       "camel_name", UnderscoresToCamelCase(field->name(), true));
2091   }
2092 
2093   for (int i = 0; i < message->real_oneof_decl_count(); i++) {
2094     auto oneof = message->oneof_decl(i);
2095     printer->Print(
2096       "static PHP_METHOD($c_name$, get$camel_name$) {\n"
2097       "  Message* intern = (Message*)Z_OBJ_P(getThis());\n"
2098       "  const upb_OneofDef *oneof = upb_MessageDef_FindOneofByName(\n"
2099       "      intern->desc->msgdef, \"$name$\");\n"
2100       "  const upb_FieldDef *field = \n"
2101       "      upb_Message_WhichOneof(intern->msg, oneof);\n"
2102       "  RETURN_STRING(field ? upb_FieldDef_Name(field) : \"\");\n"
2103       "}\n",
2104       "c_name", c_name,
2105       "name", oneof->name(),
2106       "camel_name", UnderscoresToCamelCase(oneof->name(), true));
2107   }
2108 
2109   switch (message->well_known_type()) {
2110     case Descriptor::WELLKNOWNTYPE_ANY:
2111       printer->Print(
2112           "ZEND_BEGIN_ARG_INFO_EX(arginfo_is, 0, 0, 1)\n"
2113           "  ZEND_ARG_INFO(0, proto)\n"
2114           "ZEND_END_ARG_INFO()\n"
2115           "\n"
2116       );
2117       break;
2118     case Descriptor::WELLKNOWNTYPE_TIMESTAMP:
2119       printer->Print(
2120           "ZEND_BEGIN_ARG_INFO_EX(arginfo_timestamp_fromdatetime, 0, 0, 1)\n"
2121           "  ZEND_ARG_INFO(0, datetime)\n"
2122           "ZEND_END_ARG_INFO()\n"
2123           "\n"
2124       );
2125       break;
2126     default:
2127       break;
2128   }
2129 
2130   printer->Print(
2131       "static zend_function_entry $c_name$_phpmethods[] = {\n"
2132       "  PHP_ME($c_name$, __construct, arginfo_construct, ZEND_ACC_PUBLIC)\n",
2133       "c_name", c_name);
2134 
2135   for (int i = 0; i < message->field_count(); i++) {
2136     auto field = message->field(i);
2137     printer->Print(
2138       "  PHP_ME($c_name$, get$camel_name$, arginfo_void, ZEND_ACC_PUBLIC)\n"
2139       "  PHP_ME($c_name$, set$camel_name$, arginfo_setter, ZEND_ACC_PUBLIC)\n",
2140       "c_name", c_name,
2141       "camel_name", UnderscoresToCamelCase(field->name(), true));
2142   }
2143 
2144   for (int i = 0; i < message->real_oneof_decl_count(); i++) {
2145     auto oneof = message->oneof_decl(i);
2146     printer->Print(
2147       "  PHP_ME($c_name$, get$camel_name$, arginfo_void, ZEND_ACC_PUBLIC)\n",
2148       "c_name", c_name,
2149       "camel_name", UnderscoresToCamelCase(oneof->name(), true));
2150   }
2151 
2152   // Extra hand-written functions added to the well-known types.
2153   switch (message->well_known_type()) {
2154     case Descriptor::WELLKNOWNTYPE_ANY:
2155       printer->Print(
2156         "  PHP_ME($c_name$, is, arginfo_is, ZEND_ACC_PUBLIC)\n"
2157         "  PHP_ME($c_name$, pack, arginfo_setter, ZEND_ACC_PUBLIC)\n"
2158         "  PHP_ME($c_name$, unpack, arginfo_void, ZEND_ACC_PUBLIC)\n",
2159         "c_name", c_name);
2160       break;
2161     case Descriptor::WELLKNOWNTYPE_TIMESTAMP:
2162       printer->Print(
2163         "  PHP_ME($c_name$, fromDateTime, arginfo_timestamp_fromdatetime, ZEND_ACC_PUBLIC)\n"
2164         "  PHP_ME($c_name$, toDateTime, arginfo_void, ZEND_ACC_PUBLIC)\n",
2165         "c_name", c_name);
2166       break;
2167     default:
2168       break;
2169   }
2170 
2171   printer->Print(
2172       "  ZEND_FE_END\n"
2173       "};\n"
2174       "\n"
2175       "static void $c_name$_ModuleInit() {\n"
2176       "  zend_class_entry tmp_ce;\n"
2177       "\n"
2178       "  INIT_CLASS_ENTRY(tmp_ce, \"$php_name$\",\n"
2179       "                   $c_name$_phpmethods);\n"
2180       "\n"
2181       "  $c_name$_ce = zend_register_internal_class(&tmp_ce);\n"
2182       "  $c_name$_ce->ce_flags |= ZEND_ACC_FINAL;\n"
2183       "  $c_name$_ce->create_object = Message_create;\n"
2184       "  zend_do_inheritance($c_name$_ce, message_ce);\n"
2185       "}\n"
2186       "\n",
2187       "c_name", c_name,
2188       "php_name", php_name);
2189 
2190   for (int i = 0; i < message->nested_type_count(); i++) {
2191     GenerateCMessage(message->nested_type(i), printer);
2192   }
2193   for (int i = 0; i < message->enum_type_count(); i++) {
2194     GenerateCEnum(message->enum_type(i), printer);
2195   }
2196 }
2197 
GenerateEnumCInit(const EnumDescriptor * desc,io::Printer * printer)2198 void GenerateEnumCInit(const EnumDescriptor* desc, io::Printer* printer) {
2199   std::string c_name = desc->full_name();
2200   c_name = StringReplace(c_name, ".", "_", true);
2201 
2202   printer->Print(
2203       "  $c_name$_ModuleInit();\n",
2204       "c_name", c_name);
2205 }
2206 
GenerateCInit(const Descriptor * message,io::Printer * printer)2207 void GenerateCInit(const Descriptor* message, io::Printer* printer) {
2208   std::string c_name = message->full_name();
2209   c_name = StringReplace(c_name, ".", "_", true);
2210 
2211   printer->Print(
2212       "  $c_name$_ModuleInit();\n",
2213       "c_name", c_name);
2214 
2215   for (int i = 0; i < message->nested_type_count(); i++) {
2216     GenerateCInit(message->nested_type(i), printer);
2217   }
2218   for (int i = 0; i < message->enum_type_count(); i++) {
2219     GenerateEnumCInit(message->enum_type(i), printer);
2220   }
2221 }
2222 
GenerateCWellKnownTypes(const std::vector<const FileDescriptor * > & files,GeneratorContext * context)2223 void GenerateCWellKnownTypes(const std::vector<const FileDescriptor*>& files,
2224                              GeneratorContext* context) {
2225   std::unique_ptr<io::ZeroCopyOutputStream> output(
2226       context->Open("../ext/google/protobuf/wkt.inc"));
2227   io::Printer printer(output.get(), '$');
2228 
2229   printer.Print(
2230       "// This file is generated from the .proto files for the well-known\n"
2231       "// types. Do not edit!\n\n");
2232 
2233   printer.Print(
2234       "ZEND_BEGIN_ARG_INFO_EX(arginfo_lookup, 0, 0, 1)\n"
2235       "  ZEND_ARG_INFO(0, key)\n"
2236       "ZEND_END_ARG_INFO()\n"
2237       "\n"
2238   );
2239 
2240   for (auto file : files) {
2241     printer.Print(
2242         "static void $c_name$_AddDescriptor();\n",
2243         "c_name", FilenameCName(file));
2244   }
2245 
2246   for (auto file : files) {
2247     std::string c_name = FilenameCName(file);
2248     std::string metadata_filename = GeneratedMetadataFileName(file, Options());
2249     std::string metadata_classname = FilenameToClassname(metadata_filename);
2250     std::string metadata_c_name =
2251         StringReplace(metadata_classname, "\\", "_", true);
2252     metadata_classname = StringReplace(metadata_classname, "\\", "\\\\", true);
2253     FileDescriptorProto file_proto;
2254     file->CopyTo(&file_proto);
2255     std::string serialized;
2256     file_proto.SerializeToString(&serialized);
2257     printer.Print(
2258         "/* $filename$ */\n"
2259         "\n"
2260         "zend_class_entry* $metadata_c_name$_ce;\n"
2261         "\n"
2262         "const char $c_name$_descriptor [$size$] = {\n",
2263         "filename", file->name(),
2264         "c_name", c_name,
2265         "metadata_c_name", metadata_c_name,
2266         "size", std::to_string(serialized.size()));
2267 
2268     for (size_t i = 0; i < serialized.size();) {
2269       for (size_t j = 0; j < 25 && i < serialized.size(); ++i, ++j) {
2270         printer.Print("'$ch$', ", "ch", CEscape(serialized.substr(i, 1)));
2271       }
2272       printer.Print("\n");
2273     }
2274 
2275     printer.Print(
2276         "};\n"
2277         "\n"
2278         "static void $c_name$_AddDescriptor() {\n"
2279         "  if (DescriptorPool_HasFile(\"$filename$\")) return;\n",
2280         "filename", file->name(),
2281         "c_name", c_name,
2282         "metadata_c_name", metadata_c_name);
2283 
2284     for (int i = 0; i < file->dependency_count(); i++) {
2285       std::string dep_c_name = FilenameCName(file->dependency(i));
2286       printer.Print(
2287           "  $dep_c_name$_AddDescriptor();\n",
2288           "dep_c_name", dep_c_name);
2289     }
2290 
2291     printer.Print(
2292         "  DescriptorPool_AddDescriptor(\"$filename$\", $c_name$_descriptor,\n"
2293         "                               sizeof($c_name$_descriptor));\n"
2294         "}\n"
2295         "\n"
2296         "static PHP_METHOD($metadata_c_name$, initOnce) {\n"
2297         "  $c_name$_AddDescriptor();\n"
2298         "}\n"
2299         "\n"
2300         "static zend_function_entry $metadata_c_name$_methods[] = {\n"
2301         "  PHP_ME($metadata_c_name$, initOnce, arginfo_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n"
2302         "  ZEND_FE_END\n"
2303         "};\n"
2304         "\n"
2305         "static void $metadata_c_name$_ModuleInit() {\n"
2306         "  zend_class_entry tmp_ce;\n"
2307         "\n"
2308         "  INIT_CLASS_ENTRY(tmp_ce, \"$metadata_classname$\",\n"
2309         "                   $metadata_c_name$_methods);\n"
2310         "\n"
2311         "  $metadata_c_name$_ce = zend_register_internal_class(&tmp_ce);\n"
2312         "}\n"
2313         "\n",
2314         "filename", file->name(),
2315         "c_name", c_name,
2316         "metadata_c_name", metadata_c_name,
2317         "metadata_classname", metadata_classname);
2318     for (int i = 0; i < file->message_type_count(); i++) {
2319       GenerateCMessage(file->message_type(i), &printer);
2320     }
2321     for (int i = 0; i < file->enum_type_count(); i++) {
2322       GenerateCEnum(file->enum_type(i), &printer);
2323     }
2324   }
2325 
2326   printer.Print(
2327       "static void WellKnownTypes_ModuleInit() {\n");
2328 
2329   for (auto file : files) {
2330     std::string metadata_filename = GeneratedMetadataFileName(file, Options());
2331     std::string metadata_classname = FilenameToClassname(metadata_filename);
2332     std::string metadata_c_name =
2333         StringReplace(metadata_classname, "\\", "_", true);
2334     printer.Print(
2335         "  $metadata_c_name$_ModuleInit();\n",
2336         "metadata_c_name", metadata_c_name);
2337     for (int i = 0; i < file->message_type_count(); i++) {
2338       GenerateCInit(file->message_type(i), &printer);
2339     }
2340     for (int i = 0; i < file->enum_type_count(); i++) {
2341       GenerateEnumCInit(file->enum_type(i), &printer);
2342     }
2343   }
2344 
2345   printer.Print(
2346       "}\n");
2347 }
2348 
2349 }  // namespace
2350 
GeneratedClassName(const Descriptor * desc)2351 std::string GeneratedClassName(const Descriptor* desc) {
2352   return GeneratedClassNameImpl(desc);
2353 }
2354 
GeneratedClassName(const EnumDescriptor * desc)2355 std::string GeneratedClassName(const EnumDescriptor* desc) {
2356   return GeneratedClassNameImpl(desc);
2357 }
2358 
GeneratedClassName(const ServiceDescriptor * desc)2359 std::string GeneratedClassName(const ServiceDescriptor* desc) {
2360   return GeneratedClassNameImpl(desc);
2361 }
2362 
Generate(const FileDescriptor * file,const std::string & parameter,GeneratorContext * generator_context,std::string * error) const2363 bool Generator::Generate(const FileDescriptor* file,
2364                          const std::string& parameter,
2365                          GeneratorContext* generator_context,
2366                          std::string* error) const {
2367   return Generate(file, Options(), generator_context, error);
2368 }
2369 
Generate(const FileDescriptor * file,const Options & options,GeneratorContext * generator_context,std::string * error) const2370 bool Generator::Generate(const FileDescriptor* file, const Options& options,
2371                          GeneratorContext* generator_context,
2372                          std::string* error) const {
2373   if (options.is_descriptor && file->name() != kDescriptorFile) {
2374     *error =
2375         "Can only generate PHP code for google/protobuf/descriptor.proto.\n";
2376     return false;
2377   }
2378 
2379   if (!options.is_descriptor && file->syntax() != FileDescriptor::SYNTAX_PROTO3) {
2380     *error =
2381         "Can only generate PHP code for proto3 .proto files.\n"
2382         "Please add 'syntax = \"proto3\";' to the top of your .proto file.\n";
2383     return false;
2384   }
2385 
2386   GenerateFile(file, options, generator_context);
2387 
2388   return true;
2389 }
2390 
GenerateAll(const std::vector<const FileDescriptor * > & files,const std::string & parameter,GeneratorContext * generator_context,std::string * error) const2391 bool Generator::GenerateAll(const std::vector<const FileDescriptor*>& files,
2392                             const std::string& parameter,
2393                             GeneratorContext* generator_context,
2394                             std::string* error) const {
2395   Options options;
2396 
2397   for (const auto& option : Split(parameter, ",", true)) {
2398     const std::vector<std::string> option_pair = Split(option, "=", true);
2399     if (HasPrefixString(option_pair[0], "aggregate_metadata")) {
2400       options.aggregate_metadata = true;
2401       for (const auto& prefix : Split(option_pair[1], "#", false)) {
2402         options.aggregate_metadata_prefixes.emplace(prefix);
2403         GOOGLE_LOG(INFO) << prefix;
2404       }
2405     } else if (option_pair[0] == "internal") {
2406       options.is_descriptor = true;
2407     } else if (option_pair[0] == "internal_generate_c_wkt") {
2408       GenerateCWellKnownTypes(files, generator_context);
2409     } else {
2410       GOOGLE_LOG(FATAL) << "Unknown codegen option: " << option_pair[0];
2411     }
2412   }
2413 
2414   for (auto file : files) {
2415     if (!Generate(file, options, generator_context, error)) {
2416       return false;
2417     }
2418   }
2419 
2420   return true;
2421 }
2422 
2423 }  // namespace php
2424 }  // namespace compiler
2425 }  // namespace protobuf
2426 }  // namespace google
2427