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