PHPfileatime()函数

fileatime() 函数是PHP中用于获取文件最后访问时间的内置函数。它返回文件最后一次被访问的时间戳,这对于日志分析、缓存管理和文件监控等场景非常有用。

语法

int fileatime ( string $filename )

参数说明

参数 描述 类型 是否必需
filename 要检查的文件路径 string

返回值

  • 成功时返回文件最后访问时间的Unix时间戳
  • 失败时返回 FALSE

注意事项

  • fileatime() 返回的是Unix时间戳,需要转换为可读格式
  • 在某些文件系统上,为了提高性能,访问时间可能不会实时更新
  • 文件访问时间的精度取决于文件系统
  • 使用 clearstatcache() 清除文件状态缓存以获得最新信息
  • 对于不存在的文件或权限不足的文件,函数会返回FALSE

示例代码

示例1:基本使用 - 获取文件最后访问时间
<?php
$filename = 'test.txt';

// 获取文件的最后访问时间
$atime = fileatime($filename);

if ($atime !== false) {
    echo "文件最后访问时间戳: " . $atime . "<br>";
    echo "格式化时间: " . date('Y-m-d H:i:s', $atime);
} else {
    echo "无法获取文件访问时间";
}
?>
示例2:比较文件的三种时间
<?php
function getFileTimes($filename) {
    if (!file_exists($filename)) {
        return "文件不存在";
    }

    // 获取三种时间
    $atime = fileatime($filename); // 最后访问时间
    $mtime = filemtime($filename); // 最后修改时间
    $ctime = filectime($filename); // 最后改变时间(inode修改时间)

    // 格式化输出
    return [
        '最后访问时间' => date('Y-m-d H:i:s', $atime),
        '最后修改时间' => date('Y-m-d H:i:s', $mtime),
        '最后改变时间' => date('Y-m-d H:i:s', $ctime)
    ];
}

// 使用示例
$times = getFileTimes('data.txt');
if (is_array($times)) {
    echo "<h4>文件时间信息:</h4>";
    foreach ($times as $label => $time) {
        echo "{$label}: {$time}<br>";
    }
} else {
    echo $times;
}
?>
示例3:监控文件访问频率
<?php
class FileAccessMonitor {
    private $log_file;

    public function __construct($log_file = 'access_monitor.log') {
        $this->log_file = $log_file;
    }

    public function checkFileAccess($filename, $threshold_seconds = 3600) {
        if (!file_exists($filename)) {
            return ['error' => "文件不存在"];
        }

        // 获取当前时间和文件最后访问时间
        $current_time = time();
        $last_access = fileatime($filename);

        // 计算时间差
        $time_diff = $current_time - $last_access;

        // 判断是否在阈值内被访问过
        $recently_accessed = $time_diff <= $threshold_seconds;

        // 记录日志
        $this->logAccess($filename, $last_access, $time_diff, $recently_accessed);

        return [
            'filename' => $filename,
            'last_access' => date('Y-m-d H:i:s', $last_access),
            'time_diff_seconds' => $time_diff,
            'recently_accessed' => $recently_accessed,
            'message' => $recently_accessed ?
                "文件最近被访问过({$time_diff}秒前)" :
                "文件很长时间未被访问({$time_diff}秒前)"
        ];
    }

    private function logAccess($filename, $timestamp, $time_diff, $recently_accessed) {
        $log_entry = sprintf(
            "[%s] 文件: %s, 最后访问: %s, 时间差: %d秒, 状态: %s\n",
            date('Y-m-d H:i:s'),
            $filename,
            date('Y-m-d H:i:s', $timestamp),
            $time_diff,
            $recently_accessed ? '活跃' : '不活跃'
        );

        file_put_contents($this->log_file, $log_entry, FILE_APPEND);
    }
}

// 使用示例
$monitor = new FileAccessMonitor();
$result = $monitor->checkFileAccess('important_config.php', 1800); // 30分钟阈值

echo "<pre>";
print_r($result);
echo "</pre>";
?>
示例4:自动清理长时间未访问的文件
<?php
function cleanupOldFiles($directory, $max_age_days = 30) {
    if (!is_dir($directory)) {
        return "目录不存在: {$directory}";
    }

    $max_age_seconds = $max_age_days * 24 * 3600;
    $current_time = time();
    $deleted_files = [];
    $kept_files = [];

    // 扫描目录
    $files = scandir($directory);

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

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

        // 跳过目录,只处理文件
        if (!is_file($filepath)) {
            continue;
        }

        // 获取最后访问时间
        $atime = fileatime($filepath);
        if ($atime === false) {
            continue; // 无法获取时间,跳过
        }

        // 计算未访问天数
        $days_since_access = floor(($current_time - $atime) / (24 * 3600));

        if (($current_time - $atime) > $max_age_seconds) {
            // 删除文件
            if (unlink($filepath)) {
                $deleted_files[] = [
                    'name' => $file,
                    'last_access' => date('Y-m-d H:i:s', $atime),
                    'days_since_access' => $days_since_access
                ];
            }
        } else {
            $kept_files[] = [
                'name' => $file,
                'last_access' => date('Y-m-d H:i:s', $atime),
                'days_since_access' => $days_since_access
            ];
        }
    }

    return [
        'deleted_count' => count($deleted_files),
        'kept_count' => count($kept_files),
        'deleted_files' => $deleted_files,
        'kept_files' => $kept_files
    ];
}

