xref: /PHP-5.3/ext/interbase/ibase_events.c (revision a2045ff3)
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 
32 static int le_event;
33 
_php_ibase_event_free(char * event_buf,char * result_buf)34 static 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 
_php_ibase_free_event(ibase_event * event TSRMLS_DC)41 void _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 
_php_ibase_free_event_rsrc(zend_rsrc_list_entry * rsrc TSRMLS_DC)75 static 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 
php_ibase_events_minit(INIT_FUNC_ARGS)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 
_php_ibase_event_block(ibase_db_link * ib_link,unsigned short count,char ** events,unsigned short * l,char ** event_buf,char ** result_buf)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 	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 */
PHP_FUNCTION(ibase_wait_event)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 	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 
_php_ibase_callback(ibase_event * event,unsigned short buffer_size,char * result_buf)199 static 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 */
PHP_FUNCTION(ibase_set_event_handler)263 PHP_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() */
PHP_FUNCTION(ibase_free_event_handler)380 PHP_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