blob: 238a9247891cecaf05fa553da03a5ad4db1d1554 [file] [log] [blame]
Austin Schuh40c16522018-10-28 20:27:54 -07001// 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 "protobuf.h"
32
33// Forward declare.
34static void descriptor_init_c_instance(Descriptor* intern TSRMLS_DC);
35static void descriptor_free_c(Descriptor* object TSRMLS_DC);
36
37static void field_descriptor_init_c_instance(FieldDescriptor* intern TSRMLS_DC);
38static void field_descriptor_free_c(FieldDescriptor* object TSRMLS_DC);
39
40static void enum_descriptor_init_c_instance(EnumDescriptor* intern TSRMLS_DC);
41static void enum_descriptor_free_c(EnumDescriptor* object TSRMLS_DC);
42
43static void enum_value_descriptor_init_c_instance(
44 EnumValueDescriptor *intern TSRMLS_DC);
45static void enum_value_descriptor_free_c(EnumValueDescriptor *object TSRMLS_DC);
46
47static void descriptor_pool_free_c(DescriptorPool* object TSRMLS_DC);
48static void descriptor_pool_init_c_instance(DescriptorPool* pool TSRMLS_DC);
49
50static void internal_descriptor_pool_free_c(
51 InternalDescriptorPool *object TSRMLS_DC);
52static void internal_descriptor_pool_init_c_instance(
53 InternalDescriptorPool *pool TSRMLS_DC);
54
55static void oneof_descriptor_free_c(Oneof* object TSRMLS_DC);
56static void oneof_descriptor_init_c_instance(Oneof* pool TSRMLS_DC);
57
58// -----------------------------------------------------------------------------
59// Common Utilities
60// -----------------------------------------------------------------------------
61
62static void check_upb_status(const upb_status* status, const char* msg) {
63 if (!upb_ok(status)) {
64 zend_error(E_ERROR, "%s: %s\n", msg, upb_status_errmsg(status));
65 }
66}
67
68static void upb_filedef_free(void *r) {
69 upb_filedef *f = *(upb_filedef **)r;
70 size_t i;
71
72 for (i = 0; i < upb_filedef_depcount(f); i++) {
73 upb_filedef_unref(upb_filedef_dep(f, i), f);
74 }
75
76 upb_inttable_uninit(&f->defs);
77 upb_inttable_uninit(&f->deps);
78 upb_gfree((void *)f->name);
79 upb_gfree((void *)f->package);
80 upb_gfree(f);
81}
82
83// Camel-case the field name and append "Entry" for generated map entry name.
84// e.g. map<KeyType, ValueType> foo_map => FooMapEntry
85static void append_map_entry_name(char *result, const char *field_name,
86 int pos) {
87 bool cap_next = true;
88 int i;
89
90 for (i = 0; i < strlen(field_name); ++i) {
91 if (field_name[i] == '_') {
92 cap_next = true;
93 } else if (cap_next) {
94 // Note: Do not use ctype.h due to locales.
95 if ('a' <= field_name[i] && field_name[i] <= 'z') {
96 result[pos++] = field_name[i] - 'a' + 'A';
97 } else {
98 result[pos++] = field_name[i];
99 }
100 cap_next = false;
101 } else {
102 result[pos++] = field_name[i];
103 }
104 }
105 strcat(result, "Entry");
106}
107
108#define CHECK_UPB(code, msg) \
109 do { \
110 upb_status status = UPB_STATUS_INIT; \
111 code; \
112 check_upb_status(&status, msg); \
113 } while (0)
114
115// -----------------------------------------------------------------------------
116// GPBType
117// -----------------------------------------------------------------------------
118
119zend_class_entry* gpb_type_type;
120
121static zend_function_entry gpb_type_methods[] = {
122 ZEND_FE_END
123};
124
125void gpb_type_init(TSRMLS_D) {
126 zend_class_entry class_type;
127 INIT_CLASS_ENTRY(class_type, "Google\\Protobuf\\Internal\\GPBType",
128 gpb_type_methods);
129 gpb_type_type = zend_register_internal_class(&class_type TSRMLS_CC);
130 zend_declare_class_constant_long(gpb_type_type, STR("DOUBLE"), 1 TSRMLS_CC);
131 zend_declare_class_constant_long(gpb_type_type, STR("FLOAT"), 2 TSRMLS_CC);
132 zend_declare_class_constant_long(gpb_type_type, STR("INT64"), 3 TSRMLS_CC);
133 zend_declare_class_constant_long(gpb_type_type, STR("UINT64"), 4 TSRMLS_CC);
134 zend_declare_class_constant_long(gpb_type_type, STR("INT32"), 5 TSRMLS_CC);
135 zend_declare_class_constant_long(gpb_type_type, STR("FIXED64"), 6 TSRMLS_CC);
136 zend_declare_class_constant_long(gpb_type_type, STR("FIXED32"), 7 TSRMLS_CC);
137 zend_declare_class_constant_long(gpb_type_type, STR("BOOL"), 8 TSRMLS_CC);
138 zend_declare_class_constant_long(gpb_type_type, STR("STRING"), 9 TSRMLS_CC);
139 zend_declare_class_constant_long(gpb_type_type, STR("GROUP"), 10 TSRMLS_CC);
140 zend_declare_class_constant_long(gpb_type_type, STR("MESSAGE"), 11 TSRMLS_CC);
141 zend_declare_class_constant_long(gpb_type_type, STR("BYTES"), 12 TSRMLS_CC);
142 zend_declare_class_constant_long(gpb_type_type, STR("UINT32"), 13 TSRMLS_CC);
143 zend_declare_class_constant_long(gpb_type_type, STR("ENUM"), 14 TSRMLS_CC);
144 zend_declare_class_constant_long(gpb_type_type, STR("SFIXED32"),
145 15 TSRMLS_CC);
146 zend_declare_class_constant_long(gpb_type_type, STR("SFIXED64"),
147 16 TSRMLS_CC);
148 zend_declare_class_constant_long(gpb_type_type, STR("SINT32"), 17 TSRMLS_CC);
149 zend_declare_class_constant_long(gpb_type_type, STR("SINT64"), 18 TSRMLS_CC);
150}
151
152// -----------------------------------------------------------------------------
153// Descriptor
154// -----------------------------------------------------------------------------
155
156static zend_function_entry descriptor_methods[] = {
157 PHP_ME(Descriptor, getClass, NULL, ZEND_ACC_PUBLIC)
158 PHP_ME(Descriptor, getFullName, NULL, ZEND_ACC_PUBLIC)
159 PHP_ME(Descriptor, getField, NULL, ZEND_ACC_PUBLIC)
160 PHP_ME(Descriptor, getFieldCount, NULL, ZEND_ACC_PUBLIC)
161 PHP_ME(Descriptor, getOneofDecl, NULL, ZEND_ACC_PUBLIC)
162 PHP_ME(Descriptor, getOneofDeclCount, NULL, ZEND_ACC_PUBLIC)
163 ZEND_FE_END
164};
165
166DEFINE_CLASS(Descriptor, descriptor, "Google\\Protobuf\\Descriptor");
167
168static void descriptor_free_c(Descriptor *self TSRMLS_DC) {
169 if (self->layout) {
170 free_layout(self->layout);
171 }
172 if (self->fill_handlers) {
173 upb_handlers_unref(self->fill_handlers, &self->fill_handlers);
174 }
175 if (self->fill_method) {
176 upb_pbdecodermethod_unref(self->fill_method, &self->fill_method);
177 }
178 if (self->json_fill_method) {
179 upb_json_parsermethod_unref(self->json_fill_method,
180 &self->json_fill_method);
181 }
182 if (self->pb_serialize_handlers) {
183 upb_handlers_unref(self->pb_serialize_handlers,
184 &self->pb_serialize_handlers);
185 }
186 if (self->json_serialize_handlers) {
187 upb_handlers_unref(self->json_serialize_handlers,
188 &self->json_serialize_handlers);
189 }
190 if (self->json_serialize_handlers_preserve) {
191 upb_handlers_unref(self->json_serialize_handlers_preserve,
192 &self->json_serialize_handlers_preserve);
193 }
194}
195
196static void descriptor_init_c_instance(Descriptor *desc TSRMLS_DC) {
197 desc->msgdef = NULL;
198 desc->layout = NULL;
199 desc->klass = NULL;
200 desc->fill_handlers = NULL;
201 desc->fill_method = NULL;
202 desc->json_fill_method = NULL;
203 desc->pb_serialize_handlers = NULL;
204 desc->json_serialize_handlers = NULL;
205 desc->json_serialize_handlers_preserve = NULL;
206}
207
208PHP_METHOD(Descriptor, getClass) {
209 Descriptor *intern = UNBOX(Descriptor, getThis());
210#if PHP_MAJOR_VERSION < 7
211 const char* classname = intern->klass->name;
212#else
213 const char* classname = ZSTR_VAL(intern->klass->name);
214#endif
215 PHP_PROTO_RETVAL_STRINGL(classname, strlen(classname), 1);
216}
217
218PHP_METHOD(Descriptor, getFullName) {
219 Descriptor *intern = UNBOX(Descriptor, getThis());
220 const char* fullname = upb_msgdef_fullname(intern->msgdef);
221 PHP_PROTO_RETVAL_STRINGL(fullname, strlen(fullname), 1);
222}
223
224PHP_METHOD(Descriptor, getField) {
225 long index;
226 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
227 FAILURE) {
228 zend_error(E_USER_ERROR, "Expect integer for index.\n");
229 return;
230 }
231
232 Descriptor *intern = UNBOX(Descriptor, getThis());
233 int field_num = upb_msgdef_numfields(intern->msgdef);
234 if (index < 0 || index >= field_num) {
235 zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index);
236 return;
237 }
238
239 upb_msg_field_iter iter;
240 int i;
241 for(upb_msg_field_begin(&iter, intern->msgdef), i = 0;
242 !upb_msg_field_done(&iter) && i < index;
243 upb_msg_field_next(&iter), i++);
244 const upb_fielddef *field = upb_msg_iter_field(&iter);
245
246 PHP_PROTO_HASHTABLE_VALUE field_hashtable_value = get_def_obj(field);
247 if (field_hashtable_value == NULL) {
248#if PHP_MAJOR_VERSION < 7
249 MAKE_STD_ZVAL(field_hashtable_value);
250 ZVAL_OBJ(field_hashtable_value, field_descriptor_type->create_object(
251 field_descriptor_type TSRMLS_CC));
252 Z_DELREF_P(field_hashtable_value);
253#else
254 field_hashtable_value =
255 field_descriptor_type->create_object(field_descriptor_type TSRMLS_CC);
256 GC_DELREF(field_hashtable_value);
257#endif
258 FieldDescriptor *field_php =
259 UNBOX_HASHTABLE_VALUE(FieldDescriptor, field_hashtable_value);
260 field_php->fielddef = field;
261 add_def_obj(field, field_hashtable_value);
262 }
263
264#if PHP_MAJOR_VERSION < 7
265 RETURN_ZVAL(field_hashtable_value, 1, 0);
266#else
267 GC_ADDREF(field_hashtable_value);
268 RETURN_OBJ(field_hashtable_value);
269#endif
270}
271
272PHP_METHOD(Descriptor, getFieldCount) {
273 Descriptor *intern = UNBOX(Descriptor, getThis());
274 RETURN_LONG(upb_msgdef_numfields(intern->msgdef));
275}
276
277PHP_METHOD(Descriptor, getOneofDecl) {
278 long index;
279 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
280 FAILURE) {
281 zend_error(E_USER_ERROR, "Expect integer for index.\n");
282 return;
283 }
284
285 Descriptor *intern = UNBOX(Descriptor, getThis());
286 int field_num = upb_msgdef_numoneofs(intern->msgdef);
287 if (index < 0 || index >= field_num) {
288 zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index);
289 return;
290 }
291
292 upb_msg_oneof_iter iter;
293 int i;
294 for(upb_msg_oneof_begin(&iter, intern->msgdef), i = 0;
295 !upb_msg_oneof_done(&iter) && i < index;
296 upb_msg_oneof_next(&iter), i++);
297 upb_oneofdef *oneof = upb_msg_iter_oneof(&iter);
298
299 ZVAL_OBJ(return_value, oneof_descriptor_type->create_object(
300 oneof_descriptor_type TSRMLS_CC));
301 Oneof *oneof_php = UNBOX(Oneof, return_value);
302 oneof_php->oneofdef = oneof;
303}
304
305PHP_METHOD(Descriptor, getOneofDeclCount) {
306 Descriptor *intern = UNBOX(Descriptor, getThis());
307 RETURN_LONG(upb_msgdef_numoneofs(intern->msgdef));
308}
309
310// -----------------------------------------------------------------------------
311// EnumDescriptor
312// -----------------------------------------------------------------------------
313
314static zend_function_entry enum_descriptor_methods[] = {
315 PHP_ME(EnumDescriptor, getValue, NULL, ZEND_ACC_PUBLIC)
316 PHP_ME(EnumDescriptor, getValueCount, NULL, ZEND_ACC_PUBLIC)
317 ZEND_FE_END
318};
319
320DEFINE_CLASS(EnumDescriptor, enum_descriptor,
321 "Google\\Protobuf\\EnumDescriptor");
322
323static void enum_descriptor_free_c(EnumDescriptor *self TSRMLS_DC) {
324}
325
326static void enum_descriptor_init_c_instance(EnumDescriptor *self TSRMLS_DC) {
327 self->enumdef = NULL;
328 self->klass = NULL;
329}
330
331PHP_METHOD(EnumDescriptor, getValue) {
332 long index;
333 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
334 FAILURE) {
335 zend_error(E_USER_ERROR, "Expect integer for index.\n");
336 return;
337 }
338
339 EnumDescriptor *intern = UNBOX(EnumDescriptor, getThis());
340 int field_num = upb_enumdef_numvals(intern->enumdef);
341 if (index < 0 || index >= field_num) {
342 zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index);
343 return;
344 }
345
346 upb_enum_iter iter;
347 int i;
348 for(upb_enum_begin(&iter, intern->enumdef), i = 0;
349 !upb_enum_done(&iter) && i < index;
350 upb_enum_next(&iter), i++);
351
352 ZVAL_OBJ(return_value, enum_value_descriptor_type->create_object(
353 enum_value_descriptor_type TSRMLS_CC));
354 EnumValueDescriptor *enum_value_php =
355 UNBOX(EnumValueDescriptor, return_value);
356 enum_value_php->name = upb_enum_iter_name(&iter);
357 enum_value_php->number = upb_enum_iter_number(&iter);
358}
359
360PHP_METHOD(EnumDescriptor, getValueCount) {
361 EnumDescriptor *intern = UNBOX(EnumDescriptor, getThis());
362 RETURN_LONG(upb_enumdef_numvals(intern->enumdef));
363}
364
365// -----------------------------------------------------------------------------
366// EnumValueDescriptor
367// -----------------------------------------------------------------------------
368
369static zend_function_entry enum_value_descriptor_methods[] = {
370 PHP_ME(EnumValueDescriptor, getName, NULL, ZEND_ACC_PUBLIC)
371 PHP_ME(EnumValueDescriptor, getNumber, NULL, ZEND_ACC_PUBLIC)
372 ZEND_FE_END
373};
374
375DEFINE_CLASS(EnumValueDescriptor, enum_value_descriptor,
376 "Google\\Protobuf\\EnumValueDescriptor");
377
378static void enum_value_descriptor_free_c(EnumValueDescriptor *self TSRMLS_DC) {
379}
380
381static void enum_value_descriptor_init_c_instance(EnumValueDescriptor *self TSRMLS_DC) {
382 self->name = NULL;
383 self->number = 0;
384}
385
386PHP_METHOD(EnumValueDescriptor, getName) {
387 EnumValueDescriptor *intern = UNBOX(EnumValueDescriptor, getThis());
388 PHP_PROTO_RETVAL_STRINGL(intern->name, strlen(intern->name), 1);
389}
390
391PHP_METHOD(EnumValueDescriptor, getNumber) {
392 EnumValueDescriptor *intern = UNBOX(EnumValueDescriptor, getThis());
393 RETURN_LONG(intern->number);
394}
395
396// -----------------------------------------------------------------------------
397// FieldDescriptor
398// -----------------------------------------------------------------------------
399
400static zend_function_entry field_descriptor_methods[] = {
401 PHP_ME(FieldDescriptor, getName, NULL, ZEND_ACC_PUBLIC)
402 PHP_ME(FieldDescriptor, getNumber, NULL, ZEND_ACC_PUBLIC)
403 PHP_ME(FieldDescriptor, getLabel, NULL, ZEND_ACC_PUBLIC)
404 PHP_ME(FieldDescriptor, getType, NULL, ZEND_ACC_PUBLIC)
405 PHP_ME(FieldDescriptor, isMap, NULL, ZEND_ACC_PUBLIC)
406 PHP_ME(FieldDescriptor, getEnumType, NULL, ZEND_ACC_PUBLIC)
407 PHP_ME(FieldDescriptor, getMessageType, NULL, ZEND_ACC_PUBLIC)
408 ZEND_FE_END
409};
410
411DEFINE_CLASS(FieldDescriptor, field_descriptor,
412 "Google\\Protobuf\\FieldDescriptor");
413
414static void field_descriptor_free_c(FieldDescriptor *self TSRMLS_DC) {
415}
416
417static void field_descriptor_init_c_instance(FieldDescriptor *self TSRMLS_DC) {
418 self->fielddef = NULL;
419}
420
421upb_fieldtype_t to_fieldtype(upb_descriptortype_t type) {
422 switch (type) {
423#define CASE(descriptor_type, type) \
424 case UPB_DESCRIPTOR_TYPE_##descriptor_type: \
425 return UPB_TYPE_##type;
426
427 CASE(FLOAT, FLOAT);
428 CASE(DOUBLE, DOUBLE);
429 CASE(BOOL, BOOL);
430 CASE(STRING, STRING);
431 CASE(BYTES, BYTES);
432 CASE(MESSAGE, MESSAGE);
433 CASE(GROUP, MESSAGE);
434 CASE(ENUM, ENUM);
435 CASE(INT32, INT32);
436 CASE(INT64, INT64);
437 CASE(UINT32, UINT32);
438 CASE(UINT64, UINT64);
439 CASE(SINT32, INT32);
440 CASE(SINT64, INT64);
441 CASE(FIXED32, UINT32);
442 CASE(FIXED64, UINT64);
443 CASE(SFIXED32, INT32);
444 CASE(SFIXED64, INT64);
445
446#undef CONVERT
447
448 }
449
450 zend_error(E_ERROR, "Unknown field type.");
451 return 0;
452}
453
454PHP_METHOD(FieldDescriptor, getName) {
455 FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
456 const char* name = upb_fielddef_name(intern->fielddef);
457 PHP_PROTO_RETVAL_STRINGL(name, strlen(name), 1);
458}
459
460PHP_METHOD(FieldDescriptor, getNumber) {
461 FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
462 RETURN_LONG(upb_fielddef_number(intern->fielddef));
463}
464
465PHP_METHOD(FieldDescriptor, getLabel) {
466 FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
467 RETURN_LONG(upb_fielddef_label(intern->fielddef));
468}
469
470PHP_METHOD(FieldDescriptor, getType) {
471 FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
472 RETURN_LONG(upb_fielddef_descriptortype(intern->fielddef));
473}
474
475PHP_METHOD(FieldDescriptor, isMap) {
476 FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
477 RETURN_BOOL(upb_fielddef_ismap(intern->fielddef));
478}
479
480PHP_METHOD(FieldDescriptor, getEnumType) {
481 FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
482 const upb_enumdef *enumdef = upb_fielddef_enumsubdef(intern->fielddef);
483 if (enumdef == NULL) {
484 char error_msg[100];
485 sprintf(error_msg, "Cannot get enum type for non-enum field '%s'",
486 upb_fielddef_name(intern->fielddef));
487 zend_throw_exception(NULL, error_msg, 0 TSRMLS_CC);
488 return;
489 }
490 PHP_PROTO_HASHTABLE_VALUE desc = get_def_obj(enumdef);
491
492#if PHP_MAJOR_VERSION < 7
493 RETURN_ZVAL(desc, 1, 0);
494#else
495 GC_ADDREF(desc);
496 RETURN_OBJ(desc);
497#endif
498}
499
500PHP_METHOD(FieldDescriptor, getMessageType) {
501 FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
502 const upb_msgdef *msgdef = upb_fielddef_msgsubdef(intern->fielddef);
503 if (msgdef == NULL) {
504 char error_msg[100];
505 sprintf(error_msg, "Cannot get message type for non-message field '%s'",
506 upb_fielddef_name(intern->fielddef));
507 zend_throw_exception(NULL, error_msg, 0 TSRMLS_CC);
508 return;
509 }
510 PHP_PROTO_HASHTABLE_VALUE desc = get_def_obj(msgdef);
511
512#if PHP_MAJOR_VERSION < 7
513 RETURN_ZVAL(desc, 1, 0);
514#else
515 GC_ADDREF(desc);
516 RETURN_OBJ(desc);
517#endif
518}
519
520// -----------------------------------------------------------------------------
521// Oneof
522// -----------------------------------------------------------------------------
523
524static zend_function_entry oneof_descriptor_methods[] = {
525 PHP_ME(Oneof, getName, NULL, ZEND_ACC_PUBLIC)
526 PHP_ME(Oneof, getField, NULL, ZEND_ACC_PUBLIC)
527 PHP_ME(Oneof, getFieldCount, NULL, ZEND_ACC_PUBLIC)
528 ZEND_FE_END
529};
530
531DEFINE_CLASS(Oneof, oneof_descriptor,
532 "Google\\Protobuf\\OneofDescriptor");
533
534static void oneof_descriptor_free_c(Oneof *self TSRMLS_DC) {
535}
536
537static void oneof_descriptor_init_c_instance(Oneof *self TSRMLS_DC) {
538 self->oneofdef = NULL;
539}
540
541PHP_METHOD(Oneof, getName) {
542 Oneof *intern = UNBOX(Oneof, getThis());
543 const char *name = upb_oneofdef_name(intern->oneofdef);
544 PHP_PROTO_RETVAL_STRINGL(name, strlen(name), 1);
545}
546
547PHP_METHOD(Oneof, getField) {
548 long index;
549 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
550 FAILURE) {
551 zend_error(E_USER_ERROR, "Expect integer for index.\n");
552 return;
553 }
554
555 Oneof *intern = UNBOX(Oneof, getThis());
556 int field_num = upb_oneofdef_numfields(intern->oneofdef);
557 if (index < 0 || index >= field_num) {
558 zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index);
559 return;
560 }
561
562 upb_oneof_iter iter;
563 int i;
564 for(upb_oneof_begin(&iter, intern->oneofdef), i = 0;
565 !upb_oneof_done(&iter) && i < index;
566 upb_oneof_next(&iter), i++);
567 const upb_fielddef *field = upb_oneof_iter_field(&iter);
568
569 PHP_PROTO_HASHTABLE_VALUE field_hashtable_value = get_def_obj(field);
570 if (field_hashtable_value == NULL) {
571#if PHP_MAJOR_VERSION < 7
572 MAKE_STD_ZVAL(field_hashtable_value);
573 ZVAL_OBJ(field_hashtable_value, field_descriptor_type->create_object(
574 field_descriptor_type TSRMLS_CC));
575#else
576 field_hashtable_value =
577 field_descriptor_type->create_object(field_descriptor_type TSRMLS_CC);
578#endif
579 FieldDescriptor *field_php =
580 UNBOX_HASHTABLE_VALUE(FieldDescriptor, field_hashtable_value);
581 field_php->fielddef = field;
582 add_def_obj(field, field_hashtable_value);
583 }
584
585#if PHP_MAJOR_VERSION < 7
586 RETURN_ZVAL(field_hashtable_value, 1, 0);
587#else
588 GC_ADDREF(field_hashtable_value);
589 RETURN_OBJ(field_hashtable_value);
590#endif
591}
592
593PHP_METHOD(Oneof, getFieldCount) {
594 Oneof *intern = UNBOX(Oneof, getThis());
595 RETURN_LONG(upb_oneofdef_numfields(intern->oneofdef));
596}
597
598// -----------------------------------------------------------------------------
599// DescriptorPool
600// -----------------------------------------------------------------------------
601
602static zend_function_entry descriptor_pool_methods[] = {
603 PHP_ME(DescriptorPool, getGeneratedPool, NULL,
604 ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
605 PHP_ME(DescriptorPool, getDescriptorByClassName, NULL, ZEND_ACC_PUBLIC)
606 PHP_ME(DescriptorPool, getEnumDescriptorByClassName, NULL, ZEND_ACC_PUBLIC)
607 ZEND_FE_END
608};
609
610static zend_function_entry internal_descriptor_pool_methods[] = {
611 PHP_ME(InternalDescriptorPool, getGeneratedPool, NULL,
612 ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
613 PHP_ME(InternalDescriptorPool, internalAddGeneratedFile, NULL, ZEND_ACC_PUBLIC)
614 ZEND_FE_END
615};
616
617DEFINE_CLASS(DescriptorPool, descriptor_pool,
618 "Google\\Protobuf\\DescriptorPool");
619DEFINE_CLASS(InternalDescriptorPool, internal_descriptor_pool,
620 "Google\\Protobuf\\Internal\\DescriptorPool");
621
622// wrapper of generated pool
623#if PHP_MAJOR_VERSION < 7
624zval* generated_pool_php;
625zval* internal_generated_pool_php;
626#else
627zend_object *generated_pool_php;
628zend_object *internal_generated_pool_php;
629#endif
630InternalDescriptorPool *generated_pool; // The actual generated pool
631
632void init_generated_pool_once(TSRMLS_D) {
633 if (generated_pool == NULL) {
634#if PHP_MAJOR_VERSION < 7
635 MAKE_STD_ZVAL(generated_pool_php);
636 MAKE_STD_ZVAL(internal_generated_pool_php);
637 ZVAL_OBJ(internal_generated_pool_php,
638 internal_descriptor_pool_type->create_object(
639 internal_descriptor_pool_type TSRMLS_CC));
640 generated_pool = UNBOX(InternalDescriptorPool, internal_generated_pool_php);
641 ZVAL_OBJ(generated_pool_php, descriptor_pool_type->create_object(
642 descriptor_pool_type TSRMLS_CC));
643#else
644 internal_generated_pool_php = internal_descriptor_pool_type->create_object(
645 internal_descriptor_pool_type TSRMLS_CC);
646 generated_pool = (InternalDescriptorPool *)((char *)internal_generated_pool_php -
647 XtOffsetOf(InternalDescriptorPool, std));
648 generated_pool_php =
649 descriptor_pool_type->create_object(descriptor_pool_type TSRMLS_CC);
650#endif
651 }
652}
653
654static void internal_descriptor_pool_init_c_instance(
655 InternalDescriptorPool *pool TSRMLS_DC) {
656 pool->symtab = upb_symtab_new();
657
658 ALLOC_HASHTABLE(pool->pending_list);
659 zend_hash_init(pool->pending_list, 1, NULL, ZVAL_PTR_DTOR, 0);
660}
661
662static void internal_descriptor_pool_free_c(
663 InternalDescriptorPool *pool TSRMLS_DC) {
664 upb_symtab_free(pool->symtab);
665
666 zend_hash_destroy(pool->pending_list);
667 FREE_HASHTABLE(pool->pending_list);
668}
669
670static void descriptor_pool_init_c_instance(DescriptorPool *pool TSRMLS_DC) {
671 assert(generated_pool != NULL);
672 pool->intern = generated_pool;
673}
674
675static void descriptor_pool_free_c(DescriptorPool *pool TSRMLS_DC) {
676}
677
678static void validate_enumdef(const upb_enumdef *enumdef) {
679 // Verify that an entry exists with integer value 0. (This is the default
680 // value.)
681 const char *lookup = upb_enumdef_iton(enumdef, 0);
682 if (lookup == NULL) {
683 zend_error(E_USER_ERROR,
684 "Enum definition does not contain a value for '0'.");
685 }
686}
687
688static void validate_msgdef(const upb_msgdef* msgdef) {
689 // Verify that no required fields exist. proto3 does not support these.
690 upb_msg_field_iter it;
691 for (upb_msg_field_begin(&it, msgdef);
692 !upb_msg_field_done(&it);
693 upb_msg_field_next(&it)) {
694 const upb_fielddef* field = upb_msg_iter_field(&it);
695 if (upb_fielddef_label(field) == UPB_LABEL_REQUIRED) {
696 zend_error(E_ERROR, "Required fields are unsupported in proto3.");
697 }
698 }
699}
700
701PHP_METHOD(DescriptorPool, getGeneratedPool) {
702 init_generated_pool_once(TSRMLS_C);
703#if PHP_MAJOR_VERSION < 7
704 RETURN_ZVAL(generated_pool_php, 1, 0);
705#else
706 GC_ADDREF(generated_pool_php);
707 RETURN_OBJ(generated_pool_php);
708#endif
709}
710
711PHP_METHOD(InternalDescriptorPool, getGeneratedPool) {
712 init_generated_pool_once(TSRMLS_C);
713#if PHP_MAJOR_VERSION < 7
714 RETURN_ZVAL(internal_generated_pool_php, 1, 0);
715#else
716 GC_ADDREF(internal_generated_pool_php);
717 RETURN_OBJ(internal_generated_pool_php);
718#endif
719}
720
721static size_t classname_len_max(const char *fullname,
722 const char *package,
723 const char *php_namespace,
724 const char *prefix) {
725 size_t fullname_len = strlen(fullname);
726 size_t package_len = 0;
727 size_t prefix_len = 0;
728 size_t namespace_len = 0;
729 size_t length = fullname_len;
730 int i, segment, classname_start = 0;
731
732 if (package != NULL) {
733 package_len = strlen(package);
734 }
735 if (prefix != NULL) {
736 prefix_len = strlen(prefix);
737 }
738 if (php_namespace != NULL) {
739 namespace_len = strlen(php_namespace);
740 }
741
742 // Process package
743 if (package_len > 0) {
744 segment = 1;
745 for (i = 0; i < package_len; i++) {
746 if (package[i] == '.') {
747 segment++;
748 }
749 }
750 // In case of reserved name in package.
751 length += 3 * segment;
752
753 classname_start = package_len + 1;
754 }
755
756 // Process class name
757 segment = 1;
758 for (i = classname_start; i < fullname_len; i++) {
759 if (fullname[i] == '.') {
760 segment++;
761 }
762 }
763 if (prefix_len == 0) {
764 length += 3 * segment;
765 } else {
766 length += prefix_len * segment;
767 }
768
769 // The additional 2, one is for preceding '.' and the other is for trailing 0.
770 return length + namespace_len + 2;
771}
772
773static bool is_reserved(const char *segment, int length) {
774 bool result;
775 char* lower = ALLOC_N(char, length + 1);
776 memset(lower, 0, length + 1);
777 memcpy(lower, segment, length);
778 int i = 0;
779 while(lower[i]) {
780 lower[i] = (char)tolower(lower[i]);
781 i++;
782 }
783 lower[length] = 0;
784 result = is_reserved_name(lower);
785 FREE(lower);
786 return result;
787}
788
789static char* fill_prefix(const char *segment, int length,
790 const char *prefix_given,
791 const char *package_name, char *classname) {
792 size_t i;
793
794 if (prefix_given != NULL && strcmp(prefix_given, "") != 0) {
795 size_t prefix_len = strlen(prefix_given);
796 memcpy(classname, prefix_given, strlen(prefix_given));
797 classname += prefix_len;
798 } else {
799 if (is_reserved(segment, length)) {
800 if (package_name != NULL &&
801 strcmp("google.protobuf", package_name) == 0) {
802 memcpy(classname, "GPB", 3);
803 classname += 3;
804 } else {
805 memcpy(classname, "PB", 2);
806 classname += 2;
807 }
808 }
809 }
810 return classname;
811}
812
813static char* fill_segment(const char *segment, int length,
814 char *classname, bool use_camel) {
815 memcpy(classname, segment, length);
816 if (use_camel && (segment[0] < 'A' || segment[0] > 'Z')) {
817 classname[0] += 'A' - 'a';
818 }
819 return classname + length;
820}
821
822static char* fill_namespace(const char *package, const char *namespace_given,
823 char *classname) {
824 if (namespace_given != NULL) {
825 size_t namespace_len = strlen(namespace_given);
826 memcpy(classname, namespace_given, namespace_len);
827 classname += namespace_len;
828 *classname = '\\';
829 classname++;
830 } else if (package != NULL) {
831 int i = 0, j, offset = 0;
832 size_t package_len = strlen(package);
833 while (i < package_len) {
834 j = i;
835 while (j < package_len && package[j] != '.') {
836 j++;
837 }
838 classname = fill_prefix(package + i, j - i, "", package, classname);
839 classname = fill_segment(package + i, j - i, classname, true);
840 classname[0] = '\\';
841 classname++;
842 i = j + 1;
843 }
844 }
845 return classname;
846}
847
848static char* fill_classname(const char *fullname,
849 const char *package,
850 const char *namespace_given,
851 const char *prefix, char *classname) {
852 int classname_start = 0;
853 if (package != NULL) {
854 size_t package_len = strlen(package);
855 classname_start = package_len == 0 ? 0 : package_len + 1;
856 }
857 size_t fullname_len = strlen(fullname);
858 classname = fill_prefix(fullname + classname_start,
859 fullname_len - classname_start,
860 prefix, package, classname);
861
862 int i = classname_start, j;
863 while (i < fullname_len) {
864 j = i;
865 while (j < fullname_len && fullname[j] != '.') {
866 j++;
867 }
868 classname = fill_segment(fullname + i, j - i, classname, false);
869 if (j != fullname_len) {
870 *classname = '_';
871 classname++;
872 }
873 i = j + 1;
874 }
875 return classname;
876}
877
878static char* fill_qualified_classname(const char *fullname,
879 const char *package,
880 const char *namespace_given,
881 const char *prefix, char *classname) {
882 classname = fill_namespace(package, namespace_given, classname);
883 return fill_classname(fullname, package, namespace_given, prefix, classname);
884}
885
886static void classname_no_prefix(const char *fullname, const char *package_name,
887 char *class_name) {
888 size_t i = 0, j;
889 bool first_char = true, is_reserved = false;
890 size_t pkg_name_len = package_name == NULL ? 0 : strlen(package_name);
891 size_t message_name_start = package_name == NULL ? 0 : pkg_name_len + 1;
892 size_t message_len = (strlen(fullname) - message_name_start);
893
894 // Submessage is concatenated with its containing messages by '_'.
895 for (j = message_name_start; j < message_name_start + message_len; j++) {
896 if (fullname[j] == '.') {
897 class_name[i++] = '_';
898 } else {
899 class_name[i++] = fullname[j];
900 }
901 }
902}
903
904void internal_add_generated_file(const char *data, PHP_PROTO_SIZE data_len,
905 InternalDescriptorPool *pool TSRMLS_DC) {
906 upb_filedef **files;
907 size_t i;
908
909 CHECK_UPB(files = upb_loaddescriptor(data, data_len, &pool, &status),
910 "Parse binary descriptors to internal descriptors failed");
911
912 // This method is called only once in each file.
913 assert(files[0] != NULL);
914 assert(files[1] == NULL);
915
916 CHECK_UPB(upb_symtab_addfile(pool->symtab, files[0], &status),
917 "Unable to add file to DescriptorPool");
918
919 // For each enum/message, we need its PHP class, upb descriptor and its PHP
920 // wrapper. These information are needed later for encoding, decoding and type
921 // checking. However, sometimes we just have one of them. In order to find
922 // them quickly, here, we store the mapping for them.
923 for (i = 0; i < upb_filedef_defcount(files[0]); i++) {
924 const upb_def *def = upb_filedef_def(files[0], i);
925 switch (upb_def_type(def)) {
926#define CASE_TYPE(def_type, def_type_lower, desc_type, desc_type_lower) \
927 case UPB_DEF_##def_type: { \
928 CREATE_HASHTABLE_VALUE(desc, desc_php, desc_type, desc_type_lower##_type); \
929 const upb_##def_type_lower *def_type_lower = \
930 upb_downcast_##def_type_lower(def); \
931 desc->def_type_lower = def_type_lower; \
932 add_def_obj(desc->def_type_lower, desc_php); \
933 /* Unlike other messages, MapEntry is shared by all map fields and doesn't \
934 * have generated PHP class.*/ \
935 if (upb_def_type(def) == UPB_DEF_MSG && \
936 upb_msgdef_mapentry(upb_downcast_msgdef(def))) { \
937 break; \
938 } \
939 /* Prepend '.' to package name to make it absolute. In the 5 additional \
940 * bytes allocated, one for '.', one for trailing 0, and 3 for 'GPB' if \
941 * given message is google.protobuf.Empty.*/ \
942 const char *fullname = upb_##def_type_lower##_fullname(def_type_lower); \
943 const char *package = upb_filedef_package(files[0]); \
944 const char *php_namespace = upb_filedef_phpnamespace(files[0]); \
945 const char *prefix_given = upb_filedef_phpprefix(files[0]); \
946 size_t classname_len = classname_len_max(fullname, package, \
947 php_namespace, prefix_given); \
948 char *classname = ecalloc(sizeof(char), classname_len); \
949 fill_qualified_classname(fullname, package, php_namespace, \
950 prefix_given, classname); \
951 PHP_PROTO_CE_DECLARE pce; \
952 if (php_proto_zend_lookup_class(classname, strlen(classname), &pce) == \
953 FAILURE) { \
954 zend_error(E_ERROR, "Generated message class %s hasn't been defined", \
955 classname); \
956 return; \
957 } else { \
958 desc->klass = PHP_PROTO_CE_UNREF(pce); \
959 } \
960 add_ce_obj(desc->klass, desc_php); \
961 add_proto_obj(upb_##def_type_lower##_fullname(desc->def_type_lower), \
962 desc_php); \
963 efree(classname); \
964 break; \
965 }
966
967 CASE_TYPE(MSG, msgdef, Descriptor, descriptor)
968 CASE_TYPE(ENUM, enumdef, EnumDescriptor, enum_descriptor)
969#undef CASE_TYPE
970
971 default:
972 break;
973 }
974 }
975
976 for (i = 0; i < upb_filedef_defcount(files[0]); i++) {
977 const upb_def *def = upb_filedef_def(files[0], i);
978 if (upb_def_type(def) == UPB_DEF_MSG) {
979 const upb_msgdef *msgdef = upb_downcast_msgdef(def);
980 PHP_PROTO_HASHTABLE_VALUE desc_php = get_def_obj(msgdef);
981 build_class_from_descriptor(desc_php TSRMLS_CC);
982 }
983 }
984
985 upb_filedef_unref(files[0], &pool);
986 upb_gfree(files);
987}
988
989PHP_METHOD(InternalDescriptorPool, internalAddGeneratedFile) {
990 char *data = NULL;
991 PHP_PROTO_SIZE data_len;
992 upb_filedef **files;
993 size_t i;
994
995 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &data_len) ==
996 FAILURE) {
997 return;
998 }
999
1000 InternalDescriptorPool *pool = UNBOX(InternalDescriptorPool, getThis());
1001 internal_add_generated_file(data, data_len, pool TSRMLS_CC);
1002}
1003
1004PHP_METHOD(DescriptorPool, getDescriptorByClassName) {
1005 DescriptorPool *public_pool = UNBOX(DescriptorPool, getThis());
1006 InternalDescriptorPool *pool = public_pool->intern;
1007
1008 char *classname = NULL;
1009 PHP_PROTO_SIZE classname_len;
1010
1011 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &classname,
1012 &classname_len) == FAILURE) {
1013 return;
1014 }
1015
1016 PHP_PROTO_CE_DECLARE pce;
1017 if (php_proto_zend_lookup_class(classname, classname_len, &pce) ==
1018 FAILURE) {
1019 RETURN_NULL();
1020 }
1021
1022 PHP_PROTO_HASHTABLE_VALUE desc = get_ce_obj(PHP_PROTO_CE_UNREF(pce));
1023 if (desc == NULL) {
1024 RETURN_NULL();
1025 }
1026
1027 zend_class_entry* instance_ce = HASHTABLE_VALUE_CE(desc);
1028
1029 if (!instanceof_function(instance_ce, descriptor_type TSRMLS_CC)) {
1030 RETURN_NULL();
1031 }
1032
1033#if PHP_MAJOR_VERSION < 7
1034 RETURN_ZVAL(desc, 1, 0);
1035#else
1036 GC_ADDREF(desc);
1037 RETURN_OBJ(desc);
1038#endif
1039}
1040
1041PHP_METHOD(DescriptorPool, getEnumDescriptorByClassName) {
1042 DescriptorPool *public_pool = UNBOX(DescriptorPool, getThis());
1043 InternalDescriptorPool *pool = public_pool->intern;
1044
1045 char *classname = NULL;
1046 PHP_PROTO_SIZE classname_len;
1047
1048 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &classname,
1049 &classname_len) == FAILURE) {
1050 return;
1051 }
1052
1053 PHP_PROTO_CE_DECLARE pce;
1054 if (php_proto_zend_lookup_class(classname, classname_len, &pce) ==
1055 FAILURE) {
1056 RETURN_NULL();
1057 }
1058
1059 PHP_PROTO_HASHTABLE_VALUE desc = get_ce_obj(PHP_PROTO_CE_UNREF(pce));
1060 if (desc == NULL) {
1061 RETURN_NULL();
1062 }
1063
1064 zend_class_entry* instance_ce = HASHTABLE_VALUE_CE(desc);
1065
1066 if (!instanceof_function(instance_ce, enum_descriptor_type TSRMLS_CC)) {
1067 RETURN_NULL();
1068 }
1069
1070#if PHP_MAJOR_VERSION < 7
1071 RETURN_ZVAL(desc, 1, 0);
1072#else
1073 GC_ADDREF(desc);
1074 RETURN_OBJ(desc);
1075#endif
1076}