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