diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 79941a7d13d3..45da30d32cc8 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -114,6 +114,7 @@ zend_bool accel_startup_ok = 0; static char *zps_failure_reason = NULL; char *zps_api_failure_reason = NULL; zend_bool file_cache_only = 0; /* process uses file cache only */ +zend_bool allow_cache = 1; /* process does not use any cache (takes precedence over file_cache_only) */ #if ENABLE_FILE_CACHE_FALLBACK zend_bool fallback_process = 0; /* process uses file cache fallback */ #endif @@ -469,7 +470,7 @@ zend_string* ZEND_FASTCALL accel_new_interned_string(zend_string *str) uint32_t pos, *hash_slot; zend_string *s; - if (UNEXPECTED(file_cache_only)) { + if (UNEXPECTED(file_cache_only || !allow_cache)) { return str; } @@ -1947,6 +1948,28 @@ zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int type) return op_array; } +// Optimize opcodes without caching the file in shared memory or in disk +zend_op_array *no_cache_compile_file(zend_file_handle *file_handle, int type) +{ + zend_op_array *op_array = NULL; + zend_persistent_script *persistent_script; // not actually persistent with opcache.allow_cache=0. + + if (is_stream_path(file_handle->filename) && + !is_cacheable_stream_path(file_handle->filename)) { + return accelerator_orig_compile_file(file_handle, type); + } + + // Take the same code path as if attempting to save a blacklisted script to file cache. + persistent_script = opcache_compile_file(file_handle, type, NULL, &op_array); + if (persistent_script) { + // Attempt to optimize the script before returning it. + zend_optimize_script(&persistent_script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level); + + return zend_accel_load_script(persistent_script, /* from_memory */ 0); + } + return op_array; +} + int check_persistent_script_access(zend_persistent_script *persistent_script) { char *phar_path, *ptr; @@ -1983,11 +2006,19 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type) ZCG(cache_opline) = NULL; ZCG(cache_persistent_script) = NULL; if (file_handle->filename - && ZCG(accel_directives).file_cache && ZCG(enabled) && accel_startup_ok) { - return file_cache_compile_file(file_handle, type); + if (!allow_cache) { + return no_cache_compile_file(file_handle, type); + } else if (ZCG(accel_directives).file_cache) { + return file_cache_compile_file(file_handle, type); + } } return accelerator_orig_compile_file(file_handle, type); + } else if (!allow_cache) { + // TODO: Why is opcache_get_status using accelerator_enabled instead of enabled? + ZCG(cache_opline) = NULL; + ZCG(cache_persistent_script) = NULL; + return no_cache_compile_file(file_handle, type); } else if (file_cache_only) { ZCG(cache_opline) = NULL; ZCG(cache_persistent_script) = NULL; @@ -2313,7 +2344,7 @@ static int persistent_stream_open_function(const char *filename, zend_file_handl /* zend_resolve_path() replacement for PHP 5.3 and above */ static zend_string* persistent_zend_resolve_path(const char *filename, size_t filename_len) { - if (!file_cache_only && + if (!file_cache_only && allow_cache && ZCG(accelerator_enabled)) { /* check if callback is called from include_once or it's a main request */ @@ -2437,7 +2468,7 @@ int accel_activate(INIT_FUNC_ARGS) ZCG(cwd_key_len) = 0; ZCG(cwd_check) = 1; - if (file_cache_only) { + if (file_cache_only || !allow_cache) { ZCG(accelerator_enabled) = 0; return SUCCESS; } @@ -2997,7 +3028,8 @@ static int accel_post_startup(void) /* End of non-SHM dependent initializations */ /********************************************/ file_cache_only = ZCG(accel_directives).file_cache_only; - if (!file_cache_only) { + allow_cache = ZCG(accel_directives).allow_cache; + if (!file_cache_only && allow_cache) { size_t shm_size = ZCG(accel_directives).memory_consumption; #ifdef HAVE_JIT size_t jit_size = 0; @@ -3085,7 +3117,7 @@ static int accel_post_startup(void) zend_shared_alloc_unlock(); SHM_PROTECT(); - } else if (!ZCG(accel_directives).file_cache) { + } else if (file_cache_only && !ZCG(accel_directives).file_cache) { accel_startup_ok = 0; zend_accel_error(ACCEL_LOG_FATAL, "opcache.file_cache_only is set without a proper setting of opcache.file_cache"); return SUCCESS; @@ -3149,7 +3181,7 @@ static int accel_post_startup(void) zend_optimizer_startup(); - if (!file_cache_only && ZCG(accel_directives).interned_strings_buffer) { + if (!file_cache_only && allow_cache && ZCG(accel_directives).interned_strings_buffer) { accel_use_shm_interned_strings(); } @@ -3167,6 +3199,7 @@ void accel_shutdown(void) { zend_ini_entry *ini_entry; zend_bool _file_cache_only = 0; + zend_bool _allow_cache = 1; #ifdef HAVE_JIT zend_jit_shutdown(); @@ -3188,6 +3221,7 @@ void accel_shutdown(void) } _file_cache_only = file_cache_only; + _allow_cache = allow_cache; accel_reset_pcre_cache(); @@ -3195,8 +3229,8 @@ void accel_shutdown(void) ts_free_id(accel_globals_id); #endif - if (!_file_cache_only) { - /* Delay SHM detach */ + if (!_file_cache_only && _allow_cache) { + /* Delay SHM detach - this only needs to be done if SHM is used */ orig_post_shutdown_cb = zend_post_shutdown_cb; zend_post_shutdown_cb = accel_post_shutdown; } @@ -4786,6 +4820,10 @@ static int accel_finish_startup(void) } if (ZCG(accel_directives).preload && *ZCG(accel_directives).preload) { + if (!allow_cache) { + zend_accel_error(ACCEL_LOG_ERROR, "Preloading cannot be combined with allow_cache=0"); + return FAILURE; + } #ifdef ZEND_WIN32 zend_accel_error(ACCEL_LOG_ERROR, "Preloading is not supported on Windows"); return FAILURE; @@ -4809,6 +4847,10 @@ static int accel_finish_startup(void) zend_bool old_reset_signals = SIGG(reset); #endif + if (UNEXPECTED(!allow_cache)) { + zend_accel_error(ACCEL_LOG_WARNING, "Preloading doesn't work in \"allow_cache=0\" mode"); + return SUCCESS; + } if (UNEXPECTED(file_cache_only)) { zend_accel_error(ACCEL_LOG_WARNING, "Preloading doesn't work in \"file_cache_only\" mode"); return SUCCESS; diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h index 05a05fa1b68a..e522231ef2bf 100644 --- a/ext/opcache/ZendAccelerator.h +++ b/ext/opcache/ZendAccelerator.h @@ -187,6 +187,7 @@ typedef struct _zend_accel_directives { #endif char *file_cache; zend_bool file_cache_only; + zend_bool allow_cache; zend_bool file_cache_consistency_checks; #if ENABLE_FILE_CACHE_FALLBACK zend_bool file_cache_fallback; @@ -295,6 +296,7 @@ extern char accel_uname_id[32]; #endif extern zend_bool accel_startup_ok; extern zend_bool file_cache_only; +extern zend_bool allow_cache; #if ENABLE_FILE_CACHE_FALLBACK extern zend_bool fallback_process; #endif diff --git a/ext/opcache/tests/001_cli.phpt b/ext/opcache/tests/001_cli.phpt index 3be1a52228eb..ff906e828cc7 100644 --- a/ext/opcache/tests/001_cli.phpt +++ b/ext/opcache/tests/001_cli.phpt @@ -4,6 +4,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.file_cache_only=0 +opcache.optimization_level=-1 --SKIPIF-- --FILE-- @@ -13,8 +14,12 @@ $status = opcache_get_status(); var_dump($config["directives"]["opcache.enable"]); var_dump($config["directives"]["opcache.enable_cli"]); var_dump($status["opcache_enabled"]); +var_dump($status["optimizations_enabled"]); +var_dump($status["allow_cache"]); ?> --EXPECT-- bool(true) bool(true) bool(true) +bool(true) +bool(true) diff --git a/ext/opcache/tests/allow_cache_1.phpt b/ext/opcache/tests/allow_cache_1.phpt new file mode 100644 index 000000000000..035de58a7392 --- /dev/null +++ b/ext/opcache/tests/allow_cache_1.phpt @@ -0,0 +1,23 @@ +--TEST-- +allow_cache_1: Opcache indicates if optimizations and caching are enabled +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.allow_cache=0 +opcache.preload= +--SKIPIF-- + +--FILE-- + +--EXPECT-- +array(3) { + ["optimizations_enabled"]=> + bool(true) + ["opcache_enabled"]=> + bool(false) + ["allow_cache"]=> + bool(false) +} diff --git a/ext/opcache/tests/allow_cache_2.phpt b/ext/opcache/tests/allow_cache_2.phpt new file mode 100644 index 000000000000..9eafa8bda3d5 --- /dev/null +++ b/ext/opcache/tests/allow_cache_2.phpt @@ -0,0 +1,42 @@ +--TEST-- +allow_cache_2: allow_cache=0 takes precedence over file_cache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache_only=1 +opcache.file_cache="{TMP}" +opcache.allow_cache=0 +opcache.opt_debug_level=0x20000 +opcache.optimization_level=-1 +opcache.preload= +--SKIPIF-- + + +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=7, args=0, vars=1, tmps=1) + ; (after optimizer) + ; %sallow_cache_2.php:1-8 +0000 INIT_FCALL 0 %d string("opcache_get_status") +0001 V1 = DO_ICALL +0002 ASSIGN CV0($status) V1 +0003 INIT_FCALL 1 %d string("var_dump") +0004 SEND_VAR CV0($status) 1 +0005 DO_ICALL +0006 RETURN int(1) +array(3) { + ["optimizations_enabled"]=> + bool(true) + ["opcache_enabled"]=> + bool(false) + ["allow_cache"]=> + bool(false) +} diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c index 4a535591c0e3..11f8a889a614 100644 --- a/ext/opcache/zend_accelerator_module.c +++ b/ext/opcache/zend_accelerator_module.c @@ -301,6 +301,7 @@ ZEND_INI_BEGIN() #if ENABLE_FILE_CACHE_FALLBACK STD_PHP_INI_BOOLEAN("opcache.file_cache_fallback" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_fallback, zend_accel_globals, accel_globals) #endif + STD_PHP_INI_ENTRY("opcache.allow_cache" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.allow_cache, zend_accel_globals, accel_globals) #ifdef HAVE_HUGE_CODE_PAGES STD_PHP_INI_BOOLEAN("opcache.huge_code_pages" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.huge_code_pages, zend_accel_globals, accel_globals) #endif @@ -405,6 +406,10 @@ void zend_accel_override_file_functions(void) { zend_function *old_function; if (ZCG(enabled) && accel_startup_ok && ZCG(accel_directives).file_override_enabled) { + if (!allow_cache) { + zend_accel_error(ACCEL_LOG_WARNING, "file_override_enabled has no effect when allow_cache=0"); + return; + } if (file_cache_only) { zend_accel_error(ACCEL_LOG_WARNING, "file_override_enabled has no effect when file_cache_only is set"); return; @@ -438,7 +443,7 @@ void zend_accel_info(ZEND_MODULE_INFO_FUNC_ARGS) { php_info_print_table_start(); - if (ZCG(accelerator_enabled) || file_cache_only) { + if ((ZCG(accelerator_enabled) || file_cache_only) && allow_cache) { php_info_print_table_row(2, "Opcode Caching", "Up and Running"); } else { php_info_print_table_row(2, "Opcode Caching", "Disabled"); @@ -448,12 +453,12 @@ void zend_accel_info(ZEND_MODULE_INFO_FUNC_ARGS) } else { php_info_print_table_row(2, "Optimization", "Disabled"); } - if (!file_cache_only) { + if (!file_cache_only && allow_cache) { php_info_print_table_row(2, "SHM Cache", "Enabled"); } else { php_info_print_table_row(2, "SHM Cache", "Disabled"); } - if (ZCG(accel_directives).file_cache) { + if (ZCG(accel_directives).file_cache && allow_cache) { php_info_print_table_row(2, "File Cache", "Enabled"); } else { php_info_print_table_row(2, "File Cache", "Disabled"); @@ -471,7 +476,7 @@ void zend_accel_info(ZEND_MODULE_INFO_FUNC_ARGS) #else php_info_print_table_row(2, "JIT", "Not Available"); #endif - if (file_cache_only) { + if (file_cache_only || !allow_cache) { // TODO test failure mode if (!accel_startup_ok || zps_api_failure_reason) { php_info_print_table_row(2, "Startup Failed", zps_api_failure_reason); } else { @@ -613,8 +618,15 @@ ZEND_FUNCTION(opcache_get_status) array_init(return_value); /* Trivia */ + /* Whether opcodes get optimized */ + add_assoc_bool(return_value, "optimizations_enabled", ZCG(enabled) && accel_startup_ok && ZCG(accel_directives).optimization_level); + /* Whether the caching is enabled */ add_assoc_bool(return_value, "opcache_enabled", ZCG(accelerator_enabled)); + add_assoc_bool(return_value, "allow_cache", allow_cache); + if (!allow_cache) { + return; + } if (ZCG(accel_directives).file_cache) { add_assoc_string(return_value, "file_cache", ZCG(accel_directives).file_cache); } @@ -784,6 +796,7 @@ ZEND_FUNCTION(opcache_get_configuration) #if ENABLE_FILE_CACHE_FALLBACK add_assoc_bool(&directives, "opcache.file_cache_fallback", ZCG(accel_directives).file_cache_fallback); #endif + add_assoc_bool(&directives, "opcache.allow_cache", ZCG(accel_directives).allow_cache); add_assoc_long(&directives, "opcache.file_update_protection", ZCG(accel_directives).file_update_protection); add_assoc_long(&directives, "opcache.opt_debug_level", ZCG(accel_directives).opt_debug_level); diff --git a/php.ini-development b/php.ini-development index 1fcd0e45a2a9..56341f9530d2 100644 --- a/php.ini-development +++ b/php.ini-development @@ -1856,6 +1856,10 @@ ldap.max_links = -1 ; cache is required. ;opcache.file_cache_fallback=1 +; If set to 0, it disables both the shared memory cache and the file cache, +; while opcache continues to optimize code. +;opcache.allow_cache=1 + ; Enables or disables copying of PHP code (text segment) into HUGE PAGES. ; This should improve performance, but requires appropriate OS configuration. ;opcache.huge_code_pages=0 diff --git a/php.ini-production b/php.ini-production index 761b0c210728..758faa435e3f 100644 --- a/php.ini-production +++ b/php.ini-production @@ -1858,6 +1858,10 @@ ldap.max_links = -1 ; cache is required. ;opcache.file_cache_fallback=1 +; If set to 0, it disables both the shared memory cache and the file cache, +; while opcache continues to optimize code. +;opcache.allow_cache=1 + ; Enables or disables copying of PHP code (text segment) into HUGE PAGES. ; This should improve performance, but requires appropriate OS configuration. ;opcache.huge_code_pages=1