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