From 250b9276cb21fd975c44630600b8e99bc5109013 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 9 Sep 2022 15:04:09 +0200 Subject: [PATCH 1/3] Refactor accel_finish_startup --- ext/opcache/ZendAccelerator.c | 351 ++++++++++++++++++---------------- 1 file changed, 186 insertions(+), 165 deletions(-) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 81a865049b88f..41ec2ee1081b5 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -4565,206 +4565,227 @@ static void preload_send_header(sapi_header_struct *sapi_header, void *server_co { } -static int accel_finish_startup(void) +#ifndef ZEND_WIN32 +static int accel_finish_startup_preload(bool in_child) { - if (!ZCG(enabled) || !accel_startup_ok) { - return SUCCESS; - } + int ret = SUCCESS; + int rc; + int orig_error_reporting; + + int (*orig_activate)(void) = sapi_module.activate; + int (*orig_deactivate)(void) = sapi_module.deactivate; + void (*orig_register_server_variables)(zval *track_vars_array) = sapi_module.register_server_variables; + int (*orig_header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers) = sapi_module.header_handler; + int (*orig_send_headers)(sapi_headers_struct *sapi_headers) = sapi_module.send_headers; + void (*orig_send_header)(sapi_header_struct *sapi_header, void *server_context)= sapi_module.send_header; + char *(*orig_getenv)(const char *name, size_t name_len) = sapi_module.getenv; + size_t (*orig_ub_write)(const char *str, size_t str_length) = sapi_module.ub_write; + void (*orig_flush)(void *server_context) = sapi_module.flush; +#ifdef ZEND_SIGNALS + bool old_reset_signals = SIGG(reset); +#endif + + sapi_module.activate = NULL; + sapi_module.deactivate = NULL; + sapi_module.register_server_variables = NULL; + sapi_module.header_handler = preload_header_handler; + sapi_module.send_headers = preload_send_headers; + sapi_module.send_header = preload_send_header; + sapi_module.getenv = NULL; + sapi_module.ub_write = preload_ub_write; + sapi_module.flush = preload_flush; + + zend_interned_strings_switch_storage(1); - if (ZCG(accel_directives).preload && *ZCG(accel_directives).preload) { -#ifdef ZEND_WIN32 - zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Preloading is not supported on Windows"); - return FAILURE; -#else - bool in_child = false; - int ret = SUCCESS; - int rc; - int orig_error_reporting; - - int (*orig_activate)(void) = sapi_module.activate; - int (*orig_deactivate)(void) = sapi_module.deactivate; - void (*orig_register_server_variables)(zval *track_vars_array) = sapi_module.register_server_variables; - int (*orig_header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers) = sapi_module.header_handler; - int (*orig_send_headers)(sapi_headers_struct *sapi_headers) = sapi_module.send_headers; - void (*orig_send_header)(sapi_header_struct *sapi_header, void *server_context)= sapi_module.send_header; - char *(*orig_getenv)(const char *name, size_t name_len) = sapi_module.getenv; - size_t (*orig_ub_write)(const char *str, size_t str_length) = sapi_module.ub_write; - void (*orig_flush)(void *server_context) = sapi_module.flush; #ifdef ZEND_SIGNALS - bool old_reset_signals = SIGG(reset); + SIGG(reset) = false; #endif - if (UNEXPECTED(file_cache_only)) { - zend_accel_error(ACCEL_LOG_WARNING, "Preloading doesn't work in \"file_cache_only\" mode"); - return SUCCESS; + orig_error_reporting = EG(error_reporting); + EG(error_reporting) = 0; + + rc = php_request_startup(); + + EG(error_reporting) = orig_error_reporting; + + if (rc == SUCCESS) { + bool orig_report_memleaks; + + /* don't send headers */ + SG(headers_sent) = true; + SG(request_info).no_headers = true; + php_output_set_status(0); + + ZCG(auto_globals_mask) = 0; + ZCG(request_time) = (time_t)sapi_get_request_time(); + ZCG(cache_opline) = NULL; + ZCG(cache_persistent_script) = NULL; + ZCG(include_path_key_len) = 0; + ZCG(include_path_check) = true; + + ZCG(cwd) = NULL; + ZCG(cwd_key_len) = 0; + ZCG(cwd_check) = true; + + if (accel_preload(ZCG(accel_directives).preload, in_child) != SUCCESS) { + ret = FAILURE; } + preload_flush(NULL); - /* exclusive lock */ - zend_shared_alloc_lock(); + orig_report_memleaks = PG(report_memleaks); + PG(report_memleaks) = false; +#ifdef ZEND_SIGNALS + /* We may not have registered signal handlers due to SIGG(reset)=0, so + * also disable the check that they are registered. */ + SIGG(check) = false; +#endif + php_request_shutdown(NULL); /* calls zend_shared_alloc_unlock(); */ + PG(report_memleaks) = orig_report_memleaks; + } else { + zend_shared_alloc_unlock(); + ret = FAILURE; + } +#ifdef ZEND_SIGNALS + SIGG(reset) = old_reset_signals; +#endif - if (ZCSG(preload_script)) { - /* Preloading was done in another process */ - preload_load(); - zend_shared_alloc_unlock(); - return SUCCESS; + sapi_module.activate = orig_activate; + sapi_module.deactivate = orig_deactivate; + sapi_module.register_server_variables = orig_register_server_variables; + sapi_module.header_handler = orig_header_handler; + sapi_module.send_headers = orig_send_headers; + sapi_module.send_header = orig_send_header; + sapi_module.getenv = orig_getenv; + sapi_module.ub_write = orig_ub_write; + sapi_module.flush = orig_flush; + + sapi_activate(); + + return ret; +} + +static int accel_finish_startup_preload_subprocess(pid_t *pid) +{ + uid_t euid = geteuid(); + if (euid != 0) { + if (ZCG(accel_directives).preload_user + && *ZCG(accel_directives).preload_user) { + zend_accel_error(ACCEL_LOG_WARNING, "\"opcache.preload_user\" is ignored"); } - uid_t euid = geteuid(); - if (euid == 0) { - pid_t pid; - struct passwd *pw; + *pid = -1; + return SUCCESS; + } - if (!ZCG(accel_directives).preload_user - || !*ZCG(accel_directives).preload_user) { - zend_shared_alloc_unlock(); - zend_accel_error_noreturn(ACCEL_LOG_FATAL, "\"opcache.preload\" requires \"opcache.preload_user\" when running under uid 0"); - return FAILURE; - } + if (!ZCG(accel_directives).preload_user + || !*ZCG(accel_directives).preload_user) { - pw = getpwnam(ZCG(accel_directives).preload_user); - if (pw == NULL) { - zend_shared_alloc_unlock(); - zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to getpwnam(\"%s\")", ZCG(accel_directives).preload_user); - return FAILURE; - } - if (pw->pw_uid != euid) { - pid = fork(); - if (pid == -1) { - zend_shared_alloc_unlock(); - zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to fork()"); - return FAILURE; - } else if (pid == 0) { /* children */ - if (setgid(pw->pw_gid) < 0) { - zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setgid(%d)", pw->pw_gid); - exit(1); - } - if (initgroups(pw->pw_name, pw->pw_gid) < 0) { - zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to initgroups(\"%s\", %d)", pw->pw_name, pw->pw_uid); - exit(1); - } - if (setuid(pw->pw_uid) < 0) { - zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setuid(%d)", pw->pw_uid); - exit(1); - } - in_child = true; - } else { /* parent */ - int status; + zend_shared_alloc_unlock(); + zend_accel_error_noreturn(ACCEL_LOG_FATAL, "\"opcache.preload\" requires \"opcache.preload_user\" when running under uid 0"); + return FAILURE; + } - if (waitpid(pid, &status, 0) < 0) { - zend_shared_alloc_unlock(); - zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to waitpid(%d)", pid); - return FAILURE; - } + struct passwd *pw = getpwnam(ZCG(accel_directives).preload_user); + if (pw == NULL) { + zend_shared_alloc_unlock(); + zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to getpwnam(\"%s\")", ZCG(accel_directives).preload_user); + return FAILURE; + } - if (ZCSG(preload_script)) { - preload_load(); - } + if (pw->pw_uid == euid) { + *pid = -1; + return SUCCESS; + } - zend_shared_alloc_unlock(); - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { - return SUCCESS; - } else { - return FAILURE; - } - } - } - } else { - if (ZCG(accel_directives).preload_user - && *ZCG(accel_directives).preload_user) { - zend_accel_error(ACCEL_LOG_WARNING, "\"opcache.preload_user\" is ignored"); - } + *pid = fork(); + if (*pid == -1) { + zend_shared_alloc_unlock(); + zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to fork()"); + return FAILURE; + } + + if (*pid == 0) { /* children */ + if (setgid(pw->pw_gid) < 0) { + zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setgid(%d)", pw->pw_gid); + exit(1); + } + if (initgroups(pw->pw_name, pw->pw_gid) < 0) { + zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to initgroups(\"%s\", %d)", pw->pw_name, pw->pw_uid); + exit(1); + } + if (setuid(pw->pw_uid) < 0) { + zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setuid(%d)", pw->pw_uid); + exit(1); } + } - sapi_module.activate = NULL; - sapi_module.deactivate = NULL; - sapi_module.register_server_variables = NULL; - sapi_module.header_handler = preload_header_handler; - sapi_module.send_headers = preload_send_headers; - sapi_module.send_header = preload_send_header; - sapi_module.getenv = NULL; - sapi_module.ub_write = preload_ub_write; - sapi_module.flush = preload_flush; + return SUCCESS; +} +#endif /* ZEND_WIN32 */ - zend_interned_strings_switch_storage(1); +static int accel_finish_startup(void) +{ + if (!ZCG(enabled) || !accel_startup_ok) { + return SUCCESS; + } -#ifdef ZEND_SIGNALS - SIGG(reset) = false; -#endif + if (!(ZCG(accel_directives).preload && *ZCG(accel_directives).preload)) { + return SUCCESS; + } - orig_error_reporting = EG(error_reporting); - EG(error_reporting) = 0; +#ifdef ZEND_WIN32 + zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Preloading is not supported on Windows"); + return FAILURE; +#else /* ZEND_WIN32 */ - rc = php_request_startup(); + if (UNEXPECTED(file_cache_only)) { + zend_accel_error(ACCEL_LOG_WARNING, "Preloading doesn't work in \"file_cache_only\" mode"); + return SUCCESS; + } - EG(error_reporting) = orig_error_reporting; + /* exclusive lock */ + zend_shared_alloc_lock(); - if (rc == SUCCESS) { - bool orig_report_memleaks; + if (ZCSG(preload_script)) { + /* Preloading was done in another process */ + preload_load(); + zend_shared_alloc_unlock(); + return SUCCESS; + } - /* don't send headers */ - SG(headers_sent) = true; - SG(request_info).no_headers = true; - php_output_set_status(0); - ZCG(auto_globals_mask) = 0; - ZCG(request_time) = (time_t)sapi_get_request_time(); - ZCG(cache_opline) = NULL; - ZCG(cache_persistent_script) = NULL; - ZCG(include_path_key_len) = 0; - ZCG(include_path_check) = true; + pid_t pid; + if (accel_finish_startup_preload_subprocess(&pid) == FAILURE) { + zend_shared_alloc_unlock(); + return FAILURE; + } - ZCG(cwd) = NULL; - ZCG(cwd_key_len) = 0; - ZCG(cwd_check) = true; + if (pid == -1) { /* no subprocess was needed */ + return accel_finish_startup_preload(false); + } else if (pid == 0) { /* subprocess */ + int ret = accel_finish_startup_preload(true); - if (accel_preload(ZCG(accel_directives).preload, in_child) != SUCCESS) { - ret = FAILURE; - } - preload_flush(NULL); + exit(ret == SUCCESS ? 0 : 1); + } else { /* parent */ + int status; - orig_report_memleaks = PG(report_memleaks); - PG(report_memleaks) = false; -#ifdef ZEND_SIGNALS - /* We may not have registered signal handlers due to SIGG(reset)=0, so - * also disable the check that they are registered. */ - SIGG(check) = false; -#endif - php_request_shutdown(NULL); /* calls zend_shared_alloc_unlock(); */ - PG(report_memleaks) = orig_report_memleaks; - } else { + if (waitpid(pid, &status, 0) < 0) { zend_shared_alloc_unlock(); - ret = FAILURE; + zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to waitpid(%d)", pid); } -#ifdef ZEND_SIGNALS - SIGG(reset) = old_reset_signals; -#endif - sapi_module.activate = orig_activate; - sapi_module.deactivate = orig_deactivate; - sapi_module.register_server_variables = orig_register_server_variables; - sapi_module.header_handler = orig_header_handler; - sapi_module.send_headers = orig_send_headers; - sapi_module.send_header = orig_send_header; - sapi_module.getenv = orig_getenv; - sapi_module.ub_write = orig_ub_write; - sapi_module.flush = orig_flush; - - sapi_activate(); - - if (in_child) { - if (ret == SUCCESS) { - exit(0); - } else { - exit(2); - } + if (ZCSG(preload_script)) { + preload_load(); } - return ret; -#endif + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + return SUCCESS; + } else { + return FAILURE; + } } - - return SUCCESS; +#endif /* ZEND_WIN32 */ } ZEND_EXT_API zend_extension zend_extension_entry = { From 127ea5c268409a59e8b6d70b90943dd6743680d8 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 9 Sep 2022 15:05:39 +0200 Subject: [PATCH 2/3] Do not require opcache.preload_user in cli SAPIs --- ext/opcache/ZendAccelerator.c | 7 +++ ext/opcache/tests/preload_user.inc | 5 ++ ext/opcache/tests/preload_user_001.phpt | 28 +++++++++ ext/opcache/tests/preload_user_002.phpt | 28 +++++++++ ext/opcache/tests/preload_user_003.phpt | 29 ++++++++++ ext/opcache/tests/preload_user_004.phpt | 72 +++++++++++++++++++++++ ext/opcache/tests/preload_user_005.phpt | 77 +++++++++++++++++++++++++ run-tests.php | 16 ++++- 8 files changed, 259 insertions(+), 3 deletions(-) create mode 100644 ext/opcache/tests/preload_user.inc create mode 100644 ext/opcache/tests/preload_user_001.phpt create mode 100644 ext/opcache/tests/preload_user_002.phpt create mode 100644 ext/opcache/tests/preload_user_003.phpt create mode 100644 ext/opcache/tests/preload_user_004.phpt create mode 100644 ext/opcache/tests/preload_user_005.phpt diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 41ec2ee1081b5..7db9fb7b5f52b 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -4680,6 +4680,13 @@ static int accel_finish_startup_preload_subprocess(pid_t *pid) if (!ZCG(accel_directives).preload_user || !*ZCG(accel_directives).preload_user) { + bool sapi_requires_preload_user = !(strcmp(sapi_module.name, "cli") == 0 + || strcmp(sapi_module.name, "phpdbg") == 0); + + if (!sapi_requires_preload_user) { + *pid = -1; + return SUCCESS; + } zend_shared_alloc_unlock(); zend_accel_error_noreturn(ACCEL_LOG_FATAL, "\"opcache.preload\" requires \"opcache.preload_user\" when running under uid 0"); diff --git a/ext/opcache/tests/preload_user.inc b/ext/opcache/tests/preload_user.inc new file mode 100644 index 0000000000000..0c0de78842f93 --- /dev/null +++ b/ext/opcache/tests/preload_user.inc @@ -0,0 +1,5 @@ + +--FILE-- + +OK +--EXPECTF-- +%sWarning "opcache.preload_user" is ignored +bool(true) +bool(false) +OK diff --git a/ext/opcache/tests/preload_user_002.phpt b/ext/opcache/tests/preload_user_002.phpt new file mode 100644 index 0000000000000..90b2be8c27640 --- /dev/null +++ b/ext/opcache/tests/preload_user_002.phpt @@ -0,0 +1,28 @@ +--TEST-- +preload_user sets the process uid +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_user.inc +opcache.preload_user={ENV:TEST_NON_ROOT_USER} +opcache.log_verbosity_level=2 +--EXTENSIONS-- +opcache +posix +--SKIPIF-- + +--FILE-- + +OK +--EXPECTF-- +bool(false) +bool(true) +bool(false) +OK diff --git a/ext/opcache/tests/preload_user_003.phpt b/ext/opcache/tests/preload_user_003.phpt new file mode 100644 index 0000000000000..56f69644cb587 --- /dev/null +++ b/ext/opcache/tests/preload_user_003.phpt @@ -0,0 +1,29 @@ +--TEST-- +preload_user has no effect when preload_user is the current user +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_user.inc +opcache.preload_user=root +opcache.log_verbosity_level=2 +--EXTENSIONS-- +opcache +posix +--SKIPIF-- + +--FILE-- + +OK +--EXPECTF-- +bool(true) +bool(true) +bool(false) +OK diff --git a/ext/opcache/tests/preload_user_004.phpt b/ext/opcache/tests/preload_user_004.phpt new file mode 100644 index 0000000000000..71cb5e09cf1c2 --- /dev/null +++ b/ext/opcache/tests/preload_user_004.phpt @@ -0,0 +1,72 @@ +--TEST-- +preload_user is required when euid is 0 under non-cli SAPIs +--INI-- +--EXTENSIONS-- +opcache +posix +--SKIPIF-- + +--FILE-- + +OK +EOT; + +$args = []; +if (file_exists(ini_get('extension_dir').'/opcache.so')) { + $args[] = '-dzend_extension='.ini_get('extension_dir').'/opcache.so'; +} +if (file_exists(ini_get('extension_dir').'/posix.so')) { + $args[] = '-dextension='.ini_get('extension_dir').'/posix.so'; +} +$args = [ + ...$args, + '-dopcache.enable=1', + '-dopcache.optimization_level=-1', + '-dopcache.preload='.__DIR__.'/preload.inc', + '-dopcache.log_verbosity_level=2', +]; + +$tester = new FPM\Tester($cfg, $code); +$tester->start($args); +var_dump($tester->getLogLines(1)); +$tester->terminate(); +$tester->close(); + +?> +Done +--EXPECTF-- +array(1) { + [0]=> + string(%d) "%sFatal Error "opcache.preload" requires "opcache.preload_user" when running under uid 0 +" +} +Done +--CLEAN-- + diff --git a/ext/opcache/tests/preload_user_005.phpt b/ext/opcache/tests/preload_user_005.phpt new file mode 100644 index 0000000000000..2276b1a5cf268 --- /dev/null +++ b/ext/opcache/tests/preload_user_005.phpt @@ -0,0 +1,77 @@ +--TEST-- +preload_user=root is allowed under non-cli SAPIs +--INI-- +--EXTENSIONS-- +opcache +posix +--SKIPIF-- + +--FILE-- + +OK +EOT; + +$args = []; +if (file_exists(ini_get('extension_dir').'/opcache.so')) { + $args[] = '-dzend_extension='.ini_get('extension_dir').'/opcache.so'; +} +if (file_exists(ini_get('extension_dir').'/posix.so')) { + $args[] = '-dextension='.ini_get('extension_dir').'/posix.so'; +} +$args = [ + ...$args, + '-dopcache.enable=1', + '-dopcache.optimization_level=-1', + '-dopcache.preload='.__DIR__.'/preload.inc', + '-dopcache.preload_user=root', + '-dopcache.log_verbosity_level=2', + '-R', +]; + +$tester = new FPM\Tester($cfg, $code); +$tester->start($args); +$tester->expectLogStartNotices(); +$tester + ->request() + ->expectBody([ + 'bool(true)', + 'bool(false)', + 'OK', + ]); +$tester->terminate(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/run-tests.php b/run-tests.php index 157a821ddfeec..951ce5af646ff 100755 --- a/run-tests.php +++ b/run-tests.php @@ -2067,9 +2067,6 @@ function run_test(string $php, $file, array $env): string // Make sure warnings still show up on the second run. $ini_settings['opcache.record_warnings'] = '1'; } - if (extension_loaded('posix') && posix_getuid() === 0) { - $ini_settings['opcache.preload_user'] = 'root'; - } // Any special ini settings // these may overwrite the test defaults... @@ -2078,6 +2075,19 @@ function run_test(string $php, $file, array $env): string $ini = str_replace('{TMP}', sys_get_temp_dir(), $ini); $replacement = IS_WINDOWS ? '"' . PHP_BINARY . ' -r \"while ($in = fgets(STDIN)) echo $in;\" > $1"' : 'tee $1 >/dev/null'; $ini = preg_replace('/{MAIL:(\S+)}/', $replacement, $ini); + $skip = false; + $ini = preg_replace_callback('/{ENV:(\S+)}/', function ($m) use (&$skip) { + $name = $m[1]; + $value = getenv($name); + if ($value === false) { + $skip = sprintf('Environment variable %s is not set', $name); + return ''; + } + return $value; + }, $ini); + if ($skip !== false) { + return skip_test($tested, $tested_file, $shortname, $skip); + } settings2array(preg_split("/[\n\r]+/", $ini), $ini_settings); if ($num_repeats > 1 && isset($ini_settings['opcache.opt_debug_level'])) { From 1adb5c1d7d95bbc2e1a44d452ecca7c52e061bca Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 30 Sep 2022 14:55:28 +0200 Subject: [PATCH 3/3] Improve warning message --- ext/opcache/ZendAccelerator.c | 2 +- ext/opcache/tests/preload_user_001.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 7db9fb7b5f52b..e7e673cd4e7f8 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -4670,7 +4670,7 @@ static int accel_finish_startup_preload_subprocess(pid_t *pid) if (euid != 0) { if (ZCG(accel_directives).preload_user && *ZCG(accel_directives).preload_user) { - zend_accel_error(ACCEL_LOG_WARNING, "\"opcache.preload_user\" is ignored"); + zend_accel_error(ACCEL_LOG_WARNING, "\"opcache.preload_user\" is ignored because the current user is not \"root\""); } *pid = -1; diff --git a/ext/opcache/tests/preload_user_001.phpt b/ext/opcache/tests/preload_user_001.phpt index 98da8e8460165..2a7cb2955441a 100644 --- a/ext/opcache/tests/preload_user_001.phpt +++ b/ext/opcache/tests/preload_user_001.phpt @@ -22,7 +22,7 @@ var_dump(function_exists("f2")); ?> OK --EXPECTF-- -%sWarning "opcache.preload_user" is ignored +%sWarning "opcache.preload_user" is ignored because the current user is not "root" bool(true) bool(false) OK