// 使用示例
$result = cleanupOldFiles('temp_files', 7); // 清理超过7天未访问的文件
echo "清理完成: 删除了 {$result['deleted_count']} 个文件,保留了 {$result['kept_count']} 个文件";
?>
示例5:文件访问时间缓存管理
<?php
class FileTimeCache {
    private $cache = [];
    private $cache_ttl = 60; // 缓存60秒

    public function getAccessTime($filename, $force_refresh = false) {
        // 检查缓存
        if (!$force_refresh && isset($this->cache[$filename])) {
            $cached = $this->cache[$filename];
            if (time() - $cached['timestamp'] < $this->cache_ttl) {
                return $cached['data'];
            }
        }

        // 清除文件状态缓存
        clearstatcache(true, $filename);

        // 获取访问时间
        $atime = fileatime($filename);

        if ($atime === false) {
            return false;
        }

        // 格式化数据
        $result = [
            'timestamp' => $atime,
            'formatted' => date('Y-m-d H:i:s', $atime),
            'relative' => $this->getRelativeTime($atime)
        ];

        // 更新缓存
        $this->cache[$filename] = [
            'data' => $result,
            'timestamp' => time()
        ];

        return $result;
    }

    private function getRelativeTime($timestamp) {
        $diff = time() - $timestamp;

        if ($diff < 60) {
            return "刚刚";
        } elseif ($diff < 3600) {
            return floor($diff / 60) . "分钟前";
        } elseif ($diff < 86400) {
            return floor($diff / 3600) . "小时前";
        } elseif ($diff < 2592000) {
            return floor($diff / 86400) . "天前";
        } else {
            return floor($diff / 2592000) . "个月前";
        }
    }

    public function clearCache() {
        $this->cache = [];
    }
}

// 使用示例
$cache = new FileTimeCache();

// 第一次获取(从文件系统读取)
$time1 = $cache->getAccessTime('data.txt');
echo "第一次获取: " . $time1['formatted'] . " ({$time1['relative']})<br>";

// 第二次获取(从缓存读取)
$time2 = $cache->getAccessTime('data.txt');
echo "第二次获取: " . $time2['formatted'] . " ({$time2['relative']})<br>";

// 强制刷新
$time3 = $cache->getAccessTime('data.txt', true);
echo "强制刷新: " . $time3['formatted'] . " ({$time3['relative']})";
?>
示例6:检测文件是否被篡改
<?php
class FileIntegrityChecker {
    private $baseline_file = 'baseline.json';

    public function createBaseline($directory) {
        $baseline = [];

        $files = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($directory),
            RecursiveIteratorIterator::SELF_FIRST
        );

        foreach ($files as $file) {
            if ($file->isFile()) {
                $filepath = $file->getPathname();
                $baseline[$filepath] = [
                    'atime' => fileatime($filepath),
                    'mtime' => filemtime($filepath),
                    'ctime' => filectime($filepath),
                    'size' => filesize($filepath),
                    'hash' => md5_file($filepath)
                ];
            }
        }

        // 保存基线数据
        file_put_contents(
            $this->baseline_file,
            json_encode($baseline, JSON_PRETTY_PRINT)
        );

        return count($baseline);
    }

    public function checkIntegrity() {
        if (!file_exists($this->baseline_file)) {
            return ['error' => '基线文件不存在'];
        }

        $baseline = json_decode(file_get_contents($this->baseline_file), true);
        $changes = [];

        foreach ($baseline as $filepath => $original_data) {
            if (!file_exists($filepath)) {
                $changes[$filepath] = '文件已删除';
                continue;
            }

            $current_atime = fileatime($filepath);
            $current_mtime = filemtime($filepath);
            $current_ctime = filectime($filepath);
            $current_size = filesize($filepath);
            $current_hash = md5_file($filepath);

            $detected_changes = [];

            if ($current_atime != $original_data['atime']) {
                $detected_changes[] = '访问时间变化';
            }
            if ($current_mtime != $original_data['mtime']) {
                $detected_changes[] = '修改时间变化';
            }
            if ($current_ctime != $original_data['ctime']) {
                $detected_changes[] = 'inode变化';
            }
            if ($current_size != $original_data['size']) {
                $detected_changes[] = '大小变化';
            }
            if ($current_hash != $original_data['hash']) {
                $detected_changes[] = '内容变化';
            }

            if (!empty($detected_changes)) {
                $changes[$filepath] = $detected_changes;
            }
        }

        return [
            'total_files' => count($baseline),
            'changed_files' => count($changes),
            'changes' => $changes
        ];
    }
}

