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