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