PHP fstat()函数

fstat()函数通过已打开的文件指针获取文件信息,返回包含文件统计信息的数组。

语法

array|false fstat ( resource $handle )

参数说明

参数 描述
handle 文件指针资源,通常由fopen()函数创建

返回值数组结构

键名 数字索引 描述 示例值
dev 0 设备号 2050
ino 1 inode号 123456
mode 2 inode保护模式(权限) 33204
nlink 3 链接数 1
uid 4 所有者的用户ID 1000
gid 5 所有者的组ID 1000
rdev 6 设备类型(如果是inode设备) 0
size 7 文件大小(字节) 1024
atime 8 最后访问时间(Unix时间戳) 1635678900
mtime 9 最后修改时间(Unix时间戳) 1635678800
ctime 10 最后改变时间(Unix时间戳) 1635678700
blksize 11 文件系统I/O的块大小 4096
blocks 12 分配的512字节块数 8

示例代码

示例1:基本文件信息获取

<?php
// 创建测试文件
$filename = 'test.txt';
file_put_contents($filename, 'Hello, World! This is a test file.');

// 打开文件
$handle = fopen($filename, 'r');
if (!$handle) {
    die('无法打开文件');
}

// 获取文件状态信息
$stat = fstat($handle);
if ($stat === false) {
    die('无法获取文件状态');
}

// 输出所有信息
echo "<h5>文件状态信息:</h5>";
echo "<pre>" . print_r($stat, true) . "</pre>";

// 访问特定信息
echo "<h5>关键信息:</h5>";
echo "文件大小: " . $stat['size'] . " 字节<br>";
echo "最后修改时间: " . date('Y-m-d H:i:s', $stat['mtime']) . "<br>";
echo "最后访问时间: " . date('Y-m-d H:i:s', $stat['atime']) . "<br>";
echo "权限模式: " . decoct($stat['mode']) . " (八进制)<br>";

fclose($handle);
unlink($filename);
?>

示例2:文件权限和类型检查

<?php
// 获取文件类型和权限的辅助函数
function getFileTypeFromMode($mode) {
    $type = $mode & 0xF000; // 文件类型位

    switch ($type) {
        case 0xC000: return 'socket';
        case 0xA000: return 'symbolic link';
        case 0x8000: return 'regular file';
        case 0x6000: return 'block device';
        case 0x4000: return 'directory';
        case 0x2000: return 'character device';
        case 0x1000: return 'FIFO (named pipe)';
        default: return 'unknown';
    }
}

function getPermissionString($mode) {
    $permissions = '';

    // 所有者权限
    $permissions .= (($mode & 0x0100) ? 'r' : '-');
    $permissions .= (($mode & 0x0080) ? 'w' : '-');
    $permissions .= (($mode & 0x0040) ? (($mode & 0x0800) ? 's' : 'x') : (($mode & 0x0800) ? 'S' : '-'));

    // 组权限
    $permissions .= (($mode & 0x0020) ? 'r' : '-');
    $permissions .= (($mode & 0x0010) ? 'w' : '-');
    $permissions .= (($mode & 0x0008) ? (($mode & 0x0400) ? 's' : 'x') : (($mode & 0x0400) ? 'S' : '-'));

    // 其他用户权限
    $permissions .= (($mode & 0x0004) ? 'r' : '-');
    $permissions .= (($mode & 0x0002) ? 'w' : '-');
    $permissions .= (($mode & 0x0001) ? (($mode & 0x0200) ? 't' : 'x') : (($mode & 0x0200) ? 'T' : '-'));

    return $permissions;
}

// 测试函数
function analyzeFile($filename) {
    if (!file_exists($filename)) {
        return "文件不存在";
    }

    $handle = fopen($filename, 'r');
    if (!$handle) {
        return "无法打开文件";
    }

    $stat = fstat($handle);
    fclose($handle);

    if ($stat === false) {
        return "无法获取文件状态";
    }

    $result = [
        'filename' => $filename,
        'type' => getFileTypeFromMode($stat['mode']),
        'permissions' => getPermissionString($stat['mode']),
        'size' => $stat['size'],
        'owner_uid' => $stat['uid'],
        'owner_gid' => $stat['gid'],
        'last_modified' => date('Y-m-d H:i:s', $stat['mtime']),
        'last_accessed' => date('Y-m-d H:i:s', $stat['atime']),
        'inode' => $stat['ino'],
        'device' => $stat['dev']
    ];

    return $result;
}

