def.c 37 KB

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