ibase_events.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2016 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Authors: Ard Biesheuvel <a.k.biesheuvel@its.tudelft.nl> |
  16. +----------------------------------------------------------------------+
  17. */
  18. #ifdef HAVE_CONFIG_H
  19. #include "config.h"
  20. #endif
  21. #include "php.h"
  22. #if HAVE_IBASE
  23. #include "php_interbase.h"
  24. #include "php_ibase_includes.h"
  25. static int le_event;
  26. static void _php_ibase_event_free(char *event_buf, char *result_buf) /* {{{ */
  27. {
  28. isc_free(event_buf);
  29. isc_free(result_buf);
  30. }
  31. /* }}} */
  32. void _php_ibase_free_event(ibase_event *event TSRMLS_DC) /* {{{ */
  33. {
  34. unsigned short i;
  35. event->state = DEAD;
  36. if (event->link != NULL) {
  37. ibase_event **node;
  38. if (event->link->handle != NULL &&
  39. isc_cancel_events(IB_STATUS, &event->link->handle, &event->event_id)) {
  40. _php_ibase_error(TSRMLS_C);
  41. }
  42. /* delete this event from the link struct */
  43. for (node = &event->link->event_head; *node != event; node = &(*node)->event_next);
  44. *node = event->event_next;
  45. }
  46. if (event->callback) {
  47. zval_dtor(event->callback);
  48. FREE_ZVAL(event->callback);
  49. event->callback = NULL;
  50. _php_ibase_event_free(event->event_buffer,event->result_buffer);
  51. for (i = 0; i < event->event_count; ++i) {
  52. efree(event->events[i]);
  53. }
  54. efree(event->events);
  55. }
  56. }
  57. /* }}} */
  58. static void _php_ibase_free_event_rsrc(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
  59. {
  60. ibase_event *e = (ibase_event *) rsrc->ptr;
  61. _php_ibase_free_event(e TSRMLS_CC);
  62. efree(e);
  63. }
  64. /* }}} */
  65. void php_ibase_events_minit(INIT_FUNC_ARGS) /* {{{ */
  66. {
  67. le_event = zend_register_list_destructors_ex(_php_ibase_free_event_rsrc, NULL,
  68. "interbase event", module_number);
  69. }
  70. /* }}} */
  71. static void _php_ibase_event_block(ibase_db_link *ib_link, unsigned short count, /* {{{ */
  72. char **events, unsigned short *l, char **event_buf, char **result_buf)
  73. {
  74. ISC_STATUS dummy_result[20];
  75. unsigned long dummy_count[15];
  76. /**
  77. * Unfortunately, there's no clean and portable way in C to pass arguments to
  78. * a variadic function if you don't know the number of arguments at compile time.
  79. * (And even if there were a way, the Interbase API doesn't provide a version of
  80. * this function that takes a va_list as an argument)
  81. *
  82. * In this case, the number of arguments is limited to 18 by the underlying API,
  83. * so we can work around it.
  84. */
  85. *l = (unsigned short) isc_event_block(event_buf, result_buf, count, events[0],
  86. events[1], events[2], events[3], events[4], events[5], events[6], events[7],
  87. events[8], events[9], events[10], events[11], events[12], events[13], events[14]);
  88. /**
  89. * Currently, this is the only way to correctly initialize an event buffer.
  90. * This is clearly something that should be fixed, cause the semantics of
  91. * isc_wait_for_event() indicate that it blocks until an event occurs.
  92. * If the Firebird people ever fix this, these lines should be removed,
  93. * otherwise, events will have to fire twice before ibase_wait_event() returns.
  94. */
  95. isc_wait_for_event(dummy_result, &ib_link->handle, *l, *event_buf, *result_buf);
  96. isc_event_counts(dummy_count, *l, *event_buf, *result_buf);
  97. }
  98. /* }}} */
  99. /* {{{ proto string ibase_wait_event([resource link_identifier,] string event [, string event [, ...]])
  100. Waits for any one of the passed Interbase events to be posted by the database, and returns its name */
  101. PHP_FUNCTION(ibase_wait_event)
  102. {
  103. zval ***args;
  104. ibase_db_link *ib_link;
  105. int num_args;
  106. char *event_buffer, *result_buffer, *events[15];
  107. unsigned short i = 0, event_count = 0, buffer_size;
  108. unsigned long occurred_event[15];
  109. RESET_ERRMSG;
  110. /* no more than 15 events */
  111. if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 16) {
  112. WRONG_PARAM_COUNT;
  113. }
  114. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &num_args) == FAILURE) {
  115. return;
  116. }
  117. if (Z_TYPE_PP(args[0]) == IS_RESOURCE) {
  118. if (!ZEND_FETCH_RESOURCE2_NO_RETURN(ib_link, ibase_db_link *, args[0], -1, "InterBase link", le_link, le_plink)) {
  119. efree(args);
  120. RETURN_FALSE;
  121. }
  122. i = 1;
  123. } else {
  124. if (ZEND_NUM_ARGS() > 15) {
  125. efree(args);
  126. WRONG_PARAM_COUNT;
  127. }
  128. if (!ZEND_FETCH_RESOURCE2_NO_RETURN(ib_link, ibase_db_link *, NULL, IBG(default_link), "InterBase link", le_link, le_plink)) {
  129. efree(args);
  130. RETURN_FALSE;
  131. }
  132. }
  133. for (; i < ZEND_NUM_ARGS(); ++i) {
  134. convert_to_string_ex(args[i]);
  135. events[event_count++] = Z_STRVAL_PP(args[i]);
  136. }
  137. /* fills the required data structure with information about the events */
  138. _php_ibase_event_block(ib_link, event_count, events, &buffer_size, &event_buffer, &result_buffer);
  139. /* now block until an event occurs */
  140. if (isc_wait_for_event(IB_STATUS, &ib_link->handle, buffer_size, event_buffer, result_buffer)) {
  141. _php_ibase_error(TSRMLS_C);
  142. _php_ibase_event_free(event_buffer,result_buffer);
  143. efree(args);
  144. RETURN_FALSE;
  145. }
  146. /* find out which event occurred */
  147. isc_event_counts(occurred_event, buffer_size, event_buffer, result_buffer);
  148. for (i = 0; i < event_count; ++i) {
  149. if (occurred_event[i]) {
  150. char *result = estrdup(events[i]);
  151. _php_ibase_event_free(event_buffer,result_buffer);
  152. efree(args);
  153. RETURN_STRING(result,0);
  154. }
  155. }
  156. /* If we reach this line, isc_wait_for_event() did return, but we don't know
  157. which event fired. */
  158. _php_ibase_event_free(event_buffer,result_buffer);
  159. efree(args);
  160. RETURN_FALSE;
  161. }
  162. /* }}} */
  163. static isc_callback _php_ibase_callback(ibase_event *event, /* {{{ */
  164. unsigned short buffer_size, char *result_buf)
  165. {
  166. /* this function is called asynchronously by the Interbase client library. */
  167. TSRMLS_FETCH_FROM_CTX(event->thread_ctx);
  168. /**
  169. * The callback function is called when the event is first registered and when the event
  170. * is cancelled. I consider this is a bug. By clearing event->callback first and setting
  171. * it to -1 later, we make sure nothing happens if no event was actually posted.
  172. */
  173. switch (event->state) {
  174. unsigned short i;
  175. unsigned long occurred_event[15];
  176. zval event_name, link_id, return_value, *args[2];
  177. default: /* == DEAD */
  178. break;
  179. case ACTIVE:
  180. args[0] = &event_name;
  181. args[1] = &link_id;
  182. /* copy the updated results into the result buffer */
  183. memcpy(event->result_buffer, result_buf, buffer_size);
  184. INIT_ZVAL(event_name);
  185. INIT_ZVAL(link_id);
  186. ZVAL_RESOURCE(&link_id, event->link_res_id);
  187. /* find out which event occurred */
  188. isc_event_counts(occurred_event, buffer_size, event->event_buffer, event->result_buffer);
  189. for (i = 0; i < event->event_count; ++i) {
  190. if (occurred_event[i]) {
  191. ZVAL_STRING(&event_name,event->events[i],0);
  192. break;
  193. }
  194. }
  195. /* call the callback provided by the user */
  196. if (SUCCESS != call_user_function(EG(function_table), NULL,
  197. event->callback, &return_value, 2, args TSRMLS_CC)) {
  198. _php_ibase_module_error("Error calling callback %s" TSRMLS_CC, Z_STRVAL_P(event->callback));
  199. break;
  200. }
  201. if (Z_TYPE(return_value) == IS_BOOL && !Z_BVAL(return_value)) {
  202. event->state = DEAD;
  203. break;
  204. }
  205. case NEW:
  206. /* re-register the event */
  207. if (isc_que_events(IB_STATUS, &event->link->handle, &event->event_id, buffer_size,
  208. event->event_buffer,(isc_callback)_php_ibase_callback, (void *)event)) {
  209. _php_ibase_error(TSRMLS_C);
  210. }
  211. event->state = ACTIVE;
  212. }
  213. return 0;
  214. }
  215. /* }}} */
  216. /* {{{ proto resource ibase_set_event_handler([resource link_identifier,] callback handler, string event [, string event [, ...]])
  217. Register the callback for handling each of the named events */
  218. PHP_FUNCTION(ibase_set_event_handler)
  219. {
  220. /**
  221. * The callback passed to this function should take an event name (string) and a
  222. * link resource id (int) as arguments. The value returned from the function is
  223. * used to determine if the event handler should remain set.
  224. */
  225. char *cb_name;
  226. zval ***args, **cb_arg;
  227. ibase_db_link *ib_link;
  228. ibase_event *event;
  229. unsigned short i = 1, buffer_size;
  230. int link_res_id, num_args;
  231. RESET_ERRMSG;
  232. /* Minimum and maximum number of arguments allowed */
  233. if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > 17) {
  234. WRONG_PARAM_COUNT;
  235. }
  236. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &num_args) == FAILURE) {
  237. return;
  238. }
  239. /* get a working link */
  240. if (Z_TYPE_PP(args[0]) != IS_STRING) {
  241. /* resource, callback, event_1 [, ... event_15]
  242. * No more than 15 events
  243. */
  244. if (ZEND_NUM_ARGS() < 3 || ZEND_NUM_ARGS() > 17) {
  245. efree(args);
  246. WRONG_PARAM_COUNT;
  247. }
  248. cb_arg = args[1];
  249. i = 2;
  250. if (!ZEND_FETCH_RESOURCE2_NO_RETURN(ib_link, ibase_db_link *, args[0], -1, "InterBase link", le_link, le_plink)) {
  251. efree(args);
  252. RETURN_FALSE;
  253. }
  254. convert_to_long_ex(args[0]);
  255. link_res_id = Z_LVAL_PP(args[0]);
  256. } else {
  257. /* callback, event_1 [, ... event_15]
  258. * No more than 15 events
  259. */
  260. if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > 16) {
  261. efree(args);
  262. WRONG_PARAM_COUNT;
  263. }
  264. cb_arg = args[0];
  265. if (!ZEND_FETCH_RESOURCE2_NO_RETURN(ib_link, ibase_db_link *, NULL, IBG(default_link), "InterBase link", le_link, le_plink)) {
  266. efree(args);
  267. RETURN_FALSE;
  268. }
  269. link_res_id = IBG(default_link);
  270. }
  271. /* get the callback */
  272. if (!zend_is_callable(*cb_arg, 0, &cb_name TSRMLS_CC)) {
  273. _php_ibase_module_error("Callback argument %s is not a callable function" TSRMLS_CC, cb_name);
  274. efree(cb_name);
  275. efree(args);
  276. RETURN_FALSE;
  277. }
  278. efree(cb_name);
  279. /* allocate the event resource */
  280. event = (ibase_event *) safe_emalloc(sizeof(ibase_event), 1, 0);
  281. TSRMLS_SET_CTX(event->thread_ctx);
  282. event->link_res_id = link_res_id;
  283. event->link = ib_link;
  284. event->event_count = 0;
  285. event->state = NEW;
  286. event->events = (char **) safe_emalloc(sizeof(char *),ZEND_NUM_ARGS()-i,0);
  287. ALLOC_ZVAL(event->callback);
  288. *event->callback = **cb_arg;
  289. INIT_PZVAL(event->callback);
  290. zval_copy_ctor(event->callback);
  291. for (; i < ZEND_NUM_ARGS(); ++i) {
  292. convert_to_string_ex(args[i]);
  293. event->events[event->event_count++] = estrdup(Z_STRVAL_PP(args[i]));
  294. }
  295. /* fills the required data structure with information about the events */
  296. _php_ibase_event_block(ib_link, event->event_count, event->events,
  297. &buffer_size, &event->event_buffer, &event->result_buffer);
  298. /* now register the events with the Interbase API */
  299. if (isc_que_events(IB_STATUS, &ib_link->handle, &event->event_id, buffer_size,
  300. event->event_buffer,(isc_callback)_php_ibase_callback, (void *)event)) {
  301. _php_ibase_error(TSRMLS_C);
  302. efree(event);
  303. efree(args);
  304. RETURN_FALSE;
  305. }
  306. event->event_next = ib_link->event_head;
  307. ib_link->event_head = event;
  308. ZEND_REGISTER_RESOURCE(return_value, event, le_event);
  309. zend_list_addref(Z_LVAL_P(return_value));
  310. efree(args);
  311. }
  312. /* }}} */
  313. /* {{{ proto bool ibase_free_event_handler(resource event)
  314. Frees the event handler set by ibase_set_event_handler() */
  315. PHP_FUNCTION(ibase_free_event_handler)
  316. {
  317. zval *event_arg;
  318. RESET_ERRMSG;
  319. if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &event_arg)) {
  320. ibase_event *event;
  321. ZEND_FETCH_RESOURCE(event, ibase_event *, &event_arg, -1, "Interbase event", le_event);
  322. event->state = DEAD;
  323. zend_list_delete(Z_LVAL_P(event_arg));
  324. RETURN_TRUE;
  325. } else {
  326. RETURN_FALSE;
  327. }
  328. }
  329. /* }}} */
  330. #endif /* HAVE_IBASE */
  331. /*
  332. * Local variables:
  333. * tab-width: 4
  334. * c-basic-offset: 4
  335. * End:
  336. * vim600: sw=4 ts=4 fdm=marker
  337. * vim<600: sw=4 ts=4
  338. */