filemtime() 函数是PHP中用于获取文件最后修改时间的内置函数。它返回文件内容最后一次被修改的时间戳,这对于缓存管理、文件同步和更新检测等场景非常有用。
重要提示
filemtime() 返回的是文件内容修改时间(modification time),这与 filectime()(inode修改时间)和 fileatime()(访问时间)有本质区别。只有当文件内容实际改变时,这个时间才会更新。
语法
int filemtime ( string $filename )
参数说明
| 参数 | 描述 | 类型 | 是否必需 |
|---|---|---|---|
| filename | 要检查的文件路径 | string | 是 |
返回值
- 成功时返回文件最后修改时间的Unix时间戳
- 失败时返回 FALSE
注意事项
filemtime()只反映文件内容的变化,不反映权限或所有权的变化- 返回的是Unix时间戳,需要使用
date()函数格式化为可读格式 - 使用
clearstatcache()清除文件状态缓存以获得最新信息 - 对于不存在的文件或权限不足的文件,函数会返回FALSE
- 某些文件系统可能为了性能而延迟更新时间戳
示例代码
示例1:基本使用 - 获取文件最后修改时间
<?php
$filename = 'data.txt';
// 获取文件的最后修改时间
$mtime = filemtime($filename);
if ($mtime !== false) {
echo "文件最后修改时间戳: " . $mtime . "<br>";
echo "格式化时间: " . date('Y-m-d H:i:s', $mtime);
} else {
echo "无法获取文件修改时间";
}
?>
示例2:监控文件变化
<?php
class FileChangeMonitor {
private $monitored_files = [];
public function monitorFile($filename, $callback = null) {
if (!file_exists($filename)) {
return ['status' => 'error', 'message' => '文件不存在'];
}
clearstatcache(true, $filename);
$current_mtime = filemtime($filename);
$last_mtime = $this->monitored_files[$filename] ?? null;
$this->monitored_files[$filename] = $current_mtime;
if ($last_mtime !== null && $current_mtime !== $last_mtime) {
$changed = true;
$message = "文件 {$filename} 已修改 (" .
date('Y-m-d H:i:s', $last_mtime) . " → " .
date('Y-m-d H:i:s', $current_mtime) . ")";
} else {
$changed = false;
$message = "文件 {$filename} 未修改 (" .
date('Y-m-d H:i:s', $current_mtime) . ")";
}
// 如果有回调函数,执行回调
if ($changed && $callback !== null && is_callable($callback)) {
$callback($filename, $last_mtime, $current_mtime);
}
return [
'filename' => $filename,
'changed' => $changed,
'last_mtime' => $last_mtime,
'current_mtime' => $current_mtime,
'message' => $message
];
}
public function monitorMultiple($files) {
$results = [];
$changes = [];
foreach ($files as $filename) {
$result = $this->monitorFile($filename);
$results[] = $result;
if ($result['changed']) {
$changes[] = $result;
}
}
return [
'total_files' => count($results),
'changed_files' => count($changes),
'results' => $results,
'changes' => $changes
];
}
}
// 使用示例
$monitor = new FileChangeMonitor();
// 监控单个文件
$result1 = $monitor->monitorFile('config.php');
echo $result1['message'] . "<br>";
// 监控多个文件
$files = ['index.php', 'style.css', 'script.js'];
$result2 = $monitor->monitorMultiple($files);
echo "监控了 {$result2['total_files']} 个文件,其中 {$result2['changed_files']} 个文件有变化<br>";
// 使用回调函数
$monitor->monitorFile('data.json', function($filename, $old_time, $new_time) {
echo "检测到 {$filename} 变化,触发回调函数<br>";
// 可以在这里执行清理缓存、重新加载配置等操作
});
?>
示例3:缓存系统实现
<?php
class FileBasedCache {
private $cache_dir;
private $default_ttl = 3600; // 默认缓存1小时
public function __construct($cache_dir = 'cache') {
$this->cache_dir = $cache_dir;
// 确保缓存目录存在
if (!file_exists($cache_dir)) {
mkdir($cache_dir, 0755, true);
}
}
public function get($key, $source_file = null) {
$cache_file = $this->getCacheFile($key);
// 如果缓存文件不存在
if (!file_exists($cache_file)) {
return $this->refreshCache($key, $source_file);
}
// 如果提供了源文件,检查是否过期
if ($source_file !== null && file_exists($source_file)) {
$cache_mtime = filemtime($cache_file);
$source_mtime = filemtime($source_file);
// 如果源文件比缓存文件新,刷新缓存
if ($source_mtime > $cache_mtime) {
return $this->refreshCache($key, $source_file);
}
}
// 检查TTL是否过期
$cache_age = time() - filemtime($cache_file);
if ($cache_age > $this->default_ttl) {
return $this->refreshCache($key, $source_file);
}
// 返回缓存内容
$content = file_get_contents($cache_file);
return unserialize($content);
}
public function set($key, $data, $ttl = null) {
$cache_file = $this->getCacheFile($key);
$content = serialize($data);
file_put_contents($cache_file, $content);
// 设置文件修改时间为未来,用于TTL计算
if ($ttl !== null) {
$future_time = time() + $ttl;
touch($cache_file, $future_time);
}
return true;
}
public function delete($key) {
$cache_file = $this->getCacheFile($key);
if (file_exists($cache_file)) {
return unlink($cache_file);
}
return true;
}
public function clear() {
$files = glob($this->cache_dir . '/*.cache');
foreach ($files as $file) {
if (is_file($file)) {
unlink($file);
}
}
return count($files);
}
public function getStats() {
$files = glob($this->cache_dir . '/*.cache');
$stats = [
'total_files' => count($files),
'total_size' => 0,
'oldest_file' => null,
'newest_file' => null,
'expired_files' => 0
];
$now = time();
$oldest_time = PHP_INT_MAX;
$newest_time = 0;
foreach ($files as $file) {
$size = filesize($file);
$mtime = filemtime($file);
$stats['total_size'] += $size;
// 检查是否过期
if ($mtime < $now) {
$stats['expired_files']++;
}
// 更新最旧/最新文件信息
if ($mtime < $oldest_time) {
$oldest_time = $mtime;
$stats['oldest_file'] = [
'file' => basename($file),
'mtime' => $mtime,
'formatted' => date('Y-m-d H:i:s', $mtime)
];
}
if ($mtime > $newest_time) {
$newest_time = $mtime;
$stats['newest_file'] = [
'file' => basename($file),
'mtime' => $mtime,
'formatted' => date('Y-m-d H:i:s', $mtime)
];
}
}
$stats['total_size_mb'] = round($stats['total_size'] / (1024 * 1024), 2);
return $stats;
}
private function getCacheFile($key) {
$safe_key = preg_replace('/[^a-zA-Z0-9_-]/', '_', $key);
return $this->cache_dir . '/' . $safe_key . '.cache';
}
private function refreshCache($key, $source_file) {
// 这里应该包含实际的缓存生成逻辑
// 示例:从源文件读取数据并处理
if ($source_file !== null && file_exists($source_file)) {
$data = file_get_contents($source_file);
// 处理数据...
$processed_data = json_decode($data, true);
$this->set($key, $processed_data);
return $processed_data;
}
return null;
}
}
// 使用示例
$cache = new FileBasedCache();
// 设置缓存(缓存1小时)
$data = ['name' => 'John', 'age' => 30, 'city' => 'New York'];
$cache->set('user_profile', $data, 3600);
// 获取缓存
$cached_data = $cache->get('user_profile');
if ($cached_data) {
echo "从缓存中获取数据: " . print_r($cached_data, true) . "<br>";
}
// 基于源文件变化的缓存
$config_data = $cache->get('app_config', 'config.json');
echo "配置文件缓存: " . ($config_data ? '已缓存' : '未缓存') . "<br>";
// 获取缓存统计
$stats = $cache->getStats();
echo "缓存统计: {$stats['total_files']} 个文件, {$stats['total_size_mb']} MB, {$stats['expired_files']} 个已过期文件<br>";
?>
示例4:静态资源版本控制
<?php
class AssetVersioner {
private $base_path;
private $cache = [];
public function __construct($base_path = '') {
$this->base_path = $base_path;
}
public function version($asset_path) {
// 检查缓存
if (isset($this->cache[$asset_path])) {
return $this->cache[$asset_path];
}
$full_path = $this->base_path . $asset_path;
if (!file_exists($full_path)) {
// 文件不存在,返回原始路径
$this->cache[$asset_path] = $asset_path;
return $asset_path;
}
// 获取文件修改时间作为版本号
$mtime = filemtime($full_path);
// 创建版本化路径
$pathinfo = pathinfo($asset_path);
$versioned_path = $pathinfo['dirname'] . '/' .
$pathinfo['filename'] . '.' . $mtime .
(isset($pathinfo['extension']) ? '.' . $pathinfo['extension'] : '');
$this->cache[$asset_path] = $versioned_path;
return $versioned_path;
}
public function htmlTag($asset_path, $attributes = []) {
$versioned_path = $this->version($asset_path);
$pathinfo = pathinfo($asset_path);
$extension = strtolower($pathinfo['extension'] ?? '');
$default_attributes = ['href' => $versioned_path, 'rel' => 'stylesheet'];
$attributes = array_merge($default_attributes, $attributes);
switch ($extension) {
case 'css':
return '<link' . $this->buildAttributes($attributes) . '>';
case 'js':
unset($attributes['rel']);
$attributes['src'] = $versioned_path;
return '<script' . $this->buildAttributes($attributes) . '></script>';
case 'jpg':
case 'jpeg':
case 'png':
case 'gif':
case 'svg':
$attributes['src'] = $versioned_path;
return '<img' . $this->buildAttributes($attributes) . '>';
default:
return $versioned_path;
}
}
public function buildAttributes($attributes) {
$html = '';
foreach ($attributes as $key => $value) {
$html .= ' ' . htmlspecialchars($key) . '="' . htmlspecialchars($value) . '"';
}
return $html;
}
public function clearCache() {
$this->cache = [];
}
}
// 使用示例
$versioner = new AssetVersioner('/var/www/html/public/');
// 生成版本化的CSS链接
echo $versioner->htmlTag('css/app.css') . "<br>";
// 输出: <link href="css/app.1625078400.css" rel="stylesheet">
// 生成版本化的JS链接
echo $versioner->htmlTag('js/main.js') . "<br>";
// 输出: <script src="js/main.1625078400.js"></script>
// 生成版本化的图片
echo $versioner->htmlTag('images/logo.png', ['alt' => 'Logo', 'class' => 'logo']) . "<br>";
// 输出: <img src="images/logo.1625078400.png" alt="Logo" class="logo">
// 直接获取版本化路径
$versioned_path = $versioner->version('assets/data.json');
echo "版本化路径: {$versioned_path}<br>";
// 在模板中使用
function asset($path) {
static $versioner = null;
if ($versioner === null) {
$versioner = new AssetVersioner(__DIR__ . '/public/');
}
return $versioner->version($path);
}
echo '<link href="' . asset('css/style.css') . '" rel="stylesheet">';
?>
示例5:自动部署检测系统
<?php
class DeploymentMonitor {
private $deployment_file = 'deployment.json';
private $lock_file = 'deployment.lock';
private $max_lock_time = 300; // 5分钟
public function checkForDeployment() {
// 检查是否有部署锁
if ($this->isLocked()) {
return ['status' => 'locked', 'message' => '部署进行中,请稍后重试'];
}
// 获取当前部署信息
$current_deployment = $this->getCurrentDeployment();
// 扫描关键文件变化
$changes = $this->scanForChanges($current_deployment);
if (empty($changes['changed_files']) && empty($changes['new_files']) && empty($changes['deleted_files'])) {
return [
'status' => 'unchanged',
'message' => '没有检测到文件变化',
'last_deployment' => $current_deployment['timestamp'] ?? null
];
}
// 创建锁文件,防止并发部署
$this->createLock();
try {
// 执行部署前检查
$pre_check = $this->preDeploymentCheck($changes);
if (!$pre_check['success']) {
$this->removeLock();
return [
'status' => 'failed',
'message' => '部署前检查失败: ' . $pre_check['message']
];
}
// 执行部署操作
$deployment_result = $this->executeDeployment($changes);
// 更新部署记录
$this->updateDeploymentRecord($changes, $deployment_result);
$this->removeLock();
return [
'status' => 'success',
'message' => '部署完成',
'changes' => $changes,
'deployment_result' => $deployment_result,
'timestamp' => time()
];
} catch (Exception $e) {
$this->removeLock();
return [
'status' => 'error',
'message' => '部署过程中发生错误: ' . $e->getMessage()
];
}
}
private function isLocked() {
if (!file_exists($this->lock_file)) {
return false;
}
$lock_time = filemtime($this->lock_file);
$lock_age = time() - $lock_time;
// 如果锁文件存在时间过长,可能是异常中断,自动清除
if ($lock_age > $this->max_lock_time) {
unlink($this->lock_file);
return false;
}
return true;
}
private function createLock() {
file_put_contents($this->lock_file, time());
}
private function removeLock() {
if (file_exists($this->lock_file)) {
unlink($this->lock_file);
}
}
private function getCurrentDeployment() {
if (!file_exists($this->deployment_file)) {
return ['timestamp' => 0, 'files' => []];
}
$data = file_get_contents($this->deployment_file);
return json_decode($data, true) ?? ['timestamp' => 0, 'files' => []];
}
private function scanForChanges($last_deployment) {
$changes = [
'changed_files' => [],
'new_files' => [],
'deleted_files' => []
];
// 扫描目录中的关键文件
$directories = ['app/', 'config/', 'public/css/', 'public/js/'];
foreach ($directories as $dir) {
if (!is_dir($dir)) continue;
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $file) {
if ($file->isDot()) continue;
$filepath = $file->getPathname();
$relative_path = str_replace('\\', '/', substr($filepath, strlen($dir) - 1));
clearstatcache(true, $filepath);
if ($file->isFile()) {
$current_mtime = filemtime($filepath);
// 检查文件是否在上一轮部署中
if (isset($last_deployment['files'][$relative_path])) {
$last_mtime = $last_deployment['files'][$relative_path];
if ($current_mtime > $last_mtime) {
$changes['changed_files'][$relative_path] = [
'old_mtime' => $last_mtime,
'new_mtime' => $current_mtime,
'size' => $file->getSize()
];
}
} else {
// 新文件
$changes['new_files'][$relative_path] = [
'mtime' => $current_mtime,
'size' => $file->getSize()
];
}
}
}
}
// 检查删除的文件
if (isset($last_deployment['files'])) {
foreach ($last_deployment['files'] as $filepath => $mtime) {
$full_path = $this->resolvePath($filepath);
if (!file_exists($full_path)) {
$changes['deleted_files'][$filepath] = $mtime;
}
}
}
return $changes;
}
private function preDeploymentCheck($changes) {
// 检查是否有PHP语法错误
foreach (array_merge(
array_keys($changes['changed_files']),
array_keys($changes['new_files'])
) as $file) {
if (pathinfo($file, PATHINFO_EXTENSION) === 'php') {
$full_path = $this->resolvePath($file);
$check_result = $this->checkPhpSyntax($full_path);
if (!$check_result['valid']) {
return [
'success' => false,
'message' => "PHP语法错误: {$file} - " . $check_result['error']
];
}
}
}
return ['success' => true, 'message' => '所有检查通过'];
}
private function checkPhpSyntax($filename) {
$output = null;
$return_var = null;
exec("php -l " . escapeshellarg($filename) . " 2>&1", $output, $return_var);
if ($return_var !== 0) {
return [
'valid' => false,
'error' => implode("\n", $output)
];
}
return ['valid' => true, 'output' => $output];
}
private function executeDeployment($changes) {
$result = [
'timestamp' => time(),
'actions' => []
];
// 清理缓存
$this->clearCaches();
$result['actions'][] = '缓存已清理';
// 这里可以添加更多的部署操作
// 例如:运行数据库迁移、重启服务等
return $result;
}
private function updateDeploymentRecord($changes, $deployment_result) {
$new_record = [
'timestamp' => time(),
'files' => []
];
// 扫描当前所有文件
$directories = ['app/', 'config/', 'public/css/', 'public/js/'];
foreach ($directories as $dir) {
if (!is_dir($dir)) continue;
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $file) {
if ($file->isDot()) continue;
if ($file->isFile()) {
$filepath = $file->getPathname();
$relative_path = str_replace('\\', '/', substr($filepath, strlen($dir) - 1));
$new_record['files'][$relative_path] = filemtime($filepath);
}
}
}
// 保存部署记录
file_put_contents(
$this->deployment_file,
json_encode($new_record, JSON_PRETTY_PRINT)
);
}
private function clearCaches() {
// 清理常见的缓存目录
$cache_dirs = ['cache/', 'tmp/', 'storage/framework/cache/'];
foreach ($cache_dirs as $dir) {
if (is_dir($dir)) {
$this->clearDirectory($dir);
}
}
// 清理opcache(如果启用)
if (function_exists('opcache_reset')) {
opcache_reset();
}
}
private function clearDirectory($dir) {
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($iterator as $file) {
if ($file->isDir()) {
rmdir($file->getPathname());
} else {
unlink($file->getPathname());
}
}
}
private function resolvePath($relative_path) {
// 根据实际情况解析路径
$base_dirs = ['app/', 'config/', 'public/css/', 'public/js/'];
foreach ($base_dirs as $dir) {
$full_path = $dir . ltrim($relative_path, '/');
if (file_exists($full_path)) {
return $full_path;
}
}
return $relative_path;
}
}
// 使用示例
$monitor = new DeploymentMonitor();
$result = $monitor->checkForDeployment();
echo "<h4>部署检测结果:</h4>";
echo "状态: {$result['status']}<br>";
echo "消息: {$result['message']}<br>";
if ($result['status'] === 'success' && isset($result['changes'])) {
$changes = $result['changes'];
echo "<h5>文件变化:</h5>";
echo "修改的文件: " . count($changes['changed_files']) . "<br>";
echo "新增的文件: " . count($changes['new_files']) . "<br>";
echo "删除的文件: " . count($changes['deleted_files']) . "<br>";
if (!empty($changes['changed_files'])) {
echo "<h6>修改的文件详情:</h6>";
foreach (array_slice($changes['changed_files'], 0, 3) as $file => $info) {
echo "{$file} - " . date('Y-m-d H:i:s', $info['new_mtime']) . "<br>";
}
}
}
?>
与类似函数的比较
| 函数 | 描述 | 返回内容 | 适用场景 |
|---|---|---|---|
filemtime() |
获取文件内容修改时间 | Unix时间戳 | 检测文件内容变化、缓存失效 |
filectime() |
获取文件inode修改时间 | Unix时间戳 | 检测文件权限、所有权变化 |
fileatime() |
获取文件最后访问时间 | Unix时间戳 | 监控文件访问频率 |
stat() |
获取文件所有状态信息 | 包含多个时间戳的数组 | 需要完整文件信息的场景 |
touch() |
设置文件访问和修改时间 | 布尔值(成功/失败) | 修改文件时间戳 |
性能优化建议
最佳实践
- 对于频繁检查的文件,缓存
filemtime()的结果以避免重复调用 - 在处理多个文件时,使用
clearstatcache()确保获取最新信息 - 使用绝对路径而不是相对路径,避免路径解析开销
- 对于大型目录树,考虑使用文件系统监控扩展(如inotify)而不是轮询
- 将
filemtime()与其他文件信息函数结合使用,一次性获取所需信息 - 在Web应用中,避免在每次请求中都检查大量文件的修改时间
常见应用场景
缓存管理
通过比较源文件和缓存文件的filemtime(),确定是否需要更新缓存。
版本控制
使用文件修改时间作为静态资源的版本号,解决浏览器缓存问题。
自动部署
监控文件变化,自动触发部署流程,提高开发效率。
完整示例:智能文件监控系统
<?php
class IntelligentFileMonitor {
private $config = [
'monitor_directories' => ['app/', 'config/', 'public/'],
'ignore_patterns' => ['/\.git/', '/\.svn/', '/\.idea/', '/node_modules/', '/vendor/'],
'cache_ttl' => 60, // 缓存60秒
'max_files_per_scan' => 1000
];
private $file_cache = [];
private $last_scan_time = 0;
public function scanForChanges() {
$now = time();
// 如果最近扫描过且缓存未过期,使用缓存
if (($now - $this->last_scan_time) < $this->config['cache_ttl'] && !empty($this->file_cache)) {
return ['status' => 'cached', 'message' => '使用缓存数据'];
}
$changes = [
'new_files' => [],
'modified_files' => [],
'deleted_files' => [],
'scan_stats' => [
'total_scanned' => 0,
'total_processed' => 0,
'scan_duration' => 0
]
];
$start_time = microtime(true);
$file_count = 0;
foreach ($this->config['monitor_directories'] as $directory) {
if (!is_dir($directory)) {
continue;
}
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($directory),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $file) {
if ($file->isDot()) {
continue;
}
// 限制扫描文件数量
if ($file_count++ >= $this->config['max_files_per_scan']) {
break 2;
}
$filepath = $file->getPathname();
// 检查是否应该忽略此文件
if ($this->shouldIgnore($filepath)) {
continue;
}
$relative_path = $this->getRelativePath($filepath, $directory);
$changes['scan_stats']['total_scanned']++;
if ($file->isFile()) {
clearstatcache(true, $filepath);
$current_mtime = filemtime($filepath);
$current_size = $file->getSize();
// 检查文件是否在缓存中
if (isset($this->file_cache[$relative_path])) {
$cached_info = $this->file_cache[$relative_path];
if ($cached_info['mtime'] !== $current_mtime) {
$changes['modified_files'][$relative_path] = [
'old_mtime' => $cached_info['mtime'],
'new_mtime' => $current_mtime,
'old_size' => $cached_info['size'],
'new_size' => $current_size,
'type' => pathinfo($filepath, PATHINFO_EXTENSION)
];
$changes['scan_stats']['total_processed']++;
}
} else {
// 新文件
$changes['new_files'][$relative_path] = [
'mtime' => $current_mtime,
'size' => $current_size,
'type' => pathinfo($filepath, PATHINFO_EXTENSION)
];
$changes['scan_stats']['total_processed']++;
}
// 更新缓存
$this->file_cache[$relative_path] = [
'mtime' => $current_mtime,
'size' => $current_size
];
}
}
}
// 检查删除的文件
foreach ($this->file_cache as $cached_path => $cached_info) {
$full_path = $this->resolvePath($cached_path);
if (!file_exists($full_path)) {
$changes['deleted_files'][$cached_path] = $cached_info;
$changes['scan_stats']['total_processed']++;
unset($this->file_cache[$cached_path]);
}
}
$changes['scan_stats']['scan_duration'] = round(microtime(true) - $start_time, 3);
$this->last_scan_time = $now;
return [
'status' => 'success',
'timestamp' => $now,
'changes' => $changes,
'summary' => [
'scanned' => $changes['scan_stats']['total_scanned'],
'new' => count($changes['new_files']),
'modified' => count($changes['modified_files']),
'deleted' => count($changes['deleted_files'])
]
];
}
private function shouldIgnore($filepath) {
foreach ($this->config['ignore_patterns'] as $pattern) {
if (preg_match($pattern, $filepath)) {
return true;
}
}
return false;
}
private function getRelativePath($filepath, $base_directory) {
$base_len = strlen($base_directory);
if (strpos($filepath, $base_directory) === 0) {
return substr($filepath, $base_len);
}
return $filepath;
}
private function resolvePath($relative_path) {
foreach ($this->config['monitor_directories'] as $directory) {
$full_path = $directory . $relative_path;
if (file_exists($full_path)) {
return $full_path;
}
}
return $relative_path;
}
public function getFileHistory($relative_path, $limit = 10) {
// 这里可以集成版本控制系统或自定义历史记录
// 示例:读取文件的备份或日志
$history = [];
$backup_dir = 'backups/files/';
if (is_dir($backup_dir)) {
$pattern = $backup_dir . preg_quote($relative_path, '/') . '\..*\.bak';
$backups = glob($pattern);
foreach ($backups as $backup) {
if (preg_match('/\.(\d+)\.bak$/', $backup, $matches)) {
$timestamp = $matches[1];
$history[] = [
'timestamp' => $timestamp,
'formatted' => date('Y-m-d H:i:s', $timestamp),
'size' => filesize($backup),
'path' => $backup
];
}
}
}
// 按时间倒序排序
usort($history, function($a, $b) {
return $b['timestamp'] - $a['timestamp'];
});
return array_slice($history, 0, $limit);
}
public function exportReport($format = 'json') {
$scan_result = $this->scanForChanges();
switch ($format) {
case 'json':
return json_encode($scan_result, JSON_PRETTY_PRINT);
case 'html':
return $this->generateHtmlReport($scan_result);
case 'text':
return $this->generateTextReport($scan_result);
default:
return $scan_result;
}
}
private function generateHtmlReport($data) {
$html = "<!DOCTYPE html>\n";
$html .= "<html><head><title>文件监控报告</title></head><body>\n";
$html .= "<h1>文件监控报告</h1>\n";
$html .= "<p>生成时间: " . date('Y-m-d H:i:s') . "</p>\n";
$summary = $data['summary'];
$html .= "<h2>摘要</h2>\n";
$html .= "<ul>\n";
$html .= "<li>扫描文件数: " . $summary['scanned'] . "</li>\n";
$html .= "<li>新增文件: " . $summary['new'] . "</li>\n";
$html .= "<li>修改文件: " . $summary['modified'] . "</li>\n";
$html .= "<li>删除文件: " . $summary['deleted'] . "</li>\n";
$html .= "</ul>\n";
if (!empty($data['changes']['new_files'])) {
$html .= "<h3>新增文件</h3>\n<ul>\n";
foreach ($data['changes']['new_files'] as $file => $info) {
$html .= "<li>{$file} (" . date('Y-m-d H:i:s', $info['mtime']) . ")</li>\n";
}
$html .= "</ul>\n";
}
return $html . "</body></html>";
}
private function generateTextReport($data) {
$text = "文件监控报告\n";
$text .= "==============\n\n";
$text .= "生成时间: " . date('Y-m-d H:i:s') . "\n\n";
$summary = $data['summary'];
$text .= "摘要:\n";
$text .= "- 扫描文件数: " . $summary['scanned'] . "\n";
$text .= "- 新增文件: " . $summary['new'] . "\n";
$text .= "- 修改文件: " . $summary['modified'] . "\n";
$text .= "- 删除文件: " . $summary['deleted'] . "\n\n";
return $text;
}
}
// 使用示例
$monitor = new IntelligentFileMonitor();
// 扫描变化
$result = $monitor->scanForChanges();
if ($result['status'] === 'success') {
echo "<h4>智能文件监控结果:</h4>";
echo "扫描时间: " . date('Y-m-d H:i:s', $result['timestamp']) . "<br>";
$summary = $result['summary'];
echo "扫描统计:<br>";
echo "扫描文件数: {$summary['scanned']}<br>";
echo "新增文件: {$summary['new']}<br>";
echo "修改文件: {$summary['modified']}<br>";
echo "删除文件: {$summary['deleted']}<br>";
// 获取文件历史
$history = $monitor->getFileHistory('config/app.php', 3);
if (!empty($history)) {
echo "<h5>配置文件历史:</h5>";
foreach ($history as $record) {
echo "{$record['formatted']} - {$record['size']} 字节<br>";
}
}
// 导出报告
$json_report = $monitor->exportReport('json');
$report_file = 'monitor_report_' . date('Ymd_His') . '.json';
file_put_contents($report_file, $json_report);
echo "<div class='alert alert-info'>报告已保存到: {$report_file}</div>";
}
?>