Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(61)

Side by Side Diff: third_party/protobuf/src/google/protobuf/compiler/php/php_generator.cc

Issue 2495533002: third_party/protobuf: Update to HEAD (83d681ee2c) (Closed)
Patch Set: Make chrome settings proto generated file a component Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 using google::protobuf::internal::scoped_ptr;
44
45 const std::string kDescriptorFile = "google/protobuf/descriptor.proto";
46 const std::string kEmptyFile = "google/protobuf/empty.proto";
47 const std::string kEmptyMetadataFile = "GPBMetadata/Google/Protobuf/GPBEmpty.php ";
48 const std::string kDescriptorMetadataFile =
49 "GPBMetadata/Google/Protobuf/Internal/Descriptor.php";
50 const std::string kDescriptorDirName = "Google/Protobuf/Internal";
51 const std::string kDescriptorPackageName = "Google\\Protobuf\\Internal";
52
53 namespace google {
54 namespace protobuf {
55 namespace compiler {
56 namespace php {
57
58 // Forward decls.
59 std::string PhpName(const std::string& full_name, bool is_descriptor);
60 std::string DefaultForField(FieldDescriptor* field);
61 std::string IntToString(int32 value);
62 std::string FilenameToClassname(const string& filename);
63 std::string GeneratedMetadataFileName(const std::string& proto_file,
64 bool is_descriptor);
65 std::string LabelForField(FieldDescriptor* field);
66 std::string TypeName(FieldDescriptor* field);
67 std::string UnderscoresToCamelCase(const string& name, bool cap_first_letter);
68 std::string EscapeDollor(const string& to_escape);
69 std::string BinaryToHex(const string& binary);
70 void Indent(io::Printer* printer);
71 void Outdent(io::Printer* printer);
72 void GenerateMessageDocComment(io::Printer* printer, const Descriptor* message);
73 void GenerateFieldDocComment(io::Printer* printer,
74 const FieldDescriptor* field);
75 void GenerateEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_);
76 void GenerateEnumValueDocComment(io::Printer* printer,
77 const EnumValueDescriptor* value);
78
79 std::string RenameEmpty(const std::string& name) {
80 if (name == "Empty") {
81 return "GPBEmpty";
82 } else {
83 return name;
84 }
85 }
86
87 std::string MessagePrefix(const Descriptor* message) {
88 // Empty cannot be php class name.
89 if (message->name() == "Empty" &&
90 message->file()->package() == "google.protobuf") {
91 return "GPB";
92 } else {
93 return "";
94 }
95 }
96
97 std::string MessageName(const Descriptor* message, bool is_descriptor) {
98 string message_name = message->name();
99 const Descriptor* descriptor = message->containing_type();
100 while (descriptor != NULL) {
101 message_name = descriptor->name() + '_' + message_name;
102 descriptor = descriptor->containing_type();
103 }
104 message_name = MessagePrefix(message) + message_name;
105
106 return PhpName(message->file()->package(), is_descriptor) + '\\' +
107 message_name;
108 }
109
110 std::string MessageFullName(const Descriptor* message, bool is_descriptor) {
111 if (is_descriptor) {
112 return StringReplace(message->full_name(),
113 "google.protobuf",
114 "google.protobuf.internal", false);
115 } else {
116 return message->full_name();
117 }
118 }
119
120 std::string EnumFullName(const EnumDescriptor* envm, bool is_descriptor) {
121 if (is_descriptor) {
122 return StringReplace(envm->full_name(),
123 "google.protobuf",
124 "google.protobuf.internal", false);
125 } else {
126 return envm->full_name();
127 }
128 }
129
130 std::string EnumClassName(const EnumDescriptor* envm) {
131 string enum_class_name = envm->name();
132 const Descriptor* descriptor = envm->containing_type();
133 while (descriptor != NULL) {
134 enum_class_name = descriptor->name() + '_' + enum_class_name;
135 descriptor = descriptor->containing_type();
136 }
137 return enum_class_name;
138 }
139
140 std::string EnumName(const EnumDescriptor* envm, bool is_descriptor) {
141 string enum_name = EnumClassName(envm);
142 return PhpName(envm->file()->package(), is_descriptor) + '\\' + enum_name;
143 }
144
145 std::string PhpName(const std::string& full_name, bool is_descriptor) {
146 if (is_descriptor) {
147 return kDescriptorPackageName;
148 }
149
150 std::string result;
151 bool cap_next_letter = true;
152 for (int i = 0; i < full_name.size(); i++) {
153 if ('a' <= full_name[i] && full_name[i] <= 'z' && cap_next_letter) {
154 result += full_name[i] + ('A' - 'a');
155 cap_next_letter = false;
156 } else if (full_name[i] == '.') {
157 result += '\\';
158 cap_next_letter = true;
159 } else {
160 result += full_name[i];
161 cap_next_letter = false;
162 }
163 }
164 return result;
165 }
166
167 std::string DefaultForField(const FieldDescriptor* field) {
168 switch (field->type()) {
169 case FieldDescriptor::TYPE_INT32:
170 case FieldDescriptor::TYPE_INT64:
171 case FieldDescriptor::TYPE_UINT32:
172 case FieldDescriptor::TYPE_UINT64:
173 case FieldDescriptor::TYPE_SINT32:
174 case FieldDescriptor::TYPE_SINT64:
175 case FieldDescriptor::TYPE_FIXED32:
176 case FieldDescriptor::TYPE_FIXED64:
177 case FieldDescriptor::TYPE_SFIXED32:
178 case FieldDescriptor::TYPE_SFIXED64:
179 case FieldDescriptor::TYPE_ENUM: return "0";
180 case FieldDescriptor::TYPE_DOUBLE:
181 case FieldDescriptor::TYPE_FLOAT: return "0.0";
182 case FieldDescriptor::TYPE_BOOL: return "false";
183 case FieldDescriptor::TYPE_STRING:
184 case FieldDescriptor::TYPE_BYTES: return "''";
185 case FieldDescriptor::TYPE_MESSAGE:
186 case FieldDescriptor::TYPE_GROUP: return "null";
187 default: assert(false); return "";
188 }
189 }
190
191 std::string GeneratedMetadataFileName(const std::string& proto_file,
192 bool is_descriptor) {
193 int start_index = 0;
194 int first_index = proto_file.find_first_of("/", start_index);
195 std::string result = "GPBMetadata/";
196
197 if (proto_file == kEmptyFile) {
198 return kEmptyMetadataFile;
199 }
200 if (is_descriptor) {
201 return kDescriptorMetadataFile;
202 }
203
204 // Append directory name.
205 std::string file_no_suffix;
206 int lastindex = proto_file.find_last_of(".");
207 if (proto_file == kEmptyFile) {
208 return kEmptyMetadataFile;
209 } else {
210 file_no_suffix = proto_file.substr(0, lastindex);
211 }
212
213 while (first_index != string::npos) {
214 result += UnderscoresToCamelCase(
215 file_no_suffix.substr(start_index, first_index - start_index), true);
216 result += "/";
217 start_index = first_index + 1;
218 first_index = file_no_suffix.find_first_of("/", start_index);
219 }
220
221 // Append file name.
222 result += RenameEmpty(UnderscoresToCamelCase(
223 file_no_suffix.substr(start_index, first_index - start_index), true));
224
225 return result += ".php";
226 }
227
228 std::string GeneratedMessageFileName(const Descriptor* message,
229 bool is_descriptor) {
230 std::string result = MessageName(message, is_descriptor);
231 for (int i = 0; i < result.size(); i++) {
232 if (result[i] == '\\') {
233 result[i] = '/';
234 }
235 }
236 return result + ".php";
237 }
238
239 std::string GeneratedEnumFileName(const EnumDescriptor* en,
240 bool is_descriptor) {
241 std::string result = EnumName(en, is_descriptor);
242 for (int i = 0; i < result.size(); i++) {
243 if (result[i] == '\\') {
244 result[i] = '/';
245 }
246 }
247 return result + ".php";
248 }
249
250 std::string IntToString(int32 value) {
251 std::ostringstream os;
252 os << value;
253 return os.str();
254 }
255
256 std::string LabelForField(const FieldDescriptor* field) {
257 switch (field->label()) {
258 case FieldDescriptor::LABEL_OPTIONAL: return "optional";
259 case FieldDescriptor::LABEL_REQUIRED: return "required";
260 case FieldDescriptor::LABEL_REPEATED: return "repeated";
261 default: assert(false); return "";
262 }
263 }
264
265 std::string TypeName(const FieldDescriptor* field) {
266 switch (field->type()) {
267 case FieldDescriptor::TYPE_INT32: return "int32";
268 case FieldDescriptor::TYPE_INT64: return "int64";
269 case FieldDescriptor::TYPE_UINT32: return "uint32";
270 case FieldDescriptor::TYPE_UINT64: return "uint64";
271 case FieldDescriptor::TYPE_SINT32: return "sint32";
272 case FieldDescriptor::TYPE_SINT64: return "sint64";
273 case FieldDescriptor::TYPE_FIXED32: return "fixed32";
274 case FieldDescriptor::TYPE_FIXED64: return "fixed64";
275 case FieldDescriptor::TYPE_SFIXED32: return "sfixed32";
276 case FieldDescriptor::TYPE_SFIXED64: return "sfixed64";
277 case FieldDescriptor::TYPE_DOUBLE: return "double";
278 case FieldDescriptor::TYPE_FLOAT: return "float";
279 case FieldDescriptor::TYPE_BOOL: return "bool";
280 case FieldDescriptor::TYPE_ENUM: return "enum";
281 case FieldDescriptor::TYPE_STRING: return "string";
282 case FieldDescriptor::TYPE_BYTES: return "bytes";
283 case FieldDescriptor::TYPE_MESSAGE: return "message";
284 case FieldDescriptor::TYPE_GROUP: return "group";
285 default: assert(false); return "";
286 }
287 }
288
289 std::string EnumOrMessageSuffix(
290 const FieldDescriptor* field, bool is_descriptor) {
291 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
292 return ", '" + MessageFullName(field->message_type(), is_descriptor) + "'";
293 }
294 if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
295 return ", '" + EnumFullName(field->enum_type(), is_descriptor) + "'";
296 }
297 return "";
298 }
299
300 // Converts a name to camel-case. If cap_first_letter is true, capitalize the
301 // first letter.
302 std::string UnderscoresToCamelCase(const string& input, bool cap_first_letter) {
303 std::string result;
304 for (int i = 0; i < input.size(); i++) {
305 if ('a' <= input[i] && input[i] <= 'z') {
306 if (cap_first_letter) {
307 result += input[i] + ('A' - 'a');
308 } else {
309 result += input[i];
310 }
311 cap_first_letter = false;
312 } else if ('A' <= input[i] && input[i] <= 'Z') {
313 if (i == 0 && !cap_first_letter) {
314 // Force first letter to lower-case unless explicitly told to
315 // capitalize it.
316 result += input[i] + ('a' - 'A');
317 } else {
318 // Capital letters after the first are left as-is.
319 result += input[i];
320 }
321 cap_first_letter = false;
322 } else if ('0' <= input[i] && input[i] <= '9') {
323 result += input[i];
324 cap_first_letter = true;
325 } else {
326 cap_first_letter = true;
327 }
328 }
329 // Add a trailing "_" if the name should be altered.
330 if (input[input.size() - 1] == '#') {
331 result += '_';
332 }
333 return result;
334 }
335
336 std::string EscapeDollor(const string& to_escape) {
337 return StringReplace(to_escape, "$", "\\$", true);
338 }
339
340 std::string BinaryToHex(const string& src) {
341 string dest;
342 size_t i;
343 unsigned char symbol[16] = {
344 '0', '1', '2', '3',
345 '4', '5', '6', '7',
346 '8', '9', 'a', 'b',
347 'c', 'd', 'e', 'f',
348 };
349
350 dest.resize(src.size() * 2);
351 char* append_ptr = &dest[0];
352
353 for (i = 0; i < src.size(); i++) {
354 *append_ptr++ = symbol[(src[i] & 0xf0) >> 4];
355 *append_ptr++ = symbol[src[i] & 0x0f];
356 }
357
358 return dest;
359 }
360
361 void Indent(io::Printer* printer) {
362 printer->Indent();
363 printer->Indent();
364 }
365 void Outdent(io::Printer* printer) {
366 printer->Outdent();
367 printer->Outdent();
368 }
369
370 void GenerateField(const FieldDescriptor* field, io::Printer* printer,
371 bool is_descriptor) {
372 if (field->is_repeated()) {
373 GenerateFieldDocComment(printer, field);
374 printer->Print(
375 "private $^name^;\n",
376 "name", field->name());
377 } else if (field->containing_oneof()) {
378 // Oneof fields are handled by GenerateOneofField.
379 return;
380 } else {
381 GenerateFieldDocComment(printer, field);
382 printer->Print(
383 "private $^name^ = ^default^;\n",
384 "name", field->name(),
385 "default", DefaultForField(field));
386 }
387
388 if (is_descriptor) {
389 printer->Print(
390 "private $has_^name^ = false;\n",
391 "name", field->name());
392 }
393 }
394
395 void GenerateOneofField(const OneofDescriptor* oneof, io::Printer* printer) {
396 // Oneof property needs to be protected in order to be accessed by parent
397 // class in implementation.
398 printer->Print(
399 "protected $^name^;\n",
400 "name", oneof->name());
401 }
402
403 void GenerateFieldAccessor(const FieldDescriptor* field, bool is_descriptor,
404 io::Printer* printer) {
405 const OneofDescriptor* oneof = field->containing_oneof();
406
407 // Generate getter.
408 if (oneof != NULL) {
409 GenerateFieldDocComment(printer, field);
410 printer->Print(
411 "public function get^camel_name^()\n"
412 "{\n"
413 " return $this->readOneof(^number^);\n"
414 "}\n\n",
415 "camel_name", UnderscoresToCamelCase(field->name(), true),
416 "number", IntToString(field->number()));
417 } else {
418 GenerateFieldDocComment(printer, field);
419 printer->Print(
420 "public function get^camel_name^()\n"
421 "{\n"
422 " return $this->^name^;\n"
423 "}\n\n",
424 "camel_name", UnderscoresToCamelCase(field->name(), true), "name",
425 field->name());
426 }
427
428 // Generate setter.
429 GenerateFieldDocComment(printer, field);
430 printer->Print(
431 "public function set^camel_name^(^var^)\n"
432 "{\n",
433 "camel_name", UnderscoresToCamelCase(field->name(), true),
434 "var", (field->is_repeated() ||
435 field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ?
436 "&$var": "$var");
437
438 Indent(printer);
439
440 // Type check.
441 if (field->is_map()) {
442 } else if (field->is_repeated()) {
443 printer->Print(
444 "GPBUtil::checkRepeatedField($var, "
445 "\\Google\\Protobuf\\Internal\\GPBType::^type^",
446 "type", ToUpper(field->type_name()));
447 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
448 printer->Print(
449 ", \\^class_name^);\n",
450 "class_name",
451 MessageName(field->message_type(), is_descriptor) + "::class");
452 } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
453 printer->Print(
454 ", ^class_name^);\n",
455 "class_name",
456 EnumName(field->enum_type(), is_descriptor) + "::class");
457 } else {
458 printer->Print(");\n");
459 }
460 } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
461 printer->Print(
462 "GPBUtil::checkMessage($var, \\^class_name^::class);\n",
463 "class_name", MessageName(field->message_type(), is_descriptor));
464 } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
465 printer->Print(
466 "GPBUtil::checkEnum($var, \\^class_name^::class);\n",
467 "class_name", EnumName(field->enum_type(), is_descriptor));
468 } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
469 printer->Print(
470 "GPBUtil::checkString($var, ^utf8^);\n",
471 "utf8",
472 field->type() == FieldDescriptor::TYPE_STRING ? "True": "False");
473 } else {
474 printer->Print(
475 "GPBUtil::check^type^($var);\n",
476 "type", UnderscoresToCamelCase(field->cpp_type_name(), true));
477 }
478
479 if (oneof != NULL) {
480 printer->Print(
481 "$this->writeOneof(^number^, $var);\n",
482 "number", IntToString(field->number()));
483 } else {
484 printer->Print(
485 "$this->^name^ = $var;\n",
486 "name", field->name());
487 }
488
489 // Set has bit for proto2 only.
490 if (is_descriptor) {
491 printer->Print(
492 "$this->has_^field_name^ = true;\n",
493 "field_name", field->name());
494 }
495
496 Outdent(printer);
497
498 printer->Print(
499 "}\n\n");
500
501 // Generate has method for proto2 only.
502 if (is_descriptor) {
503 printer->Print(
504 "public function has^camel_name^()\n"
505 "{\n"
506 " return $this->has_^field_name^;\n"
507 "}\n\n",
508 "camel_name", UnderscoresToCamelCase(field->name(), true),
509 "field_name", field->name());
510 }
511 }
512
513 void GenerateEnumToPool(const EnumDescriptor* en, io::Printer* printer) {
514 printer->Print(
515 "$pool->addEnum('^name^', "
516 "\\Google\\Protobuf\\Internal\\^class_name^::class)\n",
517 "name", EnumFullName(en, true),
518 "class_name", en->name());
519 Indent(printer);
520
521 for (int i = 0; i < en->value_count(); i++) {
522 const EnumValueDescriptor* value = en->value(i);
523 printer->Print(
524 "->value(\"^name^\", ^number^)\n",
525 "name", value->name(),
526 "number", IntToString(value->number()));
527 }
528 printer->Print("->finalizeToPool();\n\n");
529 Outdent(printer);
530 }
531
532 void GenerateMessageToPool(const string& name_prefix, const Descriptor* message,
533 io::Printer* printer) {
534 // Don't generate MapEntry messages -- we use the PHP extension's native
535 // support for map fields instead.
536 if (message->options().map_entry()) {
537 return;
538 }
539 string class_name = name_prefix.empty()?
540 message->name() : name_prefix + "_" + message->name();
541
542 printer->Print(
543 "$pool->addMessage('^message^', "
544 "\\Google\\Protobuf\\Internal\\^class_name^::class)\n",
545 "message", MessageFullName(message, true),
546 "class_name", class_name);
547
548 Indent(printer);
549
550 for (int i = 0; i < message->field_count(); i++) {
551 const FieldDescriptor* field = message->field(i);
552 if (field->is_map()) {
553 const FieldDescriptor* key =
554 field->message_type()->FindFieldByName("key");
555 const FieldDescriptor* val =
556 field->message_type()->FindFieldByName("value");
557 printer->Print(
558 "->map('^field^', \\Google\\Protobuf\\Internal\\GPBType::^key^, "
559 "\\Google\\Protobuf\\Internal\\GPBType::^value^, ^number^^other^)\n",
560 "field", field->name(),
561 "key", ToUpper(key->type_name()),
562 "value", ToUpper(val->type_name()),
563 "number", SimpleItoa(field->number()),
564 "other", EnumOrMessageSuffix(val, true));
565 } else if (!field->containing_oneof()) {
566 printer->Print(
567 "->^label^('^field^', "
568 "\\Google\\Protobuf\\Internal\\GPBType::^type^, ^number^^other^)\n",
569 "field", field->name(),
570 "label", LabelForField(field),
571 "type", ToUpper(field->type_name()),
572 "number", SimpleItoa(field->number()),
573 "other", EnumOrMessageSuffix(field, true));
574 }
575 }
576
577 // oneofs.
578 for (int i = 0; i < message->oneof_decl_count(); i++) {
579 const OneofDescriptor* oneof = message->oneof_decl(i);
580 printer->Print("->oneof(^name^)\n",
581 "name", oneof->name());
582 Indent(printer);
583 for (int index = 0; index < oneof->field_count(); index++) {
584 const FieldDescriptor* field = oneof->field(index);
585 printer->Print(
586 "->value('^field^', "
587 "\\Google\\Protobuf\\Internal\\GPBType::^type^, ^number^^other^)\n",
588 "field", field->name(),
589 "type", ToUpper(field->type_name()),
590 "number", SimpleItoa(field->number()),
591 "other", EnumOrMessageSuffix(field, true));
592 }
593 printer->Print("->finish()\n");
594 Outdent(printer);
595 }
596
597 printer->Print(
598 "->finalizeToPool();\n");
599
600 Outdent(printer);
601
602 printer->Print(
603 "\n");
604
605 for (int i = 0; i < message->nested_type_count(); i++) {
606 GenerateMessageToPool(class_name, message->nested_type(i), printer);
607 }
608 for (int i = 0; i < message->enum_type_count(); i++) {
609 GenerateEnumToPool(message->enum_type(i), printer);
610 }
611 }
612
613 void GenerateAddFileToPool(const FileDescriptor* file, bool is_descriptor,
614 io::Printer* printer) {
615 printer->Print(
616 "public static $is_initialized = false;\n\n"
617 "public static function initOnce() {\n");
618 Indent(printer);
619
620 printer->Print(
621 "$pool = \\Google\\Protobuf\\Internal\\"
622 "DescriptorPool::getGeneratedPool();\n\n"
623 "if (static::$is_initialized == true) {\n"
624 " return;\n"
625 "}\n");
626
627 if (is_descriptor) {
628 for (int i = 0; i < file->message_type_count(); i++) {
629 GenerateMessageToPool("", file->message_type(i), printer);
630 }
631 for (int i = 0; i < file->enum_type_count(); i++) {
632 GenerateEnumToPool(file->enum_type(i), printer);
633 }
634
635 printer->Print(
636 "$pool->finish();\n");
637 } else {
638 for (int i = 0; i < file->dependency_count(); i++) {
639 const std::string& name = file->dependency(i)->name();
640 std::string dependency_filename =
641 GeneratedMetadataFileName(name, is_descriptor);
642 printer->Print(
643 "\\^name^::initOnce();\n",
644 "name", FilenameToClassname(dependency_filename));
645 }
646
647 // Add messages and enums to descriptor pool.
648 FileDescriptorSet files;
649 FileDescriptorProto* file_proto = files.add_file();
650 file->CopyTo(file_proto);
651 string files_data;
652 files.SerializeToString(&files_data);
653
654 printer->Print("$pool->internalAddGeneratedFile(hex2bin(\n");
655 Indent(printer);
656
657 // Only write 30 bytes per line.
658 static const int kBytesPerLine = 30;
659 for (int i = 0; i < files_data.size(); i += kBytesPerLine) {
660 printer->Print(
661 "\"^data^\"^dot^\n",
662 "data", BinaryToHex(files_data.substr(i, kBytesPerLine)),
663 "dot", i + kBytesPerLine < files_data.size() ? " ." : "");
664 }
665
666 Outdent(printer);
667 printer->Print(
668 "));\n\n");
669 }
670 printer->Print(
671 "static::$is_initialized = true;\n");
672 Outdent(printer);
673 printer->Print("}\n");
674 }
675
676 void GenerateUseDeclaration(bool is_descriptor, io::Printer* printer) {
677 if (!is_descriptor) {
678 printer->Print(
679 "use Google\\Protobuf\\Internal\\GPBType;\n"
680 "use Google\\Protobuf\\Internal\\RepeatedField;\n"
681 "use Google\\Protobuf\\Internal\\GPBUtil;\n\n");
682 } else {
683 printer->Print(
684 "use Google\\Protobuf\\Internal\\GPBType;\n"
685 "use Google\\Protobuf\\Internal\\GPBWire;\n"
686 "use Google\\Protobuf\\Internal\\RepeatedField;\n"
687 "use Google\\Protobuf\\Internal\\InputStream;\n\n"
688 "use Google\\Protobuf\\Internal\\GPBUtil;\n\n");
689 }
690 }
691
692 void GenerateHead(const FileDescriptor* file, io::Printer* printer) {
693 printer->Print(
694 "<?php\n"
695 "# Generated by the protocol buffer compiler. DO NOT EDIT!\n"
696 "# source: ^filename^\n"
697 "\n",
698 "filename", file->name());
699 }
700
701 std::string FilenameToClassname(const string& filename) {
702 int lastindex = filename.find_last_of(".");
703 std::string result = filename.substr(0, lastindex);
704 for (int i = 0; i < result.size(); i++) {
705 if (result[i] == '/') {
706 result[i] = '\\';
707 }
708 }
709 return result;
710 }
711
712 void GenerateMetadataFile(const FileDescriptor* file,
713 bool is_descriptor,
714 GeneratorContext* generator_context) {
715 std::string filename = GeneratedMetadataFileName(file->name(), is_descriptor);
716 scoped_ptr<io::ZeroCopyOutputStream> output(
717 generator_context->Open(filename));
718 io::Printer printer(output.get(), '^');
719
720 GenerateHead(file, &printer);
721
722 std::string fullname = FilenameToClassname(filename);
723 int lastindex = fullname.find_last_of("\\");
724
725 printer.Print(
726 "namespace ^name^;\n\n",
727 "name", fullname.substr(0, lastindex));
728
729 if (lastindex != string::npos) {
730 printer.Print(
731 "class ^name^\n"
732 "{\n",
733 "name", fullname.substr(lastindex + 1));
734 } else {
735 printer.Print(
736 "class ^name^\n"
737 "{\n",
738 "name", fullname);
739 }
740 Indent(&printer);
741
742 GenerateAddFileToPool(file, is_descriptor, &printer);
743
744 Outdent(&printer);
745 printer.Print("}\n\n");
746 }
747
748 void GenerateEnumFile(const FileDescriptor* file, const EnumDescriptor* en,
749 bool is_descriptor, GeneratorContext* generator_context) {
750 std::string filename = GeneratedEnumFileName(en, is_descriptor);
751 scoped_ptr<io::ZeroCopyOutputStream> output(
752 generator_context->Open(filename));
753 io::Printer printer(output.get(), '^');
754
755 GenerateHead(file, &printer);
756
757 std::string fullname = FilenameToClassname(filename);
758 int lastindex = fullname.find_last_of("\\");
759
760 GenerateEnumDocComment(&printer, en);
761 if (lastindex != string::npos) {
762 printer.Print(
763 "namespace ^name^;\n\n",
764 "name", fullname.substr(0, lastindex));
765
766 printer.Print(
767 "class ^name^\n"
768 "{\n",
769 "name", fullname.substr(lastindex + 1));
770 } else {
771 printer.Print(
772 "class ^name^\n"
773 "{\n",
774 "name", fullname);
775 }
776 Indent(&printer);
777
778 for (int i = 0; i < en->value_count(); i++) {
779 const EnumValueDescriptor* value = en->value(i);
780 GenerateEnumValueDocComment(&printer, value);
781 printer.Print("const ^name^ = ^number^;\n",
782 "name", value->name(),
783 "number", IntToString(value->number()));
784 }
785
786 Outdent(&printer);
787 printer.Print("}\n\n");
788 }
789
790 void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message,
791 bool is_descriptor,
792 GeneratorContext* generator_context) {
793 // Don't generate MapEntry messages -- we use the PHP extension's native
794 // support for map fields instead.
795 if (message->options().map_entry()) {
796 return;
797 }
798
799 std::string filename = GeneratedMessageFileName(message, is_descriptor);
800 scoped_ptr<io::ZeroCopyOutputStream> output(
801 generator_context->Open(filename));
802 io::Printer printer(output.get(), '^');
803
804 GenerateHead(file, &printer);
805
806 std::string fullname = FilenameToClassname(filename);
807 int lastindex = fullname.find_last_of("\\");
808
809 if (!file->package().empty()) {
810 printer.Print(
811 "namespace ^name^;\n\n",
812 "name", fullname.substr(0, lastindex));
813 }
814
815 GenerateUseDeclaration(is_descriptor, &printer);
816
817 GenerateMessageDocComment(&printer, message);
818 if (lastindex != string::npos) {
819 printer.Print(
820 "class ^name^ extends \\Google\\Protobuf\\Internal\\Message\n"
821 "{\n",
822 "name", fullname.substr(lastindex + 1));
823 } else {
824 printer.Print(
825 "class ^name^ extends \\Google\\Protobuf\\Internal\\Message\n"
826 "{\n",
827 "name", fullname);
828 }
829 Indent(&printer);
830
831 // Field and oneof definitions.
832 for (int i = 0; i < message->field_count(); i++) {
833 const FieldDescriptor* field = message->field(i);
834 GenerateField(field, &printer, is_descriptor);
835 }
836 for (int i = 0; i < message->oneof_decl_count(); i++) {
837 const OneofDescriptor* oneof = message->oneof_decl(i);
838 GenerateOneofField(oneof, &printer);
839 }
840 printer.Print("\n");
841
842 printer.Print(
843 "public function __construct() {\n");
844 Indent(&printer);
845
846 std::string metadata_filename =
847 GeneratedMetadataFileName(file->name(), is_descriptor);
848 std::string metadata_fullname = FilenameToClassname(metadata_filename);
849 printer.Print(
850 "\\^fullname^::initOnce();\n"
851 "parent::__construct();\n",
852 "fullname", metadata_fullname);
853
854 Outdent(&printer);
855 printer.Print("}\n\n");
856
857 // Field and oneof accessors.
858 for (int i = 0; i < message->field_count(); i++) {
859 const FieldDescriptor* field = message->field(i);
860 GenerateFieldAccessor(field, is_descriptor, &printer);
861 }
862 for (int i = 0; i < message->oneof_decl_count(); i++) {
863 const OneofDescriptor* oneof = message->oneof_decl(i);
864 printer.Print(
865 "public function get^camel_name^()\n"
866 "{\n"
867 " return $this->^name^;\n"
868 "}\n\n",
869 "camel_name", UnderscoresToCamelCase(oneof->name(), true), "name",
870 oneof->name());
871 }
872
873 Outdent(&printer);
874 printer.Print("}\n\n");
875
876 // Nested messages and enums.
877 for (int i = 0; i < message->nested_type_count(); i++) {
878 GenerateMessageFile(file, message->nested_type(i), is_descriptor,
879 generator_context);
880 }
881 for (int i = 0; i < message->enum_type_count(); i++) {
882 GenerateEnumFile(file, message->enum_type(i), is_descriptor,
883 generator_context);
884 }
885 }
886
887 void GenerateFile(const FileDescriptor* file, bool is_descriptor,
888 GeneratorContext* generator_context) {
889 GenerateMetadataFile(file, is_descriptor, generator_context);
890 for (int i = 0; i < file->message_type_count(); i++) {
891 GenerateMessageFile(file, file->message_type(i), is_descriptor,
892 generator_context);
893 }
894 for (int i = 0; i < file->enum_type_count(); i++) {
895 GenerateEnumFile(file, file->enum_type(i), is_descriptor,
896 generator_context);
897 }
898 }
899
900 static string EscapePhpdoc(const string& input) {
901 string result;
902 result.reserve(input.size() * 2);
903
904 char prev = '*';
905
906 for (string::size_type i = 0; i < input.size(); i++) {
907 char c = input[i];
908 switch (c) {
909 case '*':
910 // Avoid "/*".
911 if (prev == '/') {
912 result.append("&#42;");
913 } else {
914 result.push_back(c);
915 }
916 break;
917 case '/':
918 // Avoid "*/".
919 if (prev == '*') {
920 result.append("&#47;");
921 } else {
922 result.push_back(c);
923 }
924 break;
925 case '@':
926 // '@' starts phpdoc tags including the @deprecated tag, which will
927 // cause a compile-time error if inserted before a declaration that
928 // does not have a corresponding @Deprecated annotation.
929 result.append("&#64;");
930 break;
931 case '<':
932 // Avoid interpretation as HTML.
933 result.append("&lt;");
934 break;
935 case '>':
936 // Avoid interpretation as HTML.
937 result.append("&gt;");
938 break;
939 case '&':
940 // Avoid interpretation as HTML.
941 result.append("&amp;");
942 break;
943 case '\\':
944 // Java interprets Unicode escape sequences anywhere!
945 result.append("&#92;");
946 break;
947 default:
948 result.push_back(c);
949 break;
950 }
951
952 prev = c;
953 }
954
955 return result;
956 }
957
958 static void GenerateDocCommentBodyForLocation(
959 io::Printer* printer, const SourceLocation& location) {
960 string comments = location.leading_comments.empty() ?
961 location.trailing_comments : location.leading_comments;
962 if (!comments.empty()) {
963 // TODO(teboring): Ideally we should parse the comment text as Markdown and
964 // write it back as HTML, but this requires a Markdown parser. For now
965 // we just use <pre> to get fixed-width text formatting.
966
967 // If the comment itself contains block comment start or end markers,
968 // HTML-escape them so that they don't accidentally close the doc comment.
969 comments = EscapePhpdoc(comments);
970
971 vector<string> lines = Split(comments, "\n");
972 while (!lines.empty() && lines.back().empty()) {
973 lines.pop_back();
974 }
975
976 printer->Print(" * <pre>\n");
977 for (int i = 0; i < lines.size(); i++) {
978 // Most lines should start with a space. Watch out for lines that start
979 // with a /, since putting that right after the leading asterisk will
980 // close the comment.
981 if (!lines[i].empty() && lines[i][0] == '/') {
982 printer->Print(" * ^line^\n", "line", lines[i]);
983 } else {
984 printer->Print(" *^line^\n", "line", lines[i]);
985 }
986 }
987 printer->Print(
988 " * </pre>\n"
989 " *\n");
990 }
991 }
992
993 template <typename DescriptorType>
994 static void GenerateDocCommentBody(
995 io::Printer* printer, const DescriptorType* descriptor) {
996 SourceLocation location;
997 if (descriptor->GetSourceLocation(&location)) {
998 GenerateDocCommentBodyForLocation(printer, location);
999 }
1000 }
1001
1002 static string FirstLineOf(const string& value) {
1003 string result = value;
1004
1005 string::size_type pos = result.find_first_of('\n');
1006 if (pos != string::npos) {
1007 result.erase(pos);
1008 }
1009
1010 return result;
1011 }
1012
1013 void GenerateMessageDocComment(io::Printer* printer,
1014 const Descriptor* message) {
1015 printer->Print("/**\n");
1016 GenerateDocCommentBody(printer, message);
1017 printer->Print(
1018 " * Protobuf type <code>^fullname^</code>\n"
1019 " */\n",
1020 "fullname", EscapePhpdoc(message->full_name()));
1021 }
1022
1023 void GenerateFieldDocComment(io::Printer* printer,
1024 const FieldDescriptor* field) {
1025 // In theory we should have slightly different comments for setters, getters,
1026 // etc., but in practice everyone already knows the difference between these
1027 // so it's redundant information.
1028
1029 // We start the comment with the main body based on the comments from the
1030 // .proto file (if present). We then end with the field declaration, e.g.:
1031 // optional string foo = 5;
1032 // If the field is a group, the debug string might end with {.
1033 printer->Print("/**\n");
1034 GenerateDocCommentBody(printer, field);
1035 printer->Print(
1036 " * <code>^def^</code>\n",
1037 "def", EscapePhpdoc(FirstLineOf(field->DebugString())));
1038 printer->Print(" */\n");
1039 }
1040
1041 void GenerateEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_) {
1042 printer->Print("/**\n");
1043 GenerateDocCommentBody(printer, enum_);
1044 printer->Print(
1045 " * Protobuf enum <code>^fullname^</code>\n"
1046 " */\n",
1047 "fullname", EscapePhpdoc(enum_->full_name()));
1048 }
1049
1050 void GenerateEnumValueDocComment(io::Printer* printer,
1051 const EnumValueDescriptor* value) {
1052 printer->Print("/**\n");
1053 GenerateDocCommentBody(printer, value);
1054 printer->Print(
1055 " * <code>^def^</code>\n"
1056 " */\n",
1057 "def", EscapePhpdoc(FirstLineOf(value->DebugString())));
1058 }
1059
1060 bool Generator::Generate(const FileDescriptor* file, const string& parameter,
1061 GeneratorContext* generator_context,
1062 string* error) const {
1063 bool is_descriptor = parameter == "internal";
1064
1065 if (is_descriptor && file->name() != kDescriptorFile) {
1066 *error =
1067 "Can only generate PHP code for google/protobuf/descriptor.proto.\n";
1068 return false;
1069 }
1070
1071 if (!is_descriptor && file->syntax() != FileDescriptor::SYNTAX_PROTO3) {
1072 *error =
1073 "Can only generate PHP code for proto3 .proto files.\n"
1074 "Please add 'syntax = \"proto3\";' to the top of your .proto file.\n";
1075 return false;
1076 }
1077
1078 GenerateFile(file, is_descriptor, generator_context);
1079
1080 return true;
1081 }
1082
1083 } // namespace php
1084 } // namespace compiler
1085 } // namespace protobuf
1086 } // namespace google
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698