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