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.
778 lines
21 KiB
778 lines
21 KiB
/* {{{ macros */ |
|
#include <stdlib.h> |
|
#include <stdio.h> |
|
#include <string.h> |
|
|
|
#include <signal.h> |
|
|
|
#include "xcache.h" |
|
|
|
#ifdef HAVE_XCACHE_OPTIMIZER |
|
# include "mod_optimizer/xc_optimizer.h" |
|
#endif |
|
#ifdef HAVE_XCACHE_CACHER |
|
# include "mod_cacher/xc_cacher.h" |
|
#endif |
|
#ifdef HAVE_XCACHE_COVERAGER |
|
# include "mod_coverager/xc_coverager.h" |
|
#endif |
|
#ifdef HAVE_XCACHE_DISASSEMBLER |
|
# include "mod_disassembler/xc_disassembler.h" |
|
#endif |
|
|
|
#include "xcache_globals.h" |
|
#include "xcache/xc_extension.h" |
|
#include "xcache/xc_ini.h" |
|
#include "xcache/xc_const_string.h" |
|
#include "xcache/xc_opcode_spec.h" |
|
#include "xcache/xc_utils.h" |
|
#include "util/xc_stack.h" |
|
|
|
#include "php.h" |
|
#include "ext/standard/info.h" |
|
#include "ext/standard/php_string.h" |
|
/* }}} */ |
|
|
|
/* {{{ globals */ |
|
static char *xc_coredump_dir = NULL; |
|
static zend_bool xc_disable_on_crash = 0; |
|
|
|
static zend_compile_file_t *old_compile_file = NULL; |
|
|
|
zend_bool xc_test = 0; |
|
|
|
ZEND_DECLARE_MODULE_GLOBALS(xcache) |
|
|
|
/* }}} */ |
|
|
|
static zend_op_array *xc_check_initial_compile_file(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */ |
|
{ |
|
XG(initial_compile_file_called) = 1; |
|
return old_compile_file(h, type TSRMLS_CC); |
|
} |
|
/* }}} */ |
|
|
|
/* module helper function */ |
|
static int xc_init_constant(int module_number TSRMLS_DC) /* {{{ */ |
|
{ |
|
typedef struct { |
|
const char *prefix; |
|
zend_uchar (*getsize)(); |
|
const char *(*get)(zend_uchar i); |
|
} xc_meminfo_t; |
|
xc_meminfo_t nameinfos[] = { |
|
{ "", xc_get_op_type_count, xc_get_op_type }, |
|
{ "", xc_get_data_type_count, xc_get_data_type }, |
|
{ "", xc_get_opcode_count, xc_get_opcode }, |
|
{ "OPSPEC_", xc_get_op_spec_count, xc_get_op_spec }, |
|
{ NULL, NULL, NULL } |
|
}; |
|
xc_meminfo_t* p; |
|
zend_uchar i, count; |
|
char const_name[96]; |
|
int const_name_len; |
|
int undefdone = 0; |
|
|
|
for (p = nameinfos; p->getsize; p ++) { |
|
count = p->getsize(); |
|
for (i = 0; i < count; i ++) { |
|
const char *name = p->get(i); |
|
if (!name) continue; |
|
if (strcmp(name, "UNDEF") == 0) { |
|
if (undefdone) continue; |
|
undefdone = 1; |
|
} |
|
const_name_len = snprintf(const_name, sizeof(const_name), "XC_%s%s", p->prefix, name); |
|
zend_register_long_constant(const_name, const_name_len+1, i, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC); |
|
} |
|
} |
|
|
|
zend_register_long_constant(ZEND_STRS("XC_SIZEOF_TEMP_VARIABLE"), sizeof(temp_variable), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC); |
|
zend_register_stringl_constant(ZEND_STRS("XCACHE_VERSION"), ZEND_STRL(XCACHE_VERSION), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC); |
|
zend_register_stringl_constant(ZEND_STRS("XCACHE_MODULES"), ZEND_STRL(XCACHE_MODULES), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC); |
|
return 0; |
|
} |
|
/* }}} */ |
|
/* {{{ PHP_GINIT_FUNCTION(xcache) */ |
|
static |
|
#ifdef PHP_GINIT_FUNCTION |
|
PHP_GINIT_FUNCTION(xcache) |
|
#else |
|
void xc_init_globals(zend_xcache_globals* xcache_globals TSRMLS_DC) |
|
#endif |
|
{ |
|
memset(xcache_globals, 0, sizeof(zend_xcache_globals)); |
|
|
|
#ifdef HAVE_XCACHE_CONSTANT |
|
zend_hash_init_ex(&xcache_globals->internal_constant_table, 1, NULL, (dtor_func_t) xc_zend_constant_dtor, 1, 0); |
|
#endif |
|
zend_hash_init_ex(&xcache_globals->internal_function_table, 1, NULL, NULL, 1, 0); |
|
zend_hash_init_ex(&xcache_globals->internal_class_table, 1, NULL, NULL, 1, 0); |
|
} |
|
/* }}} */ |
|
/* {{{ PHP_GSHUTDOWN_FUNCTION(xcache) */ |
|
static |
|
#ifdef PHP_GSHUTDOWN_FUNCTION |
|
PHP_GSHUTDOWN_FUNCTION(xcache) |
|
#else |
|
void xc_shutdown_globals(zend_xcache_globals* xcache_globals TSRMLS_DC) |
|
#endif |
|
{ |
|
size_t i; |
|
|
|
if (xcache_globals->php_holds != NULL) { |
|
for (i = 0; i < xcache_globals->php_holds_size; i ++) { |
|
xc_stack_destroy(&xcache_globals->php_holds[i]); |
|
} |
|
free(xcache_globals->php_holds); |
|
xcache_globals->php_holds = NULL; |
|
xcache_globals->php_holds_size = 0; |
|
} |
|
|
|
if (xcache_globals->var_holds != NULL) { |
|
for (i = 0; i < xcache_globals->var_holds_size; i ++) { |
|
xc_stack_destroy(&xcache_globals->var_holds[i]); |
|
} |
|
free(xcache_globals->var_holds); |
|
xcache_globals->var_holds = NULL; |
|
xcache_globals->var_holds_size = 0; |
|
} |
|
|
|
if (xcache_globals->internal_table_copied) { |
|
#ifdef HAVE_XCACHE_CONSTANT |
|
zend_hash_destroy(&xcache_globals->internal_constant_table); |
|
#endif |
|
zend_hash_destroy(&xcache_globals->internal_function_table); |
|
zend_hash_destroy(&xcache_globals->internal_class_table); |
|
} |
|
} |
|
/* }}} */ |
|
|
|
/* {{{ proto int xcache_get_refcount(mixed variable) |
|
XCache internal uses only: Get reference count of variable */ |
|
PHP_FUNCTION(xcache_get_refcount) |
|
{ |
|
zval *variable; |
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &variable) == FAILURE) { |
|
RETURN_NULL(); |
|
} |
|
|
|
RETURN_LONG(Z_REFCOUNT(*variable)); |
|
} |
|
/* }}} */ |
|
/* {{{ proto bool xcache_get_isref(mixed variable) |
|
XCache internal uses only: Check if variable data is marked referenced */ |
|
#ifdef ZEND_BEGIN_ARG_INFO_EX |
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_get_isref, 0, 0, 1) |
|
ZEND_ARG_INFO(1, variable) |
|
ZEND_END_ARG_INFO() |
|
#else |
|
static unsigned char arginfo_xcache_get_isref[] = { 1, BYREF_FORCE }; |
|
#endif |
|
|
|
PHP_FUNCTION(xcache_get_isref) |
|
{ |
|
zval *variable; |
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &variable) == FAILURE) { |
|
RETURN_NULL(); |
|
} |
|
|
|
RETURN_BOOL(Z_ISREF(*variable) && Z_REFCOUNT(*variable) >= 3); |
|
} |
|
/* }}} */ |
|
#ifdef HAVE_XCACHE_DPRINT |
|
/* {{{ proto bool xcache_dprint(mixed value) |
|
Prints variable (or value) internal struct (debug only) */ |
|
PHP_FUNCTION(xcache_dprint) |
|
{ |
|
zval *value; |
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) { |
|
return; |
|
} |
|
xc_dprint_zval(value, 0 TSRMLS_CC); |
|
} |
|
/* }}} */ |
|
#endif |
|
/* {{{ proto string xcache_asm(string filename) |
|
*/ |
|
#ifdef HAVE_XCACHE_ASSEMBLER |
|
PHP_FUNCTION(xcache_asm) |
|
{ |
|
} |
|
#endif |
|
/* }}} */ |
|
/* {{{ proto string xcache_encode(string filename) |
|
Encode php file into XCache opcode encoded format */ |
|
#ifdef HAVE_XCACHE_ENCODER |
|
PHP_FUNCTION(xcache_encode) |
|
{ |
|
} |
|
#endif |
|
/* }}} */ |
|
/* {{{ proto bool xcache_decode_file(string filename) |
|
Decode(load) opcode from XCache encoded format file */ |
|
#ifdef HAVE_XCACHE_DECODER |
|
PHP_FUNCTION(xcache_decode_file) |
|
{ |
|
} |
|
#endif |
|
/* }}} */ |
|
/* {{{ proto bool xcache_decode_string(string data) |
|
Decode(load) opcode from XCache encoded format data */ |
|
#ifdef HAVE_XCACHE_DECODER |
|
PHP_FUNCTION(xcache_decode_string) |
|
{ |
|
} |
|
#endif |
|
/* }}} */ |
|
/* {{{ xc_call_getter */ |
|
typedef const char *(xc_name_getter_t)(zend_uchar type); |
|
static void xc_call_getter(xc_name_getter_t getter, int count, INTERNAL_FUNCTION_PARAMETERS) |
|
{ |
|
long spec; |
|
const char *name; |
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) { |
|
return; |
|
} |
|
if (spec >= 0 && spec < count) { |
|
name = getter((zend_uchar) spec); |
|
if (name) { |
|
/* RETURN_STRING */ |
|
int len = strlen(name); |
|
return_value->value.str.len = len; |
|
return_value->value.str.val = estrndup(name, len); |
|
return_value->type = IS_STRING; |
|
return; |
|
} |
|
} |
|
RETURN_NULL(); |
|
} |
|
/* }}} */ |
|
/* {{{ proto string xcache_get_op_type(int op_type) */ |
|
PHP_FUNCTION(xcache_get_op_type) |
|
{ |
|
xc_call_getter(xc_get_op_type, xc_get_op_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU); |
|
} |
|
/* }}} */ |
|
/* {{{ proto string xcache_get_data_type(int type) */ |
|
PHP_FUNCTION(xcache_get_data_type) |
|
{ |
|
xc_call_getter(xc_get_data_type, xc_get_data_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU); |
|
} |
|
/* }}} */ |
|
/* {{{ proto string xcache_get_opcode(int opcode) */ |
|
PHP_FUNCTION(xcache_get_opcode) |
|
{ |
|
xc_call_getter(xc_get_opcode, xc_get_opcode_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU); |
|
} |
|
/* }}} */ |
|
/* {{{ proto string xcache_get_op_spec(int op_type) */ |
|
PHP_FUNCTION(xcache_get_op_spec) |
|
{ |
|
xc_call_getter(xc_get_op_spec, xc_get_op_spec_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU); |
|
} |
|
/* }}} */ |
|
/* {{{ proto string xcache_get_opcode_spec(int opcode) */ |
|
PHP_FUNCTION(xcache_get_opcode_spec) |
|
{ |
|
long spec; |
|
const xc_opcode_spec_t *opspec; |
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) { |
|
return; |
|
} |
|
if ((zend_uchar) spec <= xc_get_opcode_spec_count()) { |
|
opspec = xc_get_opcode_spec((zend_uchar) spec); |
|
if (opspec) { |
|
array_init(return_value); |
|
add_assoc_long_ex(return_value, ZEND_STRS("ext"), opspec->ext); |
|
add_assoc_long_ex(return_value, ZEND_STRS("op1"), opspec->op1); |
|
add_assoc_long_ex(return_value, ZEND_STRS("op2"), opspec->op2); |
|
add_assoc_long_ex(return_value, ZEND_STRS("res"), opspec->res); |
|
return; |
|
} |
|
} |
|
RETURN_NULL(); |
|
} |
|
/* }}} */ |
|
/* {{{ proto mixed xcache_get_special_value(zval value) |
|
XCache internal use only: For decompiler to get static value with type fixed */ |
|
PHP_FUNCTION(xcache_get_special_value) |
|
{ |
|
zval *value; |
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) { |
|
return; |
|
} |
|
|
|
switch ((Z_TYPE_P(value) & IS_CONSTANT_TYPE_MASK)) { |
|
case IS_CONSTANT: |
|
*return_value = *value; |
|
zval_copy_ctor(return_value); |
|
return_value->type = UNISW(IS_STRING, UG(unicode) ? IS_UNICODE : IS_STRING); |
|
break; |
|
|
|
case IS_CONSTANT_ARRAY: |
|
*return_value = *value; |
|
zval_copy_ctor(return_value); |
|
return_value->type = IS_ARRAY; |
|
break; |
|
|
|
default: |
|
RETURN_NULL(); |
|
} |
|
} |
|
/* }}} */ |
|
/* {{{ proto int xcache_get_type(zval value) |
|
XCache internal use only for disassembler to get variable type in engine level */ |
|
PHP_FUNCTION(xcache_get_type) |
|
{ |
|
zval *value; |
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) { |
|
return; |
|
} |
|
|
|
RETURN_LONG(Z_TYPE_P(value)); |
|
} |
|
/* }}} */ |
|
/* {{{ proto string xcache_coredump(int op_type) */ |
|
PHP_FUNCTION(xcache_coredump) |
|
{ |
|
if (xc_test) { |
|
raise(SIGSEGV); |
|
} |
|
else { |
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "xcache.test must be enabled to test xcache_coredump()"); |
|
} |
|
} |
|
/* }}} */ |
|
/* {{{ proto string xcache_is_autoglobal(string name) */ |
|
PHP_FUNCTION(xcache_is_autoglobal) |
|
{ |
|
zval *name; |
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) { |
|
return; |
|
} |
|
|
|
#ifdef IS_UNICODE |
|
convert_to_unicode(name); |
|
#else |
|
convert_to_string(name); |
|
#endif |
|
|
|
RETURN_BOOL(zend_u_hash_exists(CG(auto_globals), UG(unicode), Z_STRVAL_P(name), Z_STRLEN_P(name) + 1)); |
|
} |
|
/* }}} */ |
|
static zend_function_entry xcache_functions[] = /* {{{ */ |
|
{ |
|
PHP_FE(xcache_coredump, NULL) |
|
#ifdef HAVE_XCACHE_ASSEMBLER |
|
PHP_FE(xcache_asm, NULL) |
|
#endif |
|
#ifdef HAVE_XCACHE_ENCODER |
|
PHP_FE(xcache_encode, NULL) |
|
#endif |
|
#ifdef HAVE_XCACHE_DECODER |
|
PHP_FE(xcache_decode_file, NULL) |
|
PHP_FE(xcache_decode_string, NULL) |
|
#endif |
|
PHP_FE(xcache_get_special_value, NULL) |
|
PHP_FE(xcache_get_type, NULL) |
|
PHP_FE(xcache_get_op_type, NULL) |
|
PHP_FE(xcache_get_data_type, NULL) |
|
PHP_FE(xcache_get_opcode, NULL) |
|
PHP_FE(xcache_get_opcode_spec, NULL) |
|
PHP_FE(xcache_is_autoglobal, NULL) |
|
PHP_FE(xcache_get_refcount, NULL) |
|
PHP_FE(xcache_get_isref, arginfo_xcache_get_isref) |
|
#ifdef HAVE_XCACHE_DPRINT |
|
PHP_FE(xcache_dprint, NULL) |
|
#endif |
|
PHP_FE_END |
|
}; |
|
/* }}} */ |
|
|
|
#ifdef ZEND_WIN32 |
|
#include "dbghelp.h" |
|
typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType, |
|
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, |
|
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, |
|
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam |
|
); |
|
|
|
static PTOP_LEVEL_EXCEPTION_FILTER oldFilter = NULL; |
|
static HMODULE dbghelpModule = NULL; |
|
static char crash_dumpPath[_MAX_PATH] = { 0 }; |
|
static MINIDUMPWRITEDUMP dbghelp_MiniDumpWriteDump = NULL; |
|
|
|
static LONG WINAPI miniDumperFilter(struct _EXCEPTION_POINTERS *pExceptionInfo) /* {{{ */ |
|
{ |
|
HANDLE fileHandle; |
|
|
|
SetUnhandledExceptionFilter(oldFilter); |
|
|
|
/* create the file */ |
|
fileHandle = CreateFile(crash_dumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); |
|
|
|
if (fileHandle != INVALID_HANDLE_VALUE) { |
|
MINIDUMP_EXCEPTION_INFORMATION exceptionInformation; |
|
BOOL ok; |
|
|
|
exceptionInformation.ThreadId = GetCurrentThreadId(); |
|
exceptionInformation.ExceptionPointers = pExceptionInfo; |
|
exceptionInformation.ClientPointers = FALSE; |
|
|
|
/* write the dump */ |
|
ok = dbghelp_MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), fileHandle, MiniDumpNormal|MiniDumpWithDataSegs|MiniDumpWithIndirectlyReferencedMemory, &exceptionInformation, NULL, NULL); |
|
CloseHandle(fileHandle); |
|
if (ok) { |
|
zend_error(E_ERROR, "Saved dump file to '%s'", crash_dumpPath); |
|
return EXCEPTION_EXECUTE_HANDLER; |
|
} |
|
else { |
|
zend_error(E_ERROR, "Failed to save dump file to '%s' (error %d)", crash_dumpPath, GetLastError()); |
|
} |
|
} |
|
else { |
|
zend_error(E_ERROR, "Failed to create dump file '%s' (error %d)", crash_dumpPath, GetLastError()); |
|
} |
|
|
|
return EXCEPTION_CONTINUE_SEARCH; |
|
} |
|
/* }}} */ |
|
|
|
static void xcache_restore_crash_handler() /* {{{ */ |
|
{ |
|
if (oldFilter) { |
|
SetUnhandledExceptionFilter(oldFilter); |
|
oldFilter = NULL; |
|
} |
|
} |
|
/* }}} */ |
|
static void xcache_init_crash_handler() /* {{{ */ |
|
{ |
|
/* firstly see if dbghelp.dll is around and has the function we need |
|
look next to the EXE first, as the one in System32 might be old |
|
(e.g. Windows 2000) */ |
|
char dbghelpPath[_MAX_PATH]; |
|
|
|
if (GetModuleFileName(NULL, dbghelpPath, _MAX_PATH)) { |
|
char *slash = strchr(dbghelpPath, '\\'); |
|
if (slash) { |
|
strcpy(slash + 1, "DBGHELP.DLL"); |
|
dbghelpModule = LoadLibrary(dbghelpPath); |
|
} |
|
} |
|
|
|
if (!dbghelpModule) { |
|
/* load any version we can */ |
|
dbghelpModule = LoadLibrary("DBGHELP.DLL"); |
|
if (!dbghelpModule) { |
|
zend_error(E_ERROR, "Unable to enable crash dump saver: %s", "DBGHELP.DLL not found"); |
|
return; |
|
} |
|
} |
|
|
|
dbghelp_MiniDumpWriteDump = (MINIDUMPWRITEDUMP)GetProcAddress(dbghelpModule, "MiniDumpWriteDump"); |
|
if (!dbghelp_MiniDumpWriteDump) { |
|
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"); |
|
return; |
|
} |
|
|
|
#ifdef XCACHE_VERSION_REVISION |
|
#define REVISION "r" XCACHE_VERSION_REVISION |
|
#else |
|
#define REVISION "" |
|
#endif |
|
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()); |
|
#undef REVISION |
|
|
|
oldFilter = SetUnhandledExceptionFilter(&miniDumperFilter); |
|
} |
|
/* }}} */ |
|
#else |
|
/* old signal handlers {{{ */ |
|
typedef void (*xc_sighandler_t)(int); |
|
#define FOREACH_SIG(sig) static xc_sighandler_t old_##sig##_handler = NULL |
|
#include "util/xc_foreachcoresig.h" |
|
#undef FOREACH_SIG |
|
/* }}} */ |
|
static void xcache_signal_handler(int sig); |
|
static void xcache_restore_crash_handler() /* {{{ */ |
|
{ |
|
#define FOREACH_SIG(sig) do { \ |
|
if (old_##sig##_handler != xcache_signal_handler) { \ |
|
signal(sig, old_##sig##_handler); \ |
|
} \ |
|
else { \ |
|
signal(sig, SIG_DFL); \ |
|
} \ |
|
} while (0) |
|
#include "util/xc_foreachcoresig.h" |
|
#undef FOREACH_SIG |
|
} |
|
/* }}} */ |
|
static void xcache_init_crash_handler() /* {{{ */ |
|
{ |
|
#define FOREACH_SIG(sig) \ |
|
old_##sig##_handler = signal(sig, xcache_signal_handler) |
|
#include "util/xc_foreachcoresig.h" |
|
#undef FOREACH_SIG |
|
} |
|
/* }}} */ |
|
static void xcache_signal_handler(int sig) /* {{{ */ |
|
{ |
|
xcache_restore_crash_handler(); |
|
if (xc_coredump_dir && xc_coredump_dir[0]) { |
|
if (chdir(xc_coredump_dir) != 0) { |
|
/* error, but nothing can do about it |
|
* and should'nt print anything which might SEGV again */ |
|
} |
|
} |
|
if (xc_disable_on_crash) { |
|
xc_disable_on_crash = 0; |
|
xc_cacher_disable(); |
|
} |
|
raise(sig); |
|
} |
|
/* }}} */ |
|
#endif |
|
|
|
/* {{{ incompatible zend extensions handling */ |
|
typedef struct { |
|
const char *name; |
|
startup_func_t old_startup; |
|
} xc_incompatible_zend_extension_info_t; |
|
static xc_incompatible_zend_extension_info_t xc_incompatible_zend_extensions[] = { |
|
{ "Zend Optimizer", NULL } |
|
}; |
|
|
|
static xc_incompatible_zend_extension_info_t *xc_get_incompatible_zend_extension_info(const char *name) |
|
{ |
|
size_t i; |
|
|
|
for (i = 0; i < sizeof(xc_incompatible_zend_extensions) / sizeof(xc_incompatible_zend_extensions[0]); ++i) { |
|
xc_incompatible_zend_extension_info_t *incompatible_zend_extension_info = &xc_incompatible_zend_extensions[i]; |
|
if (strcmp(incompatible_zend_extension_info->name, name) == 0) { |
|
return incompatible_zend_extension_info; |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
/* }}} */ |
|
static int xc_incompatible_zend_extension_startup_hook(zend_extension *extension) /* {{{ */ |
|
{ |
|
xc_incompatible_zend_extension_info_t *incompatible_zend_extension_info = xc_get_incompatible_zend_extension_info(extension->name); |
|
int status; |
|
zend_bool catched = 0; |
|
zend_llist old_zend_extensions = zend_extensions; |
|
TSRMLS_FETCH(); |
|
|
|
/* hide all extensions from it */ |
|
zend_extensions.head = NULL; |
|
zend_extensions.count = 0; |
|
|
|
/* restore */ |
|
extension->startup = incompatible_zend_extension_info->old_startup; |
|
incompatible_zend_extension_info->old_startup = NULL; |
|
assert(extension->startup); |
|
|
|
zend_try { |
|
status = extension->startup(extension); |
|
} zend_catch { |
|
catched = 1; |
|
} zend_end_try(); |
|
|
|
zend_extensions = old_zend_extensions; |
|
if (catched) { |
|
zend_bailout(); |
|
} |
|
return status; |
|
} |
|
/* }}} */ |
|
static int xc_zend_startup(zend_extension *extension) /* {{{ */ |
|
{ |
|
zend_llist_position lpos; |
|
zend_extension *ext; |
|
|
|
ext = (zend_extension *) zend_extensions.head->data; |
|
if (strcmp(ext->name, XCACHE_NAME) != 0) { |
|
zend_error(E_WARNING, "XCache failed to load itself as the before \"%s\". compatibility downgraded", ext->name); |
|
} |
|
|
|
old_compile_file = zend_compile_file; |
|
zend_compile_file = xc_check_initial_compile_file; |
|
|
|
for (ext = (zend_extension *) zend_llist_get_first_ex(&zend_extensions, &lpos); |
|
ext; |
|
ext = (zend_extension *) zend_llist_get_next_ex(&zend_extensions, &lpos)) { |
|
xc_incompatible_zend_extension_info_t *incompatible_zend_extension_info = xc_get_incompatible_zend_extension_info(extension->name); |
|
if (incompatible_zend_extension_info) { |
|
assert(!incompatible_zend_extension_info->old_startup); |
|
incompatible_zend_extension_info->old_startup = ext->startup; |
|
ext->startup = xc_incompatible_zend_extension_startup_hook; |
|
} |
|
} |
|
return SUCCESS; |
|
} |
|
/* }}} */ |
|
static void xc_zend_shutdown(zend_extension *extension) /* {{{ */ |
|
{ |
|
} |
|
/* }}} */ |
|
/* {{{ zend extension definition structure */ |
|
static zend_extension xc_zend_extension_entry = { |
|
XCACHE_NAME, |
|
XCACHE_VERSION, |
|
XCACHE_AUTHOR, |
|
XCACHE_URL, |
|
XCACHE_COPYRIGHT, |
|
xc_zend_startup, |
|
xc_zend_shutdown, |
|
NULL, /* activate_func_t */ |
|
NULL, /* deactivate_func_t */ |
|
NULL, /* message_handler_func_t */ |
|
NULL, /* op_array_handler_func_t */ |
|
NULL, /* statement_handler_func_t */ |
|
NULL, /* fcall_begin_handler_func_t */ |
|
NULL, /* fcall_end_handler_func_t */ |
|
NULL, /* op_array_ctor_func_t */ |
|
NULL, /* op_array_dtor_func_t */ |
|
STANDARD_ZEND_EXTENSION_PROPERTIES |
|
}; |
|
/* }}} */ |
|
|
|
/* {{{ PHP_INI */ |
|
PHP_INI_BEGIN() |
|
PHP_INI_ENTRY1 ("xcache.coredump_directory", "", PHP_INI_SYSTEM, xcache_OnUpdateString, &xc_coredump_dir) |
|
PHP_INI_ENTRY1 ("xcache.disable_on_crash", "0", PHP_INI_SYSTEM, xcache_OnUpdateBool, &xc_disable_on_crash) |
|
PHP_INI_ENTRY1 ("xcache.test", "0", PHP_INI_SYSTEM, xcache_OnUpdateBool, &xc_test) |
|
STD_PHP_INI_BOOLEAN("xcache.experimental", "0", PHP_INI_ALL, OnUpdateBool, experimental, zend_xcache_globals, xcache_globals) |
|
PHP_INI_END() |
|
/* }}} */ |
|
static PHP_MINFO_FUNCTION(xcache) /* {{{ */ |
|
{ |
|
php_info_print_table_start(); |
|
php_info_print_table_row(2, "XCache Version", XCACHE_VERSION); |
|
#ifdef XCACHE_VERSION_REVISION |
|
php_info_print_table_row(2, "Revision", "r" XCACHE_VERSION_REVISION); |
|
#endif |
|
php_info_print_table_row(2, "Modules Built", XCACHE_MODULES); |
|
php_info_print_table_end(); |
|
|
|
DISPLAY_INI_ENTRIES(); |
|
} |
|
/* }}} */ |
|
static PHP_MINIT_FUNCTION(xcache) /* {{{ */ |
|
{ |
|
#ifndef PHP_GINIT |
|
ZEND_INIT_MODULE_GLOBALS(xcache, xc_init_globals, xc_shutdown_globals); |
|
#endif |
|
REGISTER_INI_ENTRIES(); |
|
|
|
if (xc_coredump_dir && xc_coredump_dir[0]) { |
|
xcache_init_crash_handler(); |
|
} |
|
|
|
xc_init_constant(module_number TSRMLS_CC); |
|
xc_shm_init_modules(); |
|
|
|
/* must be the first */ |
|
xcache_zend_extension_add(&xc_zend_extension_entry, 1); |
|
#ifdef HAVE_XCACHE_OPTIMIZER |
|
xc_optimizer_startup_module(); |
|
#endif |
|
#ifdef HAVE_XCACHE_CACHER |
|
xc_cacher_startup_module(); |
|
#endif |
|
#ifdef HAVE_XCACHE_COVERAGER |
|
xc_coverager_startup_module(); |
|
#endif |
|
#ifdef HAVE_XCACHE_DISASSEMBLER |
|
xc_disassembler_startup_module(); |
|
#endif |
|
|
|
return SUCCESS; |
|
|
|
err_init: |
|
return FAILURE; |
|
} |
|
/* }}} */ |
|
static PHP_MSHUTDOWN_FUNCTION(xcache) /* {{{ */ |
|
{ |
|
if (old_compile_file && zend_compile_file == xc_check_initial_compile_file) { |
|
zend_compile_file = old_compile_file; |
|
old_compile_file = NULL; |
|
} |
|
|
|
if (xc_coredump_dir && xc_coredump_dir[0]) { |
|
xcache_restore_crash_handler(); |
|
} |
|
if (xc_coredump_dir) { |
|
pefree(xc_coredump_dir, 1); |
|
xc_coredump_dir = NULL; |
|
} |
|
#ifndef PHP_GINIT |
|
# ifdef ZTS |
|
ts_free_id(xcache_globals_id); |
|
# else |
|
xc_shutdown_globals(&xcache_globals TSRMLS_CC); |
|
# endif |
|
#endif |
|
|
|
UNREGISTER_INI_ENTRIES(); |
|
xcache_zend_extension_remove(&xc_zend_extension_entry); |
|
return SUCCESS; |
|
} |
|
/* }}} */ |
|
/* {{{ module dependencies */ |
|
#ifdef STANDARD_MODULE_HEADER_EX |
|
static zend_module_dep xcache_module_deps[] = { |
|
ZEND_MOD_REQUIRED("standard") |
|
ZEND_MOD_CONFLICTS("apc") |
|
ZEND_MOD_CONFLICTS("eAccelerator") |
|
ZEND_MOD_CONFLICTS("Turck MMCache") |
|
ZEND_MOD_END |
|
}; |
|
#endif |
|
/* }}} */ |
|
/* {{{ module definition structure */ |
|
zend_module_entry xcache_module_entry = { |
|
#ifdef STANDARD_MODULE_HEADER_EX |
|
STANDARD_MODULE_HEADER_EX, |
|
NULL, |
|
xcache_module_deps, |
|
#else |
|
STANDARD_MODULE_HEADER, |
|
#endif |
|
XCACHE_NAME, |
|
xcache_functions, |
|
PHP_MINIT(xcache), |
|
PHP_MSHUTDOWN(xcache), |
|
NULL, /* RINIT */ |
|
NULL, /* RSHUTDOWN */ |
|
PHP_MINFO(xcache), |
|
XCACHE_VERSION, |
|
#ifdef PHP_GINIT |
|
PHP_MODULE_GLOBALS(xcache), |
|
PHP_GINIT(xcache), |
|
PHP_GSHUTDOWN(xcache), |
|
#endif |
|
#ifdef ZEND_ENGINE_2 |
|
NULL /* ZEND_MODULE_POST_ZEND_DEACTIVATE_N */, |
|
#else |
|
NULL, |
|
NULL, |
|
#endif |
|
STANDARD_MODULE_PROPERTIES_EX |
|
}; |
|
|
|
#ifdef COMPILE_DL_XCACHE |
|
ZEND_GET_MODULE(xcache) |
|
#endif |
|
/* }}} */
|
|
|