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