文件系统函数

PHP提供了丰富的文件系统函数,用于操作文件、目录和获取文件系统信息。本节将介绍这些常用函数的用法和实际应用场景。

学习目标

完成本节学习后,你将能够:

  • 掌握常用的文件系统函数
  • 理解文件权限和属性操作
  • 学会路径处理和文件信息获取
  • 了解文件系统函数的实际应用

文件信息函数

1. 基本文件信息

<?php
// 文件信息演示类
class FileInfoDemo {
    private $testFile;

    public function __construct() {
        $this->testFile = 'demo_file.txt';
        $this->createTestFile();
    }

    private function createTestFile() {
        $content = "这是一个演示文件\n用于展示文件系统函数的功能\n包含多行文本内容";
        file_put_contents($this->testFile, $content);
    }

    // 显示文件基本信息
    public function displayBasicInfo() {
        echo "<h3>文件基本信息</h3>";

        $file = $this->testFile;

        // 检查文件是否存在
        echo "文件存在:" . (file_exists($file) ? "是" : "否") . "<br>";

        if (file_exists($file)) {
            // 文件类型检查
            echo "是文件:" . (is_file($file) ? "是" : "否") . "<br>";
            echo "是目录:" . (is_dir($file) ? "是" : "否") . "<br>";
            echo "是链接:" . (is_link($file) ? "是" : "否") . "<br>";

            // 文件可访问性
            echo "可读:" . (is_readable($file) ? "是" : "否") . "<br>";
            echo "可写:" . (is_writable($file) ? "是" : "否") . "<br>";
            echo "可执行:" . (is_executable($file) ? "是" : "否") . "<br>";

            // 文件大小
            $size = filesize($file);
            echo "文件大小:" . $size . " 字节 (" . $this->formatBytes($size) . ")<br>";

            // 文件时间信息
            echo "创建时间:" . date('Y-m-d H:i:s', filectime($file)) . "<br>";
            echo "修改时间:" . date('Y-m-d H:i:s', filemtime($file)) . "<br>";
            echo "访问时间:" . date('Y-m-d H:i:s', fileatime($file)) . "<br>";

            // 文件权限
            $perms = fileperms($file);
            echo "权限(八进制):" . substr(sprintf('%o', $perms), -4) . "<br>";
            echo "权限(符号):" . $this->getPermissionSymbolic($perms) . "<br>";

            // 文件所有者和组
            if (function_exists('posix_getpwuid')) {
                $owner = posix_getpwuid(fileowner($file));
                echo "所有者:" . $owner['name'] . "<br>";
            }

            if (function_exists('posix_getgrgid')) {
                $group = posix_getgrgid(filegroup($file));
                echo "组:" . $group['name'] . "<br>";
            }

            // 文件类型
            $fileType = filetype($file);
            echo "文件类型:" . $fileType . "<br>";

            // MIME类型
            $mimeType = $this->getMimeType($file);
            echo "MIME类型:" . $mimeType . "<br>";
        }
    }

    // 格式化字节大小
    private function formatBytes($bytes) {
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];
        $bytes = max(0, $bytes);
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow = min($pow, count($units) - 1);

        $bytes /= pow(1024, $pow);
        return round($bytes, 2) . ' ' . $units[$pow];
    }

    // 获取符号权限表示
    private function getPermissionSymbolic($perms) {
        $info = '';

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

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

        // 其他权限
        $info .= ($perms & 0x0004) ? 'r' : '-';
        $info .= ($perms & 0x0002) ? 'w' : '-';
        $info .= ($perms & 0x0001) ? 'x' : '-';

        return $info;
    }

    // 获取MIME类型
    private function getMimeType($file) {
        if (function_exists('finfo_open')) {
            $finfo = finfo_open(FILEINFO_MIME_TYPE);
            $mimeType = finfo_file($finfo, $file);
            finfo_close($finfo);
            return $mimeType;
        }
        return 'application/octet-stream';
    }

    // 演示pathinfo函数
    public function demonstratePathinfo() {
        echo "<h3>路径信息分解</h3>";

        $path = '/var/www/html/project/index.php';
        $info = pathinfo($path);

        echo "<table border='1'>";
        echo "<tr><th>属性</th><th>值</th></tr>";

        echo "<tr><td>完整路径</td><td>" . htmlspecialchars($path) . "</td></tr>";
        echo "<tr><td>目录名</td><td>" . htmlspecialchars($info['dirname']) . "</td></tr>";
        echo "<tr><td>文件名</td><td>" . htmlspecialchars($info['basename']) . "</td></tr>";
        echo "<tr><td>扩展名</td><td>" . htmlspecialchars($info['extension']) . "</td></tr>";
        echo "<tr><td>文件名(无扩展名)</td><td>" . htmlspecialchars($info['filename']) . "</td></tr>";

        echo "</table>";

        // 使用pathinfo常量
        echo "<h4>使用pathinfo常量</h4>";
        echo "目录名:" . pathinfo($path, PATHINFO_DIRNAME) . "<br>";
        echo "文件名:" . pathinfo($path, PATHINFO_BASENAME) . "<br>";
        echo "扩展名:" . pathinfo($path, PATHINFO_EXTENSION) . "<br>";
        echo "文件名(无扩展名):" . pathinfo($path, PATHINFO_FILENAME) . "<br>";
    }

    // 清理测试文件
    public function cleanup() {
        if (file_exists($this->testFile)) {
            unlink($this->testFile);
        }
    }
}

