diff --git a/Zend/zend_virtual_cwd.c b/Zend/zend_virtual_cwd.c index d8a9046d04d8b..5ed89b106114c 100644 --- a/Zend/zend_virtual_cwd.c +++ b/Zend/zend_virtual_cwd.c @@ -146,6 +146,7 @@ static void cwd_globals_ctor(virtual_cwd_globals *cwd_g) /* {{{ */ cwd_g->realpath_cache_size = 0; cwd_g->realpath_cache_size_limit = REALPATH_CACHE_SIZE; cwd_g->realpath_cache_ttl = REALPATH_CACHE_TTL; + cwd_g->enable_stat_cache = true; memset(cwd_g->realpath_cache, 0, sizeof(cwd_g->realpath_cache)); } /* }}} */ diff --git a/Zend/zend_virtual_cwd.h b/Zend/zend_virtual_cwd.h index 728e3ba69d888..833f7a1de9f38 100644 --- a/Zend/zend_virtual_cwd.h +++ b/Zend/zend_virtual_cwd.h @@ -223,6 +223,7 @@ typedef struct _virtual_cwd_globals { zend_long realpath_cache_size; zend_long realpath_cache_size_limit; zend_long realpath_cache_ttl; + bool enable_stat_cache; realpath_cache_bucket *realpath_cache[1024]; } virtual_cwd_globals; diff --git a/ext/standard/filestat.c b/ext/standard/filestat.c index 0d3114191964c..3d452b5856f05 100644 --- a/ext/standard/filestat.c +++ b/ext/standard/filestat.c @@ -784,20 +784,22 @@ PHPAPI void php_stat(zend_string *filename, int type, zval *return_value) } do { - /* Try to hit the cache first */ - if (flags & PHP_STREAM_URL_STAT_LINK) { - if (filename == BG(CurrentLStatFile) - || (BG(CurrentLStatFile) - && zend_string_equal_content(filename, BG(CurrentLStatFile)))) { - memcpy(&ssb, &BG(lssb), sizeof(php_stream_statbuf)); - break; - } - } else { - if (filename == BG(CurrentStatFile) - || (BG(CurrentStatFile) - && zend_string_equal_content(filename, BG(CurrentStatFile)))) { - memcpy(&ssb, &BG(ssb), sizeof(php_stream_statbuf)); - break; + if (CWDG(enable_stat_cache)) { + /* Try to hit the cache first */ + if (flags & PHP_STREAM_URL_STAT_LINK) { + if (filename == BG(CurrentLStatFile) + || (BG(CurrentLStatFile) + && zend_string_equal_content(filename, BG(CurrentLStatFile)))) { + memcpy(&ssb, &BG(lssb), sizeof(php_stream_statbuf)); + break; + } + } else { + if (filename == BG(CurrentStatFile) + || (BG(CurrentStatFile) + && zend_string_equal_content(filename, BG(CurrentStatFile)))) { + memcpy(&ssb, &BG(ssb), sizeof(php_stream_statbuf)); + break; + } } } @@ -825,21 +827,23 @@ PHPAPI void php_stat(zend_string *filename, int type, zval *return_value) RETURN_FALSE; } - /* Drop into cache */ - if (flags & PHP_STREAM_URL_STAT_LINK) { - if (BG(CurrentLStatFile)) { - zend_string_release(BG(CurrentLStatFile)); + if (CWDG(enable_stat_cache)) { + /* Drop into cache */ + if (flags & PHP_STREAM_URL_STAT_LINK) { + if (BG(CurrentLStatFile)) { + zend_string_release(BG(CurrentLStatFile)); + } + BG(CurrentLStatFile) = zend_string_copy(filename); + memcpy(&BG(lssb), &ssb, sizeof(php_stream_statbuf)); } - BG(CurrentLStatFile) = zend_string_copy(filename); - memcpy(&BG(lssb), &ssb, sizeof(php_stream_statbuf)); - } - if (!(flags & PHP_STREAM_URL_STAT_LINK) - || !S_ISLNK(ssb.sb.st_mode)) { - if (BG(CurrentStatFile)) { - zend_string_release(BG(CurrentStatFile)); + if (!(flags & PHP_STREAM_URL_STAT_LINK) + || !S_ISLNK(ssb.sb.st_mode)) { + if (BG(CurrentStatFile)) { + zend_string_release(BG(CurrentStatFile)); + } + BG(CurrentStatFile) = zend_string_copy(filename); + memcpy(&BG(ssb), &ssb, sizeof(php_stream_statbuf)); } - BG(CurrentStatFile) = zend_string_copy(filename); - memcpy(&BG(ssb), &ssb, sizeof(php_stream_statbuf)); } } while (0); diff --git a/ext/standard/tests/file/bug28790.cache.phpt b/ext/standard/tests/file/bug28790.cache.phpt new file mode 100644 index 0000000000000..c05f935483d45 --- /dev/null +++ b/ext/standard/tests/file/bug28790.cache.phpt @@ -0,0 +1,60 @@ +--TEST-- +Bug #28790: Add php.ini option to disable stat cache (with cache) +--FILE-- + +--EXPECT-- +lstat: testfile exists. +stat: testfile exists. +lstat: testfile exists (it shouldn't). +stat: testfile exists (it shouldn't). +stat impossiblefile does not exist. +lstat: testfile exists (it shouldn't). +stat: testfile exists (it shouldn't). +is_file(stat): php binary exists. +lstat: testfile exists (it shouldn't). +lstat: php binary exists. diff --git a/ext/standard/tests/file/bug28790.no-cache.phpt b/ext/standard/tests/file/bug28790.no-cache.phpt new file mode 100644 index 0000000000000..1abb066cd7e6a --- /dev/null +++ b/ext/standard/tests/file/bug28790.no-cache.phpt @@ -0,0 +1,57 @@ +--TEST-- +Bug #28790: Add php.ini option to disable stat cache (without cache) +--INI-- +enable_stat_cache = False +--FILE-- + +--EXPECT-- +lstat: testfile exists. +stat: testfile exists. +stat impossiblefile does not exist. +is_file(stat): php binary exists. +lstat: php binary exists. diff --git a/main/main.c b/main/main.c index 0f56ef3d93021..8d6b0a722f0ec 100644 --- a/main/main.c +++ b/main/main.c @@ -739,6 +739,7 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("realpath_cache_size", "4096K", PHP_INI_SYSTEM, OnUpdateLong, realpath_cache_size_limit, virtual_cwd_globals, cwd_globals) STD_PHP_INI_ENTRY("realpath_cache_ttl", "120", PHP_INI_SYSTEM, OnUpdateLong, realpath_cache_ttl, virtual_cwd_globals, cwd_globals) + STD_PHP_INI_BOOLEAN("enable_stat_cache", "1", PHP_INI_SYSTEM, OnUpdateBool, enable_stat_cache, virtual_cwd_globals, cwd_globals) STD_PHP_INI_ENTRY("user_ini.filename", ".user.ini", PHP_INI_SYSTEM, OnUpdateString, user_ini_filename, php_core_globals, core_globals) STD_PHP_INI_ENTRY("user_ini.cache_ttl", "300", PHP_INI_SYSTEM, OnUpdateLong, user_ini_cache_ttl, php_core_globals, core_globals) diff --git a/php.ini-development b/php.ini-development index 84c0832b49208..dd2b25b29c150 100644 --- a/php.ini-development +++ b/php.ini-development @@ -356,6 +356,12 @@ disable_classes = ; https://php.net/realpath-cache-ttl ;realpath_cache_ttl = 120 +; Allows user to disable the "stat cache". By default php remembers the +; results of the last call to stat() and lstat() and functions that call +; it underneath. Setting this to Off disables that behaviour. +; http://php.net/enable-stat-cache +;enable_stat_cache = On + ; Enables or disables the circular reference collector. ; https://php.net/zend.enable-gc zend.enable_gc = On diff --git a/php.ini-production b/php.ini-production index c9cacaec91304..836fccb51cf8e 100644 --- a/php.ini-production +++ b/php.ini-production @@ -356,6 +356,12 @@ disable_classes = ; https://php.net/realpath-cache-ttl ;realpath_cache_ttl = 120 +; Allows user to disable the "stat cache". By default php remembers the +; results of the last call to stat() and lstat() and functions that call +; it underneath. Setting this to Off disables that behaviour. +; http://php.net/enable-stat-cache +;enable_stat_cache = On + ; Enables or disables the circular reference collector. ; https://php.net/zend.enable-gc zend.enable_gc = On