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