// 使用示例
$demo = new FileInfoDemo();
$demo->displayBasicInfo();
echo "<hr>";
$demo->demonstratePathinfo();
$demo->cleanup();
?>

2. 文件比较和差异

<?php
// 文件比较工具类
class FileComparator {
    // 比较两个文件是否相同
    public function compareFiles($file1, $file2) {
        if (!file_exists($file1) || !file_exists($file2)) {
            throw new Exception("文件不存在");
        }

        // 比较文件大小
        $size1 = filesize($file1);
        $size2 = filesize($file2);

        if ($size1 !== $size2) {
            return [
                'identical' => false,
                'reason' => '文件大小不同',
                'size1' => $size1,
                'size2' => $size2
            ];
        }

        // 比较文件内容
        $content1 = file_get_contents($file1);
        $content2 = file_get_contents($file2);

        if ($content1 === $content2) {
            return [
                'identical' => true,
                'reason' => '文件完全相同'
            ];
        } else {
            // 找到第一个不同的位置
            $diffPosition = $this->findFirstDifference($content1, $content2);
            return [
                'identical' => false,
                'reason' => '文件内容不同',
                'first_diff_position' => $diffPosition
            ];
        }
    }

    // 找到第一个不同的位置
    private function findFirstDifference($str1, $str2) {
        $len = min(strlen($str1), strlen($str2));
        for ($i = 0; $i < $len; $i++) {
            if ($str1[$i] !== $str2[$i]) {
                return $i;
            }
        }
        return $len; // 字符串长度不同
    }

    // 显示文件差异
    public function showDiff($file1, $file2) {
        echo "<h3>文件差异比较</h3>";

        try {
            $result = $this->compareFiles($file1, $file2);

            echo "<p><strong>比较结果:</strong></p>";
            if ($result['identical']) {
                echo "<p style='color: green;'>✓ 文件完全相同</p>";
            } else {
                echo "<p style='color: red;'>✗ " . $result['reason'] . "</p>";

                if (isset($result['size1'])) {
                    echo "<p>文件1大小:{$result['size1']} 字节</p>";
                    echo "<p>文件2大小:{$result['size2']} 字节</p>";
                }

                if (isset($result['first_diff_position'])) {
                    echo "<p>第一个不同位置:第 {$result['first_diff_position']} 个字符</p>";
                }
            }

        } catch (Exception $e) {
            echo "<p style='color: red;'>错误:" . $e->getMessage() . "</p>";
        }
    }

    // 比较文件修改时间
    public function compareModificationTimes($file1, $file2) {
        if (!file_exists($file1) || !file_exists($file2)) {
            throw new Exception("文件不存在");
        }

        $time1 = filemtime($file1);
        $time2 = filemtime($file2);

        if ($time1 === $time2) {
            return 'same';
        } elseif ($time1 > $time2) {
            return 'newer';
        } else {
            return 'older';
        }
    }
}

