message.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. // Protocol Buffers - Google's data interchange format
  2. // Copyright 2014 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 <stdlib.h>
  32. #include "protobuf.h"
  33. #include "utf8.h"
  34. zend_class_entry* message_type;
  35. zend_object_handlers* message_handlers;
  36. static const char TYPE_URL_PREFIX[] = "type.googleapis.com/";
  37. static zend_function_entry message_methods[] = {
  38. PHP_ME(Message, clear, NULL, ZEND_ACC_PUBLIC)
  39. PHP_ME(Message, serializeToString, NULL, ZEND_ACC_PUBLIC)
  40. PHP_ME(Message, mergeFromString, NULL, ZEND_ACC_PUBLIC)
  41. PHP_ME(Message, serializeToJsonString, NULL, ZEND_ACC_PUBLIC)
  42. PHP_ME(Message, mergeFromJsonString, NULL, ZEND_ACC_PUBLIC)
  43. PHP_ME(Message, mergeFrom, NULL, ZEND_ACC_PUBLIC)
  44. PHP_ME(Message, readOneof, NULL, ZEND_ACC_PROTECTED)
  45. PHP_ME(Message, writeOneof, NULL, ZEND_ACC_PROTECTED)
  46. PHP_ME(Message, whichOneof, NULL, ZEND_ACC_PROTECTED)
  47. PHP_ME(Message, __construct, NULL, ZEND_ACC_PROTECTED)
  48. {NULL, NULL, NULL}
  49. };
  50. // Forward declare static functions.
  51. #if PHP_MAJOR_VERSION < 7
  52. static void message_set_property(zval* object, zval* member, zval* value,
  53. php_proto_zend_literal key TSRMLS_DC);
  54. static zval* message_get_property(zval* object, zval* member, int type,
  55. const zend_literal* key TSRMLS_DC);
  56. static zval** message_get_property_ptr_ptr(zval* object, zval* member, int type,
  57. php_proto_zend_literal key TSRMLS_DC);
  58. static HashTable* message_get_gc(zval* object, zval*** table, int* n TSRMLS_DC);
  59. #else
  60. static void message_set_property(zval* object, zval* member, zval* value,
  61. void** cache_slot);
  62. static zval* message_get_property(zval* object, zval* member, int type,
  63. void** cache_slot, zval* rv);
  64. static zval* message_get_property_ptr_ptr(zval* object, zval* member, int type,
  65. void** cache_slot);
  66. static HashTable* message_get_gc(zval* object, zval** table, int* n);
  67. #endif
  68. static HashTable* message_get_properties(zval* object TSRMLS_DC);
  69. // -----------------------------------------------------------------------------
  70. // PHP Message Handlers
  71. // -----------------------------------------------------------------------------
  72. // Define object free method.
  73. PHP_PROTO_OBJECT_FREE_START(MessageHeader, message)
  74. FREE(intern->data);
  75. PHP_PROTO_OBJECT_FREE_END
  76. PHP_PROTO_OBJECT_DTOR_START(MessageHeader, message)
  77. PHP_PROTO_OBJECT_DTOR_END
  78. // Define object create method.
  79. PHP_PROTO_OBJECT_CREATE_START(MessageHeader, message)
  80. // Because php call this create func before calling the sub-message's
  81. // constructor defined in PHP, it's possible that the decriptor of this class
  82. // hasn't been added to descritpor pool (when the class is first
  83. // instantiated). In that case, we will defer the initialization of the custom
  84. // data to the parent Message's constructor, which will be called by
  85. // sub-message's constructors after the descriptor has been added.
  86. PHP_PROTO_OBJECT_CREATE_END(MessageHeader, message)
  87. // Init class entry.
  88. PHP_PROTO_INIT_CLASS_START("Google\\Protobuf\\Internal\\Message",
  89. MessageHeader, message)
  90. message_handlers->write_property = message_set_property;
  91. message_handlers->read_property = message_get_property;
  92. message_handlers->get_property_ptr_ptr = message_get_property_ptr_ptr;
  93. message_handlers->get_properties = message_get_properties;
  94. message_handlers->get_gc = message_get_gc;
  95. PHP_PROTO_INIT_CLASS_END
  96. #if PHP_MAJOR_VERSION < 7
  97. static void message_set_property(zval* object, zval* member, zval* value,
  98. php_proto_zend_literal key TSRMLS_DC) {
  99. #else
  100. static void message_set_property(zval* object, zval* member, zval* value,
  101. void** cache_slot) {
  102. #endif
  103. if (Z_TYPE_P(member) != IS_STRING) {
  104. zend_error(E_USER_ERROR, "Unexpected type for field name");
  105. return;
  106. }
  107. #if PHP_MAJOR_VERSION < 7 || (PHP_MAJOR_VERSION == 7 && PHP_MINOR_VERSION == 0)
  108. if (Z_OBJCE_P(object) != EG(scope)) {
  109. #else
  110. if (Z_OBJCE_P(object) != zend_get_executed_scope()) {
  111. #endif
  112. // User cannot set property directly (e.g., $m->a = 1)
  113. zend_error(E_USER_ERROR, "Cannot access private property.");
  114. return;
  115. }
  116. const upb_fielddef* field;
  117. MessageHeader* self = UNBOX(MessageHeader, object);
  118. field = upb_msgdef_ntofz(self->descriptor->msgdef, Z_STRVAL_P(member));
  119. if (field == NULL) {
  120. zend_error(E_USER_ERROR, "Unknown field: %s", Z_STRVAL_P(member));
  121. }
  122. layout_set(self->descriptor->layout, self, field, value TSRMLS_CC);
  123. }
  124. #if PHP_MAJOR_VERSION < 7
  125. static zval* message_get_property(zval* object, zval* member, int type,
  126. const zend_literal* key TSRMLS_DC) {
  127. #else
  128. static zval* message_get_property(zval* object, zval* member, int type,
  129. void** cache_slot, zval* rv) {
  130. #endif
  131. if (Z_TYPE_P(member) != IS_STRING) {
  132. zend_error(E_USER_ERROR, "Property name has to be a string.");
  133. return PHP_PROTO_GLOBAL_UNINITIALIZED_ZVAL;
  134. }
  135. #if PHP_MAJOR_VERSION < 7 || (PHP_MAJOR_VERSION == 7 && PHP_MINOR_VERSION == 0)
  136. if (Z_OBJCE_P(object) != EG(scope)) {
  137. #else
  138. if (Z_OBJCE_P(object) != zend_get_executed_scope()) {
  139. #endif
  140. // User cannot get property directly (e.g., $a = $m->a)
  141. zend_error(E_USER_ERROR, "Cannot access private property.");
  142. return PHP_PROTO_GLOBAL_UNINITIALIZED_ZVAL;
  143. }
  144. MessageHeader* self = UNBOX(MessageHeader, object);
  145. const upb_fielddef* field;
  146. field = upb_msgdef_ntofz(self->descriptor->msgdef, Z_STRVAL_P(member));
  147. if (field == NULL) {
  148. return PHP_PROTO_GLOBAL_UNINITIALIZED_ZVAL;
  149. }
  150. zend_property_info* property_info;
  151. #if PHP_MAJOR_VERSION < 7
  152. property_info =
  153. zend_get_property_info(Z_OBJCE_P(object), member, true TSRMLS_CC);
  154. return layout_get(
  155. self->descriptor->layout, message_data(self), field,
  156. OBJ_PROP(Z_OBJ_P(object), property_info->offset) TSRMLS_CC);
  157. #else
  158. property_info =
  159. zend_get_property_info(Z_OBJCE_P(object), Z_STR_P(member), true);
  160. return layout_get(
  161. self->descriptor->layout, message_data(self), field,
  162. OBJ_PROP(Z_OBJ_P(object), property_info->offset) TSRMLS_CC);
  163. #endif
  164. }
  165. #if PHP_MAJOR_VERSION < 7
  166. static zval** message_get_property_ptr_ptr(zval* object, zval* member, int type,
  167. php_proto_zend_literal key
  168. TSRMLS_DC) {
  169. #else
  170. static zval* message_get_property_ptr_ptr(zval* object, zval* member, int type,
  171. void** cache_slot) {
  172. #endif
  173. return NULL;
  174. }
  175. static HashTable* message_get_properties(zval* object TSRMLS_DC) {
  176. return NULL;
  177. }
  178. static HashTable* message_get_gc(zval* object, CACHED_VALUE** table,
  179. int* n TSRMLS_DC) {
  180. zend_object* zobj = Z_OBJ_P(object);
  181. *table = zobj->properties_table;
  182. *n = zobj->ce->default_properties_count;
  183. return NULL;
  184. }
  185. // -----------------------------------------------------------------------------
  186. // C Message Utilities
  187. // -----------------------------------------------------------------------------
  188. void* message_data(MessageHeader* msg) {
  189. return msg->data;
  190. }
  191. void custom_data_init(const zend_class_entry* ce,
  192. MessageHeader* intern PHP_PROTO_TSRMLS_DC) {
  193. Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_ce_obj(ce));
  194. intern->data = ALLOC_N(uint8_t, desc->layout->size);
  195. memset(message_data(intern), 0, desc->layout->size);
  196. // We wrap first so that everything in the message object is GC-rooted in
  197. // case a collection happens during object creation in layout_init().
  198. intern->descriptor = desc;
  199. layout_init(desc->layout, message_data(intern),
  200. &intern->std PHP_PROTO_TSRMLS_CC);
  201. }
  202. void build_class_from_descriptor(
  203. PHP_PROTO_HASHTABLE_VALUE php_descriptor TSRMLS_DC) {
  204. Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, php_descriptor);
  205. // Map entries don't have existing php class.
  206. if (upb_msgdef_mapentry(desc->msgdef)) {
  207. return;
  208. }
  209. zend_class_entry* registered_ce = desc->klass;
  210. if (desc->layout == NULL) {
  211. MessageLayout* layout = create_layout(desc->msgdef);
  212. desc->layout = layout;
  213. }
  214. registered_ce->create_object = message_create;
  215. }
  216. // -----------------------------------------------------------------------------
  217. // PHP Methods
  218. // -----------------------------------------------------------------------------
  219. // At the first time the message is created, the class entry hasn't been
  220. // modified. As a result, the first created instance will be a normal zend
  221. // object. Here, we manually modify it to our message in such a case.
  222. PHP_METHOD(Message, __construct) {
  223. zend_class_entry* ce = Z_OBJCE_P(getThis());
  224. if (EXPECTED(class_added(ce))) {
  225. MessageHeader* intern = UNBOX(MessageHeader, getThis());
  226. custom_data_init(ce, intern PHP_PROTO_TSRMLS_CC);
  227. }
  228. }
  229. PHP_METHOD(Message, clear) {
  230. MessageHeader* msg = UNBOX(MessageHeader, getThis());
  231. Descriptor* desc = msg->descriptor;
  232. zend_class_entry* ce = desc->klass;
  233. object_properties_init(&msg->std, ce);
  234. layout_init(desc->layout, message_data(msg), &msg->std TSRMLS_CC);
  235. }
  236. PHP_METHOD(Message, mergeFrom) {
  237. zval* value;
  238. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &value,
  239. message_type) == FAILURE) {
  240. return;
  241. }
  242. MessageHeader* from = UNBOX(MessageHeader, value);
  243. MessageHeader* to = UNBOX(MessageHeader, getThis());
  244. if(from->descriptor != to->descriptor) {
  245. zend_error(E_USER_ERROR, "Cannot merge messages with different class.");
  246. return;
  247. }
  248. layout_merge(from->descriptor->layout, from, to TSRMLS_CC);
  249. }
  250. PHP_METHOD(Message, readOneof) {
  251. PHP_PROTO_LONG index;
  252. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
  253. FAILURE) {
  254. return;
  255. }
  256. MessageHeader* msg = UNBOX(MessageHeader, getThis());
  257. const upb_fielddef* field = upb_msgdef_itof(msg->descriptor->msgdef, index);
  258. int property_cache_index =
  259. msg->descriptor->layout->fields[upb_fielddef_index(field)].cache_index;
  260. zval* property_ptr = CACHED_PTR_TO_ZVAL_PTR(
  261. OBJ_PROP(Z_OBJ_P(getThis()), property_cache_index));
  262. // Unlike singular fields, oneof fields share cached property. So we cannot
  263. // let lay_get modify the cached property. Instead, we pass in the return
  264. // value directly.
  265. layout_get(msg->descriptor->layout, message_data(msg), field,
  266. ZVAL_PTR_TO_CACHED_PTR(return_value) TSRMLS_CC);
  267. }
  268. PHP_METHOD(Message, writeOneof) {
  269. PHP_PROTO_LONG index;
  270. zval* value;
  271. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz", &index, &value) ==
  272. FAILURE) {
  273. return;
  274. }
  275. MessageHeader* msg = UNBOX(MessageHeader, getThis());
  276. const upb_fielddef* field = upb_msgdef_itof(msg->descriptor->msgdef, index);
  277. layout_set(msg->descriptor->layout, msg, field, value TSRMLS_CC);
  278. }
  279. PHP_METHOD(Message, whichOneof) {
  280. char* oneof_name;
  281. PHP_PROTO_SIZE length;
  282. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &oneof_name,
  283. &length) == FAILURE) {
  284. return;
  285. }
  286. MessageHeader* msg = UNBOX(MessageHeader, getThis());
  287. const upb_oneofdef* oneof =
  288. upb_msgdef_ntoo(msg->descriptor->msgdef, oneof_name, length);
  289. const char* oneof_case_name = layout_get_oneof_case(
  290. msg->descriptor->layout, message_data(msg), oneof TSRMLS_CC);
  291. PHP_PROTO_RETURN_STRING(oneof_case_name, 1);
  292. }
  293. // -----------------------------------------------------------------------------
  294. // Any
  295. // -----------------------------------------------------------------------------
  296. static zend_function_entry any_methods[] = {
  297. PHP_ME(Any, __construct, NULL, ZEND_ACC_PUBLIC)
  298. PHP_ME(Any, getTypeUrl, NULL, ZEND_ACC_PUBLIC)
  299. PHP_ME(Any, setTypeUrl, NULL, ZEND_ACC_PUBLIC)
  300. PHP_ME(Any, getValue, NULL, ZEND_ACC_PUBLIC)
  301. PHP_ME(Any, setValue, NULL, ZEND_ACC_PUBLIC)
  302. PHP_ME(Any, pack, NULL, ZEND_ACC_PUBLIC)
  303. PHP_ME(Any, unpack, NULL, ZEND_ACC_PUBLIC)
  304. PHP_ME(Any, is, NULL, ZEND_ACC_PUBLIC)
  305. {NULL, NULL, NULL}
  306. };
  307. zend_class_entry* any_type;
  308. // Init class entry.
  309. PHP_PROTO_INIT_SUBMSGCLASS_START("Google\\Protobuf\\Any", Any, any)
  310. zend_class_implements(any_type TSRMLS_CC, 1, message_type);
  311. zend_declare_property_string(any_type, "type_url", strlen("type_url"),
  312. "" ,ZEND_ACC_PRIVATE TSRMLS_CC);
  313. zend_declare_property_string(any_type, "value", strlen("value"),
  314. "" ,ZEND_ACC_PRIVATE TSRMLS_CC);
  315. PHP_PROTO_INIT_SUBMSGCLASS_END
  316. void hex_to_binary(const char* hex, char** binary, int* binary_len) {
  317. int i;
  318. int hex_len = strlen(hex);
  319. *binary_len = hex_len / 2;
  320. *binary = ALLOC_N(char, *binary_len);
  321. for (i = 0; i < *binary_len; i++) {
  322. char value = 0;
  323. if (hex[i * 2] >= '0' && hex[i * 2] <= '9') {
  324. value += (hex[i * 2] - '0') * 16;
  325. } else {
  326. value += (hex[i * 2] - 'a' + 10) * 16;
  327. }
  328. if (hex[i * 2 + 1] >= '0' && hex[i * 2 + 1] <= '9') {
  329. value += hex[i * 2 + 1] - '0';
  330. } else {
  331. value += hex[i * 2 + 1] - 'a' + 10;
  332. }
  333. (*binary)[i] = value;
  334. }
  335. }
  336. PHP_METHOD(Any, __construct) {
  337. PHP_PROTO_HASHTABLE_VALUE desc_php = get_ce_obj(any_type);
  338. if (desc_php == NULL) {
  339. init_generated_pool_once(TSRMLS_C);
  340. const char* generated_file =
  341. "0acd010a19676f6f676c652f70726f746f6275662f616e792e70726f746f"
  342. "120f676f6f676c652e70726f746f62756622260a03416e7912100a087479"
  343. "70655f75726c180120012809120d0a0576616c756518022001280c426f0a"
  344. "13636f6d2e676f6f676c652e70726f746f6275664208416e7950726f746f"
  345. "50015a256769746875622e636f6d2f676f6c616e672f70726f746f627566"
  346. "2f7074797065732f616e79a20203475042aa021e476f6f676c652e50726f"
  347. "746f6275662e57656c6c4b6e6f776e5479706573620670726f746f33";
  348. char* binary;
  349. int binary_len;
  350. hex_to_binary(generated_file, &binary, &binary_len);
  351. internal_add_generated_file(binary, binary_len, generated_pool TSRMLS_CC);
  352. FREE(binary);
  353. }
  354. MessageHeader* intern = UNBOX(MessageHeader, getThis());
  355. custom_data_init(any_type, intern PHP_PROTO_TSRMLS_CC);
  356. }
  357. PHP_METHOD(Any, getTypeUrl) {
  358. zval member;
  359. PHP_PROTO_ZVAL_STRING(&member, "type_url", 1);
  360. PHP_PROTO_FAKE_SCOPE_BEGIN(any_type);
  361. #if PHP_MAJOR_VERSION < 7
  362. zval* value = message_handlers->read_property(getThis(), &member, BP_VAR_R,
  363. NULL PHP_PROTO_TSRMLS_CC);
  364. #else
  365. zval* value = message_handlers->read_property(getThis(), &member, BP_VAR_R,
  366. NULL, NULL PHP_PROTO_TSRMLS_CC);
  367. #endif
  368. PHP_PROTO_FAKE_SCOPE_END;
  369. PHP_PROTO_RETVAL_ZVAL(value);
  370. }
  371. PHP_METHOD(Any, setTypeUrl) {
  372. char *type_url = NULL;
  373. PHP_PROTO_SIZE type_url_len;
  374. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &type_url,
  375. &type_url_len) == FAILURE) {
  376. return;
  377. }
  378. zval member;
  379. zval value;
  380. PHP_PROTO_ZVAL_STRING(&member, "type_url", 1);
  381. PHP_PROTO_ZVAL_STRINGL(&value, type_url, type_url_len, 1);
  382. PHP_PROTO_FAKE_SCOPE_BEGIN(any_type);
  383. message_handlers->write_property(getThis(), &member, &value,
  384. NULL PHP_PROTO_TSRMLS_CC);
  385. PHP_PROTO_FAKE_SCOPE_END;
  386. PHP_PROTO_RETVAL_ZVAL(getThis());
  387. }
  388. PHP_METHOD(Any, getValue) {
  389. zval member;
  390. PHP_PROTO_ZVAL_STRING(&member, "value", 1);
  391. PHP_PROTO_FAKE_SCOPE_BEGIN(any_type);
  392. zval* value =
  393. php_proto_message_read_property(getThis(), &member PHP_PROTO_TSRMLS_CC);
  394. PHP_PROTO_FAKE_SCOPE_END;
  395. PHP_PROTO_RETVAL_ZVAL(value);
  396. }
  397. PHP_METHOD(Any, setValue) {
  398. char *value = NULL;
  399. PHP_PROTO_SIZE value_len;
  400. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &value,
  401. &value_len) == FAILURE) {
  402. return;
  403. }
  404. zval member;
  405. zval value_to_set;
  406. PHP_PROTO_ZVAL_STRING(&member, "value", 1);
  407. PHP_PROTO_ZVAL_STRINGL(&value_to_set, value, value_len, 1);
  408. PHP_PROTO_FAKE_SCOPE_BEGIN(any_type);
  409. message_handlers->write_property(getThis(), &member, &value_to_set,
  410. NULL PHP_PROTO_TSRMLS_CC);
  411. PHP_PROTO_FAKE_SCOPE_END;
  412. PHP_PROTO_RETVAL_ZVAL(getThis());
  413. }
  414. PHP_METHOD(Any, unpack) {
  415. // Get type url.
  416. zval type_url_member;
  417. PHP_PROTO_ZVAL_STRING(&type_url_member, "type_url", 1);
  418. PHP_PROTO_FAKE_SCOPE_BEGIN(any_type);
  419. zval* type_url_php = php_proto_message_read_property(
  420. getThis(), &type_url_member PHP_PROTO_TSRMLS_CC);
  421. PHP_PROTO_FAKE_SCOPE_END;
  422. // Get fully-qualified name from type url.
  423. size_t url_prefix_len = strlen(TYPE_URL_PREFIX);
  424. const char* type_url = Z_STRVAL_P(type_url_php);
  425. size_t type_url_len = Z_STRLEN_P(type_url_php);
  426. if (url_prefix_len > type_url_len ||
  427. strncmp(TYPE_URL_PREFIX, type_url, url_prefix_len) != 0) {
  428. zend_throw_exception(
  429. NULL, "Type url needs to be type.googleapis.com/fully-qulified",
  430. 0 TSRMLS_CC);
  431. return;
  432. }
  433. const char* fully_qualified_name = type_url + url_prefix_len;
  434. PHP_PROTO_HASHTABLE_VALUE desc_php = get_proto_obj(fully_qualified_name);
  435. if (desc_php == NULL) {
  436. zend_throw_exception(
  437. NULL, "Specified message in any hasn't been added to descriptor pool",
  438. 0 TSRMLS_CC);
  439. return;
  440. }
  441. Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, desc_php);
  442. zend_class_entry* klass = desc->klass;
  443. ZVAL_OBJ(return_value, klass->create_object(klass TSRMLS_CC));
  444. MessageHeader* msg = UNBOX(MessageHeader, return_value);
  445. custom_data_init(klass, msg PHP_PROTO_TSRMLS_CC);
  446. // Get value.
  447. zval value_member;
  448. PHP_PROTO_ZVAL_STRING(&value_member, "value", 1);
  449. PHP_PROTO_FAKE_SCOPE_RESTART(any_type);
  450. zval* value = php_proto_message_read_property(
  451. getThis(), &value_member PHP_PROTO_TSRMLS_CC);
  452. PHP_PROTO_FAKE_SCOPE_END;
  453. merge_from_string(Z_STRVAL_P(value), Z_STRLEN_P(value), desc, msg);
  454. }
  455. PHP_METHOD(Any, pack) {
  456. zval* val;
  457. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &val) ==
  458. FAILURE) {
  459. return;
  460. }
  461. if (!instanceof_function(Z_OBJCE_P(val), message_type TSRMLS_CC)) {
  462. zend_error(E_USER_ERROR, "Given value is not an instance of Message.");
  463. return;
  464. }
  465. // Set value by serialized data.
  466. zval data;
  467. serialize_to_string(val, &data TSRMLS_CC);
  468. zval member;
  469. PHP_PROTO_ZVAL_STRING(&member, "value", 1);
  470. PHP_PROTO_FAKE_SCOPE_BEGIN(any_type);
  471. message_handlers->write_property(getThis(), &member, &data,
  472. NULL PHP_PROTO_TSRMLS_CC);
  473. PHP_PROTO_FAKE_SCOPE_END;
  474. // Set type url.
  475. Descriptor* desc =
  476. UNBOX_HASHTABLE_VALUE(Descriptor, get_ce_obj(Z_OBJCE_P(val)));
  477. const char* fully_qualified_name = upb_msgdef_fullname(desc->msgdef);
  478. size_t type_url_len =
  479. strlen(TYPE_URL_PREFIX) + strlen(fully_qualified_name) + 1;
  480. char* type_url = ALLOC_N(char, type_url_len);
  481. sprintf(type_url, "%s%s", TYPE_URL_PREFIX, fully_qualified_name);
  482. zval type_url_php;
  483. PHP_PROTO_ZVAL_STRING(&type_url_php, type_url, 1);
  484. PHP_PROTO_ZVAL_STRING(&member, "type_url", 1);
  485. PHP_PROTO_FAKE_SCOPE_RESTART(any_type);
  486. message_handlers->write_property(getThis(), &member, &type_url_php,
  487. NULL PHP_PROTO_TSRMLS_CC);
  488. PHP_PROTO_FAKE_SCOPE_END;
  489. FREE(type_url);
  490. }
  491. PHP_METHOD(Any, is) {
  492. zend_class_entry *klass = NULL;
  493. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "C", &klass) ==
  494. FAILURE) {
  495. return;
  496. }
  497. PHP_PROTO_HASHTABLE_VALUE desc_php = get_ce_obj(klass);
  498. if (desc_php == NULL) {
  499. RETURN_BOOL(false);
  500. }
  501. // Create corresponded type url.
  502. Descriptor* desc =
  503. UNBOX_HASHTABLE_VALUE(Descriptor, get_ce_obj(klass));
  504. const char* fully_qualified_name = upb_msgdef_fullname(desc->msgdef);
  505. size_t type_url_len =
  506. strlen(TYPE_URL_PREFIX) + strlen(fully_qualified_name) + 1;
  507. char* type_url = ALLOC_N(char, type_url_len);
  508. sprintf(type_url, "%s%s", TYPE_URL_PREFIX, fully_qualified_name);
  509. // Fetch stored type url.
  510. zval member;
  511. PHP_PROTO_ZVAL_STRING(&member, "type_url", 1);
  512. PHP_PROTO_FAKE_SCOPE_BEGIN(any_type);
  513. zval* value =
  514. php_proto_message_read_property(getThis(), &member PHP_PROTO_TSRMLS_CC);
  515. PHP_PROTO_FAKE_SCOPE_END;
  516. // Compare two type url.
  517. bool is = strcmp(type_url, Z_STRVAL_P(value)) == 0;
  518. FREE(type_url);
  519. RETURN_BOOL(is);
  520. }