// 使用示例
$checker = new FileIntegrityChecker();

// 创建基线
$file_count = $checker->createBaseline('important_files');
echo "已创建基线,包含 {$file_count} 个文件<br>";

// 检查完整性
$result = $checker->checkIntegrity();
echo "完整性检查结果: {$result['changed_files']} 个文件发生变化";
?>

与类似函数的比较

函数 描述 返回内容 适用场景
fileatime() 获取文件最后访问时间 Unix时间戳 监控文件访问频率、缓存管理
filemtime() 获取文件最后修改时间 Unix时间戳 检测文件内容变化、版本控制
filectime() 获取文件inode最后改变时间 Unix时间戳 检测权限、所有权变化
stat() 获取文件所有状态信息 包含多个时间戳的数组 需要完整文件信息的场景
lstat() 获取文件或符号链接状态 数组(与stat类似) 处理符号链接的场景

性能优化建议

最佳实践
  • 对于频繁查询的文件时间,使用缓存机制避免重复调用
  • 在处理多个文件时,使用clearstatcache()确保获取最新信息
  • 注意某些文件系统可能为了性能而延迟更新访问时间
  • 使用绝对路径而不是相对路径,避免路径解析开销
  • 结合其他文件时间函数(filemtime、filectime)进行综合判断

常见错误和解决方法

常见问题
  • 返回FALSE: 文件不存在或权限不足,先检查file_exists()is_readable()
  • 时间不准确: 某些文件系统为了性能禁用访问时间更新,检查文件系统设置
  • 缓存问题: 使用clearstatcache()清除缓存以获得最新信息
  • 性能问题: 避免在循环中多次调用,考虑批量处理或缓存结果
完整示例:文件访问统计系统
<?php
class FileAccessStatistics {
    private $stats_file = 'access_stats.json';
    private $stats;

    public function __construct() {
        $this->loadStats();
    }

    private function loadStats() {
        if (file_exists($this->stats_file)) {
            $this->stats = json_decode(file_get_contents($this->stats_file), true);
        } else {
            $this->stats = [];
        }
    }

    private function saveStats() {
        file_put_contents(
            $this->stats_file,
            json_encode($this->stats, JSON_PRETTY_PRINT)
        );
    }

    public function recordAccess($filename) {
        clearstatcache(true, $filename);

        $atime = fileatime($filename);
        if ($atime === false) {
            return false;
        }

        $date = date('Y-m-d', $atime);

        if (!isset($this->stats[$filename])) {
            $this->stats[$filename] = [
                'first_access' => $atime,
                'last_access' => $atime,
                'access_count' => 0,
                'daily_stats' => []
            ];
        }

        // 更新统计
        $this->stats[$filename]['last_access'] = $atime;
        $this->stats[$filename]['access_count']++;

        if (!isset($this->stats[$filename]['daily_stats'][$date])) {
            $this->stats[$filename]['daily_stats'][$date] = 0;
        }
        $this->stats[$filename]['daily_stats'][$date]++;

        $this->saveStats();

        return true;
    }

    public function getStats($filename = null) {
        if ($filename) {
            return $this->stats[$filename] ?? null;
        }

        return $this->stats;
    }

    public function getMostAccessedFiles($limit = 10) {
        $files = $this->stats;

        uasort($files, function($a, $b) {
            return $b['access_count'] - $a['access_count'];
        });

        return array_slice($files, 0, $limit, true);
    }
}

// 使用示例
$stats = new FileAccessStatistics();

// 记录文件访问
$stats->recordAccess('config.php');
$stats->recordAccess('database.php');

// 获取统计信息
$top_files = $stats->getMostAccessedFiles(5);

echo "<h4>最常访问的文件:</h4>";
foreach ($top_files as $filename => $data) {
    $first = date('Y-m-d H:i:s', $data['first_access']);
    $last = date('Y-m-d H:i:s', $data['last_access']);
    echo "{$filename}: 访问次数 {$data['access_count']}, 首次访问 {$first}, 最后访问 {$last}<br>";
}
?>