// 使用示例
try {
    // 创建测试文件
    file_put_contents('test1.txt', "Hello World\nThis is a test file.");
    file_put_contents('test2.txt', "Hello World\nThis is a test file.");
    file_put_contents('test3.txt', "Hello World\nThis is a different file.");

    $comparator = new FileComparator();

    // 比较相同文件
    $comparator->showDiff('test1.txt', 'test2.txt');

    // 比较不同文件
    $comparator->showDiff('test1.txt', 'test3.txt');

    // 比较修改时间
    sleep(1); // 等待1秒
    file_put_contents('test1.txt', "Hello World\nThis is an updated file.");

    $timeComparison = $comparator->compareModificationTimes('test1.txt', 'test2.txt');
    echo "<p>修改时间比较:test1.txt 相对于 test2.txt 是 {$timeComparison}</p>";

    // 清理测试文件
    unlink('test1.txt');
    unlink('test2.txt');
    unlink('test3.txt');

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

路径操作函数

1. 路径处理

<?php
// 路径处理演示类
class PathHandler {
    // 演示各种路径函数
    public function demonstratePathFunctions() {
        echo "<h3>路径处理函数演示</h3>";

        // 基本路径信息
        echo "<h4>基本路径信息</h4>";
        echo "当前工作目录:" . getcwd() . "<br>";
        echo "脚本文件路径:" . __FILE__ . "<br>";
        echo "脚本目录:" . __DIR__ . "<br>";
        echo "目录分隔符:" . DIRECTORY_SEPARATOR . "<br>";
        echo "路径分隔符:" . PATH_SEPARATOR . "<br>";

        // 路径组合
        echo "<h4>路径组合</h4>";
        $parts = ['home', 'user', 'documents', 'file.txt'];
        $path = implode(DIRECTORY_SEPARATOR, $parts);
        echo "组合路径:" . $path . "<br>";

        // 不同系统的路径规范化
        echo "<h4>路径规范化</h4>";
        $paths = [
            '/var/www/../html/index.php',
            'C:\\Users\\..\\Documents\\file.txt',
            './config/../data/file.txt',
            'folder/subfolder/../../file.txt'
        ];

        foreach ($paths as $path) {
            $normalized = $this->normalizePath($path);
            echo "原始路径:" . htmlspecialchars($path) . "<br>";
            echo "规范化路径:" . htmlspecialchars($normalized) . "<br>";
            echo "<hr>";
        }
    }

    // 路径规范化函数
    private function normalizePath($path) {
        // 替换反斜杠为正斜杠(便于处理)
        $path = str_replace('\\', '/', $path);

        // 分割路径
        $parts = explode('/', $path);
        $normalized = [];

        foreach ($parts as $part) {
            if ($part === '' || $part === '.') {
                continue;
            } elseif ($part === '..') {
                if (!empty($normalized)) {
                    array_pop($normalized);
                }
            } else {
                $normalized[] = $part;
            }
        }

        // 处理绝对路径
        if (strpos($path, '/') === 0) {
            return '/' . implode(DIRECTORY_SEPARATOR, $normalized);
        } else {
            return implode(DIRECTORY_SEPARATOR, $normalized);
        }
    }

    // 相对路径转绝对路径
    public function relativeToAbsolute($relativePath) {
        if (DIRECTORY_SEPARATOR === '\\') {
            // Windows系统
            if (preg_match('/^[A-Za-z]:/', $relativePath)) {
                return $relativePath; // 已经是绝对路径
            }
        } else {
            // Unix系统
            if ($relativePath[0] === '/') {
                return $relativePath; // 已经是绝对路径
            }
        }

        // 相对于当前工作目录
        return getcwd() . DIRECTORY_SEPARATOR . $relativePath;
    }

    // 获取相对路径
    public function getRelativePath($from, $to) {
        $from = $this->normalizePath($from);
        $to = $this->normalizePath($to);

        $fromParts = explode(DIRECTORY_SEPARATOR, $from);
        $toParts = explode(DIRECTORY_SEPARATOR, $to);

        // 找到共同的前缀
        $commonLength = 0;
        $minLength = min(count($fromParts), count($toParts));

        for ($i = 0; $i < $minLength; $i++) {
            if ($fromParts[$i] === $toParts[$i]) {
                $commonLength++;
            } else {
                break;
            }
        }

        // 计算需要返回的层级
        $upCount = count($fromParts) - $commonLength;
        $relativeParts = array_fill(0, $upCount, '..');

        // 添加目标路径的剩余部分
        $remainingParts = array_slice($toParts, $commonLength);
        $relativeParts = array_merge($relativeParts, $remainingParts);

        if (empty($relativeParts)) {
            return '.';
        }

        return implode(DIRECTORY_SEPARATOR, $relativeParts);
    }

    // 演示相对路径计算
    public function demonstrateRelativePaths() {
        echo "<h3>相对路径计算</h3>";

        $examples = [
            ['/home/user/documents', '/home/user/pictures/file.jpg'],
            ['/var/www/html', '/var/www/html/css/style.css'],
            ['/home/user/project/src', '/home/user/project/tests/test.php'],
        ];

        foreach ($examples as $example) {
            $from = $example[0];
            $to = $example[1];
            $relative = $this->getRelativePath($from, $to);

            echo "从:<strong>" . htmlspecialchars($from) . "</strong><br>";
            echo "到:<strong>" . htmlspecialchars($to) . "</strong><br>";
            echo "相对路径:<code>" . htmlspecialchars($relative) . "</code><br>";
            echo "<hr>";
        }
    }
}

// 使用示例
$pathHandler = new PathHandler();
$pathHandler->demonstratePathFunctions();
echo "<hr>";
$pathHandler->demonstrateRelativePaths();
?>

2. 临时文件处理

<?php
// 临时文件处理类
class TempFileManager {
    private $tempFiles = [];

    // 创建临时文件
    public function createTempFile($prefix = 'tmp', $suffix = '') {
        $tempDir = sys_get_temp_dir();
        $tempFile = tempnam($tempDir, $prefix);

        if ($tempFile === false) {
            throw new Exception("无法创建临时文件");
        }

        // 添加后缀
        if ($suffix !== '') {
            $tempFileWithSuffix = $tempFile . $suffix;
            rename($tempFile, $tempFileWithSuffix);
            $tempFile = $tempFileWithSuffix;
        }

        $this->tempFiles[] = $tempFile;
        return $tempFile;
    }

    // 创建临时目录
    public function createTempDir($prefix = 'tmp') {
        $tempDir = sys_get_temp_dir();
        $tempFile = tempnam($tempDir, $prefix);

        if ($tempFile === false) {
            throw new Exception("无法创建临时文件/目录");
        }

        // 删除临时文件并创建目录
        unlink($tempFile);
        if (mkdir($tempFile, 0755)) {
            $this->tempFiles[] = $tempFile;
            return $tempFile;
        } else {
            throw new Exception("无法创建临时目录");
        }
    }

    // 演示临时文件用法
    public function demonstrateTempFiles() {
        echo "<h3>临时文件管理演示</h3>";

        try {
            // 创建临时文件
            $tempFile = $this->createTempFile('myapp_', '.log');
            echo "创建临时文件:" . htmlspecialchars($tempFile) . "<br>";

            // 写入内容
            $content = "这是一条日志记录\n时间:" . date('Y-m-d H:i:s') . "\n";
            file_put_contents($tempFile, $content);
            echo "写入内容成功<br>";

            // 读取内容
            $readContent = file_get_contents($tempFile);
            echo "读取内容:<pre>" . htmlspecialchars($readContent) . "</pre>";

            // 创建临时目录
            $tempDir = $this->createTempDir('myapp_');
            echo "创建临时目录:" . htmlspecialchars($tempDir) . "<br>";

            // 在临时目录中创建文件
            $subFile = $tempDir . DIRECTORY_SEPARATOR . 'data.txt';
            file_put_contents($subFile, "临时目录中的数据文件");
            echo "在临时目录中创建文件:" . htmlspecialchars($subFile) . "<br>";

            // 列出临时目录内容
            $files = scandir($tempDir);
            echo "临时目录内容:<ul>";
            foreach ($files as $file) {
                if ($file !== '.' && $file !== '..') {
                    echo "<li>" . htmlspecialchars($file) . "</li>";
                }
            }
            echo "</ul>";

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

    // 清理所有临时文件
    public function cleanup() {
        foreach ($this->tempFiles as $path) {
            if (is_file($path)) {
                unlink($path);
            } elseif (is_dir($path)) {
                $this->deleteDirectory($path);
            }
        }
        $this->tempFiles = [];
        echo "临时文件清理完成<br>";
    }

    // 递归删除目录
    private function deleteDirectory($dir) {
        if (!is_dir($dir)) {
            return;
        }

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

            $path = $dir . DIRECTORY_SEPARATOR . $file;
            if (is_dir($path)) {
                $this->deleteDirectory($path);
            } else {
                unlink($path);
            }
        }

        rmdir($dir);
    }

    // 获取临时文件列表
    public function getTempFiles() {
        return $this->tempFiles;
    }
}

// 使用示例
$tempManager = new TempFileManager();
$tempManager->demonstrateTempFiles();

echo "<h4>创建的临时文件:</h4>";
$tempFiles = $tempManager->getTempFiles();
foreach ($tempFiles as $file) {
    echo "- " . htmlspecialchars($file) . " (" . (is_dir($file) ? "目录" : "文件") . ")<br>";
}

// 演示清理(实际应用中可能在脚本结束时自动清理)
echo "<h4>清理临时文件:</h4>";
$tempManager->cleanup();
?>

文件系统工具函数

1. 文件查找和搜索

<?php
// 文件搜索工具类
class FileSearcher {
    private $results;
    private $searchStats;

    public function __construct() {
        $this->results = [];
        $this->searchStats = [
            'files_searched' => 0,
            'dirs_searched' => 0,
            'matches_found' => 0,
            'start_time' => 0,
            'end_time' => 0
        ];
    }

    // 按文件名搜索
    public function searchByName($directory, $pattern, $recursive = true, $caseSensitive = false) {
        $this->resetStats();
        $this->searchStats['start_time'] = microtime(true);

        $this->searchByNameRecursive($directory, $pattern, $recursive, $caseSensitive);

        $this->searchStats['end_time'] = microtime(true);
        return $this->results;
    }

    private function searchByNameRecursive($directory, $pattern, $recursive, $caseSensitive) {
        if (!is_dir($directory)) {
            return;
        }

        $this->searchStats['dirs_searched']++;
        $items = scandir($directory);

        foreach ($items as $item) {
            if ($item === '.' || $item === '..') {
                continue;
            }

            $path = $directory . DIRECTORY_SEPARATOR . $item;

            if (is_file($path)) {
                $this->searchStats['files_searched']++;

                $filename = $caseSensitive ? $item : strtolower($item);
                $searchPattern = $caseSensitive ? $pattern : strtolower($pattern);

                if ($this->matchesPattern($filename, $searchPattern)) {
                    $this->results[] = [
                        'path' => $path,
                        'name' => $item,
                        'size' => filesize($path),
                        'modified' => filemtime($path),
                        'match_type' => 'filename'
                    ];
                    $this->searchStats['matches_found']++;
                }
            } elseif (is_dir($path) && $recursive) {
                $this->searchByNameRecursive($path, $pattern, $recursive, $caseSensitive);
            }
        }
    }

    // 按内容搜索
    public function searchByContent($directory, $contentPattern, $fileExtensions = [], $recursive = true) {
        $this->resetStats();
        $this->searchStats['start_time'] = microtime(true);

        $this->searchByContentRecursive($directory, $contentPattern, $fileExtensions, $recursive);

        $this->searchStats['end_time'] = microtime(true);
        return $this->results;
    }

    private function searchByContentRecursive($directory, $contentPattern, $fileExtensions, $recursive) {
        if (!is_dir($directory)) {
            return;
        }

        $this->searchStats['dirs_searched']++;
        $items = scandir($directory);

        foreach ($items as $item) {
            if ($item === '.' || $item === '..') {
                continue;
            }

            $path = $directory . DIRECTORY_SEPARATOR . $item;

            if (is_file($path)) {
                $this->searchStats['files_searched']++;

                // 检查文件扩展名
                if (!empty($fileExtensions)) {
                    $extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
                    if (!in_array($extension, $fileExtensions)) {
                        continue;
                    }
                }

                // 搜索文件内容
                if ($this->searchFileContent($path, $contentPattern)) {
                    $this->results[] = [
                        'path' => $path,
                        'name' => $item,
                        'size' => filesize($path),
                        'modified' => filemtime($path),
                        'match_type' => 'content'
                    ];
                    $this->searchStats['matches_found']++;
                }
            } elseif (is_dir($path) && $recursive) {
                $this->searchByContentRecursive($path, $contentPattern, $fileExtensions, $recursive);
            }
        }
    }

    // 搜索文件内容
    private function searchFileContent($filePath, $pattern) {
        $handle = fopen($filePath, 'r');
        if (!$handle) {
            return false;
        }

        while (($line = fgets($handle)) !== false) {
            if (stripos($line, $pattern) !== false) {
                fclose($handle);
                return true;
            }
        }

        fclose($handle);
        return false;
    }

    // 模式匹配
    private function matchesPattern($filename, $pattern) {
        // 支持通配符匹配
        if (strpos($pattern, '*') !== false || strpos($pattern, '?') !== false) {
            return fnmatch($pattern, $filename);
        } else {
            // 简单字符串包含匹配
            return strpos($filename, $pattern) !== false;
        }
    }

    // 重置统计信息
    private function resetStats() {
        $this->results = [];
        $this->searchStats = [
            'files_searched' => 0,
            'dirs_searched' => 0,
            'matches_found' => 0,
            'start_time' => 0,
            'end_time' => 0
        ];
    }

    // 获取搜索统计
    public function getSearchStats() {
        $duration = $this->searchStats['end_time'] - $this->searchStats['start_time'];
        $this->searchStats['duration'] = round($duration, 4);
        return $this->searchStats;
    }

    // 显示搜索结果
    public function displayResults() {
        echo "<h3>搜索结果</h3>";

        $stats = $this->getSearchStats();
        echo "<p>搜索统计:</p>";
        echo "<ul>";
        echo "<li>搜索的目录:{$stats['dirs_searched']}</li>";
        echo "<li>搜索的文件:{$stats['files_searched']}</li>";
        echo "<li>找到的匹配:{$stats['matches_found']}</li>";
        echo "<li>耗时:{$stats['duration']} 秒</li>";
        echo "</ul>";

        if (!empty($this->results)) {
            echo "<h4>匹配的文件:</h4>";
            echo "<table border='1' style='border-collapse: collapse; width: 100%;'>";
            echo "<tr><th>文件名</th><th>路径</th><th>大小</th><th>修改时间</th><th>匹配类型</th></tr>";

            foreach ($this->results as $result) {
                $size = $this->formatBytes($result['size']);
                $modified = date('Y-m-d H:i:s', $result['modified']);
                $matchType = $result['match_type'] === 'filename' ? '文件名' : '内容';

                echo "<tr>";
                echo "<td>" . htmlspecialchars($result['name']) . "</td>";
                echo "<td>" . htmlspecialchars($result['path']) . "</td>";
                echo "<td>{$size}</td>";
                echo "<td>{$modified}</td>";
                echo "<td>{$matchType}</td>";
                echo "</tr>";
            }

            echo "</table>";
        } else {
            echo "<p>没有找到匹配的文件。</p>";
        }
    }

    // 格式化字节大小
    private function formatBytes($bytes) {
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];
        $bytes = max(0, $bytes);
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow = min($pow, count($units) - 1);

        $bytes /= pow(1024, $pow);
        return round($bytes, 2) . ' ' . $units[$pow];
    }
}

// 使用示例
$searcher = new FileSearcher();

// 搜索当前目录下所有.php文件
echo "<h2>文件搜索演示</h2>";

echo "<h3>搜索PHP文件:</h3>";
$phpFiles = $searcher->searchByName('.', '*.php', true);
$searcher->displayResults();

echo "<hr>";

echo "<h3>搜索包含'function'的文本文件:</h3>";
$textFiles = $searcher->searchByContent('.', 'function', ['txt', 'php', 'md'], true);
$searcher->displayResults();
?>

2. 文件系统监控

<?php
// 文件系统监控类(简单实现)
class FileSystemMonitor {
    private $watchedFiles;
    private $lastModified;

    public function __construct() {
        $this->watchedFiles = [];
        $this->lastModified = [];
    }

    // 添加监控文件
    public function watchFile($filePath) {
        if (file_exists($filePath)) {
            $this->watchedFiles[] = $filePath;
            $this->lastModified[$filePath] = filemtime($filePath);
            return true;
        }
        return false;
    }

    // 检查文件变化
    public function checkChanges() {
        $changes = [];

        foreach ($this->watchedFiles as $file) {
            if (!file_exists($file)) {
                $changes[] = [
                    'file' => $file,
                    'type' => 'deleted',
                    'timestamp' => time()
                ];
                unset($this->lastModified[$file]);
                continue;
            }

            $currentModified = filemtime($file);
            $lastModified = $this->lastModified[$file] ?? 0;

            if ($currentModified > $lastModified) {
                $changes[] = [
                    'file' => $file,
                    'type' => 'modified',
                    'timestamp' => $currentModified,
                    'old_time' => $lastModified,
                    'new_time' => $currentModified
                ];
                $this->lastModified[$file] = $currentModified;
            }
        }

        return $changes;
    }

    // 获取监控状态
    public function getWatchStatus() {
        $status = [];
        foreach ($this->watchedFiles as $file) {
            if (file_exists($file)) {
                $status[] = [
                    'file' => $file,
                    'exists' => true,
                    'size' => filesize($file),
                    'modified' => date('Y-m-d H:i:s', filemtime($file)),
                    'last_check' => $this->lastModified[$file] ? date('Y-m-d H:i:s', $this->lastModified[$file]) : '未知'
                ];
            } else {
                $status[] = [
                    'file' => $file,
                    'exists' => false,
                    'last_check' => $this->lastModified[$file] ? date('Y-m-d H:i:s', $this->lastModified[$file]) : '未知'
                ];
            }
        }
        return $status;
    }

    // 显示监控状态
    public function displayStatus() {
        $status = $this->getWatchStatus();

        echo "<h3>文件监控状态</h3>";
        echo "<table border='1' style='border-collapse: collapse;'>";
        echo "<tr><th>文件</th><th>状态</th><th>大小</th><th>修改时间</th><th>上次检查</th></tr>";

        foreach ($status as $info) {
            echo "<tr>";
            echo "<td>" . htmlspecialchars($info['file']) . "</td>";
            echo "<td>" . ($info['exists'] ? '✓ 存在' : '✗ 不存在') . "</td>";
            echo "<td>" . ($info['exists'] ? $this->formatBytes($info['size']) : '-') . "</td>";
            echo "<td>" . ($info['exists'] ? $info['modified'] : '-') . "</td>";
            echo "<td>" . $info['last_check'] . "</td>";
            echo "</tr>";
        }

        echo "</table>";
    }

    // 显示变化
    public function displayChanges($changes) {
        if (empty($changes)) {
            echo "<p>没有检测到文件变化。</p>";
            return;
        }

        echo "<h3>文件变化</h3>";
        foreach ($changes as $change) {
            $file = htmlspecialchars($change['file']);
            $time = date('Y-m-d H:i:s', $change['timestamp']);

            if ($change['type'] === 'modified') {
                echo "<p>📝 文件已修改:{$file} (时间: {$time})</p>";
            } elseif ($change['type'] === 'deleted') {
                echo "<p>🗑️ 文件已删除:{$file} (时间: {$time})</p>";
            }
        }
    }

    // 格式化字节大小
    private function formatBytes($bytes) {
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];
        $bytes = max(0, $bytes);
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow = min($pow, count($units) - 1);

        $bytes /= pow(1024, $pow);
        return round($bytes, 2) . ' ' . $units[$pow];
    }
}

