1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2013 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/* $Id$ */ 20 21#ifdef HAVE_CONFIG_H 22#include "config.h" 23#endif 24 25#include "php.h" 26 27#if HAVE_IBASE 28 29#include "php_interbase.h" 30#include "php_ibase_includes.h" 31 32static int le_event; 33 34static void _php_ibase_event_free(char *event_buf, char *result_buf) /* {{{ */ 35{ 36 isc_free(event_buf); 37 isc_free(result_buf); 38} 39/* }}} */ 40 41void _php_ibase_free_event(ibase_event *event TSRMLS_DC) /* {{{ */ 42{ 43 unsigned short i; 44 45 event->state = DEAD; 46 47 if (event->link != NULL) { 48 ibase_event **node; 49 50 if (event->link->handle != NULL && 51 isc_cancel_events(IB_STATUS, &event->link->handle, &event->event_id)) { 52 _php_ibase_error(TSRMLS_C); 53 } 54 55 /* delete this event from the link struct */ 56 for (node = &event->link->event_head; *node != event; node = &(*node)->event_next); 57 *node = event->event_next; 58 } 59 60 if (event->callback) { 61 zval_dtor(event->callback); 62 FREE_ZVAL(event->callback); 63 event->callback = NULL; 64 65 _php_ibase_event_free(event->event_buffer,event->result_buffer); 66 67 for (i = 0; i < event->event_count; ++i) { 68 efree(event->events[i]); 69 } 70 efree(event->events); 71 } 72} 73/* }}} */ 74 75static void _php_ibase_free_event_rsrc(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */ 76{ 77 ibase_event *e = (ibase_event *) rsrc->ptr; 78 79 _php_ibase_free_event(e TSRMLS_CC); 80 81 efree(e); 82} 83/* }}} */ 84 85void 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 92static 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 unsigned long 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 */ 127PHP_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 unsigned long 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() TSRMLS_CC, "+", &args, &num_args) == FAILURE) { 144 return; 145 } 146 147 if (Z_TYPE_PP(args[0]) == IS_RESOURCE) { 148 if (!ZEND_FETCH_RESOURCE2_NO_RETURN(ib_link, ibase_db_link *, args[0], -1, "InterBase link", le_link, le_plink)) { 149 efree(args); 150 RETURN_FALSE; 151 } 152 i = 1; 153 } else { 154 if (ZEND_NUM_ARGS() > 15) { 155 efree(args); 156 WRONG_PARAM_COUNT; 157 } 158 if (!ZEND_FETCH_RESOURCE2_NO_RETURN(ib_link, ibase_db_link *, NULL, IBG(default_link), "InterBase link", le_link, le_plink)) { 159 efree(args); 160 RETURN_FALSE; 161 } 162 } 163 164 for (; i < ZEND_NUM_ARGS(); ++i) { 165 convert_to_string_ex(args[i]); 166 events[event_count++] = Z_STRVAL_PP(args[i]); 167 } 168 169 /* fills the required data structure with information about the events */ 170 _php_ibase_event_block(ib_link, event_count, events, &buffer_size, &event_buffer, &result_buffer); 171 172 /* now block until an event occurs */ 173 if (isc_wait_for_event(IB_STATUS, &ib_link->handle, buffer_size, event_buffer, result_buffer)) { 174 _php_ibase_error(TSRMLS_C); 175 _php_ibase_event_free(event_buffer,result_buffer); 176 efree(args); 177 RETURN_FALSE; 178 } 179 180 /* find out which event occurred */ 181 isc_event_counts(occurred_event, buffer_size, event_buffer, result_buffer); 182 for (i = 0; i < event_count; ++i) { 183 if (occurred_event[i]) { 184 char *result = estrdup(events[i]); 185 _php_ibase_event_free(event_buffer,result_buffer); 186 efree(args); 187 RETURN_STRING(result,0); 188 } 189 } 190 191 /* If we reach this line, isc_wait_for_event() did return, but we don't know 192 which event fired. */ 193 _php_ibase_event_free(event_buffer,result_buffer); 194 efree(args); 195 RETURN_FALSE; 196} 197/* }}} */ 198 199static isc_callback _php_ibase_callback(ibase_event *event, /* {{{ */ 200 unsigned short buffer_size, char *result_buf) 201{ 202 /* this function is called asynchronously by the Interbase client library. */ 203 TSRMLS_FETCH_FROM_CTX(event->thread_ctx); 204 205 /** 206 * The callback function is called when the event is first registered and when the event 207 * is cancelled. I consider this is a bug. By clearing event->callback first and setting 208 * it to -1 later, we make sure nothing happens if no event was actually posted. 209 */ 210 switch (event->state) { 211 unsigned short i; 212 unsigned long occurred_event[15]; 213 zval event_name, link_id, return_value, *args[2]; 214 215 default: /* == DEAD */ 216 break; 217 case ACTIVE: 218 args[0] = &event_name; 219 args[1] = &link_id; 220 221 /* copy the updated results into the result buffer */ 222 memcpy(event->result_buffer, result_buf, buffer_size); 223 224 INIT_ZVAL(event_name); 225 INIT_ZVAL(link_id); 226 ZVAL_RESOURCE(&link_id, event->link_res_id); 227 228 /* find out which event occurred */ 229 isc_event_counts(occurred_event, buffer_size, event->event_buffer, event->result_buffer); 230 for (i = 0; i < event->event_count; ++i) { 231 if (occurred_event[i]) { 232 ZVAL_STRING(&event_name,event->events[i],0); 233 break; 234 } 235 } 236 237 /* call the callback provided by the user */ 238 if (SUCCESS != call_user_function(EG(function_table), NULL, 239 event->callback, &return_value, 2, args TSRMLS_CC)) { 240 _php_ibase_module_error("Error calling callback %s" TSRMLS_CC, Z_STRVAL_P(event->callback)); 241 break; 242 } 243 244 if (Z_TYPE(return_value) == IS_BOOL && !Z_BVAL(return_value)) { 245 event->state = DEAD; 246 break; 247 } 248 case NEW: 249 /* re-register the event */ 250 if (isc_que_events(IB_STATUS, &event->link->handle, &event->event_id, buffer_size, 251 event->event_buffer,(isc_callback)_php_ibase_callback, (void *)event)) { 252 253 _php_ibase_error(TSRMLS_C); 254 } 255 event->state = ACTIVE; 256 } 257 return 0; 258} 259/* }}} */ 260 261/* {{{ proto resource ibase_set_event_handler([resource link_identifier,] callback handler, string event [, string event [, ...]]) 262 Register the callback for handling each of the named events */ 263PHP_FUNCTION(ibase_set_event_handler) 264{ 265 /** 266 * The callback passed to this function should take an event name (string) and a 267 * link resource id (int) as arguments. The value returned from the function is 268 * used to determine if the event handler should remain set. 269 */ 270 char *cb_name; 271 zval ***args, **cb_arg; 272 ibase_db_link *ib_link; 273 ibase_event *event; 274 unsigned short i = 1, buffer_size; 275 int link_res_id, num_args; 276 277 RESET_ERRMSG; 278 279 /* Minimum and maximum number of arguments allowed */ 280 if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > 17) { 281 WRONG_PARAM_COUNT; 282 } 283 284 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &num_args) == FAILURE) { 285 return; 286 } 287 288 /* get a working link */ 289 if (Z_TYPE_PP(args[0]) != IS_STRING) { 290 /* resource, callback, event_1 [, ... event_15] 291 * No more than 15 events 292 */ 293 if (ZEND_NUM_ARGS() < 3 || ZEND_NUM_ARGS() > 17) { 294 efree(args); 295 WRONG_PARAM_COUNT; 296 } 297 298 cb_arg = args[1]; 299 i = 2; 300 301 if (!ZEND_FETCH_RESOURCE2_NO_RETURN(ib_link, ibase_db_link *, args[0], -1, "InterBase link", le_link, le_plink)) { 302 efree(args); 303 RETURN_FALSE; 304 } 305 306 convert_to_long_ex(args[0]); 307 link_res_id = Z_LVAL_PP(args[0]); 308 309 } else { 310 /* callback, event_1 [, ... event_15] 311 * No more than 15 events 312 */ 313 if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > 16) { 314 efree(args); 315 WRONG_PARAM_COUNT; 316 } 317 318 cb_arg = args[0]; 319 320 if (!ZEND_FETCH_RESOURCE2_NO_RETURN(ib_link, ibase_db_link *, NULL, IBG(default_link), "InterBase link", le_link, le_plink)) { 321 efree(args); 322 RETURN_FALSE; 323 } 324 link_res_id = IBG(default_link); 325 } 326 327 /* get the callback */ 328 if (!zend_is_callable(*cb_arg, 0, &cb_name TSRMLS_CC)) { 329 _php_ibase_module_error("Callback argument %s is not a callable function" TSRMLS_CC, cb_name); 330 efree(cb_name); 331 efree(args); 332 RETURN_FALSE; 333 } 334 efree(cb_name); 335 336 /* allocate the event resource */ 337 event = (ibase_event *) safe_emalloc(sizeof(ibase_event), 1, 0); 338 TSRMLS_SET_CTX(event->thread_ctx); 339 event->link_res_id = link_res_id; 340 event->link = ib_link; 341 event->event_count = 0; 342 event->state = NEW; 343 event->events = (char **) safe_emalloc(sizeof(char *),ZEND_NUM_ARGS()-i,0); 344 345 ALLOC_ZVAL(event->callback); 346 *event->callback = **cb_arg; 347 INIT_PZVAL(event->callback); 348 zval_copy_ctor(event->callback); 349 350 for (; i < ZEND_NUM_ARGS(); ++i) { 351 convert_to_string_ex(args[i]); 352 event->events[event->event_count++] = estrdup(Z_STRVAL_PP(args[i])); 353 } 354 355 /* fills the required data structure with information about the events */ 356 _php_ibase_event_block(ib_link, event->event_count, event->events, 357 &buffer_size, &event->event_buffer, &event->result_buffer); 358 359 /* now register the events with the Interbase API */ 360 if (isc_que_events(IB_STATUS, &ib_link->handle, &event->event_id, buffer_size, 361 event->event_buffer,(isc_callback)_php_ibase_callback, (void *)event)) { 362 363 _php_ibase_error(TSRMLS_C); 364 efree(event); 365 efree(args); 366 RETURN_FALSE; 367 } 368 369 event->event_next = ib_link->event_head; 370 ib_link->event_head = event; 371 372 ZEND_REGISTER_RESOURCE(return_value, event, le_event); 373 zend_list_addref(Z_LVAL_P(return_value)); 374 efree(args); 375} 376/* }}} */ 377 378/* {{{ proto bool ibase_free_event_handler(resource event) 379 Frees the event handler set by ibase_set_event_handler() */ 380PHP_FUNCTION(ibase_free_event_handler) 381{ 382 zval *event_arg; 383 384 RESET_ERRMSG; 385 386 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &event_arg)) { 387 ibase_event *event; 388 389 ZEND_FETCH_RESOURCE(event, ibase_event *, &event_arg, -1, "Interbase event", le_event); 390 391 event->state = DEAD; 392 393 zend_list_delete(Z_LVAL_P(event_arg)); 394 RETURN_TRUE; 395 } else { 396 RETURN_FALSE; 397 } 398} 399/* }}} */ 400 401#endif /* HAVE_IBASE */ 402 403/* 404 * Local variables: 405 * tab-width: 4 406 * c-basic-offset: 4 407 * End: 408 * vim600: sw=4 ts=4 fdm=marker 409 * vim<600: sw=4 ts=4 410 */ 411