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.

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