// 使用示例
$monitor = new FileSystemMonitor();

// 创建一些测试文件
file_put_contents('monitor_test1.txt', 'Initial content 1');
file_put_contents('monitor_test2.txt', 'Initial content 2');

// 添加文件到监控
$monitor->watchFile('monitor_test1.txt');
$monitor->watchFile('monitor_test2.txt');
$monitor->watchFile('nonexistent_file.txt');

echo "<h2>文件系统监控演示</h2>";

// 显示初始状态
$monitor->displayStatus();

echo "<h3>检查文件变化:</h3>";
$changes = $monitor->checkChanges();
$monitor->displayChanges($changes);

echo "<hr>";

// 修改文件
echo "<h3>修改文件后重新检查:</h3>";
sleep(1); // 确保时间戳不同
file_put_contents('monitor_test1.txt', 'Modified content 1');

$changes = $monitor->checkChanges();
$monitor->displayChanges($changes);

// 显示更新后的状态
echo "<hr>";
$monitor->displayStatus();

// 清理测试文件
unlink('monitor_test1.txt');
unlink('monitor_test2.txt');
?>

实用工具类

1. 文件备份工具

<?php
// 文件备份工具类
class FileBackupTool {
    private $backupDir;
    private $maxBackups;
    private $compressionEnabled;

