summaryrefslogtreecommitdiff
path: root/ext/interbase/ibase_events.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/interbase/ibase_events.c')
-rw-r--r--ext/interbase/ibase_events.c336
1 files changed, 336 insertions, 0 deletions
diff --git a/ext/interbase/ibase_events.c b/ext/interbase/ibase_events.c
new file mode 100644
index 0000000000..90a5cac4e1
--- /dev/null
+++ b/ext/interbase/ibase_events.c
@@ -0,0 +1,336 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2004 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.0 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_0.txt. |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Ard Biesheuvel <a.k.biesheuvel@its.tudelft.nl> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+
+#if HAVE_IBASE
+
+#include "php_interbase.h"
+
+void _php_ibase_event_free(char *event_buf, char *result_buf) /* {{{ */
+{
+ isc_free(event_buf);
+ isc_free(result_buf);
+}
+/* }}} */
+
+static void _php_ibase_event_block(ibase_db_link *ib_link, unsigned short count, char **events,
+ unsigned short *l, char **event_buf, char **result_buf) /* {{{ */
+{
+ ISC_STATUS dummy_result[20];
+ unsigned long dummy_count[15];
+
+ /**
+ * Unfortunately, there's no clean and portable way in C to pass arguments to
+ * a variadic function if you don't know the number of arguments at compile time.
+ * (And even if there were a way, the Interbase API doesn't provide a version of
+ * this function that takes a va_list as an argument)
+ *
+ * In this case, the number of arguments is limited to 18 by the underlying API,
+ * so we can work around it.
+ */
+
+ *l = (unsigned short) isc_event_block(event_buf, result_buf, count, events[0],
+ events[1], events[2], events[3], events[4], events[5], events[6], events[7],
+ events[8], events[9], events[10], events[11], events[12], events[13], events[14]);
+
+ /**
+ * Currently, this is the only way to correctly initialize an event buffer.
+ * This is clearly something that should be fixed, cause the semantics of
+ * isc_wait_for_event() indicate that it blocks until an event occurs.
+ * If the Firebird people ever fix this, these lines should be removed,
+ * otherwise, events will have to fire twice before ibase_wait_event() returns.
+ */
+
+ isc_wait_for_event(dummy_result, &ib_link->handle, *l, *event_buf, *result_buf);
+ isc_event_counts(dummy_count, *l, *event_buf, *result_buf);
+}
+/* }}} */
+
+/* {{{ proto string ibase_wait_event([resource link_identifier,] string event [, string event [, ...]])
+ Waits for any one of the passed Interbase events to be posted by the database, and returns its name */
+PHP_FUNCTION(ibase_wait_event)
+{
+ zval ***args;
+ ibase_db_link *ib_link;
+ char *event_buffer, *result_buffer, *events[15];
+ unsigned short i = 0, event_count = 0, buffer_size;
+ unsigned long occurred_event[15];
+
+ RESET_ERRMSG;
+
+ /* no more than 15 events */
+ if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 16) {
+ WRONG_PARAM_COUNT;
+ }
+
+ args = (zval ***) safe_emalloc(sizeof(zval **), ZEND_NUM_ARGS(), 0);
+ if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
+ efree(args);
+ RETURN_FALSE;
+ }
+
+ if (Z_TYPE_PP(args[0]) == IS_RESOURCE) {
+
+ ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, args[0], -1, "InterBase link", le_link, le_plink);
+ i = 1;
+
+ } else {
+
+ if (ZEND_NUM_ARGS() > 15) {
+ efree(args);
+ WRONG_PARAM_COUNT;
+ }
+
+ ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, NULL, IBG(default_link), "InterBase link", le_link, le_plink);
+ }
+
+ for (; i < ZEND_NUM_ARGS(); ++i) {
+ convert_to_string_ex(args[i]);
+ events[event_count++] = Z_STRVAL_PP(args[i]);
+ }
+
+ /* fills the required data structure with information about the events */
+ _php_ibase_event_block(ib_link, event_count, events, &buffer_size, &event_buffer, &result_buffer);
+
+ /* now block until an event occurs */
+ if (isc_wait_for_event(IB_STATUS, &ib_link->handle, buffer_size, event_buffer, result_buffer)) {
+ _php_ibase_error(TSRMLS_C);
+ _php_ibase_event_free(event_buffer,result_buffer);
+ efree(args);
+ RETURN_FALSE;
+ }
+
+ /* find out which event occurred */
+ isc_event_counts(occurred_event, buffer_size, event_buffer, result_buffer);
+ for (i = 0; i < event_count; ++i) {
+ if (occurred_event[i]) {
+ char *result = estrdup(events[i]);
+ _php_ibase_event_free(event_buffer,result_buffer);
+ efree(args);
+ RETURN_STRING(result,0);
+ }
+ }
+
+ /* If we reach this line, isc_wait_for_event() did return, but we don't know
+ which event fired. */
+ _php_ibase_event_free(event_buffer,result_buffer);
+ efree(args);
+ RETURN_FALSE;
+}
+/* }}} */
+
+static isc_callback _php_ibase_callback(ibase_event *event,
+ unsigned short buffer_size, char *result_buf) /* {{{ */
+{
+ /* this function is called asynchronously by the Interbase client library. */
+ TSRMLS_FETCH_FROM_CTX(event->thread_ctx);
+
+ /**
+ * The callback function is called when the event is first registered and when the event
+ * is cancelled. I consider this is a bug. By clearing event->callback, we make sure
+ * nothing happens if no event was actually posted.
+ */
+ if (event->callback != NULL) {
+
+ unsigned short i;
+ unsigned long occurred_event[15];
+ zval event_name, link_id, return_value, *args[2];
+
+ /* initialize at runtime to satisify picky compilers */
+ args[0] = &event_name;
+ args[1] = &link_id;
+
+ /* copy the updated results into the result buffer */
+ memcpy(event->result_buffer, result_buf, buffer_size);
+
+ INIT_ZVAL(event_name);
+ INIT_ZVAL(link_id);
+ ZVAL_RESOURCE(&link_id, event->link_res_id);
+
+ /* find out which event occurred */
+ isc_event_counts(occurred_event, buffer_size, event->event_buffer, event->result_buffer);
+ for (i = 0; i < event->event_count; ++i) {
+ if (occurred_event[i]) {
+ ZVAL_STRING(&event_name,event->events[i],0);
+ break;
+ }
+ }
+
+ /* call the callback provided by the user */
+ if (SUCCESS != call_user_function(EG(function_table), NULL,
+ event->callback, &return_value, 2, args TSRMLS_CC)) {
+ _php_ibase_module_error("Error calling callback %s" TSRMLS_CC, Z_STRVAL_P(event->callback));
+ return 0;
+ }
+
+ if (Z_TYPE(return_value) == IS_BOOL && !Z_BVAL(return_value)) {
+ return 0;
+ }
+ }
+
+ /* re-register the event */
+ if (isc_que_events(IB_STATUS, &event->link->handle, &event->event_id, buffer_size,
+ event->event_buffer,(isc_callback)_php_ibase_callback, (void *)event)) {
+
+ _php_ibase_error(TSRMLS_C);
+ }
+ return 0;
+}
+/* }}} */
+
+/* {{{ proto resource ibase_set_event_handler([resource link_identifier,] callback handler, string event [, string event [, ...]])
+ Register the callback for handling each of the named events */
+PHP_FUNCTION(ibase_set_event_handler)
+{
+ /**
+ * The callback passed to this function should take an event name (string) and a
+ * link resource id (int) as arguments. The value returned from the function is
+ * used to determine if the event handler should remain set.
+ */
+
+ zval ***args, **cb_arg;
+ ibase_db_link *ib_link;
+ ibase_event *event;
+ char *callback_name;
+ unsigned short i = 1, buffer_size;
+ int link_res_id;
+
+ RESET_ERRMSG;
+
+ /* no more than 15 events */
+ if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 17) {
+ WRONG_PARAM_COUNT;
+ }
+
+ args = (zval ***) safe_emalloc(sizeof(zval **), ZEND_NUM_ARGS(), 0);
+ if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
+ efree(args);
+ RETURN_FALSE;
+ }
+
+ /* get a working link */
+ if (Z_TYPE_PP(args[0]) != IS_STRING) {
+
+ cb_arg = args[1];
+ i = 2;
+
+ ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, args[0], -1,
+ "InterBase link", le_link, le_plink);
+
+ convert_to_long_ex(args[0]);
+ link_res_id = Z_LVAL_PP(args[0]);
+
+ } else {
+
+ if (ZEND_NUM_ARGS() > 16) {
+ efree(args);
+ WRONG_PARAM_COUNT;
+ }
+
+ cb_arg = args[0];
+
+ ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, NULL, IBG(default_link),
+ "InterBase link", le_link, le_plink);
+ link_res_id = IBG(default_link);
+ }
+
+ /* get the callback */
+ if (!zend_is_callable(*cb_arg, 0, &callback_name)) {
+ _php_ibase_module_error("Callback argument %s is not a callable function"
+ TSRMLS_CC, callback_name);
+ efree(callback_name);
+ efree(args);
+ RETURN_FALSE;
+ }
+ efree(callback_name);
+
+ /* allocate the event resource */
+ event = (ibase_event *) safe_emalloc(sizeof(ibase_event), 1, 0);
+ TSRMLS_SET_CTX(event->thread_ctx);
+ event->link_res_id = link_res_id;
+ event->link = ib_link;
+ event->event_count = 0;
+ event->callback = NULL;
+ event->events = (char **) safe_emalloc(sizeof(char *),ZEND_NUM_ARGS()-i,0);
+
+ for (; i < ZEND_NUM_ARGS(); ++i) {
+ convert_to_string_ex(args[i]);
+ event->events[event->event_count++] = estrdup(Z_STRVAL_PP(args[i]));
+ }
+
+ /* fills the required data structure with information about the events */
+ _php_ibase_event_block(ib_link, event->event_count, event->events,
+ &buffer_size, &event->event_buffer, &event->result_buffer);
+
+ /* now register the events with the Interbase API */
+ if (isc_que_events(IB_STATUS, &ib_link->handle, &event->event_id, buffer_size,
+ event->event_buffer,(isc_callback)_php_ibase_callback, (void *)event)) {
+
+ _php_ibase_error(TSRMLS_C);
+ efree(args);
+ efree(event);
+ RETURN_FALSE;
+ }
+
+ ALLOC_ZVAL(event->callback);
+ *event->callback = **cb_arg;
+ INIT_PZVAL(event->callback);
+ zval_copy_ctor(event->callback);
+
+ efree(args);
+
+ ZEND_REGISTER_RESOURCE(return_value, event, le_event);
+ zend_list_addref(Z_LVAL_P(return_value));
+}
+
+/* {{{ proto bool ibase_free_event_handler(resource event)
+ Frees the event handler set by ibase_set_event_handler() */
+PHP_FUNCTION(ibase_free_event_handler)
+{
+ zval **event_arg;
+ ibase_event *event;
+
+ RESET_ERRMSG;
+
+ if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &event_arg) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ ZEND_FETCH_RESOURCE(event, ibase_event *, event_arg, -1, "Interbase event", le_event);
+ zend_list_delete(Z_LVAL_PP(event_arg));
+ RETURN_TRUE;
+}
+/* }}} */
+
+#endif /* HAVE_IBASE */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */