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