// 创建测试文件
$filename = 'example.txt';
file_put_contents($filename, 'Test content');
chmod($filename, 0644); // 设置权限

// 分析文件
$analysis = analyzeFile($filename);
if (is_array($analysis)) {
    echo "<h5>文件分析结果:</h5>";
    echo "<table class='table table-bordered'>";
    foreach ($analysis as $key => $value) {
        echo "<tr><td style='width:30%;'><strong>" . ucfirst(str_replace('_', ' ', $key)) . "</strong></td><td>$value</td></tr>";
    }
    echo "</table>";
}

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

示例3:与stat()函数的比较

<?php
// 创建测试文件
$filename = 'comparison.txt';
file_put_contents($filename, 'Test file for stat() vs fstat() comparison.');

echo "<h5>stat() vs fstat() 比较:</h5>";

// 使用stat()函数
echo "<h6>使用stat()函数:</h6>";
$stat1 = stat($filename);
if ($stat1 !== false) {
    echo "文件大小: " . $stat1['size'] . " 字节<br>";
    echo "修改时间: " . date('Y-m-d H:i:s', $stat1['mtime']) . "<br>";
}

// 使用fstat()函数
echo "<h6>使用fstat()函数:</h6>";
$handle = fopen($filename, 'r');
if ($handle) {
    $stat2 = fstat($handle);
    if ($stat2 !== false) {
        echo "文件大小: " . $stat2['size'] . " 字节<br>";
        echo "修改时间: " . date('Y-m-d H:i:s', $stat2['mtime']) . "<br>";
    }
    fclose($handle);
}

// 性能比较
function performanceComparison($filename, $iterations = 1000) {
    $results = [];

    // 测试stat()
    $start = microtime(true);
    for ($i = 0; $i < $iterations; $i++) {
        stat($filename);
    }
    $results['stat'] = microtime(true) - $start;

    // 测试fstat()
    $handle = fopen($filename, 'r');
    $start = microtime(true);
    for ($i = 0; $i < $iterations; $i++) {
        fstat($handle);
    }
    $results['fstat'] = microtime(true) - $start;
    fclose($handle);

    return $results;
}

// 执行性能测试
$iterations = 10000;
$perf = performanceComparison($filename, $iterations);

echo "<h6>性能比较({$iterations}次调用):</h6>";
echo "stat()耗时: " . number_format($perf['stat'], 4) . " 秒<br>";
echo "fstat()耗时: " . number_format($perf['fstat'], 4) . " 秒<br>";

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

示例4:文件监控类

<?php
class FileMonitor {
    private $filename;
    private $handle;
    private $lastStat = null;

    public function __construct($filename) {
        $this->filename = $filename;
        $this->handle = fopen($filename, 'r');

        if (!$this->handle) {
            throw new Exception("无法打开文件: {$filename}");
        }

        // 初始状态
        $this->lastStat = $this->getCurrentStat();
    }

    /**
     * 获取当前文件状态
     */
    private function getCurrentStat() {
        $stat = fstat($this->handle);
        if ($stat === false) {
            throw new Exception("无法获取文件状态");
        }
        return $stat;
    }

    /**
     * 检查文件是否被修改
     */
    public function isModified() {
        $current = $this->getCurrentStat();
        return $current['mtime'] !== $this->lastStat['mtime'];
    }

    /**
     * 检查文件大小是否变化
     */
    public function isSizeChanged() {
        $current = $this->getCurrentStat();
        return $current['size'] !== $this->lastStat['size'];
    }

    /**
     * 检查文件是否被删除(通过inode判断)
     */
    public function isDeleted() {
        clearstatcache(); // 清除缓存
        return !file_exists($this->filename);
    }

    /**
     * 获取文件变化详情
     */
    public function getChanges() {
        $current = $this->getCurrentStat();
        $changes = [];

        if ($current['mtime'] !== $this->lastStat['mtime']) {
            $changes['modified'] = [
                'old' => $this->lastStat['mtime'],
                'new' => $current['mtime'],
                'old_str' => date('Y-m-d H:i:s', $this->lastStat['mtime']),
                'new_str' => date('Y-m-d H:i:s', $current['mtime'])
            ];
        }

        if ($current['size'] !== $this->lastStat['size']) {
            $changes['size'] = [
                'old' => $this->lastStat['size'],
                'new' => $current['size'],
                'diff' => $current['size'] - $this->lastStat['size']
            ];
        }

        // 更新最后状态
        $this->lastStat = $current;

        return $changes;
    }

