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