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