    public function __construct($backupDir = 'backups', $maxBackups = 10, $compressionEnabled = false) {
        $this->backupDir = $backupDir;
        $this->maxBackups = $maxBackups;
        $this->compressionEnabled = $compressionEnabled;

        if (!is_dir($this->backupDir)) {
            mkdir($this->backupDir, 0755, true);
        }
    }

    // 备份单个文件
    public function backupFile($filePath, $description = '') {
        if (!file_exists($filePath)) {
            throw new Exception("源文件不存在:{$filePath}");
        }

        $filename = basename($filePath);
        $timestamp = date('Y-m-d_H-i-s');
        $backupName = $filename . '_' . $timestamp;

        if ($this->compressionEnabled) {
            $backupName .= '.gz';
            $backupPath = $this->backupDir . DIRECTORY_SEPARATOR . $backupName;

            // 使用gzip压缩
            $source = fopen($filePath, 'rb');
            $destination = gzopen($backupPath, 'wb9');

            while (!feof($source)) {
                $chunk = fread($source, 8192);
                gzwrite($destination, $chunk);
            }

            fclose($source);
            gzclose($destination);
        } else {
            $backupPath = $this->backupDir . DIRECTORY_SEPARATOR . $backupName;
            copy($filePath, $backupPath);
        }

        // 创建备份信息文件
        $info = [
            'original_path' => $filePath,
            'backup_path' => $backupPath,
            'timestamp' => time(),
            'description' => $description,
            'size' => filesize($filePath),
            'compressed' => $this->compressionEnabled,
            'md5' => md5_file($filePath)
        ];

        $infoFile = $backupPath . '.info';
        file_put_contents($infoFile, serialize($info));

        // 清理旧备份
        $this->cleanupOldBackups($filename);

        return $backupPath;
    }

