PHP clearstatcache() 函数

说明: clearstatcache() 函数用于清除文件状态缓存,以确保获取最新的文件信息。

语法

void clearstatcache ([ bool $clear_realpath_cache = false [, string $filename ]] )

参数说明

参数 描述 默认值
clear_realpath_cache 可选。是否清除真实路径缓存 false
filename 可选。指定要清除缓存的文件路径
仅在clear_realpath_cache为true时有效
-

返回值

  • 没有返回值(void)

为什么需要清除缓存?

PHP会对某些文件系统函数的结果进行缓存,以提高性能。但在以下情况下,缓存可能会导致获取到过时的信息:

  • 文件被频繁创建、删除或修改
  • 文件权限在脚本执行期间被更改
  • 文件大小在短时间内发生变化
  • 使用符号链接或真实路径可能发生变化

这时就需要使用 clearstatcache() 来清除缓存,获取最新的文件信息。

受影响的函数

以下函数的结果会被PHP缓存,调用clearstatcache()可以清除这些函数的缓存:

  • stat()
  • lstat()
  • file_exists()
  • is_writable()
  • is_readable()
  • is_executable()
  • is_file()
  • is_dir()
  • is_link()
  • filectime()
  • fileatime()
  • filemtime()
  • fileinode()
  • filegroup()
  • fileowner()

示例

示例 1:基本使用 - 文件大小变化

<?php
// 创建一个文件并写入内容
$filename = "test.txt";
file_put_contents($filename, "初始内容");

// 第一次获取文件大小
$size1 = filesize($filename);
echo "第一次文件大小: " . $size1 . " 字节\n";

// 追加内容到文件
file_put_contents($filename, "\n追加的内容", FILE_APPEND);

// 第二次获取文件大小(可能会获取缓存的值)
$size2 = filesize($filename);
echo "第二次文件大小(未清除缓存): " . $size2 . " 字节\n";

// 清除缓存
clearstatcache();

// 第三次获取文件大小
$size3 = filesize($filename);
echo "第三次文件大小(已清除缓存): " . $size3 . " 字节\n";

// 清理
unlink($filename);
?>

示例 2:文件权限变化

<?php
$filename = "example.php";

// 创建文件
touch($filename);

// 设置文件权限为只读
chmod($filename, 0444);

// 检查是否可写
$writable1 = is_writable($filename);
echo "第一次检查是否可写: " . ($writable1 ? '是' : '否') . "\n";

// 修改文件权限为可写
chmod($filename, 0644);

// 再次检查是否可写(可能会获取缓存的值)
$writable2 = is_writable($filename);
echo "第二次检查是否可写(未清除缓存): " . ($writable2 ? '是' : '否') . "\n";

// 清除缓存
clearstatcache();

// 第三次检查是否可写
$writable3 = is_writable($filename);
echo "第三次检查是否可写(已清除缓存): " . ($writable3 ? '是' : '否') . "\n";

// 清理
unlink($filename);
?>

示例 3:文件存在性检查

<?php
$filename = "temp_file.txt";

// 检查文件是否存在
echo "文件创建前是否存在: " . (file_exists($filename) ? '是' : '否') . "\n";

// 创建文件
file_put_contents($filename, "测试内容");

// 检查文件是否存在(可能会获取缓存的值)
echo "文件创建后是否存在(未清除缓存): " . (file_exists($filename) ? '是' : '否') . "\n";

// 清除缓存
clearstatcache();

// 再次检查文件是否存在
echo "文件创建后是否存在(已清除缓存): " . (file_exists($filename) ? '是' : '否') . "\n";

// 删除文件
unlink($filename);

// 检查文件是否存在
echo "文件删除后是否存在(未清除缓存): " . (file_exists($filename) ? '是' : '否') . "\n";

// 清除缓存
clearstatcache();

// 再次检查文件是否存在
echo "文件删除后是否存在(已清除缓存): " . (file_exists($filename) ? '是' : '否') . "\n";
?>

示例 4:清除真实路径缓存

<?php
// 创建一个符号链接
$target = "real_file.txt";
$link = "symlink.txt";

// 创建目标文件
file_put_contents($target, "真实文件内容");

// 创建符号链接
if (!file_exists($link)) {
    symlink($target, $link);
}

// 获取真实路径
$realpath1 = realpath($link);
echo "第一次真实路径: " . $realpath1 . "\n";

// 删除并重新创建符号链接
unlink($link);
symlink($target, $link);

// 获取真实路径(可能会获取缓存的值)
$realpath2 = realpath($link);
echo "第二次真实路径(未清除缓存): " . $realpath2 . "\n";

// 清除真实路径缓存
clearstatcache(true, $link);

// 获取真实路径
$realpath3 = realpath($link);
echo "第三次真实路径(已清除缓存): " . $realpath3 . "\n";

// 清理
unlink($link);
unlink($target);
?>

示例 5:文件监控器类

<?php
/**
 * 文件状态监控器类
 */
class FileStatusMonitor {
    private $filename;
    private $lastModified;
    private $lastSize;

    public function __construct($filename) {
        $this->filename = $filename;
        $this->updateStatus();
    }

    /**
     * 更新文件状态(清除缓存并获取最新信息)
     */
    private function updateStatus() {
        clearstatcache();
        if (file_exists($this->filename)) {
            $this->lastModified = filemtime($this->filename);
            $this->lastSize = filesize($this->filename);
        } else {
            $this->lastModified = null;
            $this->lastSize = null;
        }
    }

