PHPfilemtime()函数

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>";
}
?>