    // 备份目录
    public function backupDirectory($dirPath, $description = '') {
        if (!is_dir($dirPath)) {
            throw new Exception("目录不存在:{$dirPath}");
        }

        $dirname = basename($dirPath);
        $timestamp = date('Y-m-d_H-i-s');
        $backupName = $dirname . '_' . $timestamp . '.tar';

        if ($this->compressionEnabled) {
            $backupName .= '.gz';
        }

        $backupPath = $this->backupDir . DIRECTORY_SEPARATOR . $backupName;

        // 创建tar压缩包
        $phar = new PharData($backupPath . '.temp');
        $phar->buildFromDirectory($dirPath);

        if ($this->compressionEnabled) {
            $phar->compress(Phar::GZ);
            unlink($backupPath . '.temp');
            $backupPath .= '.gz';
        } else {
            rename($backupPath . '.temp', $backupPath);
        }

        // 创建备份信息
        $info = [
            'original_path' => $dirPath,
            'backup_path' => $backupPath,
            'timestamp' => time(),
            'description' => $description,
            'type' => 'directory',
            'compressed' => $this->compressionEnabled
        ];

        $infoFile = $backupPath . '.info';
        file_put_contents($infoFile, serialize($info));

        return $backupPath;
    }

    // 列出所有备份
    public function listBackups() {
        $backups = [];
        $files = scandir($this->backupDir);

        foreach ($files as $file) {
            if ($file === '.' || $file === '..' || substr($file, -5) === '.info') {
                continue;
            }

            $backupPath = $this->backupDir . DIRECTORY_SEPARATOR . $file;
            $infoFile = $backupPath . '.info';

            $backup = [
                'name' => $file,
                'path' => $backupPath,
                'size' => filesize($backupPath),
                'created' => filemtime($backupPath)
            ];

            if (file_exists($infoFile)) {
                $info = unserialize(file_get_contents($infoFile));
                $backup = array_merge($backup, $info);
            }

            $backups[] = $backup;
        }

        // 按创建时间排序
        usort($backups, function($a, $b) {
            return $b['created'] - $a['created'];
        });

        return $backups;
    }

