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