Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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 @@ -240,6 +240,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 @@ -791,20 +791,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)))) {
stat_sb = &BG(lssb).sb;
break;
}
} else {
if (filename == BG(CurrentStatFile)
|| (BG(CurrentStatFile)
&& zend_string_equal_content(filename, BG(CurrentStatFile)))) {
stat_sb = &BG(ssb).sb;
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)))) {
stat_sb = &BG(lssb).sb;
break;
}
} else {
if (filename == BG(CurrentStatFile)
|| (BG(CurrentStatFile)
&& zend_string_equal_content(filename, BG(CurrentStatFile)))) {
stat_sb = &BG(ssb).sb;
break;
}
}
}

Expand Down Expand Up @@ -832,21 +834,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 @@ -816,6 +816,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 @@ -361,6 +361,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 @@ -361,6 +361,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
Loading