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