    /**
     * 检查文件是否被修改
     */
    public function isModified() {
        $previousModified = $this->lastModified;
        $previousSize = $this->lastSize;

        $this->updateStatus();

        if (!file_exists($this->filename)) {
            return $previousModified !== null; // 文件被删除
        }

        return ($this->lastModified !== $previousModified) ||
               ($this->lastSize !== $previousSize);
    }

    /**
     * 获取文件信息
     */
    public function getFileInfo() {
        $this->updateStatus();

        if (!file_exists($this->filename)) {
            return ['exists' => false];
        }

        clearstatcache();
        return [
            'exists' => true,
            'size' => $this->lastSize,
            'modified' => $this->lastModified,
            'readable' => is_readable($this->filename),
            'writable' => is_writable($this->filename),
            'permissions' => decoct(fileperms($this->filename) & 0777),
            'owner' => fileowner($this->filename)
        ];
    }
}

// 使用示例
$monitor = new FileStatusMonitor("config.json");

// 模拟文件变化
file_put_contents("config.json", json_encode(['version' => '1.0']));

if ($monitor->isModified()) {
    echo "文件已被修改\n";
    $info = $monitor->getFileInfo();
    print_r($info);
} else {
    echo "文件未修改\n";
}

// 清理
unlink("config.json");
?>

示例 6:批量文件操作时的缓存管理

<?php
/**
 * 批量处理文件,正确处理缓存
 */
function processFiles($directory) {
    if (!is_dir($directory)) {
        return false;
    }

    $results = [];
    $files = scandir($directory);

    foreach ($files as $file) {
        if ($file == '.' || $file == '..') {
            continue;
        }

        $filepath = $directory . '/' . $file;

        // 在处理每个文件前清除缓存
        clearstatcache();

        $info = [
            'filename' => $file,
            'path' => $filepath,
            'exists' => file_exists($filepath),
            'is_dir' => is_dir($filepath),
            'is_file' => is_file($filepath),
            'size' => filesize($filepath),
            'modified' => filemtime($filepath),
            'readable' => is_readable($filepath),
            'writable' => is_writable($filepath)
        ];

        $results[] = $info;
    }

    return $results;
}

/**
 * 监控文件夹变化
 */
function monitorDirectoryChanges($directory, $interval = 1) {
    $previousState = [];

    while (true) {
        // 清除缓存以确保获取最新状态
        clearstatcache();

        $currentState = [];
        $files = scandir($directory);

        foreach ($files as $file) {
            if ($file == '.' || $file == '..') {
                continue;
            }

            $filepath = $directory . '/' . $file;
            clearstatcache(); // 为每个文件清除缓存

            $currentState[$file] = [
                'size' => filesize($filepath),
                'modified' => filemtime($filepath)
            ];
        }

        // 检测变化
        $changes = [];
        foreach ($currentState as $file => $info) {
            if (!isset($previousState[$file])) {
                $changes[] = "新增文件: $file";
            } elseif ($info['size'] != $previousState[$file]['size'] ||
                     $info['modified'] != $previousState[$file]['modified']) {
                $changes[] = "文件修改: $file";
            }
        }

        // 检测删除的文件
        foreach ($previousState as $file => $info) {
            if (!isset($currentState[$file])) {
                $changes[] = "删除文件: $file";
            }
        }

        if (!empty($changes)) {
            echo date('Y-m-d H:i:s') . " 检测到变化:\n";
            foreach ($changes as $change) {
                echo "  - $change\n";
            }
        }

        $previousState = $currentState;
        sleep($interval);
    }
}

// 使用示例(注释掉以避免无限循环)
/*
$results = processFiles("/tmp");
print_r($results);

// monitorDirectoryChanges("/tmp"); // 这会无限循环,谨慎使用
*/
?>

最佳实践

使用建议
  1. 适时调用:只在文件状态可能发生变化时才调用clearstatcache()
  2. 避免过度使用:频繁调用会影响性能,因为每次调用都会重新获取文件信息
  3. 针对文件调用:如果只关心特定文件,可以在检查该文件前调用
  4. 批量操作:批量处理文件时,在处理每个文件前清除缓存
  5. 真实路径缓存:只有在处理符号链接或复杂路径时才需要清除真实路径缓存
  6. 性能权衡:在性能和准确性之间找到平衡点
  7. 错误处理:即使清除了缓存,也要考虑文件可能不存在的情况
常见误区
误区1:每次调用文件函数前都清除缓存
// 不推荐:过度清除缓存
clearstatcache();
if (file_exists($file)) {
    clearstatcache();
    $size = filesize($file);
    clearstatcache();
    $modified = filemtime($file);
}

// 推荐:批量操作时一次性清除
clearstatcache();
$exists = file_exists($file);
$size = filesize($file);
$modified = filemtime($file);
误区2:忽略真实路径缓存
// 当处理符号链接时,需要清除真实路径缓存
symlink('/original/path/file.txt', '/link/file.txt');

// 修改原文件位置后...
// 需要清除真实路径缓存以获取新的真实路径
clearstatcache(true, '/link/file.txt');
$realpath = realpath('/link/file.txt');

相关函数