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