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