    /**
     * 监控文件变化(持续监控)
     */
    public function monitor($interval = 1, $maxChecks = 10) {
        $changes = [];

        for ($i = 0; $i < $maxChecks; $i++) {
            $currentChanges = $this->getChanges();

            if (!empty($currentChanges)) {
                $changes[] = [
                    'time' => time(),
                    'changes' => $currentChanges
                ];

                echo "检测到文件变化(" . date('Y-m-d H:i:s') . "):<br>";
                foreach ($currentChanges as $type => $change) {
                    echo "- $type: " . json_encode($change) . "<br>";
                }
            }

            sleep($interval);
        }

        return $changes;
    }

    /**
     * 获取文件统计信息
     */
    public function getStats() {
        return $this->getCurrentStat();
    }

    public function __destruct() {
        if ($this->handle) {
            fclose($this->handle);
        }
    }
}

// 使用示例
try {
    // 创建测试文件
    $filename = 'monitored.txt';
    file_put_contents($filename, 'Initial content');

    // 创建监控器
    $monitor = new FileMonitor($filename);

    echo "<h5>初始文件状态:</h5>";
    $initialStats = $monitor->getStats();
    echo "文件大小: " . $initialStats['size'] . " 字节<br>";
    echo "最后修改时间: " . date('Y-m-d H:i:s', $initialStats['mtime']) . "<br>";

    echo "<h5>模拟文件变化:</h5>";

    // 第一次修改
    sleep(1);
    file_put_contents($filename, 'Modified content - first change');
    echo "第一次修改完成<br>";

    // 检查变化
    if ($monitor->isModified()) {
        echo "检测到文件被修改<br>";
        $changes = $monitor->getChanges();
        echo "变化详情: " . json_encode($changes) . "<br>";
    }

    // 第二次修改
    sleep(1);
    file_put_contents($filename, 'Modified content - second change with more data');
    echo "第二次修改完成<br>";

    // 检查变化
    if ($monitor->isSizeChanged()) {
        echo "检测到文件大小变化<br>";
        $changes = $monitor->getChanges();
        echo "大小变化: " . json_encode($changes['size']) . "<br>";
    }

    // 清理
    unlink($filename);

} catch (Exception $e) {
    echo "错误: " . $e->getMessage();
}
?>

注意事项

重要提示:
  • 缓存机制:PHP会缓存fstat()的结果,使用clearstatcache()清除缓存以获取最新信息
  • 文件句柄要求:fstat()需要有效的文件句柄,而stat()可以直接使用文件名
  • 符号链接:fstat()返回符号链接本身的信息,而lstat()专门用于符号链接
  • 性能差异:对于已打开的文件,fstat()通常比stat()更快,因为它不需要重新打开文件
  • 跨平台差异:不同操作系统(Windows/Unix)返回的数组元素可能有所不同
  • 时间戳精度:某些文件系统可能不支持纳秒级时间戳精度
  • 权限解读:mode字段需要按位操作来解析文件类型和权限
  • 大文件支持:对于大于2GB的文件,size字段在32位系统上可能不正确
  • 网络文件:对于网络文件系统(NFS等),某些信息可能不可用或不准确

fstat() vs stat() vs lstat()

函数 输入 输出 特点 适用场景
fstat() 文件句柄 文件状态数组 需要已打开的文件句柄,性能较好 已打开文件的信息获取
stat() 文件路径 文件状态数组 直接使用文件名,自动解析符号链接 常规文件信息获取
lstat() 文件路径 文件状态数组 专门用于符号链接,不跟随链接 符号链接信息获取

相关函数

  • stat() - 给出文件的信息
  • lstat() - 给出文件或符号链接的信息
  • clearstatcache() - 清除文件状态缓存
  • fileperms() - 取得文件权限
  • fileowner() - 取得文件的所有者
  • filegroup() - 取得文件的组
  • filesize() - 取得文件大小
  • filemtime() - 取得文件修改时间
  • fileatime() - 取得文件访问时间
  • filectime() - 取得文件的inode修改时间

典型应用场景

文件变化监控

监控文件是否被修改、删除或权限变更,用于日志轮转、配置文件监控等。

文件权限检查

检查文件的权限设置,确保安全性和访问控制。

性能优化

在需要多次访问同一文件信息时,使用fstat()比stat()更高效。

文件同步

通过比较文件的mtime和size,判断文件是否需要同步或更新。