def.c 35 KB

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