XCache is a fast, stable PHP opcode cacher that has been proven and is now running on production servers under high load. https://xcache.lighttpd.net/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

710 lines
16 KiB

  1. #if 0
  2. # define XCACHE_DEBUG
  3. #endif
  4. #include "xc_optimizer.h"
  5. #include "xcache/xc_extension.h"
  6. #include "xcache/xc_ini.h"
  7. #include "xcache/xc_utils.h"
  8. #include "util/xc_stack.h"
  9. #include "util/xc_trace.h"
  10. #include "xcache_globals.h"
  11. #include "ext/standard/info.h"
  12. #ifdef XCACHE_DEBUG
  13. #error 1
  14. # include "xc_processor.h"
  15. # include "xcache/xc_const_string.h"
  16. # include "ext/standard/php_var.h"
  17. #endif
  18. #ifdef IS_CV
  19. # define XCACHE_IS_CV IS_CV
  20. #else
  21. # define XCACHE_IS_CV 16
  22. #endif
  23. #ifdef ZEND_ENGINE_2_4
  24. # undef Z_OP_CONSTANT
  25. /* Z_OP_CONSTANT is used before pass_two is applied */
  26. # define Z_OP_CONSTANT(op) op_array->literals[op.constant].constant
  27. #endif
  28. typedef zend_uint bbid_t;
  29. #define BBID_INVALID ((bbid_t) -1)
  30. /* {{{ basic block */
  31. typedef struct _bb_t {
  32. bbid_t id;
  33. zend_bool used;
  34. zend_bool alloc;
  35. zend_op *opcodes;
  36. int count;
  37. int size;
  38. bbid_t fall;
  39. #ifdef ZEND_ENGINE_2
  40. bbid_t catch;
  41. #endif
  42. zend_uint opnum; /* opnum after joining basic block */
  43. } bb_t;
  44. /* }}} */
  45. /* basic blocks */
  46. typedef xc_stack_t bbs_t;
  47. /* op array helper functions */
  48. static int op_array_convert_switch(zend_op_array *op_array) /* {{{ */
  49. {
  50. zend_uint i;
  51. if (op_array->brk_cont_array == NULL) {
  52. return SUCCESS;
  53. }
  54. for (i = 0; i < op_array->last; i ++) {
  55. zend_op *opline = &op_array->opcodes[i];
  56. zend_brk_cont_element *jmp_to;
  57. zend_bool can_convert = 1;
  58. int array_offset, nest_levels, original_nest_levels;
  59. switch (opline->opcode) {
  60. case ZEND_BRK:
  61. case ZEND_CONT:
  62. break;
  63. #ifdef ZEND_GOTO
  64. case ZEND_GOTO:
  65. #endif
  66. continue;
  67. default:
  68. continue;
  69. }
  70. if (Z_OP_TYPE(opline->op2) != IS_CONST
  71. || Z_OP_CONSTANT(opline->op2).type != IS_LONG) {
  72. return FAILURE;
  73. }
  74. nest_levels = Z_OP_CONSTANT(opline->op2).value.lval;
  75. original_nest_levels = nest_levels;
  76. array_offset = Z_OP(opline->op1).opline_num;
  77. do {
  78. if (array_offset == -1) {
  79. /* this is a runtime error in ZE
  80. zend_error(E_ERROR, "Cannot break/continue %d level%s", original_nest_levels, (original_nest_levels == 1) ? "" : "s");
  81. */
  82. return FAILURE;
  83. }
  84. jmp_to = &op_array->brk_cont_array[array_offset];
  85. if (nest_levels > 1) {
  86. zend_op *brk_opline = &op_array->opcodes[jmp_to->brk];
  87. switch (brk_opline->opcode) {
  88. case ZEND_SWITCH_FREE:
  89. case ZEND_FREE:
  90. #ifdef EXT_TYPE_FREE_ON_RETURN
  91. if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN))
  92. #endif
  93. {
  94. can_convert = 0;
  95. }
  96. break;
  97. }
  98. }
  99. array_offset = jmp_to->parent;
  100. } while (--nest_levels > 0);
  101. if (can_convert) {
  102. /* rewrite to jmp */
  103. switch (opline->opcode) {
  104. case ZEND_BRK:
  105. Z_OP(opline->op1).opline_num = jmp_to->brk;
  106. break;
  107. case ZEND_CONT:
  108. Z_OP(opline->op1).opline_num = jmp_to->cont;
  109. break;
  110. }
  111. Z_OP_TYPE(opline->op2) = IS_UNUSED;
  112. opline->opcode = ZEND_JMP;
  113. }
  114. }
  115. return SUCCESS;
  116. }
  117. /* }}} */
  118. /* {{{ op_flowinfo helper func */
  119. enum {
  120. XC_OPNUM_INVALID = -1
  121. };
  122. typedef struct {
  123. int jmpout_op1;
  124. int jmpout_op2;
  125. int jmpout_ext;
  126. zend_bool fall;
  127. } op_flowinfo_t;
  128. static void op_flowinfo_init(op_flowinfo_t *fi)
  129. {
  130. fi->jmpout_op1 = fi->jmpout_op2 = fi->jmpout_ext = XC_OPNUM_INVALID;
  131. fi->fall = 0;
  132. }
  133. /* }}} */
  134. static int op_get_flowinfo(op_flowinfo_t *fi, zend_op *opline) /* {{{ */
  135. {
  136. op_flowinfo_init(fi);
  137. /* break=will fall */
  138. switch (opline->opcode) {
  139. #ifdef ZEND_HANDLE_EXCEPTION
  140. case ZEND_HANDLE_EXCEPTION:
  141. #endif
  142. case ZEND_RETURN:
  143. case ZEND_EXIT:
  144. return SUCCESS; /* no fall */
  145. #ifdef ZEND_GOTO
  146. case ZEND_GOTO:
  147. #endif
  148. case ZEND_JMP:
  149. fi->jmpout_op1 = Z_OP(opline->op1).opline_num;
  150. return SUCCESS; /* no fall */
  151. case ZEND_JMPZNZ:
  152. fi->jmpout_op2 = Z_OP(opline->op2).opline_num;
  153. fi->jmpout_ext = (int) opline->extended_value;
  154. return SUCCESS; /* no fall */
  155. case ZEND_JMPZ:
  156. case ZEND_JMPNZ:
  157. case ZEND_JMPZ_EX:
  158. case ZEND_JMPNZ_EX:
  159. #ifdef ZEND_JMP_SET
  160. case ZEND_JMP_SET:
  161. #endif
  162. #ifdef ZEND_JMP_SET_VAR
  163. case ZEND_JMP_SET_VAR:
  164. #endif
  165. #ifdef ZEND_JMP_NO_CTOR
  166. case ZEND_JMP_NO_CTOR:
  167. #endif
  168. #ifdef ZEND_NEW
  169. case ZEND_NEW:
  170. #endif
  171. #ifdef ZEND_FE_RESET
  172. case ZEND_FE_RESET:
  173. #endif
  174. case ZEND_FE_FETCH:
  175. fi->jmpout_op2 = Z_OP(opline->op2).opline_num;
  176. break;
  177. #ifdef ZEND_CATCH
  178. case ZEND_CATCH:
  179. fi->jmpout_ext = (int) opline->extended_value;
  180. break;
  181. #endif
  182. default:
  183. return FAILURE;
  184. }
  185. fi->fall = 1;
  186. return SUCCESS;
  187. }
  188. /* }}} */
  189. #ifdef XCACHE_DEBUG
  190. static void op_snprint(char *buf, int size, zend_uchar op_type, znode_op *op) /* {{{ */
  191. {
  192. switch (op_type) {
  193. case IS_CONST:
  194. {
  195. zval result;
  196. zval *zv = &Z_OP_CONSTANT(*op);
  197. TSRMLS_FETCH();
  198. /* TODO: update for PHP 6 */
  199. php_start_ob_buffer(NULL, 0, 1 TSRMLS_CC);
  200. php_var_export(&zv, 1 TSRMLS_CC);
  201. php_ob_get_buffer(&result TSRMLS_CC);
  202. php_end_ob_buffer(0, 0 TSRMLS_CC);
  203. snprintf(buf, size, Z_STRVAL(result));
  204. zval_dtor(&result);
  205. }
  206. break;
  207. case IS_TMP_VAR:
  208. snprintf(buf, size, "t@%d", Z_OP(*op).var);
  209. break;
  210. case XCACHE_IS_CV:
  211. case IS_VAR:
  212. snprintf(buf, size, "v@%d", Z_OP(*op).var);
  213. break;
  214. case IS_UNUSED:
  215. if (Z_OP(*op).opline_num) {
  216. snprintf(buf, size, "u#%d", Z_OP(op).opline_num);
  217. }
  218. else {
  219. snprintf(buf, size, "-");
  220. }
  221. break;
  222. default:
  223. snprintf(buf, size, "%d %d", op->op_type, Z_OP(op).var);
  224. }
  225. }
  226. /* }}} */
  227. static void op_print(int line, zend_op *first, zend_op *end) /* {{{ */
  228. {
  229. zend_op *opline;
  230. for (opline = first; opline < end; opline ++) {
  231. char buf_r[20];
  232. char buf_1[20];
  233. char buf_2[20];
  234. op_snprint(buf_r, sizeof(buf_r), Z_OP_TYPE(opline->result), &opline->result);
  235. op_snprint(buf_1, sizeof(buf_1), Z_OP_TYPE(opline->op1), &opline->op1);
  236. op_snprint(buf_2, sizeof(buf_2), Z_OP_TYPE(opline->op2), &opline->op2);
  237. fprintf(stderr,
  238. "%3d %3d"
  239. " %-25s%-5s%-20s%-20s%5lu\r\n"
  240. , opline->lineno, opline - first + line
  241. , xc_get_opcode(opline->opcode), buf_r, buf_1, buf_2, opline->extended_value);
  242. }
  243. }
  244. /* }}} */
  245. #endif
  246. /*
  247. * basic block functions
  248. */
  249. static bb_t *bb_new_ex(zend_op *opcodes, int count) /* {{{ */
  250. {
  251. bb_t *bb = (bb_t *) ecalloc(sizeof(bb_t), 1);
  252. bb->fall = BBID_INVALID;
  253. #ifdef ZEND_ENGINE_2
  254. bb->catch = BBID_INVALID;
  255. #endif
  256. if (opcodes) {
  257. bb->alloc = 0;
  258. bb->size = bb->count = count;
  259. bb->opcodes = opcodes;
  260. }
  261. else {
  262. bb->alloc = 1;
  263. bb->size = bb->count = 8;
  264. bb->opcodes = ecalloc(sizeof(zend_op), bb->size);
  265. }
  266. return bb;
  267. }
  268. /* }}} */
  269. #define bb_new() bb_new_ex(NULL, 0)
  270. static void bb_destroy(bb_t *bb) /* {{{ */
  271. {
  272. if (bb->alloc) {
  273. efree(bb->opcodes);
  274. }
  275. efree(bb);
  276. }
  277. /* }}} */
  278. #ifdef XCACHE_DEBUG
  279. static void bb_print(bb_t *bb, zend_op *opcodes) /* {{{ */
  280. {
  281. int line = bb->opcodes - opcodes;
  282. op_flowinfo_t fi;
  283. zend_op *last = bb->opcodes + bb->count - 1;
  284. bbid_t catchbbid;
  285. #ifdef ZEND_ENGINE_2
  286. catchbbid = BBID_INVALID;
  287. #else
  288. catchbbid = bb->catch;
  289. #endif
  290. op_get_flowinfo(&fi, last);
  291. fprintf(stderr,
  292. "\r\n==== #%-3d cnt:%-3d lno:%-3d"
  293. " %c%c"
  294. " op1:%-3d op2:%-3d ext:%-3d fal:%-3d cat:%-3d %s ====\r\n"
  295. , bb->id, bb->count, bb->alloc ? -1 : line
  296. , bb->used ? 'U' : ' ', bb->alloc ? 'A' : ' '
  297. , fi.jmpout_op1, fi.jmpout_op2, fi.jmpout_ext, bb->fall, catchbbid, xc_get_opcode(last->opcode)
  298. );
  299. op_print(line, bb->opcodes, last + 1);
  300. }
  301. /* }}} */
  302. #endif
  303. static bb_t *bbs_get(bbs_t *bbs, int n) /* {{{ */
  304. {
  305. return (bb_t *) xc_stack_get(bbs, n);
  306. }
  307. /* }}} */
  308. static int bbs_count(bbs_t *bbs) /* {{{ */
  309. {
  310. return xc_stack_count(bbs);
  311. }
  312. /* }}} */
  313. static void bbs_destroy(bbs_t *bbs) /* {{{ */
  314. {
  315. bb_t *bb;
  316. while (bbs_count(bbs)) {
  317. bb = (bb_t *) xc_stack_pop(bbs);
  318. bb_destroy(bb);
  319. }
  320. xc_stack_destroy(bbs);
  321. }
  322. /* }}} */
  323. #ifdef XCACHE_DEBUG
  324. static void bbs_print(bbs_t *bbs, zend_op *opcodes) /* {{{ */
  325. {
  326. int i;
  327. for (i = 0; i < xc_stack_count(bbs); i ++) {
  328. bb_print(bbs_get(bbs, i), opcodes);
  329. }
  330. }
  331. /* }}} */
  332. #endif
  333. #define bbs_init(bbs) xc_stack_init_ex(bbs, 16)
  334. static bb_t *bbs_add_bb(bbs_t *bbs, bb_t *bb) /* {{{ */
  335. {
  336. bb->id = (bbid_t) xc_stack_count(bbs);
  337. xc_stack_push(bbs, (void *) bb);
  338. return bb;
  339. }
  340. /* }}} */
  341. static bb_t *bbs_new_bb_ex(bbs_t *bbs, zend_op *opcodes, int count) /* {{{ */
  342. {
  343. return bbs_add_bb(bbs, bb_new_ex(opcodes, count));
  344. }
  345. /* }}} */
  346. static int bbs_build_from(bbs_t *bbs, zend_op_array *op_array, int count) /* {{{ */
  347. {
  348. int i, start;
  349. bb_t *pbb;
  350. bbid_t id;
  351. op_flowinfo_t fi;
  352. zend_op *opline;
  353. ALLOCA_FLAG(use_heap_bbids)
  354. ALLOCA_FLAG(use_heap_catchbbids)
  355. ALLOCA_FLAG(use_heap_markbbhead)
  356. bbid_t *bbids = xc_do_alloca(count * sizeof(bbid_t), use_heap_bbids);
  357. #ifdef ZEND_ENGINE_2
  358. bbid_t *catchbbids = xc_do_alloca(count * sizeof(bbid_t), use_heap_catchbbids);
  359. #endif
  360. zend_bool *markbbhead = xc_do_alloca(count * sizeof(zend_bool), use_heap_markbbhead);
  361. /* {{{ mark jmpin/jumpout */
  362. memset(markbbhead, 0, count * sizeof(zend_bool));
  363. markbbhead[0] = 1;
  364. for (i = 0; i < count; i ++) {
  365. if (op_get_flowinfo(&fi, &op_array->opcodes[i]) == SUCCESS) {
  366. if (fi.jmpout_op1 != XC_OPNUM_INVALID) {
  367. markbbhead[fi.jmpout_op1] = 1;
  368. }
  369. if (fi.jmpout_op2 != XC_OPNUM_INVALID) {
  370. markbbhead[fi.jmpout_op2] = 1;
  371. }
  372. if (fi.jmpout_ext != XC_OPNUM_INVALID) {
  373. markbbhead[fi.jmpout_ext] = 1;
  374. }
  375. if (i + 1 < count) {
  376. markbbhead[i + 1] = 1;
  377. }
  378. }
  379. }
  380. #ifdef ZEND_ENGINE_2
  381. /* mark try start */
  382. for (i = 0; i < op_array->last_try_catch; i ++) {
  383. markbbhead[op_array->try_catch_array[i].try_op] = 1;
  384. }
  385. #endif
  386. /* }}} */
  387. /* {{{ fill op lines with newly allocated id */
  388. for (i = 0; i < count; i ++) {
  389. bbids[i] = BBID_INVALID;
  390. }
  391. id = -1;
  392. for (i = 0; i < count; i ++) {
  393. if (markbbhead[i]) {
  394. id ++;
  395. }
  396. bbids[i] = id;
  397. TRACE("bbids[%d] = %d", i, id);
  398. }
  399. /* }}} */
  400. #ifdef ZEND_ENGINE_2
  401. /* {{{ fill op lines with catch id */
  402. for (i = 0; i < count; i ++) {
  403. catchbbids[i] = BBID_INVALID;
  404. }
  405. for (i = 0; i < op_array->last_try_catch; i ++) {
  406. zend_uint j;
  407. zend_try_catch_element *e = &op_array->try_catch_array[i];
  408. for (j = e->try_op; j < e->catch_op; j ++) {
  409. catchbbids[j] = bbids[e->catch_op];
  410. }
  411. }
  412. #ifdef XCACHE_DEBUG
  413. for (i = 0; i < count; i ++) {
  414. TRACE("catchbbids[%d] = %d", i, catchbbids[i]);
  415. }
  416. #endif
  417. /* }}} */
  418. #endif
  419. /* {{{ create basic blocks */
  420. start = 0;
  421. id = 0;
  422. /* loop over to deal with the last block */
  423. for (i = 1; i <= count; i ++) {
  424. if (i < count && id == bbids[i]) {
  425. continue;
  426. }
  427. opline = op_array->opcodes + start;
  428. pbb = bbs_new_bb_ex(bbs, opline, i - start);
  429. #ifdef ZEND_ENGINE_2
  430. pbb->catch = catchbbids[start];
  431. #endif
  432. /* last */
  433. opline = pbb->opcodes + pbb->count - 1;
  434. if (op_get_flowinfo(&fi, opline) == SUCCESS) {
  435. if (fi.jmpout_op1 != XC_OPNUM_INVALID) {
  436. Z_OP(opline->op1).opline_num = bbids[fi.jmpout_op1];
  437. assert(Z_OP(opline->op1).opline_num != BBID_INVALID);
  438. }
  439. if (fi.jmpout_op2 != XC_OPNUM_INVALID) {
  440. Z_OP(opline->op2).opline_num = bbids[fi.jmpout_op2];
  441. assert(Z_OP(opline->op2).opline_num != BBID_INVALID);
  442. }
  443. if (fi.jmpout_ext != XC_OPNUM_INVALID) {
  444. opline->extended_value = bbids[fi.jmpout_ext];
  445. assert(opline->extended_value != BBID_INVALID);
  446. }
  447. if (fi.fall && i + 1 < count) {
  448. pbb->fall = bbids[i + 1];
  449. TRACE("fall %d", pbb->fall);
  450. assert(pbb->fall != BBID_INVALID);
  451. }
  452. }
  453. if (i >= count) {
  454. break;
  455. }
  456. start = i;
  457. id = bbids[i];
  458. }
  459. /* }}} */
  460. xc_free_alloca(markbbhead, use_heap_markbbhead);
  461. #ifdef ZEND_ENGINE_2
  462. xc_free_alloca(catchbbids, use_heap_catchbbids);
  463. #endif
  464. xc_free_alloca(bbids, use_heap_bbids);
  465. return SUCCESS;
  466. }
  467. /* }}} */
  468. static void bbs_restore_opnum(bbs_t *bbs, zend_op_array *op_array) /* {{{ */
  469. {
  470. int i;
  471. #ifdef ZEND_ENGINE_2
  472. bbid_t lasttrybbid;
  473. bbid_t lastcatchbbid;
  474. #endif
  475. for (i = 0; i < bbs_count(bbs); i ++) {
  476. op_flowinfo_t fi;
  477. bb_t *bb = bbs_get(bbs, i);
  478. zend_op *last = bb->opcodes + bb->count - 1;
  479. if (op_get_flowinfo(&fi, last) == SUCCESS) {
  480. if (fi.jmpout_op1 != XC_OPNUM_INVALID) {
  481. Z_OP(last->op1).opline_num = bbs_get(bbs, fi.jmpout_op1)->opnum;
  482. assert(Z_OP(last->op1).opline_num != BBID_INVALID);
  483. }
  484. if (fi.jmpout_op2 != XC_OPNUM_INVALID) {
  485. Z_OP(last->op2).opline_num = bbs_get(bbs, fi.jmpout_op2)->opnum;
  486. assert(Z_OP(last->op2).opline_num != BBID_INVALID);
  487. }
  488. if (fi.jmpout_ext != XC_OPNUM_INVALID) {
  489. last->extended_value = bbs_get(bbs, fi.jmpout_ext)->opnum;
  490. assert(last->extended_value != BBID_INVALID);
  491. }
  492. }
  493. }
  494. #ifdef ZEND_ENGINE_2
  495. lasttrybbid = BBID_INVALID;
  496. lastcatchbbid = BBID_INVALID;
  497. op_array->last_try_catch = 0;
  498. for (i = 0; i < bbs_count(bbs); i ++) {
  499. bb_t *bb = bbs_get(bbs, i);
  500. if (lastcatchbbid != bb->catch) {
  501. if (lasttrybbid != BBID_INVALID && lastcatchbbid != BBID_INVALID) {
  502. int try_catch_offset = op_array->last_try_catch ++;
  503. op_array->try_catch_array = erealloc(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch);
  504. op_array->try_catch_array[try_catch_offset].try_op = bbs_get(bbs, lasttrybbid)->opnum;
  505. op_array->try_catch_array[try_catch_offset].catch_op = bbs_get(bbs, lastcatchbbid)->opnum;
  506. }
  507. lasttrybbid = i;
  508. lastcatchbbid = bb->catch;
  509. }
  510. }
  511. /* it is impossible to have last bb catched */
  512. #endif
  513. }
  514. /* }}} */
  515. /*
  516. * optimize
  517. */
  518. static int xc_optimize_op_array(zend_op_array *op_array TSRMLS_DC) /* {{{ */
  519. {
  520. bbs_t bbs;
  521. if (op_array->type != ZEND_USER_FUNCTION) {
  522. return 0;
  523. }
  524. #ifdef XCACHE_DEBUG
  525. # if 0
  526. TRACE("optimize file: %s", op_array->filename);
  527. xc_dprint_zend_op_array(op_array, 0 TSRMLS_CC);
  528. # endif
  529. op_print(0, op_array->opcodes, op_array->opcodes + op_array->last);
  530. #endif
  531. if (op_array_convert_switch(op_array) == SUCCESS) {
  532. bbs_init(&bbs);
  533. if (bbs_build_from(&bbs, op_array, op_array->last) == SUCCESS) {
  534. int i;
  535. #ifdef XCACHE_DEBUG
  536. bbs_print(&bbs, op_array->opcodes);
  537. #endif
  538. /* TODO: calc opnum after basic block move */
  539. for (i = 0; i < bbs_count(&bbs); i ++) {
  540. bb_t *bb = bbs_get(&bbs, i);
  541. bb->opnum = bb->opcodes - op_array->opcodes;
  542. }
  543. bbs_restore_opnum(&bbs, op_array);
  544. }
  545. bbs_destroy(&bbs);
  546. }
  547. #ifdef XCACHE_DEBUG
  548. # if 0
  549. TRACE("%s", "after compiles");
  550. xc_dprint_zend_op_array(op_array, 0 TSRMLS_CC);
  551. # endif
  552. op_print(0, op_array->opcodes, op_array->opcodes + op_array->last);
  553. #endif
  554. return 0;
  555. }
  556. /* }}} */
  557. static void xc_optimizer_op_array_handler(zend_op_array *op_array) /* {{{ */
  558. {
  559. TSRMLS_FETCH();
  560. if (XG(optimizer)) {
  561. xc_optimize_op_array(op_array TSRMLS_CC);
  562. }
  563. }
  564. /* }}} */
  565. static int xc_coverager_zend_startup(zend_extension *extension) /* {{{ */
  566. {
  567. return SUCCESS;
  568. }
  569. /* }}} */
  570. static void xc_coverager_zend_shutdown(zend_extension *extension) /* {{{ */
  571. {
  572. }
  573. /* }}} */
  574. /* {{{ zend extension definition structure */
  575. static zend_extension xc_optimizer_zend_extension_entry = {
  576. XCACHE_NAME " Optimizer",
  577. XCACHE_VERSION,
  578. XCACHE_AUTHOR,
  579. XCACHE_URL,
  580. XCACHE_COPYRIGHT,
  581. xc_coverager_zend_startup,
  582. xc_coverager_zend_shutdown,
  583. NULL, /* activate_func_t */
  584. NULL, /* deactivate_func_t */
  585. NULL, /* message_handler_func_t */
  586. xc_optimizer_op_array_handler,
  587. NULL, /* statement_handler_func_t */
  588. NULL, /* fcall_begin_handler_func_t */
  589. NULL, /* fcall_end_handler_func_t */
  590. NULL, /* op_array_ctor_func_t */
  591. NULL, /* op_array_dtor_func_t */
  592. STANDARD_ZEND_EXTENSION_PROPERTIES
  593. };
  594. /* }}} */
  595. /* {{{ ini */
  596. PHP_INI_BEGIN()
  597. STD_PHP_INI_BOOLEAN("xcache.optimizer", "0", PHP_INI_ALL, OnUpdateBool, optimizer, zend_xcache_globals, xcache_globals)
  598. PHP_INI_END()
  599. /* }}} */
  600. static PHP_MINFO_FUNCTION(xcache_optimizer) /* {{{ */
  601. {
  602. php_info_print_table_start();
  603. php_info_print_table_row(2, "XCache Optimizer Module", "enabled");
  604. php_info_print_table_end();
  605. DISPLAY_INI_ENTRIES();
  606. }
  607. /* }}} */
  608. static PHP_MINIT_FUNCTION(xcache_optimizer) /* {{{ */
  609. {
  610. REGISTER_INI_ENTRIES();
  611. return xcache_zend_extension_add(&xc_optimizer_zend_extension_entry, 0);
  612. }
  613. /* }}} */
  614. static PHP_MSHUTDOWN_FUNCTION(xcache_optimizer) /* {{{ */
  615. {
  616. UNREGISTER_INI_ENTRIES();
  617. return xcache_zend_extension_remove(&xc_optimizer_zend_extension_entry);
  618. }
  619. /* }}} */
  620. static zend_module_entry xcache_optimizer_module_entry = { /* {{{ */
  621. STANDARD_MODULE_HEADER,
  622. XCACHE_NAME " Optimizer",
  623. NULL,
  624. PHP_MINIT(xcache_optimizer),
  625. PHP_MSHUTDOWN(xcache_optimizer),
  626. NULL,
  627. NULL,
  628. PHP_MINFO(xcache_optimizer),
  629. XCACHE_VERSION,
  630. #ifdef PHP_GINIT
  631. NO_MODULE_GLOBALS,
  632. #endif
  633. #ifdef ZEND_ENGINE_2
  634. NULL,
  635. #else
  636. NULL,
  637. NULL,
  638. #endif
  639. STANDARD_MODULE_PROPERTIES_EX
  640. };
  641. /* }}} */
  642. int xc_optimizer_startup_module() /* {{{ */
  643. {
  644. return zend_startup_module(&xcache_optimizer_module_entry);
  645. }
  646. /* }}} */