a02784c279827f6972e664fb5ff58f744fc1e3cc
[m6w6/ext-psi] / src / module.c
1
2 #ifdef HAVE_CONFIG_H
3 #include "config.h"
4 #endif
5
6 #include "php.h"
7 #include "php_ini.h"
8 #include "ext/standard/info.h"
9
10 #include "php_psi.h"
11 #include "parser.h"
12
13 #include "libjit.h"
14 #include "libffi.h"
15
16 ZEND_DECLARE_MODULE_GLOBALS(psi);
17
18 PHP_INI_BEGIN()
19 STD_PHP_INI_ENTRY("psi.engine", "ffi", PHP_INI_SYSTEM, OnUpdateString, engine, zend_psi_globals, psi_globals)
20 STD_PHP_INI_ENTRY("psi.directory", "psi.d", PHP_INI_SYSTEM, OnUpdateString, directory, zend_psi_globals, psi_globals)
21 PHP_INI_END();
22
23 void psi_error(int type, const char *msg, ...)
24 {
25 char buf[0x1000];
26 va_list argv;
27
28 va_start(argv, msg);
29 vslprintf(buf, 0x1000, msg, argv);
30 va_end(argv);
31
32 php_error(type, buf);
33 }
34
35 size_t psi_t_alignment(token_t t)
36 {
37 size_t align;
38 #define PSI_TAS_D(T) struct PSI_TAS_ ##T { \
39 char c; \
40 T x; \
41 }
42 #define PSI_TAS_P(T) struct PSI_TAS_ ## T ## _pointer { \
43 char c; \
44 T *x; \
45 }
46 #define PSI_TAS_C(T) align = offsetof(struct PSI_TAS_ ##T, x)
47 #define PSI_TAS_CASE(T) { \
48 PSI_TAS_D(T); \
49 PSI_TAS_C(T); \
50 }
51 switch (t) {
52 case PSI_T_INT8:
53 PSI_TAS_CASE(int8_t);
54 break;
55 case PSI_T_UINT8:
56 PSI_TAS_CASE(uint8_t);
57 break;
58 case PSI_T_INT16:
59 PSI_TAS_CASE(int16_t);
60 break;
61 case PSI_T_UINT16:
62 PSI_TAS_CASE(uint16_t);
63 break;
64 case PSI_T_INT32:
65 PSI_TAS_CASE(int32_t);
66 break;
67 case PSI_T_UINT32:
68 PSI_TAS_CASE(uint32_t);
69 break;
70 case PSI_T_INT64:
71 PSI_TAS_CASE(int64_t);
72 break;
73 case PSI_T_UINT64:
74 PSI_TAS_CASE(uint64_t);
75 break;
76 case PSI_T_FLOAT:
77 PSI_TAS_CASE(float);
78 break;
79 case PSI_T_DOUBLE:
80 PSI_TAS_CASE(double);
81 break;
82 case PSI_T_POINTER:
83 {
84 PSI_TAS_P(char);
85 PSI_TAS_C(char_pointer);
86 }
87 break;
88 EMPTY_SWITCH_DEFAULT_CASE();
89 }
90
91 return align;
92 }
93
94 size_t psi_t_size(token_t t)
95 {
96 size_t size;
97
98 switch (t) {
99 case PSI_T_INT8:
100 case PSI_T_UINT8:
101 size = 1;
102 break;
103 case PSI_T_INT16:
104 case PSI_T_UINT16:
105 size = 2;
106 break;
107 case PSI_T_INT:
108 size = sizeof(int);
109 break;
110 case PSI_T_INT32:
111 case PSI_T_UINT32:
112 size = 4;
113 break;
114 case PSI_T_INT64:
115 case PSI_T_UINT64:
116 size = 8;
117 break;
118 case PSI_T_FLOAT:
119 size = sizeof(float);
120 break;
121 case PSI_T_DOUBLE:
122 size = sizeof(double);
123 break;
124 case PSI_T_POINTER:
125 size = sizeof(char *);
126 break;
127 EMPTY_SWITCH_DEFAULT_CASE();
128 }
129 return size;
130 }
131
132 size_t psi_t_align(token_t t, size_t s)
133 {
134 size_t a = psi_t_alignment(t);
135 return ((s - 1) | (a - 1)) + 1;
136 }
137
138 int psi_internal_type(impl_type *type)
139 {
140 switch (type->type) {
141 case PSI_T_BOOL:
142 return _IS_BOOL;
143 case PSI_T_INT:
144 return IS_LONG;
145 case PSI_T_FLOAT:
146 case PSI_T_DOUBLE:
147 return IS_DOUBLE;
148 case PSI_T_STRING:
149 return IS_STRING;
150 case PSI_T_ARRAY:
151 return IS_ARRAY;
152 default:
153 return 0;
154 }
155 }
156
157 zend_internal_arg_info *psi_internal_arginfo(impl *impl)
158 {
159 size_t i;
160 zend_internal_arg_info *aip;
161 zend_internal_function_info *fi;
162
163 aip = calloc(impl->func->args->count + 1, sizeof(*aip));
164
165 fi = (zend_internal_function_info *) &aip[0];
166 fi->required_num_args = psi_num_min_args(impl);
167 fi->return_reference = impl->func->return_reference;
168 fi->type_hint = psi_internal_type(impl->func->return_type);
169
170 for (i = 0; i < impl->func->args->count; ++i) {
171 impl_arg *iarg = impl->func->args->args[i];
172 zend_internal_arg_info *ai = &aip[i+1];
173
174 ai->name = iarg->var->name;
175 ai->type_hint = psi_internal_type(iarg->type);
176 if (iarg->var->reference) {
177 ai->pass_by_reference = 1;
178 }
179 if (iarg->var->reference || (iarg->def && iarg->def->type == PSI_T_NULL)) {
180 ai->allow_null = 1;
181 }
182 }
183
184 return aip;
185 }
186
187 size_t psi_num_min_args(impl *impl)
188 {
189 size_t i, n = impl->func->args->count;
190
191 for (i = 0; i < impl->func->args->count; ++i) {
192 if (impl->func->args->args[i]->def) {
193 --n;
194 }
195 }
196 return n;
197 }
198
199 void psi_to_bool(zval *return_value, token_t t, impl_val *ret_val, set_value *set, decl_var *var)
200 {
201 impl_val *v = deref_impl_val(ret_val, var);
202
203 switch (t) {
204 case PSI_T_FLOAT:
205 RETVAL_DOUBLE((double) v->fval);
206 break;
207 case PSI_T_DOUBLE:
208 RETVAL_DOUBLE(v->dval);
209 break;
210 default:
211 RETVAL_LONG(v->lval);
212 break;
213 }
214 convert_to_boolean(return_value);
215 }
216
217 void psi_to_int(zval *return_value, token_t t, impl_val *ret_val, set_value *set, decl_var *var)
218 {
219 impl_val *v = deref_impl_val(ret_val, var);
220
221 switch (t) {
222 case PSI_T_FLOAT:
223 RETVAL_DOUBLE((double) v->fval);
224 break;
225 case PSI_T_DOUBLE:
226 RETVAL_DOUBLE(v->dval);
227 break;
228 default:
229 RETVAL_LONG(v->lval);
230 return;
231 }
232 convert_to_long(return_value);
233 }
234
235 void psi_to_double(zval *return_value, token_t t, impl_val *ret_val, set_value *set, decl_var *var)
236 {
237 impl_val *v = deref_impl_val(ret_val, var);
238
239 switch (t) {
240 case PSI_T_FLOAT:
241 RETVAL_DOUBLE((double) v->fval);
242 break;
243 case PSI_T_DOUBLE:
244 RETVAL_DOUBLE(v->dval);
245 break;
246 default:
247 RETVAL_DOUBLE((double) v->lval);
248 break;
249 }
250 }
251
252 void psi_to_string(zval *return_value, token_t t, impl_val *ret_val, set_value *set, decl_var *var)
253 {
254 switch (t) {
255 case PSI_T_INT8:
256 case PSI_T_UINT8:
257 if (!var->arg->var->pointer_level) {
258 RETVAL_STRINGL(&ret_val->cval, 1);
259 } else {
260 ret_val = deref_impl_val(ret_val, var);
261 if (ret_val && ret_val->ptr) {
262 RETVAL_STRING(ret_val->ptr);
263 } else {
264 RETVAL_EMPTY_STRING();
265 }
266 }
267 return;
268 case PSI_T_FLOAT:
269 RETVAL_DOUBLE((double) deref_impl_val(ret_val, var)->fval);
270 break;
271 case PSI_T_DOUBLE:
272 RETVAL_DOUBLE(deref_impl_val(ret_val, var)->dval);
273 break;
274 default:
275 RETVAL_LONG(deref_impl_val(ret_val, var)->lval);
276 break;
277 }
278 convert_to_string(return_value);
279 }
280
281
282 static impl_val *iterate(impl_val *val, token_t t, unsigned i, impl_val *tmp)
283 {
284 size_t size = psi_t_size(t);
285
286 memset(tmp, 0, sizeof(*tmp));
287 memcpy(tmp, val->ptr + size * i, size);
288 return tmp;
289 }
290
291 void psi_from_zval(impl_val *mem, decl_arg *spec, zval *zv, void **tmp)
292 {
293 decl_type *type = real_decl_type(spec->type);
294
295 switch (type->type) {
296 case PSI_T_FLOAT:
297 mem->fval = (float) zval_get_double(zv);
298 break;
299 case PSI_T_DOUBLE:
300 mem->dval = zval_get_double(zv);
301 break;
302 case PSI_T_INT8:
303 case PSI_T_UINT8:
304 if (spec->var->pointer_level) {
305 zend_string *zs = zval_get_string(zv);
306 *tmp = mem->ptr = estrndup(zs->val, zs->len);
307 zend_string_release(zs);
308 break;
309 }
310 /* no break */
311 default:
312 mem->zend.lval = zval_get_long(zv);
313 break;
314 }
315 }
316
317 void *psi_array_to_struct(decl_struct *s, HashTable *arr)
318 {
319 size_t i, j = 0;
320 char *mem = ecalloc(1, s->size + s->args->count * sizeof(void *));
321
322 if (arr) for (i = 0; i < s->args->count; ++i) {
323 decl_arg *darg = s->args->args[i];
324 zval *entry = zend_hash_str_find_ind(arr, darg->var->name, strlen(darg->var->name));
325
326 if (entry) {
327 impl_val val;
328 void *tmp = NULL;
329
330 memset(&tmp, 0, sizeof(tmp));
331 psi_from_zval(&val, darg, entry, &tmp);
332 memcpy(mem + darg->layout->pos, &val, darg->layout->len);
333 if (tmp) {
334 ((void **)(mem + s->size))[j++] = tmp;
335 }
336 }
337 }
338 return mem;
339 }
340
341 void psi_to_array(zval *return_value, token_t t, impl_val *ret_val, set_value *set, decl_var *var)
342 {
343 zval ele;
344 unsigned i;
345 impl_val tmp;
346
347 array_init(return_value);
348
349 if (t == PSI_T_STRUCT) {
350 decl_struct *s = real_decl_type(var->arg->type)->strct;
351 ret_val = deref_impl_val(ret_val, var);
352
353 ZEND_ASSERT(s);
354
355 if (set->count) {
356 /* explicit member casts */
357 for (i = 0; i < set->count; ++i) {
358 zval ztmp;
359 impl_val *tmp_ptr;
360 set_value *sub_set = set->inner[i];
361 decl_var *sub_var = sub_set->vars->vars[0];
362 decl_arg *sub_arg = sub_var->arg;
363
364 if (sub_arg) {
365 token_t t = real_decl_type(sub_arg->type)->type;
366 void *ptr = malloc(sub_arg->layout->len);
367
368 memcpy(ptr, (char *) ret_val->ptr + sub_arg->layout->pos,
369 sub_arg->layout->len);
370 tmp_ptr = enref_impl_val(ptr, sub_arg->var);
371 sub_set->func->handler(&ztmp, t, tmp_ptr, sub_set, sub_var);
372 add_assoc_zval(return_value, sub_var->name, &ztmp);
373 free(tmp_ptr);
374 if (tmp_ptr != ptr) {
375 free(ptr);
376 }
377 }
378 }
379 }
380 return;
381
382 // for (i = 0; i < s->args->count; ++i) {
383 // decl_arg *darg = s->args->args[i];
384 // impl_val tmp, tmp_ptr;
385 // zval ztmp;
386 // char *ptr = (char *) ret_val->ptr + darg->layout->pos;
387 //
388 // tmp_ptr.ptr = &tmp;
389 // memset(&tmp, 0, sizeof(tmp));
390 // memcpy(&tmp, ptr, darg->layout->len);
391 // switch (real_decl_type(darg->type)->type) {
392 // case PSI_T_FLOAT:
393 // case PSI_T_DOUBLE:
394 // psi_to_double(&ztmp, real_decl_type(darg->type)->type, &tmp, darg->var);
395 // break;
396 // case PSI_T_INT8:
397 // case PSI_T_UINT8:
398 // if (darg->var->pointer_level) {
399 // psi_to_string(&ztmp, real_decl_type(darg->type)->type, &tmp_ptr, darg->var);
400 // break;
401 // }
402 // /* no break */
403 // case PSI_T_INT16:
404 // case PSI_T_UINT16:
405 // case PSI_T_INT32:
406 // case PSI_T_UINT32:
407 // case PSI_T_INT64:
408 // case PSI_T_UINT64:
409 // psi_to_int(&ztmp, real_decl_type(darg->type)->type, &tmp, darg->var);
410 // break;
411 // case PSI_T_STRUCT:
412 // psi_to_array(&ztmp, real_decl_type(darg->type)->type, &tmp_ptr, darg->var);
413 // break;
414 // default:
415 // printf("t=%d\n", real_decl_type(darg->type)->type);
416 // abort();
417 // }
418 // add_assoc_zval(return_value, darg->var->name, &ztmp);
419 // }
420 return;
421 }
422 ret_val = deref_impl_val(ret_val, var);
423 for (i = 0; i < var->arg->var->array_size; ++i) {
424 impl_val *ptr = iterate(ret_val, t, i, &tmp);
425
426 switch (t) {
427 case PSI_T_FLOAT:
428 ZVAL_DOUBLE(&ele, (double) ptr->fval);
429 break;
430 case PSI_T_DOUBLE:
431 ZVAL_DOUBLE(&ele, ptr->dval);
432 break;
433 default:
434 ZVAL_LONG(&ele, ptr->lval);
435 break;
436 }
437
438 add_next_index_zval(return_value, &ele);
439 }
440 }
441
442 static inline ZEND_RESULT_CODE psi_parse_args(zend_execute_data *execute_data, impl *impl)
443 {
444 impl_arg *iarg;
445
446 if (!impl->func->args->count) {
447 return zend_parse_parameters_none();
448 }
449
450 ZEND_PARSE_PARAMETERS_START(psi_num_min_args(impl), impl->func->args->count)
451 nextarg:
452 iarg = impl->func->args->args[_i];
453 if (iarg->def) {
454 Z_PARAM_OPTIONAL;
455 }
456 if (PSI_T_BOOL == iarg->type->type) {
457 if (iarg->def) {
458 iarg->val.zend.bval = iarg->def->type == PSI_T_TRUE ? 1 : 0;
459 }
460 Z_PARAM_BOOL(iarg->val.zend.bval);
461 } else if (PSI_T_INT == iarg->type->type) {
462 if (iarg->def) {
463 iarg->val.zend.lval = zend_atol(iarg->def->text, strlen(iarg->def->text));
464 }
465 Z_PARAM_LONG(iarg->val.zend.lval);
466 } else if (PSI_T_FLOAT == iarg->type->type || PSI_T_DOUBLE == iarg->type->type) {
467 if (iarg->def) {
468 iarg->val.dval = zend_strtod(iarg->def->text, NULL);
469 }
470 Z_PARAM_DOUBLE(iarg->val.dval);
471 } else if (PSI_T_STRING == iarg->type->type) {
472 struct {char *val; size_t len;} str;
473 if (iarg->def) {
474 /* FIXME */
475 str.len = strlen(iarg->def->text) - 2;
476 str.val = &iarg->def->text[1];
477 }
478 Z_PARAM_STR_EX(iarg->val.zend.str, 1, 0);
479 if (iarg->val.zend.str) {
480 zend_string_addref(iarg->val.zend.str);
481 } else if (iarg->def) {
482 iarg->val.zend.str = zend_string_init(str.val, str.len, 0);
483 }
484 } else if (PSI_T_ARRAY == iarg->type->type) {
485 /* handled as _zv in let or set */
486 Z_PARAM_PROLOGUE(0);
487 } else {
488 error_code = ZPP_ERROR_FAILURE;
489 break;
490 }
491 iarg->_zv = _arg;
492 if (_i < _max_num_args) {
493 goto nextarg;
494 }
495 ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
496
497 return SUCCESS;
498 }
499
500 static inline void *psi_do_calloc(let_calloc *alloc)
501 {
502 decl_type *type = real_decl_type(alloc->type);
503 size_t size;
504
505 if (type->type == PSI_T_STRUCT) {
506 /* psi_do_clean expects at least one NULL pointer after the struct */
507 size = type->strct->size + sizeof(void *);
508 } else {
509 size = psi_t_size(type->type);
510 }
511
512 return ecalloc(alloc->n, size);
513 }
514
515 static inline void *psi_do_let(decl_arg *darg)
516 {
517 impl_arg *iarg = darg->let->arg;
518 impl_val *arg_val;
519
520 darg->let->ptr = &darg->let->out;
521 arg_val = darg->let->ptr;
522
523 if (!iarg) {
524 /* let foo = calloc(1, long);
525 * let foo = NULL;
526 * let foo;
527 */
528 if (darg->let->val->func && darg->let->val->func->type == PSI_T_CALLOC) {
529 arg_val->ptr = psi_do_calloc(darg->let->val->func->alloc);
530 darg->let->mem = arg_val->ptr;
531 } else if (darg->var->array_size) {
532 arg_val->ptr = ecalloc(darg->var->array_size, sizeof(*arg_val));
533 darg->let->mem = arg_val->ptr;
534 } else {
535 memset(arg_val, 0, sizeof(*arg_val));
536 }
537 } else {
538
539 switch (darg->let->val->func->type) {
540 case PSI_T_BOOLVAL:
541 if (iarg->type->type == PSI_T_BOOL) {
542 arg_val->cval = iarg->val.zend.bval;
543 } else {
544 arg_val->cval = zend_is_true(iarg->_zv);
545 }
546 break;
547 case PSI_T_INTVAL:
548 if (iarg->type->type == PSI_T_INT) {
549 arg_val->lval = iarg->val.zend.lval;
550 } else {
551 arg_val->lval = zval_get_long(iarg->_zv);
552 }
553 break;
554 case PSI_T_STRVAL:
555 if (iarg->type->type == PSI_T_STRING) {
556 arg_val->ptr = estrdup(iarg->val.zend.str->val);
557 darg->let->mem = arg_val->ptr;
558 zend_string_release(iarg->val.zend.str);
559 } else {
560 zend_string *zs = zval_get_string(iarg->_zv);
561 arg_val->ptr = estrdup(zs->val);
562 darg->let->mem = arg_val->ptr;
563 zend_string_release(zs);
564 }
565 break;
566 case PSI_T_STRLEN:
567 if (iarg->type->type == PSI_T_STRING) {
568 arg_val->lval = iarg->val.zend.str->len;
569 zend_string_release(iarg->val.zend.str);
570 } else {
571 zend_string *zs = zval_get_string(iarg->_zv);
572 arg_val->lval = zs->len;
573 zend_string_release(zs);
574 }
575 break;
576 case PSI_T_ARRVAL:
577 if (iarg->type->type == PSI_T_ARRAY) {
578 decl_type *type = real_decl_type(darg->type);
579
580 switch (type->type) {
581 case PSI_T_STRUCT:
582 arg_val->ptr = psi_array_to_struct(type->strct, HASH_OF(iarg->_zv));
583 darg->let->mem = arg_val->ptr;
584 break;
585 }
586 }
587 break;
588 EMPTY_SWITCH_DEFAULT_CASE();
589 }
590 }
591
592 if (darg->let->val && darg->let->val->is_reference) {
593 return &darg->let->ptr;
594 } else {
595 return darg->let->ptr;
596 }
597 }
598
599 static inline void psi_do_set(zval *return_value, set_value *set)
600 {
601 impl_val *val = (impl_val *) &set->vars->vars[0]->arg->let->ptr;
602 token_t t = real_decl_type(set->vars->vars[0]->arg->type)->type;
603
604 ZVAL_DEREF(return_value);
605 zval_dtor(return_value);
606
607 set->func->handler(return_value, t, val, set, set->vars->vars[0]);
608 }
609
610 static inline void psi_do_return(zval *return_value, return_stmt *ret, impl_val *ret_val)
611 {
612 token_t t = real_decl_type(ret->decl->type)->type;
613
614 ret->set->func->handler(return_value, t, ret_val, ret->set, ret->decl->var);
615 }
616
617 static inline void psi_do_free(free_stmt *fre)
618 {
619 size_t i, j;
620 impl_val dummy;
621
622 for (i = 0; i < fre->calls->count; ++i) {
623 free_call *f = fre->calls->list[i];
624
625 for (j = 0; j < f->vars->count; ++j) {
626 decl_var *dvar = f->vars->vars[j];
627 decl_arg *darg = dvar->arg;
628
629 f->decl->call.args[j] = &darg->let->out;
630 }
631
632 PSI_ContextCall(&PSI_G(context), &dummy, f->decl);
633 }
634 }
635
636 static inline void psi_do_clean(impl *impl)
637 {
638 size_t i;
639
640 for (i = 0; i < impl->func->args->count; ++i ) {
641 impl_arg *iarg = impl->func->args->args[i];
642
643 switch (iarg->type->type) {
644 case PSI_T_STRING:
645 if (iarg->val.zend.str) {
646 zend_string_release(iarg->val.zend.str);
647 }
648 break;
649 }
650 }
651
652 if (impl->decl->args) for (i = 0; i < impl->decl->args->count; ++i) {
653 decl_arg *darg = impl->decl->args->args[i];
654
655 if (darg->let && darg->let->mem) {
656 decl_type *type = real_decl_type(darg->type);
657
658 if (type->type == PSI_T_STRUCT) {
659 void **ptr = (void **) ((char *) darg->let->mem + type->strct->size);
660
661 while (*ptr) {
662 efree(*ptr++);
663 }
664 }
665 efree(darg->let->mem);
666 darg->let->mem = NULL;
667 }
668 }
669 }
670
671 void psi_call(zend_execute_data *execute_data, zval *return_value, impl *impl)
672 {
673 impl_val ret_val;
674 size_t i;
675
676 if (SUCCESS != psi_parse_args(execute_data, impl)) {
677 return;
678 }
679
680 if (impl->decl->args) {
681 for (i = 0; i < impl->decl->args->count; ++i) {
682 decl_arg *darg = impl->decl->args->args[i];
683
684 impl->decl->call.args[i] = psi_do_let(darg);
685 }
686 }
687
688 memset(&ret_val, 0, sizeof(ret_val));
689 PSI_ContextCall(&PSI_G(context), &ret_val, impl->decl);
690
691 psi_do_return(return_value, impl->stmts->ret.list[0], &ret_val);
692
693 for (i = 0; i < impl->stmts->set.count; ++i) {
694 set_stmt *set = impl->stmts->set.list[i];
695
696 if (set->arg->_zv) {
697 psi_do_set(set->arg->_zv, set->val);
698 }
699 }
700
701 for (i = 0; i < impl->stmts->fre.count; ++i) {
702 free_stmt *fre = impl->stmts->fre.list[i];
703
704 psi_do_free(fre);
705 }
706
707 psi_do_clean(impl);
708 }
709
710 PHP_MINIT_FUNCTION(psi)
711 {
712 PSI_ContextOps *ops;
713
714 REGISTER_INI_ENTRIES();
715
716 if (!strcasecmp(PSI_G(engine), "jit")) {
717 ops = PSI_Libjit();
718 } else {
719 ops = PSI_Libffi();
720 }
721
722 PSI_ContextInit(&PSI_G(context), ops, psi_error);
723 PSI_ContextBuild(&PSI_G(context), PSI_G(directory));
724
725 return SUCCESS;
726 }
727 PHP_MSHUTDOWN_FUNCTION(psi)
728 {
729 PSI_ContextDtor(&PSI_G(context));
730
731 UNREGISTER_INI_ENTRIES();
732
733 return SUCCESS;
734 }
735
736 /* Remove if there's nothing to do at request start */
737 /* {{{ PHP_RINIT_FUNCTION
738 */
739 PHP_RINIT_FUNCTION(psi)
740 {
741 #if defined(COMPILE_DL_PSI) && defined(ZTS)
742 ZEND_TSRMLS_CACHE_UPDATE();
743 #endif
744 return SUCCESS;
745 }
746 /* }}} */
747
748 /* Remove if there's nothing to do at request end */
749 /* {{{ PHP_RSHUTDOWN_FUNCTION
750 */
751 PHP_RSHUTDOWN_FUNCTION(psi)
752 {
753 return SUCCESS;
754 }
755 /* }}} */
756
757 PHP_MINFO_FUNCTION(psi)
758 {
759 php_info_print_table_start();
760 php_info_print_table_header(2, "psi support", "enabled");
761 php_info_print_table_end();
762
763 DISPLAY_INI_ENTRIES();
764 }
765 const zend_function_entry psi_functions[] = {
766 PHP_FE_END
767 };
768
769 zend_module_entry psi_module_entry = {
770 STANDARD_MODULE_HEADER,
771 "psi",
772 psi_functions,
773 PHP_MINIT(psi),
774 PHP_MSHUTDOWN(psi),
775 PHP_RINIT(psi), /* Replace with NULL if there's nothing to do at request start */
776 PHP_RSHUTDOWN(psi), /* Replace with NULL if there's nothing to do at request end */
777 PHP_MINFO(psi),
778 PHP_PSI_VERSION,
779 STANDARD_MODULE_PROPERTIES
780 };
781
782 #ifdef COMPILE_DL_PSI
783 #ifdef ZTS
784 ZEND_TSRMLS_CACHE_DEFINE();
785 #endif
786 ZEND_GET_MODULE(psi)
787 #endif
788
789 /*
790 * Local variables:
791 * tab-width: 4
792 * c-basic-offset: 4
793 * End:
794 * vim600: noet sw=4 ts=4 fdm=marker
795 * vim<600: noet sw=4 ts=4
796 */