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.

900 lines
25 KiB

  1. /* {{{ macros */
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <signal.h>
  6. #include "xcache.h"
  7. #ifdef HAVE_XCACHE_OPTIMIZER
  8. # include "mod_optimizer/xc_optimizer.h"
  9. #endif
  10. #ifdef HAVE_XCACHE_CACHER
  11. # include "mod_cacher/xc_cacher.h"
  12. #endif
  13. #ifdef HAVE_XCACHE_COVERAGER
  14. # include "mod_coverager/xc_coverager.h"
  15. #endif
  16. #ifdef HAVE_XCACHE_DISASSEMBLER
  17. # include "mod_disassembler/xc_disassembler.h"
  18. #endif
  19. #include "xcache_globals.h"
  20. #include "xcache/xc_extension.h"
  21. #include "xcache/xc_ini.h"
  22. #include "xcache/xc_const_string.h"
  23. #include "xcache/xc_opcode_spec.h"
  24. #include "xcache/xc_utils.h"
  25. #include "util/xc_stack.h"
  26. #include "php.h"
  27. #include "ext/standard/info.h"
  28. #include "ext/standard/php_string.h"
  29. #include "SAPI.h"
  30. /* }}} */
  31. /* {{{ globals */
  32. static char *xc_coredump_dir = NULL;
  33. #ifdef ZEND_WIN32
  34. static zend_ulong xc_coredump_type = 0;
  35. #endif
  36. static zend_bool xc_disable_on_crash = 0;
  37. static zend_compile_file_t *old_compile_file = NULL;
  38. zend_bool xc_test = 0;
  39. ZEND_DECLARE_MODULE_GLOBALS(xcache)
  40. /* }}} */
  41. static zend_op_array *xc_check_initial_compile_file(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
  42. {
  43. XG(initial_compile_file_called) = 1;
  44. return old_compile_file(h, type TSRMLS_CC);
  45. }
  46. /* }}} */
  47. /* devel helper function */
  48. #if 0
  49. static void xc_list_extensions() /* {{{ */
  50. {
  51. zend_llist_element *element;
  52. zend_extension *ext;
  53. fprintf(stderr, "extensions:\n");
  54. for (element = zend_extensions.head; element; element = element->next) {
  55. ext = (zend_extension *) element->data;
  56. fprintf(stderr, " - %s\n", ext->name);
  57. }
  58. }
  59. /* }}} */
  60. #endif
  61. /* module helper function */
  62. static int xc_init_constant(int module_number TSRMLS_DC) /* {{{ */
  63. {
  64. typedef struct {
  65. const char *prefix;
  66. zend_uchar (*getsize)();
  67. const char *(*get)(zend_uchar i);
  68. } xc_nameinfo_t;
  69. xc_nameinfo_t nameinfos[] = {
  70. { "", xc_get_op_type_count, xc_get_op_type },
  71. { "", xc_get_data_type_count, xc_get_data_type },
  72. { "", xc_get_opcode_count, xc_get_opcode },
  73. { "OPSPEC_", xc_get_op_spec_count, xc_get_op_spec },
  74. { NULL, NULL, NULL }
  75. };
  76. int undefdone = 0;
  77. xc_nameinfo_t *p;
  78. for (p = nameinfos; p->getsize; p ++) {
  79. zend_uchar i, count;
  80. char const_name[96];
  81. int const_name_len;
  82. count = p->getsize();
  83. for (i = 0; i < count; i ++) {
  84. const char *name = p->get(i);
  85. if (!name) continue;
  86. if (strcmp(name, "UNDEF") == 0) {
  87. if (undefdone) continue;
  88. undefdone = 1;
  89. }
  90. const_name_len = snprintf(const_name, sizeof(const_name), "XC_%s%s", p->prefix, name);
  91. zend_register_long_constant(const_name, const_name_len+1, i, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  92. }
  93. }
  94. zend_register_long_constant(ZEND_STRS("XC_SIZEOF_TEMP_VARIABLE"), sizeof(temp_variable), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  95. zend_register_stringl_constant(ZEND_STRS("XCACHE_VERSION"), ZEND_STRL(XCACHE_VERSION), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  96. zend_register_stringl_constant(ZEND_STRS("XCACHE_MODULES"), ZEND_STRL(XCACHE_MODULES), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  97. return 0;
  98. }
  99. /* }}} */
  100. /* {{{ PHP_GINIT_FUNCTION(xcache) */
  101. #pragma GCC push_options
  102. #pragma GCC diagnostic ignored "-Wshadow"
  103. #ifdef PHP_GINIT_FUNCTION
  104. static PHP_GINIT_FUNCTION(xcache)
  105. #else
  106. static void xc_init_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
  107. #endif
  108. {
  109. #pragma GCC pop_options
  110. memset(xcache_globals, 0, sizeof(zend_xcache_globals));
  111. #ifdef HAVE_XCACHE_CONSTANT
  112. zend_hash_init_ex(&xcache_globals->internal_constant_table, 1, NULL, (dtor_func_t) xc_zend_constant_dtor, 1, 0);
  113. #endif
  114. zend_hash_init_ex(&xcache_globals->internal_function_table, 1, NULL, NULL, 1, 0);
  115. zend_hash_init_ex(&xcache_globals->internal_class_table, 1, NULL, NULL, 1, 0);
  116. }
  117. /* }}} */
  118. /* {{{ PHP_GSHUTDOWN_FUNCTION(xcache) */
  119. static
  120. #ifdef PHP_GSHUTDOWN_FUNCTION
  121. PHP_GSHUTDOWN_FUNCTION(xcache)
  122. #else
  123. void xc_shutdown_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
  124. #endif
  125. {
  126. size_t i;
  127. if (xcache_globals->php_holds != NULL) {
  128. for (i = 0; i < xcache_globals->php_holds_size; i ++) {
  129. xc_stack_destroy(&xcache_globals->php_holds[i]);
  130. }
  131. free(xcache_globals->php_holds);
  132. xcache_globals->php_holds = NULL;
  133. xcache_globals->php_holds_size = 0;
  134. }
  135. if (xcache_globals->var_holds != NULL) {
  136. for (i = 0; i < xcache_globals->var_holds_size; i ++) {
  137. xc_stack_destroy(&xcache_globals->var_holds[i]);
  138. }
  139. free(xcache_globals->var_holds);
  140. xcache_globals->var_holds = NULL;
  141. xcache_globals->var_holds_size = 0;
  142. }
  143. if (xcache_globals->internal_table_copied) {
  144. #ifdef HAVE_XCACHE_CONSTANT
  145. zend_hash_destroy(&xcache_globals->internal_constant_table);
  146. #endif
  147. zend_hash_destroy(&xcache_globals->internal_function_table);
  148. zend_hash_destroy(&xcache_globals->internal_class_table);
  149. }
  150. }
  151. /* }}} */
  152. /* {{{ proto int xcache_get_refcount(mixed variable)
  153. XCache internal uses only: Get reference count of variable */
  154. PHP_FUNCTION(xcache_get_refcount)
  155. {
  156. zval *variable;
  157. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &variable) == FAILURE) {
  158. RETURN_NULL();
  159. }
  160. RETURN_LONG(Z_REFCOUNT(*variable));
  161. }
  162. /* }}} */
  163. /* {{{ proto bool xcache_get_isref(mixed variable)
  164. XCache internal uses only: Check if variable data is marked referenced */
  165. #ifdef ZEND_BEGIN_ARG_INFO_EX
  166. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_get_isref, 0, 0, 1)
  167. ZEND_ARG_INFO(1, variable)
  168. ZEND_END_ARG_INFO()
  169. #else
  170. static unsigned char arginfo_xcache_get_isref[] = { 1, BYREF_FORCE };
  171. #endif
  172. PHP_FUNCTION(xcache_get_isref)
  173. {
  174. zval *variable;
  175. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &variable) == FAILURE) {
  176. RETURN_NULL();
  177. }
  178. RETURN_BOOL(Z_ISREF(*variable) && Z_REFCOUNT(*variable) >= 3);
  179. }
  180. /* }}} */
  181. #ifdef HAVE_XCACHE_DPRINT
  182. /* {{{ proto bool xcache_dprint(mixed value)
  183. Prints variable (or value) internal struct (debug only) */
  184. #include "xc_processor.h"
  185. PHP_FUNCTION(xcache_dprint)
  186. {
  187. zval *value;
  188. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
  189. return;
  190. }
  191. xc_dprint_zval(value, 0 TSRMLS_CC);
  192. }
  193. /* }}} */
  194. #endif
  195. /* {{{ proto string xcache_asm(string filename)
  196. */
  197. #ifdef HAVE_XCACHE_ASSEMBLER
  198. PHP_FUNCTION(xcache_asm)
  199. {
  200. }
  201. #endif
  202. /* }}} */
  203. /* {{{ proto string xcache_encode(string filename)
  204. Encode php file into XCache opcode encoded format */
  205. #ifdef HAVE_XCACHE_ENCODER
  206. PHP_FUNCTION(xcache_encode)
  207. {
  208. }
  209. #endif
  210. /* }}} */
  211. /* {{{ proto bool xcache_decode_file(string filename)
  212. Decode(load) opcode from XCache encoded format file */
  213. #ifdef HAVE_XCACHE_DECODER
  214. PHP_FUNCTION(xcache_decode_file)
  215. {
  216. }
  217. #endif
  218. /* }}} */
  219. /* {{{ proto bool xcache_decode_string(string data)
  220. Decode(load) opcode from XCache encoded format data */
  221. #ifdef HAVE_XCACHE_DECODER
  222. PHP_FUNCTION(xcache_decode_string)
  223. {
  224. }
  225. #endif
  226. /* }}} */
  227. /* {{{ xc_call_getter */
  228. typedef const char *(xc_name_getter_t)(zend_uchar type);
  229. static void xc_call_getter(xc_name_getter_t getter, int count, INTERNAL_FUNCTION_PARAMETERS)
  230. {
  231. long spec;
  232. const char *name;
  233. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
  234. return;
  235. }
  236. if (spec >= 0 && spec < count) {
  237. name = getter((zend_uchar) spec);
  238. if (name) {
  239. /* RETURN_STRING */
  240. int len = (int) strlen(name);
  241. return_value->value.str.len = len;
  242. return_value->value.str.val = estrndup(name, len);
  243. return_value->type = IS_STRING;
  244. return;
  245. }
  246. }
  247. RETURN_NULL();
  248. }
  249. /* }}} */
  250. /* {{{ proto string xcache_get_op_type(int op_type) */
  251. PHP_FUNCTION(xcache_get_op_type)
  252. {
  253. xc_call_getter(xc_get_op_type, xc_get_op_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  254. }
  255. /* }}} */
  256. /* {{{ proto string xcache_get_data_type(int type) */
  257. PHP_FUNCTION(xcache_get_data_type)
  258. {
  259. xc_call_getter(xc_get_data_type, xc_get_data_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  260. }
  261. /* }}} */
  262. /* {{{ proto string xcache_get_opcode(int opcode) */
  263. PHP_FUNCTION(xcache_get_opcode)
  264. {
  265. xc_call_getter(xc_get_opcode, xc_get_opcode_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  266. }
  267. /* }}} */
  268. /* {{{ proto string xcache_get_op_spec(int op_type) */
  269. PHP_FUNCTION(xcache_get_op_spec)
  270. {
  271. xc_call_getter(xc_get_op_spec, xc_get_op_spec_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  272. }
  273. /* }}} */
  274. /* {{{ proto string xcache_get_opcode_spec(int opcode) */
  275. PHP_FUNCTION(xcache_get_opcode_spec)
  276. {
  277. long spec;
  278. const xc_opcode_spec_t *opspec;
  279. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
  280. return;
  281. }
  282. if ((zend_uchar) spec <= xc_get_opcode_spec_count()) {
  283. opspec = xc_get_opcode_spec((zend_uchar) spec);
  284. if (opspec) {
  285. array_init(return_value);
  286. add_assoc_long_ex(return_value, ZEND_STRS("ext"), opspec->ext);
  287. add_assoc_long_ex(return_value, ZEND_STRS("op1"), opspec->op1);
  288. add_assoc_long_ex(return_value, ZEND_STRS("op2"), opspec->op2);
  289. add_assoc_long_ex(return_value, ZEND_STRS("res"), opspec->res);
  290. return;
  291. }
  292. }
  293. RETURN_NULL();
  294. }
  295. /* }}} */
  296. /* {{{ proto mixed xcache_get_special_value(zval value)
  297. XCache internal use only: For decompiler to get static value with type fixed */
  298. PHP_FUNCTION(xcache_get_special_value)
  299. {
  300. zval *value;
  301. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
  302. return;
  303. }
  304. switch ((Z_TYPE_P(value) & IS_CONSTANT_TYPE_MASK)) {
  305. case IS_CONSTANT:
  306. *return_value = *value;
  307. zval_copy_ctor(return_value);
  308. return_value->type = UNISW(IS_STRING, UG(unicode) ? IS_UNICODE : IS_STRING);
  309. break;
  310. case IS_CONSTANT_ARRAY:
  311. *return_value = *value;
  312. zval_copy_ctor(return_value);
  313. return_value->type = IS_ARRAY;
  314. break;
  315. default:
  316. RETURN_NULL();
  317. }
  318. }
  319. /* }}} */
  320. /* {{{ proto int xcache_get_type(zval value)
  321. XCache internal use only for disassembler to get variable type in engine level */
  322. PHP_FUNCTION(xcache_get_type)
  323. {
  324. zval *value;
  325. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
  326. return;
  327. }
  328. RETURN_LONG(Z_TYPE_P(value));
  329. }
  330. /* }}} */
  331. /* {{{ proto string xcache_coredump(int op_type) */
  332. PHP_FUNCTION(xcache_coredump)
  333. {
  334. if (xc_test) {
  335. char *null_ptr = NULL;
  336. *null_ptr = 0;
  337. raise(SIGSEGV);
  338. }
  339. else {
  340. php_error_docref(NULL TSRMLS_CC, E_WARNING, "xcache.test must be enabled to test xcache_coredump()");
  341. }
  342. }
  343. /* }}} */
  344. /* {{{ proto string xcache_is_autoglobal(string name) */
  345. PHP_FUNCTION(xcache_is_autoglobal)
  346. {
  347. zval *name;
  348. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
  349. return;
  350. }
  351. #ifdef IS_UNICODE
  352. convert_to_unicode(name);
  353. #else
  354. convert_to_string(name);
  355. #endif
  356. RETURN_BOOL(zend_u_hash_exists(CG(auto_globals), UG(unicode), Z_STRVAL_P(name), Z_STRLEN_P(name) + 1));
  357. }
  358. /* }}} */
  359. static zend_function_entry xcache_functions[] = /* {{{ */
  360. {
  361. PHP_FE(xcache_coredump, NULL)
  362. #ifdef HAVE_XCACHE_ASSEMBLER
  363. PHP_FE(xcache_asm, NULL)
  364. #endif
  365. #ifdef HAVE_XCACHE_ENCODER
  366. PHP_FE(xcache_encode, NULL)
  367. #endif
  368. #ifdef HAVE_XCACHE_DECODER
  369. PHP_FE(xcache_decode_file, NULL)
  370. PHP_FE(xcache_decode_string, NULL)
  371. #endif
  372. PHP_FE(xcache_get_special_value, NULL)
  373. PHP_FE(xcache_get_type, NULL)
  374. PHP_FE(xcache_get_op_type, NULL)
  375. PHP_FE(xcache_get_data_type, NULL)
  376. PHP_FE(xcache_get_opcode, NULL)
  377. PHP_FE(xcache_get_opcode_spec, NULL)
  378. PHP_FE(xcache_is_autoglobal, NULL)
  379. PHP_FE(xcache_get_refcount, NULL)
  380. PHP_FE(xcache_get_isref, arginfo_xcache_get_isref)
  381. #ifdef HAVE_XCACHE_DPRINT
  382. PHP_FE(xcache_dprint, NULL)
  383. #endif
  384. PHP_FE_END
  385. };
  386. /* }}} */
  387. #ifdef ZEND_WIN32
  388. #include "dbghelp.h"
  389. typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
  390. CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
  391. CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
  392. CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
  393. );
  394. static PTOP_LEVEL_EXCEPTION_FILTER oldFilter = NULL;
  395. static HMODULE dbghelpModule = NULL;
  396. static char crash_dumpPath[_MAX_PATH] = { 0 };
  397. static MINIDUMPWRITEDUMP dbghelp_MiniDumpWriteDump = NULL;
  398. static LONG WINAPI miniDumperFilter(struct _EXCEPTION_POINTERS *pExceptionInfo) /* {{{ */
  399. {
  400. HANDLE fileHandle;
  401. LONG ret = EXCEPTION_CONTINUE_SEARCH;
  402. SetUnhandledExceptionFilter(oldFilter);
  403. /* create the file */
  404. fileHandle = CreateFile(crash_dumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  405. if (fileHandle != INVALID_HANDLE_VALUE) {
  406. MINIDUMP_EXCEPTION_INFORMATION exceptionInformation;
  407. MINIDUMP_TYPE type = xc_coredump_type ? xc_coredump_type : (MiniDumpNormal|MiniDumpWithDataSegs|MiniDumpWithIndirectlyReferencedMemory);
  408. BOOL ok;
  409. exceptionInformation.ThreadId = GetCurrentThreadId();
  410. exceptionInformation.ExceptionPointers = pExceptionInfo;
  411. exceptionInformation.ClientPointers = FALSE;
  412. /* write the dump */
  413. ok = dbghelp_MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), fileHandle, type, &exceptionInformation, NULL, NULL);
  414. CloseHandle(fileHandle);
  415. if (ok) {
  416. zend_error(E_ERROR, "Saved dump file to '%s'", crash_dumpPath);
  417. ret = EXCEPTION_EXECUTE_HANDLER;
  418. }
  419. else {
  420. zend_error(E_ERROR, "Failed to save dump file to '%s' (error %d)", crash_dumpPath, GetLastError());
  421. }
  422. }
  423. else {
  424. zend_error(E_ERROR, "Failed to create dump file '%s' (error %d)", crash_dumpPath, GetLastError());
  425. }
  426. if (xc_disable_on_crash) {
  427. xc_disable_on_crash = 0;
  428. xc_cacher_disable();
  429. }
  430. return ret;
  431. }
  432. /* }}} */
  433. static void xcache_restore_crash_handler() /* {{{ */
  434. {
  435. if (oldFilter) {
  436. SetUnhandledExceptionFilter(oldFilter);
  437. oldFilter = NULL;
  438. }
  439. }
  440. /* }}} */
  441. static void xcache_init_crash_handler() /* {{{ */
  442. {
  443. /* firstly see if dbghelp.dll is around and has the function we need
  444. look next to the EXE first, as the one in System32 might be old
  445. (e.g. Windows 2000) */
  446. char dbghelpPath[_MAX_PATH];
  447. if (GetModuleFileName(NULL, dbghelpPath, _MAX_PATH)) {
  448. char *slash = strchr(dbghelpPath, '\\');
  449. if (slash) {
  450. strcpy(slash + 1, "DBGHELP.DLL");
  451. dbghelpModule = LoadLibrary(dbghelpPath);
  452. }
  453. }
  454. if (!dbghelpModule) {
  455. /* load any version we can */
  456. dbghelpModule = LoadLibrary("DBGHELP.DLL");
  457. if (!dbghelpModule) {
  458. zend_error(E_ERROR, "Unable to enable crash dump saver: %s", "DBGHELP.DLL not found");
  459. return;
  460. }
  461. }
  462. dbghelp_MiniDumpWriteDump = (MINIDUMPWRITEDUMP)GetProcAddress(dbghelpModule, "MiniDumpWriteDump");
  463. if (!dbghelp_MiniDumpWriteDump) {
  464. zend_error(E_ERROR, "Unable to enable crash dump saver: %s", "DBGHELP.DLL too old. Get updated dll and put it aside of php_xcache.dll");
  465. return;
  466. }
  467. #ifdef XCACHE_VERSION_REVISION
  468. #define REVISION "r" XCACHE_VERSION_REVISION
  469. #else
  470. #define REVISION ""
  471. #endif
  472. sprintf(crash_dumpPath, "%s\\php-%s-xcache-%s%s-%lu-%lu.dmp", xc_coredump_dir, zend_get_module_version("standard"), XCACHE_VERSION, REVISION, (unsigned long) time(NULL), (unsigned long) GetCurrentProcessId());
  473. #undef REVISION
  474. oldFilter = SetUnhandledExceptionFilter(&miniDumperFilter);
  475. }
  476. /* }}} */
  477. #else
  478. /* old signal handlers {{{ */
  479. typedef void (*xc_sighandler_t)(int);
  480. #define FOREACH_SIG(sig) static xc_sighandler_t old_##sig##_handler = NULL
  481. #include "util/xc_foreachcoresig.h"
  482. #undef FOREACH_SIG
  483. /* }}} */
  484. static void xcache_signal_handler(int sig);
  485. static void xcache_restore_crash_handler() /* {{{ */
  486. {
  487. #define FOREACH_SIG(sig) do { \
  488. if (old_##sig##_handler != xcache_signal_handler) { \
  489. signal(sig, old_##sig##_handler); \
  490. } \
  491. else { \
  492. signal(sig, SIG_DFL); \
  493. } \
  494. } while (0)
  495. #include "util/xc_foreachcoresig.h"
  496. #undef FOREACH_SIG
  497. }
  498. /* }}} */
  499. static void xcache_init_crash_handler() /* {{{ */
  500. {
  501. #define FOREACH_SIG(sig) \
  502. old_##sig##_handler = signal(sig, xcache_signal_handler)
  503. #include "util/xc_foreachcoresig.h"
  504. #undef FOREACH_SIG
  505. }
  506. /* }}} */
  507. static void xcache_signal_handler(int sig) /* {{{ */
  508. {
  509. xcache_restore_crash_handler();
  510. if (xc_coredump_dir && xc_coredump_dir[0]) {
  511. if (chdir(xc_coredump_dir) != 0) {
  512. /* error, but nothing can do about it
  513. * and should'nt print anything which might SEGV again */
  514. }
  515. }
  516. if (xc_disable_on_crash) {
  517. xc_disable_on_crash = 0;
  518. xc_cacher_disable();
  519. }
  520. raise(sig);
  521. }
  522. /* }}} */
  523. #endif
  524. /* {{{ incompatible zend extensions handling */
  525. typedef struct {
  526. const char *name;
  527. startup_func_t old_startup;
  528. } xc_incompatible_zend_extension_info_t;
  529. static xc_incompatible_zend_extension_info_t xc_incompatible_zend_extensions[] = {
  530. { "Zend Extension Manager", NULL },
  531. { "Zend Optimizer", NULL },
  532. { "the ionCube PHP Loader", NULL }
  533. };
  534. static xc_incompatible_zend_extension_info_t *xc_get_incompatible_zend_extension_info(const char *name)
  535. {
  536. size_t i;
  537. for (i = 0; i < sizeof(xc_incompatible_zend_extensions) / sizeof(xc_incompatible_zend_extensions[0]); ++i) {
  538. xc_incompatible_zend_extension_info_t *incompatible_zend_extension_info = &xc_incompatible_zend_extensions[i];
  539. if (strcmp(incompatible_zend_extension_info->name, name) == 0) {
  540. return incompatible_zend_extension_info;
  541. }
  542. }
  543. return NULL;
  544. }
  545. /* }}} */
  546. static void xc_zend_llist_add_element(zend_llist *list, zend_llist_element *element) /* {{{ */
  547. {
  548. if (!zend_extensions.head) {
  549. zend_extensions.head = zend_extensions.tail = element;
  550. }
  551. else {
  552. zend_extensions.tail->next = element;
  553. element->prev = zend_extensions.tail;
  554. zend_extensions.tail = element;
  555. }
  556. }
  557. /* }}} */
  558. static int xc_incompatible_zend_extension_startup_hook(zend_extension *extension) /* {{{ */
  559. {
  560. xc_incompatible_zend_extension_info_t *incompatible_zend_extension_info = xc_get_incompatible_zend_extension_info(extension->name);
  561. int status;
  562. zend_bool catched = 0;
  563. zend_llist saved_zend_extensions_container; /* without elements */
  564. zend_llist_element **saved_zend_extensions_elments;
  565. size_t new_zend_extensions_elments_count;
  566. zend_llist_element **new_zend_extensions_elments;
  567. zend_extension *ext;
  568. size_t i;
  569. zend_llist_element *element;
  570. TSRMLS_FETCH();
  571. /* restore startup hack */
  572. extension->startup = incompatible_zend_extension_info->old_startup;
  573. incompatible_zend_extension_info->old_startup = NULL;
  574. assert(extension->startup);
  575. /* save extensions list */
  576. saved_zend_extensions_container = zend_extensions;
  577. saved_zend_extensions_elments = malloc(sizeof(zend_llist_element *) * saved_zend_extensions_container.count);
  578. for (i = 0, element = saved_zend_extensions_container.head; element; ++i, element = element->next) {
  579. saved_zend_extensions_elments[i] = element;
  580. }
  581. /* hide all XCache extensions from it */
  582. zend_extensions.head = NULL;
  583. zend_extensions.tail = NULL;
  584. zend_extensions.count = 0;
  585. for (i = 0; i < saved_zend_extensions_container.count; ++i) {
  586. element = saved_zend_extensions_elments[i];
  587. element->next = element->prev = NULL;
  588. ext = (zend_extension *) element->data;
  589. if (!(strcmp(ext->name, XCACHE_NAME) == 0 || strncmp(ext->name, XCACHE_NAME " ", sizeof(XCACHE_NAME " ") - 1) == 0)) {
  590. xc_zend_llist_add_element(&zend_extensions, element);
  591. ++zend_extensions.count;
  592. }
  593. }
  594. assert(extension->startup != xc_incompatible_zend_extension_startup_hook);
  595. zend_try {
  596. status = extension->startup(extension);
  597. } zend_catch {
  598. catched = 1;
  599. } zend_end_try();
  600. /* save newly added extensions added by this extension*/
  601. new_zend_extensions_elments_count = zend_extensions.count - 1;
  602. new_zend_extensions_elments = NULL;
  603. if (new_zend_extensions_elments_count) {
  604. new_zend_extensions_elments = malloc(sizeof(zend_llist_element *) * new_zend_extensions_elments_count);
  605. element = zend_extensions.head;
  606. for (i = 0, element = element->next; element; ++i, element = element->next) {
  607. new_zend_extensions_elments[i] = element;
  608. }
  609. }
  610. /* restore original extension list*/
  611. zend_extensions = saved_zend_extensions_container;
  612. zend_extensions.head = NULL;
  613. zend_extensions.tail = NULL;
  614. zend_extensions.count = 0;
  615. for (i = 0; i < saved_zend_extensions_container.count; ++i) {
  616. element = saved_zend_extensions_elments[i];
  617. element->next = element->prev = NULL;
  618. xc_zend_llist_add_element(&zend_extensions, element);
  619. ++zend_extensions.count;
  620. ext = (zend_extension *) element->data;
  621. if (ext == extension && new_zend_extensions_elments_count) {
  622. /* add new created extension */
  623. size_t j;
  624. for (j = 0; j < new_zend_extensions_elments_count; ++j) {
  625. element = new_zend_extensions_elments[j];
  626. element->next = element->prev = NULL;
  627. xc_zend_llist_add_element(&zend_extensions, element);
  628. ++zend_extensions.count;
  629. }
  630. }
  631. }
  632. free(saved_zend_extensions_elments);
  633. if (new_zend_extensions_elments) {
  634. free(new_zend_extensions_elments);
  635. }
  636. if (catched) {
  637. zend_bailout();
  638. }
  639. return status;
  640. }
  641. /* }}} */
  642. static int xc_zend_startup(zend_extension *extension) /* {{{ */
  643. {
  644. zend_llist_position lpos;
  645. zend_extension *ext;
  646. ext = (zend_extension *) zend_extensions.head->data;
  647. if (strcmp(ext->name, XCACHE_NAME) != 0) {
  648. zend_error(E_WARNING, "XCache failed to load itself as the before \"%s\". compatibility downgraded", ext->name);
  649. }
  650. old_compile_file = zend_compile_file;
  651. zend_compile_file = xc_check_initial_compile_file;
  652. for (ext = (zend_extension *) zend_llist_get_first_ex(&zend_extensions, &lpos);
  653. ext;
  654. ext = (zend_extension *) zend_llist_get_next_ex(&zend_extensions, &lpos)) {
  655. xc_incompatible_zend_extension_info_t *incompatible_zend_extension_info = xc_get_incompatible_zend_extension_info(ext->name);
  656. if (incompatible_zend_extension_info) {
  657. assert(!incompatible_zend_extension_info->old_startup);
  658. incompatible_zend_extension_info->old_startup = ext->startup;
  659. ext->startup = xc_incompatible_zend_extension_startup_hook;
  660. }
  661. }
  662. return SUCCESS;
  663. }
  664. /* }}} */
  665. static void xc_zend_shutdown(zend_extension *extension) /* {{{ */
  666. {
  667. }
  668. /* }}} */
  669. /* {{{ zend extension definition structure */
  670. static zend_extension xc_zend_extension_entry = {
  671. XCACHE_NAME,
  672. XCACHE_VERSION,
  673. XCACHE_AUTHOR,
  674. XCACHE_URL,
  675. XCACHE_COPYRIGHT,
  676. xc_zend_startup,
  677. xc_zend_shutdown,
  678. NULL, /* activate_func_t */
  679. NULL, /* deactivate_func_t */
  680. NULL, /* message_handler_func_t */
  681. NULL, /* op_array_handler_func_t */
  682. NULL, /* statement_handler_func_t */
  683. NULL, /* fcall_begin_handler_func_t */
  684. NULL, /* fcall_end_handler_func_t */
  685. NULL, /* op_array_ctor_func_t */
  686. NULL, /* op_array_dtor_func_t */
  687. STANDARD_ZEND_EXTENSION_PROPERTIES
  688. };
  689. /* }}} */
  690. /* {{{ PHP_INI */
  691. PHP_INI_BEGIN()
  692. PHP_INI_ENTRY1 ("xcache.coredump_directory", "", PHP_INI_SYSTEM, xcache_OnUpdateString, &xc_coredump_dir)
  693. #ifdef ZEND_WIN32
  694. PHP_INI_ENTRY1 ("xcache.coredump_type", "0", PHP_INI_SYSTEM, xcache_OnUpdateULong, &xc_coredump_type)
  695. #endif
  696. PHP_INI_ENTRY1_EX ("xcache.disable_on_crash", "0", PHP_INI_SYSTEM, xcache_OnUpdateBool, &xc_disable_on_crash, zend_ini_boolean_displayer_cb)
  697. PHP_INI_ENTRY1_EX ("xcache.test", "0", PHP_INI_SYSTEM, xcache_OnUpdateBool, &xc_test, zend_ini_boolean_displayer_cb)
  698. STD_PHP_INI_BOOLEAN("xcache.experimental", "0", PHP_INI_ALL, OnUpdateBool, experimental, zend_xcache_globals, xcache_globals)
  699. PHP_INI_END()
  700. /* }}} */
  701. static PHP_MINFO_FUNCTION(xcache) /* {{{ */
  702. {
  703. php_info_print_table_start();
  704. php_info_print_table_row(2, "XCache Version", XCACHE_VERSION);
  705. #ifdef XCACHE_VERSION_REVISION
  706. php_info_print_table_row(2, "Revision", "r" XCACHE_VERSION_REVISION);
  707. #endif
  708. php_info_print_table_row(2, "Modules Built", XCACHE_MODULES);
  709. php_info_print_table_end();
  710. DISPLAY_INI_ENTRIES();
  711. }
  712. /* }}} */
  713. static PHP_MINIT_FUNCTION(xcache) /* {{{ */
  714. {
  715. #ifndef PHP_GINIT
  716. ZEND_INIT_MODULE_GLOBALS(xcache, xc_init_globals, xc_shutdown_globals);
  717. #endif
  718. REGISTER_INI_ENTRIES();
  719. if (xc_coredump_dir && xc_coredump_dir[0]) {
  720. xcache_init_crash_handler();
  721. }
  722. if (strcmp(sapi_module.name, "cli") == 0) {
  723. char *env;
  724. if ((env = getenv("XCACHE_TEST")) != NULL) {
  725. xc_test = (zend_bool) atoi(env);
  726. }
  727. }
  728. xc_init_constant(module_number TSRMLS_CC);
  729. xc_shm_init_modules();
  730. /* must be the first */
  731. xcache_zend_extension_add(&xc_zend_extension_entry, 1);
  732. #ifdef HAVE_XCACHE_OPTIMIZER
  733. xc_optimizer_startup_module();
  734. #endif
  735. #ifdef HAVE_XCACHE_CACHER
  736. xc_cacher_startup_module();
  737. #endif
  738. #ifdef HAVE_XCACHE_COVERAGER
  739. xc_coverager_startup_module();
  740. #endif
  741. #ifdef HAVE_XCACHE_DISASSEMBLER
  742. xc_disassembler_startup_module();
  743. #endif
  744. return SUCCESS;
  745. }
  746. /* }}} */
  747. static PHP_MSHUTDOWN_FUNCTION(xcache) /* {{{ */
  748. {
  749. if (old_compile_file && zend_compile_file == xc_check_initial_compile_file) {
  750. zend_compile_file = old_compile_file;
  751. old_compile_file = NULL;
  752. }
  753. if (xc_coredump_dir && xc_coredump_dir[0]) {
  754. xcache_restore_crash_handler();
  755. }
  756. if (xc_coredump_dir) {
  757. pefree(xc_coredump_dir, 1);
  758. xc_coredump_dir = NULL;
  759. }
  760. #ifndef PHP_GINIT
  761. # ifdef ZTS
  762. ts_free_id(xcache_globals_id);
  763. # else
  764. xc_shutdown_globals(&xcache_globals TSRMLS_CC);
  765. # endif
  766. #endif
  767. UNREGISTER_INI_ENTRIES();
  768. xcache_zend_extension_remove(&xc_zend_extension_entry);
  769. return SUCCESS;
  770. }
  771. /* }}} */
  772. /* {{{ module dependencies */
  773. #ifdef STANDARD_MODULE_HEADER_EX
  774. static zend_module_dep xcache_module_deps[] = {
  775. ZEND_MOD_REQUIRED("standard")
  776. ZEND_MOD_CONFLICTS("apc")
  777. ZEND_MOD_CONFLICTS("eAccelerator")
  778. ZEND_MOD_CONFLICTS("Turck MMCache")
  779. ZEND_MOD_END
  780. };
  781. #endif
  782. /* }}} */
  783. /* {{{ module definition structure */
  784. zend_module_entry xcache_module_entry = {
  785. #ifdef STANDARD_MODULE_HEADER_EX
  786. STANDARD_MODULE_HEADER_EX,
  787. NULL,
  788. xcache_module_deps,
  789. #else
  790. STANDARD_MODULE_HEADER,
  791. #endif
  792. XCACHE_NAME,
  793. xcache_functions,
  794. PHP_MINIT(xcache),
  795. PHP_MSHUTDOWN(xcache),
  796. NULL, /* RINIT */
  797. NULL, /* RSHUTDOWN */
  798. PHP_MINFO(xcache),
  799. XCACHE_VERSION,
  800. #ifdef PHP_GINIT
  801. PHP_MODULE_GLOBALS(xcache),
  802. PHP_GINIT(xcache),
  803. PHP_GSHUTDOWN(xcache),
  804. #endif
  805. #ifdef ZEND_ENGINE_2
  806. NULL /* ZEND_MODULE_POST_ZEND_DEACTIVATE_N */,
  807. #else
  808. NULL,
  809. NULL,
  810. #endif
  811. STANDARD_MODULE_PROPERTIES_EX
  812. };
  813. #ifdef COMPILE_DL_XCACHE
  814. ZEND_GET_MODULE(xcache)
  815. #endif
  816. /* }}} */