    // 清理旧备份
    private function cleanupOldBackups($filename) {
        $backups = $this->listBackups();

        // 筛选出相同原始文件的备份
        $fileBackups = [];
        foreach ($backups as $backup) {
            if (isset($backup['original_path']) && basename($backup['original_path']) === $filename) {
                $fileBackups[] = $backup;
            }
        }

        // 如果备份数量超过限制,删除最旧的
        if (count($fileBackups) > $this->maxBackups) {
            $toDelete = array_slice($fileBackups, $this->maxBackups);
            foreach ($toDelete as $backup) {
                unlink($backup['path']);
                if (file_exists($backup['path'] . '.info')) {
                    unlink($backup['path'] . '.info');
                }
            }
        }
    }

    // 显示备份列表
    public function displayBackups() {
        $backups = $this->listBackups();

        echo "<h3>备份列表</h3>";

        if (empty($backups)) {
            echo "<p>没有找到备份文件。</p>";
            return;
        }

        echo "<table border='1' style='border-collapse: collapse; width: 100%;'>";
        echo "<tr><th>原始文件</th><th>备份名称</th><th>大小</th><th>创建时间</th><th>描述</th><th>操作</th></tr>";

        foreach ($backups as $backup) {
            $size = $this->formatBytes($backup['size']);
            $created = date('Y-m-d H:i:s', $backup['created']);
            $original = isset($backup['original_path']) ? basename($backup['original_path']) : '未知';
            $description = isset($backup['description']) ? htmlspecialchars($backup['description']) : '';

            echo "<tr>";
            echo "<td>{$original}</td>";
            echo "<td>" . htmlspecialchars($backup['name']) . "</td>";
            echo "<td>{$size}</td>";
            echo "<td>{$created}</td>";
            echo "<td>{$description}</td>";
            echo "<td><a href='?restore=" . urlencode($backup['path']) . "'>恢复</a></td>";
            echo "</tr>";
        }

        echo "</table>";
    }

