Skip to content

Feature Request #28790 Add php.ini option to disable stat cache #5894

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Zend/zend_virtual_cwd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
/* }}} */
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_virtual_cwd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
58 changes: 31 additions & 27 deletions ext/standard/filestat.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}

Expand Down Expand Up @@ -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);

Expand Down
60 changes: 60 additions & 0 deletions ext/standard/tests/file/bug28790.cache.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
--TEST--
Bug #28790: Add php.ini option to disable stat cache (with cache)
--FILE--
<?php

$php = '"'.getenv('TEST_PHP_EXECUTABLE').'"';
$phpfile = getenv('TEST_PHP_EXECUTABLE');
$impossiblefile = __FILE__.DIRECTORY_SEPARATOR.'bug28790.impossible';
$testfile = __DIR__.DIRECTORY_SEPARATOR.'bug28790.file';

function all_the_stats($filename, $message) {
if (@lstat($filename)) {
print("lstat: $message.\n");
}
if (@stat($filename)) {
print("stat: $message.\n");
}
}

# Windows can use / for dir separators, so let's do that.
$qtestfile = str_replace("\\", "/", "$testfile");

# This creates a file and should emit stat & lstat messages.
passthru($php.' -n -r "touch(\\"'.$qtestfile.'\\");"');
all_the_stats("$testfile", "testfile exists");

# This deletes the file and shouldn't emit stat & lstat messages (but does).
passthru($php.' -n -r "unlink(\\"'.$qtestfile.'\\");"');
all_the_stats("$testfile", "testfile exists (it shouldn't)");

# This stats a non-existent file; still stat/lstats the deleted testfile (shouldn't).
if (!@stat("$impossiblefile")) {
print("stat impossiblefile does not exist.\n");
}
all_the_stats("$testfile", "testfile exists (it shouldn't)");

# This is_files an existing file; still can lstat the deleted testfile (shouldn't).
if (is_file("$phpfile")) {
print("is_file(stat): php binary exists.\n");
}
all_the_stats("$testfile", "testfile exists (it shouldn't)");

# This lstats an existing file; finally can't stat or lstat the deleted testfile.
if (lstat("$phpfile")) {
print("lstat: php binary exists.\n");
}
all_the_stats("$testfile", "testfile exists (it shouldn't)");

?>
--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.
57 changes: 57 additions & 0 deletions ext/standard/tests/file/bug28790.no-cache.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
--TEST--
Bug #28790: Add php.ini option to disable stat cache (without cache)
--INI--
enable_stat_cache = False
--FILE--
<?php

$php = '"'.getenv('TEST_PHP_EXECUTABLE').'"';
$phpfile = getenv('TEST_PHP_EXECUTABLE');
$impossiblefile = __FILE__.DIRECTORY_SEPARATOR.'bug28790.impossible';
$testfile = __DIR__.DIRECTORY_SEPARATOR.'bug28790.file';

function all_the_stats($filename, $message) {
if (@lstat($filename)) {
print("lstat: $message.\n");
}
if (@stat($filename)) {
print("stat: $message.\n");
}
}

# Windows can use / for dir separators, so let's do that.
$qtestfile = str_replace("\\", "/", "$testfile");

# This creates a file and should emit stat & lstat messages.
passthru($php.' -n -r "touch(\\"'.$qtestfile.'\\");"');
all_the_stats("$testfile", "testfile exists");

# This deletes the file and shouldn't emit stat or lstat messages.
passthru($php.' -n -r "unlink(\\"'.$qtestfile.'\\");"');
all_the_stats("$testfile", "testfile exists (it shouldn't)");

# This stats a non-existent file; still no testfile stat or lstat messages.
if (!@stat("$impossiblefile")) {
print("stat impossiblefile does not exist.\n");
}
all_the_stats("$testfile", "testfile exists (it shouldn't)");

# This is_files an existing file; still no testfile stat or lstat messages.
if (is_file("$phpfile")) {
print("is_file(stat): php binary exists.\n");
}
all_the_stats("$testfile", "testfile exists (it shouldn't)");

# This lstats an existing file; still no testfile stat or lstat messages.
if (lstat("$phpfile")) {
print("lstat: php binary exists.\n");
}
all_the_stats("$testfile", "testfile exists (it shouldn't)");

?>
--EXPECT--
lstat: testfile exists.
stat: testfile exists.
stat impossiblefile does not exist.
is_file(stat): php binary exists.
lstat: php binary exists.
1 change: 1 addition & 0 deletions main/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions php.ini-development
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions php.ini-production
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down