root/ext/interbase/ibase_events.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. _php_ibase_event_free
  2. _php_ibase_free_event
  3. _php_ibase_free_event_rsrc
  4. php_ibase_events_minit
  5. _php_ibase_event_block
  6. PHP_FUNCTION
  7. _php_ibase_callback
  8. PHP_FUNCTION
  9. PHP_FUNCTION

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

/* [<][>][^][v][top][bottom][index][help] */