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