    // 格式化字节大小
    private function formatBytes($bytes) {
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];
        $bytes = max(0, $bytes);
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow = min($pow, count($units) - 1);

        $bytes /= pow(1024, $pow);
        return round($bytes, 2) . ' ' . $units[$pow];
    }
}

// 使用示例
$backupTool = new FileBackupTool('file_backups', 5, false);

// 创建测试文件
file_put_contents('backup_demo.txt', "这是一个演示文件\n用于测试备份功能\n包含一些测试数据");

try {
    // 备份文件
    echo "<h2>文件备份工具演示</h2>";

    $backupPath = $backupTool->backupFile('backup_demo.txt', '初始备份');
    echo "<p>文件备份完成:" . htmlspecialchars($backupPath) . "</p>";

    // 修改文件后再次备份
    sleep(1);
    file_put_contents('backup_demo.txt', "这是修改后的文件内容\n添加了新的数据");
    $backupPath2 = $backupTool->backupFile('backup_demo.txt', '修改后备份');
    echo "<p>修改后备份完成:" . htmlspecialchars($backupPath2) . "</p>";

    // 显示备份列表
    $backupTool->displayBackups();

    // 清理
    unlink('backup_demo.txt');

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

总结

文件系统函数是PHP编程中的重要组成部分,通过本节学习,你应该掌握:

关键知识点:

  1. 文件信息获取

    • file_exists(), is_file(), is_dir() 等检查函数
    • filesize(), filemtime(), fileperms() 等属性函数
    • pathinfo() 用于路径分解
  2. 路径操作

    • dirname(), basename() 用于路径处理
    • realpath() 获取真实路径
    • 临时文件和目录的创建
  3. 实用工具

    • 文件搜索和查找
    • 文件比较和差异检测
    • 文件系统监控
    • 自动备份工具
  4. 最佳实践

    • 始终检查文件操作的结果
    • 正确处理文件权限
    • 合理使用临时文件
    • 及时清理不需要的文件

这些函数和技巧将帮助你在实际开发中更有效地处理文件系统相关的任务。