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