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