第9章:文件操作

文件操作是PHP编程中的重要组成部分,它允许我们读取、写入、修改和管理服务器上的文件。通过文件操作,你可以创建动态内容管理系统、文件上传功能、日志记录系统,以及各种数据处理应用。

学习目标

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

  • 理解PHP文件系统的基本概念
  • 掌握文件的读取、写入和修改操作
  • 学会文件上传和下载的实现方法
  • 了解目录管理和文件权限控制
  • 掌握文件处理的安全最佳实践
  • 能够构建完整的文件管理系统

文件系统基础

什么是文件系统?

文件系统是操作系统用于管理存储设备上数据的机制。在PHP中,我们可以通过内置函数与文件系统进行交互,执行各种文件和目录操作。

路径的概念

在文件操作中,路径是指定文件位置的字符串:

<?php
// 绝对路径 - 从根目录开始的完整路径
$absolutePath = '/var/www/html/index.php';
$windowsAbsolutePath = 'C:\xampp\htdocs\index.php';

// 相对路径 - 相对于当前脚本所在目录的路径
$relativePath = '../config/database.php';
$relativePath2 = 'includes/functions.php';

// 当前工作目录
echo "当前工作目录:" . getcwd() . "<br>";

// 获取脚本的绝对路径
echo "脚本路径:" . __FILE__ . "<br>";
echo "脚本目录:" . __DIR__ . "<br>";
?>

路径分隔符

不同操作系统的路径分隔符不同:

<?php
// 使用DIRECTORY_SEPARATOR获取系统分隔符
echo "路径分隔符:" . DIRECTORY_SEPARATOR . "<br>";

// 跨平台路径拼接
$path = 'uploads' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'photo.jpg';
echo "跨平台路径:" . $path . "<br>";

// 使用函数处理路径
$fullPath = 'uploads/images/photo.jpg';
$dirname = dirname($fullPath);           // 获取目录名
$basename = basename($fullPath);         // 获取文件名
$filename = pathinfo($fullPath, PATHINFO_FILENAME); // 获取不含扩展名的文件名
$extension = pathinfo($fullPath, PATHINFO_EXTENSION); // 获取扩展名

echo "目录名:" . $dirname . "<br>";
echo "文件名:" . $basename . "<br>";
echo "文件名(无扩展名):" . $filename . "<br>";
echo "扩展名:" . $extension . "<br>";
?>

文件读取操作

1. 检查文件是否存在

<?php
// 检查文件或目录是否存在
$filename = 'example.txt';

if (file_exists($filename)) {
    echo "文件存在<br>";
} else {
    echo "文件不存在<br>";
}

// 检查是否为文件
if (is_file($filename)) {
    echo "这是一个文件<br>";
}

// 检查是否为目录
if (is_dir('uploads')) {
    echo "这是一个目录<br>";
}

// 检查文件是否可读/可写/可执行
if (is_readable($filename)) {
    echo "文件可读<br>";
}
if (is_writable($filename)) {
    echo "文件可写<br>";
}
if (is_executable($filename)) {
    echo "文件可执行<br>";
}
?>

2. 获取文件信息

<?php
// 文件信息示例类
class FileInfo {
    private $filename;

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

    // 获取文件大小
    public function getSize() {
        if (file_exists($this->filename)) {
            $bytes = filesize($this->filename);
            return $this->formatBytes($bytes);
        }
        return "文件不存在";
    }

    // 获取文件修改时间
    public function getModifiedTime() {
        if (file_exists($this->filename)) {
            return date('Y-m-d H:i:s', filemtime($this->filename));
        }
        return "文件不存在";
    }

    // 获取文件创建时间
    public function getCreatedTime() {
        if (file_exists($this->filename)) {
            return date('Y-m-d H:i:s', filectime($this->filename));
        }
        return "文件不存在";
    }

    // 获取文件最后访问时间
    public function getAccessedTime() {
        if (file_exists($this->filename)) {
            return date('Y-m-d H:i:s', fileatime($this->filename));
        }
        return "文件不存在";
    }

    // 获取文件类型
    public function getType() {
        if (file_exists($this->filename)) {
            return filetype($this->filename);
        }
        return "文件不存在";
    }

    // 获取文件权限
    public function getPermissions() {
        if (file_exists($this->filename)) {
            return substr(sprintf('%o', fileperms($this->filename)), -4);
        }
        return "文件不存在";
    }

    // 获取文件MIME类型
    public function getMimeType() {
        if (file_exists($this->filename)) {
            $finfo = finfo_open(FILEINFO_MIME_TYPE);
            $mimeType = finfo_file($finfo, $this->filename);
            finfo_close($finfo);
            return $mimeType;
        }
        return "文件不存在";
    }

    // 格式化字节大小
    private function formatBytes($bytes, $precision = 2) {
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];

        for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
            $bytes /= 1024;
        }

        return round($bytes, $precision) . ' ' . $units[$i];
    }

    // 显示所有文件信息
    public function displayAllInfo() {
        echo "<h3>文件信息:{$this->filename}</h3>";
        echo "<table border='1'>";
        echo "<tr><td>文件大小</td><td>" . $this->getSize() . "</td></tr>";
        echo "<tr><td>修改时间</td><td>" . $this->getModifiedTime() . "</td></tr>";
        echo "<tr><td>创建时间</td><td>" . $this->getCreatedTime() . "</td></tr>";
        echo "<tr><td>访问时间</td><td>" . $this->getAccessedTime() . "</td></tr>";
        echo "<tr><td>文件类型</td><td>" . $this->getType() . "</td></tr>";
        echo "<tr><td>权限</td><td>" . $this->getPermissions() . "</td></tr>";
        echo "<tr><td>MIME类型</td><td>" . $this->getMimeType() . "</td></tr>";
        echo "</table>";
    }
}

// 使用示例
$fileInfo = new FileInfo('example.txt');
$fileInfo->displayAllInfo();
?>

3. 读取文件内容

<?php
// 文件读取示例类
class FileReader {
    private $filename;

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

    // 检查文件是否可读
    private function isReadable() {
        return file_exists($this->filename) && is_readable($this->filename);
    }

    // 使用file_get_contents读取整个文件
    public function readAll() {
        if (!$this->isReadable()) {
            throw new Exception("文件不可读或不存在");
        }

        $content = file_get_contents($this->filename);
        if ($content === false) {
            throw new Exception("读取文件失败");
        }

        return $content;
    }

    // 使用file()函数逐行读取
    public function readLines() {
        if (!$this->isReadable()) {
            throw new Exception("文件不可读或不存在");
        }

        $lines = file($this->filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        if ($lines === false) {
            throw new Exception("读取文件失败");
        }

        return $lines;
    }

    // 使用fopen和fgets逐行读取(适用于大文件)
    public function readLineByLine() {
        if (!$this->isReadable()) {
            throw new Exception("文件不可读或不存在");
        }

        $handle = fopen($this->filename, 'r');
        if (!$handle) {
            throw new Exception("无法打开文件");
        }

        $lines = [];
        try {
            while (($line = fgets($handle)) !== false) {
                $lines[] = trim($line);
            }
        } finally {
            fclose($handle);
        }

        return $lines;
    }

    // 按字符读取文件
    public function readByCharacter($maxChars = 100) {
        if (!$this->isReadable()) {
            throw new Exception("文件不可读或不存在");
        }

        $handle = fopen($this->filename, 'r');
        if (!$handle) {
            throw new Exception("无法打开文件");
        }

        $content = '';
        $charCount = 0;
        try {
            while (($char = fgetc($handle)) !== false && $charCount < $maxChars) {
                $content .= $char;
                $charCount++;
            }
        } finally {
            fclose($handle);
        }

        return $content;
    }

    // 读取特定范围的行
    public function readLinesRange($start, $end) {
        $lines = $this->readLines();
        return array_slice($lines, $start - 1, $end - $start + 1);
    }

    // 搜索文件内容
    public function searchContent($searchTerm) {
        $lines = $this->readLines();
        $results = [];

        foreach ($lines as $lineNumber => $line) {
            if (stripos($line, $searchTerm) !== false) {
                $results[] = [
                    'line_number' => $lineNumber + 1,
                    'content' => $line
                ];
            }
        }

        return $results;
    }

    // 显示文件内容(带行号)
    public function displayWithLineNumbers() {
        $lines = $this->readLines();
        echo "<h3>文件内容:{$this->filename}</h3>";
        echo "<pre style='background-color: #f5f5f5; padding: 10px; font-family: monospace;'>";

        foreach ($lines as $lineNumber => $line) {
            $lineNum = str_pad($lineNumber + 1, 4, ' ', STR_PAD_LEFT);
            echo "{$lineNum}: " . htmlspecialchars($line) . "\n";
        }

        echo "</pre>";
    }

    // 获取文件统计信息
    public function getStats() {
        if (!$this->isReadable()) {
            return null;
        }

        $lines = $this->readLines();
        $content = file_get_contents($this->filename);

        return [
            'total_lines' => count($lines),
            'total_characters' => strlen($content),
            'total_words' => str_word_count($content),
            'file_size' => filesize($this->filename)
        ];
    }
}

// 使用示例
try {
    $reader = new FileReader('example.txt');

    // 读取所有内容
    echo "<h3>读取所有内容:</h3>";
    echo "<pre>" . htmlspecialchars($reader->readAll()) . "</pre>";

    // 按行读取
    echo "<h3>按行读取:</h3>";
    $lines = $reader->readLines();
    echo "<ol>";
    foreach ($lines as $line) {
        echo "<li>" . htmlspecialchars($line) . "</li>";
    }
    echo "</ol>";

    // 显示统计信息
    echo "<h3>文件统计:</h3>";
    $stats = $reader->getStats();
    echo "<ul>";
    echo "<li>总行数:" . $stats['total_lines'] . "</li>";
    echo "<li>总字符数:" . $stats['total_characters'] . "</li>";
    echo "<li>总词数:" . $stats['total_words'] . "</li>";
    echo "<li>文件大小:" . $stats['file_size'] . " 字节</li>";
    echo "</ul>";

    // 搜索内容
    echo "<h3>搜索示例:</h3>";
    $results = $reader->searchContent('function');
    if (!empty($results)) {
        foreach ($results as $result) {
            echo "第{$result['line_number']}行:" . htmlspecialchars($result['content']) . "<br>";
        }
    }

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

4. 配置文件读取

<?php
// 配置文件读取类
class ConfigReader {
    private $configFile;
    private $config = [];

    public function __construct($configFile) {
        $this->configFile = $configFile;
        $this->loadConfig();
    }

    // 加载配置文件
    private function loadConfig() {
        if (!file_exists($this->configFile)) {
            throw new Exception("配置文件不存在:{$this->configFile}");
        }

        $extension = pathinfo($this->configFile, PATHINFO_EXTENSION);

        switch ($extension) {
            case 'json':
                $this->loadJsonConfig();
                break;
            case 'ini':
                $this->loadIniConfig();
                break;
            case 'php':
                $this->loadPhpConfig();
                break;
            default:
                throw new Exception("不支持的配置文件格式:{$extension}");
        }
    }

    // 加载JSON配置文件
    private function loadJsonConfig() {
        $content = file_get_contents($this->configFile);
        $this->config = json_decode($content, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new Exception("JSON解析错误:" . json_last_error_msg());
        }
    }

    // 加载INI配置文件
    private function loadIniConfig() {
        $this->config = parse_ini_file($this->configFile, true);
    }

    // 加载PHP配置文件
    private function loadPhpConfig() {
        $this->config = include $this->configFile;
    }

    // 获取配置值
    public function get($key, $default = null) {
        $keys = explode('.', $key);
        $value = $this->config;

        foreach ($keys as $k) {
            if (!isset($value[$k])) {
                return $default;
            }
            $value = $value[$k];
        }

        return $value;
    }

    // 设置配置值
    public function set($key, $value) {
        $keys = explode('.', $key);
        $config = &$this->config;

        foreach ($keys as $k) {
            if (!isset($config[$k])) {
                $config[$k] = [];
            }
            $config = &$config[$k];
        }

        $config = $value;
    }

    // 获取所有配置
    public function getAll() {
        return $this->config;
    }

    // 保存配置
    public function save() {
        $extension = pathinfo($this->configFile, PATHINFO_EXTENSION);

        switch ($extension) {
            case 'json':
                return $this->saveJsonConfig();
            case 'ini':
                return $this->saveIniConfig();
            case 'php':
                return $this->savePhpConfig();
            default:
                throw new Exception("不支持的配置文件格式:{$extension}");
        }
    }

    // 保存JSON配置
    private function saveJsonConfig() {
        $content = json_encode($this->config, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
        return file_put_contents($this->configFile, $content) !== false;
    }

    // 保存INI配置
    private function saveIniConfig() {
        $content = $this->arrayToIni($this->config);
        return file_put_contents($this->configFile, $content) !== false;
    }

    // 保存PHP配置
    private function savePhpConfig() {
        $content = "<?php\nreturn " . var_export($this->config, true) . ";\n";
        return file_put_contents($this->configFile, $content) !== false;
    }

    // 数组转INI格式
    private function arrayToIni($array, $sections = []) {
        $content = '';

        foreach ($array as $key => $value) {
            if (is_array($value)) {
                if (!empty($sections)) {
                    $content .= "\n[{$key}]\n";
                }
                $content .= $this->arrayToIni($value, array_merge($sections, [$key]));
            } else {
                $content .= "{$key} = " . (is_bool($value) ? ($value ? 'true' : 'false') : "\"{$value}\"") . "\n";
            }
        }

        return $content;
    }
}

// 使用示例
try {
    // 读取JSON配置
    $configReader = new ConfigReader('config.json');

    echo "<h3>配置值示例:</h3>";
    echo "数据库主机:" . $configReader->get('database.host', 'localhost') . "<br>";
    echo "数据库端口:" . $configReader->get('database.port', 3306) . "<br>";
    echo "应用名称:" . $configReader->get('app.name', 'MyApp') . "<br>";

    // 修改配置
    $configReader->set('app.debug', true);
    $configReader->set('app.version', '1.2.3');

    // 保存配置
    if ($configReader->save()) {
        echo "配置已保存<br>";
    }

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

文件写入操作

1. 基本文件写入

<?php
// 文件写入示例类
class FileWriter {
    private $filename;
    private $backup = true;

    public function __construct($filename, $backup = true) {
        $this->filename = $filename;
        $this->backup = $backup;
    }

    // 检查目录是否存在,不存在则创建
    private function ensureDirectory() {
        $directory = dirname($this->filename);
        if (!is_dir($directory)) {
            if (!mkdir($directory, 0755, true)) {
                throw new Exception("无法创建目录:{$directory}");
            }
        }
    }

    // 创建备份
    private function createBackup() {
        if ($this->backup && file_exists($this->filename)) {
            $backupFile = $this->filename . '.backup.' . date('YmdHis');
            if (!copy($this->filename, $backupFile)) {
                throw new Exception("创建备份失败");
            }
        }
    }

    // 写入整个文件
    public function write($content, $mode = 'w') {
        $this->ensureDirectory();
        $this->createBackup();

        $result = file_put_contents($this->filename, $content, $mode === 'a' ? FILE_APPEND : 0);
        if ($result === false) {
            throw new Exception("写入文件失败");
        }

        return $result;
    }

    // 追加内容
    public function append($content) {
        return $this->write($content, 'a');
    }

    // 写入单行
    public function writeLine($line, $mode = 'w') {
        return $this->write($line . PHP_EOL, $mode);
    }

    // 追加单行
    public function appendLine($line) {
        return $this->writeLine($line, 'a');
    }

    // 写入数组(每行一个元素)
    public function writeLines(array $lines, $mode = 'w') {
        $content = implode(PHP_EOL, $lines) . PHP_EOL;
        return $this->write($content, $mode);
    }

    // 写入JSON数据
    public function writeJson($data, $pretty = true) {
        $options = JSON_UNESCAPED_UNICODE;
        if ($pretty) {
            $options |= JSON_PRETTY_PRINT;
        }

        $content = json_encode($data, $options);
        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new Exception("JSON编码错误:" . json_last_error_msg());
        }

        return $this->write($content);
    }

    // 写入CSV数据
    public function writeCsv(array $data, $headers = null) {
        $this->ensureDirectory();
        $this->createBackup();

        $handle = fopen($this->filename, 'w');
        if (!$handle) {
            throw new Exception("无法打开文件写入");
        }

        try {
            // 写入表头
            if ($headers !== null) {
                fputcsv($handle, $headers);
            }

            // 写入数据
            foreach ($data as $row) {
                if (!is_array($row)) {
                    $row = [$row];
                }
                fputcsv($handle, $row);
            }
        } finally {
            fclose($handle);
        }

        return true;
    }

    // 清空文件
    public function clear() {
        $this->ensureDirectory();
        return $this->write('');
    }

    // 获取写入的字节数
    public function getBytesWritten() {
        return filesize($this->filename);
    }
}

// 使用示例
try {
    $writer = new FileWriter('output/test.txt');

    // 写入文本内容
    $writer->write('Hello, World!');
    $writer->appendLine('This is a new line.');
    $writer->appendLine('Another line of text.');

    // 写入数组
    $lines = [
        'Line 1: First line',
        'Line 2: Second line',
        'Line 3: Third line'
    ];
    $writer->writeLines($lines);

    // 写入JSON数据
    $data = [
        'name' => 'John Doe',
        'age' => 30,
        'email' => 'john@example.com',
        'hobbies' => ['reading', 'swimming', 'coding']
    ];
    $jsonWriter = new FileWriter('output/data.json');
    $jsonWriter->writeJson($data);

    // 写入CSV数据
    $csvData = [
        ['John', 'Doe', 30, 'john@example.com'],
        ['Jane', 'Smith', 25, 'jane@example.com'],
        ['Bob', 'Johnson', 35, 'bob@example.com']
    ];
    $csvHeaders = ['First Name', 'Last Name', 'Age', 'Email'];
    $csvWriter = new FileWriter('output/users.csv');
    $csvWriter->writeCsv($csvData, $csvHeaders);

    echo "所有文件写入完成!<br>";

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

2. 文件锁定机制

<?php
// 文件锁定示例类
class FileLock {
    private $filename;
    private $handle;
    private $lockType;

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

    // 打开文件并获取锁
    public function openWithLock($mode, $lockType = LOCK_EX) {
        $this->handle = fopen($this->filename, $mode);
        if (!$this->handle) {
            throw new Exception("无法打开文件");
        }

        // 尝试获取锁
        if (flock($this->handle, $lockType)) {
            $this->lockType = $lockType;
            return true;
        } else {
            fclose($this->handle);
            throw new Exception("无法获取文件锁");
        }
    }

    // 非阻塞锁获取
    public function openWithLockNonBlocking($mode, $lockType = LOCK_EX | LOCK_NB) {
        $this->handle = fopen($this->filename, $mode);
        if (!$this->handle) {
            throw new Exception("无法打开文件");
        }

        if (flock($this->handle, $lockType)) {
            $this->lockType = $lockType;
            return true;
        } else {
            fclose($this->handle);
            $this->handle = null;
            return false;
        }
    }

    // 写入数据
    public function write($data) {
        if (!$this->handle) {
            throw new Exception("文件未打开或未获取锁");
        }

        return fwrite($this->handle, $data);
    }

    // 读取数据
    public function read($length = 1024) {
        if (!$this->handle) {
            throw new Exception("文件未打开或未获取锁");
        }

        return fread($this->handle, $length);
    }

    // 读取所有内容
    public function readAll() {
        if (!$this->handle) {
            throw new Exception("文件未打开或未获取锁");
        }

        return stream_get_contents($this->handle);
    }

    // 释放锁并关闭文件
    public function close() {
        if ($this->handle) {
            flock($this->handle, LOCK_UN); // 释放锁
            fclose($this->handle);
            $this->handle = null;
            $this->lockType = null;
        }
    }

    // 析构函数,确保锁被释放
    public function __destruct() {
        $this->close();
    }
}

// 多进程文件写入示例
function multiProcessWriteExample() {
    $filename = 'output/multi_process.txt';

    // 模拟多个进程同时写入
    for ($i = 1; $i <= 5; $i++) {
        $pid = pcntl_fork(); // 需要pcntl扩展

        if ($pid == -1) {
            // Fork失败,使用单进程模拟
            singleProcessWrite($filename, $i);
        } elseif ($pid == 0) {
            // 子进程
            singleProcessWrite($filename, $i);
            exit(0);
        }
        // 父进程继续
    }
}

// 单进程写入函数
function singleProcessWrite($filename, $processId) {
    $fileLock = new FileLock($filename);

    try {
        $fileLock->openWithLock('a');
        $timestamp = date('Y-m-d H:i:s');
        $message = "进程 {$processId} 在 {$timestamp} 写入了数据\n";

        // 模拟一些处理时间
        usleep(rand(100000, 500000)); // 0.1-0.5秒

        $fileLock->write($message);
        $fileLock->close();

        echo "进程 {$processId} 写入完成<br>";
    } catch (Exception $e) {
        echo "进程 {$processId} 错误:" . $e->getMessage() . "<br>";
    }
}

// 访问计数器示例
class AccessCounter {
    private $filename;
    private $fileLock;

    public function __construct($filename) {
        $this->filename = $filename;
        $this->fileLock = new FileLock($filename);

        // 初始化计数器文件
        if (!file_exists($filename)) {
            file_put_contents($filename, '0');
        }
    }

    // 增加访问次数
    public function increment() {
        try {
            // 打开文件并获取独占锁
            $this->fileLock->openWithLock('r+');

            // 读取当前计数
            $content = $this->fileLock->readAll();
            $count = intval(trim($content));

            // 增加计数
            $count++;

            // 回到文件开头
            rewind($this->fileLock->handle);

            // 写入新计数
            $this->fileLock->write($count);

            // 释放锁
            $this->fileLock->close();

            return $count;
        } catch (Exception $e) {
            throw new Exception("更新计数器失败:" . $e->getMessage());
        }
    }

    // 获取当前计数
    public function getCount() {
        $content = file_get_contents($this->filename);
        return intval(trim($content));
    }

    // 重置计数器
    public function reset() {
        try {
            $this->fileLock->openWithLock('w');
            $this->fileLock->write('0');
            $this->fileLock->close();
        } catch (Exception $e) {
            throw new Exception("重置计数器失败:" . $e->getMessage());
        }
    }
}

// 使用示例
try {
    $counter = new AccessCounter('output/access_counter.txt');

    // 增加访问次数
    for ($i = 0; $i < 10; $i++) {
        $count = $counter->increment();
        echo "访问次数:{$count}<br>";
        usleep(100000); // 0.1秒延迟
    }

    echo "最终访问次数:" . $counter->getCount() . "<br>";

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

目录操作

1. 目录创建和管理

<?php
// 目录管理类
class DirectoryManager {
    private $basePath;

    public function __construct($basePath = '.') {
        $this->basePath = rtrim($basePath, '/\\');
    }

    // 创建目录
    public function createDirectory($path, $permissions = 0755) {
        $fullPath = $this->getFullPath($path);

        if (is_dir($fullPath)) {
            return true;
        }

        if (!mkdir($fullPath, $permissions, true)) {
            throw new Exception("无法创建目录:{$fullPath}");
        }

        return true;
    }

    // 删除目录及其内容
    public function deleteDirectory($path, $recursive = false) {
        $fullPath = $this->getFullPath($path);

        if (!is_dir($fullPath)) {
            return false;
        }

        if ($recursive) {
            return $this->deleteRecursive($fullPath);
        } else {
            return rmdir($fullPath);
        }
    }

    // 递归删除目录
    private function deleteRecursive($dir) {
        $files = array_diff(scandir($dir), ['.', '..']);

        foreach ($files as $file) {
            $path = $dir . DIRECTORY_SEPARATOR . $file;

            if (is_dir($path)) {
                $this->deleteRecursive($path);
            } else {
                unlink($path);
            }
        }

        return rmdir($dir);
    }

    // 复制目录
    public function copyDirectory($source, $destination) {
        $sourcePath = $this->getFullPath($source);
        $destPath = $this->getFullPath($destination);

        if (!is_dir($sourcePath)) {
            throw new Exception("源目录不存在:{$sourcePath}");
        }

        // 创建目标目录
        $this->createDirectory($destPath);

        $files = array_diff(scandir($sourcePath), ['.', '..']);

        foreach ($files as $file) {
            $sourceFile = $sourcePath . DIRECTORY_SEPARATOR . $file;
            $destFile = $destPath . DIRECTORY_SEPARATOR . $file;

            if (is_dir($sourceFile)) {
                $this->copyDirectory($source . DIRECTORY_SEPARATOR . $file,
                                    $destination . DIRECTORY_SEPARATOR . $file);
            } else {
                copy($sourceFile, $destFile);
            }
        }

        return true;
    }

    // 移动目录
    public function moveDirectory($source, $destination) {
        $this->copyDirectory($source, $destination);
        $this->deleteDirectory($source, true);
        return true;
    }

    // 列出目录内容
    public function listDirectory($path = '.', $recursive = false, $showHidden = false) {
        $fullPath = $this->getFullPath($path);

        if (!is_dir($fullPath)) {
            throw new Exception("目录不存在:{$fullPath}");
        }

        return $this->scanDirectory($fullPath, $recursive, $showHidden);
    }

    // 扫描目录
    private function scanDirectory($dir, $recursive, $showHidden) {
        $items = [];
        $files = scandir($dir);

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

            if (!$showHidden && strpos($file, '.') === 0) {
                continue;
            }

            $path = $dir . DIRECTORY_SEPARATOR . $file;
            $relativePath = str_replace($this->basePath . DIRECTORY_SEPARATOR, '', $path);

            $items[] = [
                'name' => $file,
                'path' => $path,
                'relative_path' => $relativePath,
                'type' => is_dir($path) ? 'directory' : 'file',
                'size' => is_file($path) ? filesize($path) : 0,
                'modified' => filemtime($path),
                'permissions' => substr(sprintf('%o', fileperms($path)), -4)
            ];

            if ($recursive && is_dir($path)) {
                $items = array_merge($items, $this->scanDirectory($path, $recursive, $showHidden));
            }
        }

        return $items;
    }

    // 获取目录大小
    public function getDirectorySize($path) {
        $fullPath = $this->getFullPath($path);

        if (!is_dir($fullPath)) {
            return 0;
        }

        $size = 0;
        $iterator = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($fullPath, RecursiveDirectoryIterator::SKIP_DOTS),
            RecursiveIteratorIterator::SELF_FIRST
        );

        foreach ($iterator as $file) {
            if ($file->isFile()) {
                $size += $file->getSize();
            }
        }

        return $size;
    }

    // 格式化目录大小
    public function formatSize($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];
    }

    // 检查目录是否为空
    public function isEmpty($path) {
        $fullPath = $this->getFullPath($path);

        if (!is_dir($fullPath)) {
            return true;
        }

        $files = array_diff(scandir($fullPath), ['.', '..']);
        return empty($files);
    }

    // 获取完整路径
    private function getFullPath($path) {
        if (strpos($path, '/') === 0 || (PHP_OS === 'WINNT' && preg_match('/^[A-Za-z]:/', $path))) {
            return $path; // 绝对路径
        }

        return $this->basePath . DIRECTORY_SEPARATOR . $path;
    }

    // 显示目录树
    public function displayTree($path = '.', $maxDepth = 5) {
        $fullPath = $this->getFullPath($path);

        if (!is_dir($fullPath)) {
            echo "目录不存在:{$fullPath}<br>";
            return;
        }

        echo "<h3>目录树:{$path}</h3>";
        echo "<pre>";
        $this->displayDirectoryTree($fullPath, '', $maxDepth, 0);
        echo "</pre>";
    }

    // 递归显示目录树
    private function displayDirectoryTree($dir, $prefix, $maxDepth, $currentDepth) {
        if ($currentDepth >= $maxDepth) {
            return;
        }

        $files = array_diff(scandir($dir), ['.', '..']);
        sort($files);

        $count = count($files);
        $index = 0;

        foreach ($files as $file) {
            $index++;
            $path = $dir . DIRECTORY_SEPARATOR . $file;
            $isLast = ($index === $count);
            $currentPrefix = $prefix . ($isLast ? '└── ' : '├── ');

            echo $currentPrefix . $file;
            if (is_dir($path)) {
                echo "/<br>";
                $nextPrefix = $prefix . ($isLast ? '    ' : '│   ');
                $this->displayDirectoryTree($path, $nextPrefix, $maxDepth, $currentDepth + 1);
            } else {
                $size = filesize($path);
                echo " (" . $this->formatSize($size) . ")<br>";
            }
        }
    }
}

// 使用示例
try {
    $dirManager = new DirectoryManager('.');

    // 创建目录
    $dirManager->createDirectory('uploads/images');
    $dirManager->createDirectory('uploads/documents');
    $dirManager->createDirectory('backup/2024/01');

    echo "目录创建完成<br>";

    // 列出目录内容
    $items = $dirManager->listDirectory('.', false, true);
    echo "<h3>当前目录内容:</h3>";
    echo "<table border='1'>";
    echo "<tr><th>名称</th><th>类型</th><th>大小</th><th>修改时间</th><th>权限</th></tr>";

    foreach ($items as $item) {
        $modified = date('Y-m-d H:i:s', $item['modified']);
        $size = $item['type'] === 'file' ? $dirManager->formatSize($item['size']) : '-';
        echo "<tr>";
        echo "<td>" . htmlspecialchars($item['name']) . "</td>";
        echo "<td>" . $item['type'] . "</td>";
        echo "<td>" . $size . "</td>";
        echo "<td>" . $modified . "</td>";
        echo "<td>" . $item['permissions'] . "</td>";
        echo "</tr>";
    }
    echo "</table>";

    // 获取目录大小
    $size = $dirManager->getDirectorySize('.');
    echo "<p>当前目录总大小:" . $dirManager->formatSize($size) . "</p>";

    // 显示目录树
    $dirManager->displayTree('.', 3);

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

文件上传处理

1. 基本文件上传

<?php
// 文件上传处理类
class FileUploader {
    private $uploadDir;
    private $allowedTypes = [];
    private $maxSize = 5242880; // 5MB
    private $allowedExtensions = [];
    private $overwrite = false;
    private $randomizeName = true;

    public function __construct($uploadDir) {
        $this->uploadDir = rtrim($uploadDir, '/\\');

        // 确保上传目录存在
        if (!is_dir($this->uploadDir)) {
            if (!mkdir($this->uploadDir, 0755, true)) {
                throw new Exception("无法创建上传目录:{$this->uploadDir}");
            }
        }
    }

    // 设置允许的文件类型
    public function setAllowedTypes(array $types) {
        $this->allowedTypes = $types;
        return $this;
    }

    // 设置允许的扩展名
    public function setAllowedExtensions(array $extensions) {
        $this->allowedExtensions = $extensions;
        return $this;
    }

    // 设置最大文件大小
    public function setMaxSize($size) {
        $this->maxSize = $size;
        return $this;
    }

    // 设置是否覆盖已存在文件
    public function setOverwrite($overwrite) {
        $this->overwrite = $overwrite;
        return $this;
    }

    // 设置是否随机化文件名
    public function setRandomizeName($randomize) {
        $this->randomizeName = $randomize;
        return $this;
    }

    // 上传单个文件
    public function upload($fileInput) {
        if (!isset($_FILES[$fileInput])) {
            throw new Exception("没有上传文件");
        }

        $file = $_FILES[$fileInput];

        // 验证上传
        $this->validateUpload($file);

        // 验证文件
        $this->validateFile($file);

        // 生成目标文件名
        $targetFile = $this->generateTargetFilename($file['name']);

        // 移动文件
        if (!move_uploaded_file($file['tmp_name'], $targetFile)) {
            throw new Exception("文件移动失败");
        }

        return [
            'original_name' => $file['name'],
            'file_path' => $targetFile,
            'file_size' => $file['size'],
            'file_type' => $file['type'],
            'upload_time' => date('Y-m-d H:i:s')
        ];
    }

    // 批量上传
    public function uploadMultiple($fileInput) {
        if (!isset($_FILES[$fileInput])) {
            throw new Exception("没有上传文件");
        }

        $files = $_FILES[$fileInput];
        $results = [];

        // 处理多个文件
        for ($i = 0; $i < count($files['name']); $i++) {
            if ($files['error'][$i] === UPLOAD_ERR_OK) {
                $file = [
                    'name' => $files['name'][$i],
                    'type' => $files['type'][$i],
                    'tmp_name' => $files['tmp_name'][$i],
                    'error' => $files['error'][$i],
                    'size' => $files['size'][$i]
                ];

                try {
                    $result = $this->uploadSingle($file);
                    $results[] = $result;
                } catch (Exception $e) {
                    $results[] = [
                        'error' => $e->getMessage(),
                        'original_name' => $files['name'][$i]
                    ];
                }
            } else {
                $results[] = [
                    'error' => $this->getUploadErrorMessage($files['error'][$i]),
                    'original_name' => $files['name'][$i]
                ];
            }
        }

        return $results;
    }

    // 上传单个文件(内部方法)
    private function uploadSingle($file) {
        $this->validateUpload($file);
        $this->validateFile($file);

        $targetFile = $this->generateTargetFilename($file['name']);

        if (!move_uploaded_file($file['tmp_name'], $targetFile)) {
            throw new Exception("文件移动失败");
        }

        return [
            'original_name' => $file['name'],
            'file_path' => $targetFile,
            'file_size' => $file['size'],
            'file_type' => $file['type'],
            'upload_time' => date('Y-m-d H:i:s')
        ];
    }

    // 验证上传
    private function validateUpload($file) {
        if ($file['error'] !== UPLOAD_ERR_OK) {
            throw new Exception($this->getUploadErrorMessage($file['error']));
        }

        if (!is_uploaded_file($file['tmp_name'])) {
            throw new Exception("不是有效的上传文件");
        }
    }

    // 验证文件
    private function validateFile($file) {
        // 检查文件大小
        if ($file['size'] > $this->maxSize) {
            throw new Exception("文件大小超过限制:" . $this->formatBytes($this->maxSize));
        }

        // 检查MIME类型
        if (!empty($this->allowedTypes) && !in_array($file['type'], $this->allowedTypes)) {
            throw new Exception("不支持的文件类型:{$file['type']}");
        }

        // 检查文件扩展名
        $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
        if (!empty($this->allowedExtensions) && !in_array($extension, $this->allowedExtensions)) {
            throw new Exception("不支持的文件扩展名:{$extension}");
        }

        // 验证文件内容(可选)
        if ($this->isImageFile($file)) {
            $this->validateImage($file['tmp_name']);
        }
    }

    // 验证图片文件
    private function validateImage($tmpName) {
        $imageInfo = getimagesize($tmpName);
        if ($imageInfo === false) {
            throw new Exception("不是有效的图片文件");
        }

        // 检查图片尺寸限制
        list($width, $height) = $imageInfo;
        if ($width > 4000 || $height > 4000) {
            throw new Exception("图片尺寸过大");
        }
    }

    // 判断是否为图片文件
    private function isImageFile($file) {
        return strpos($file['type'], 'image/') === 0;
    }

    // 生成目标文件名
    private function generateTargetFilename($originalName) {
        $extension = pathinfo($originalName, PATHINFO_EXTENSION);
        $basename = pathinfo($originalName, PATHINFO_FILENAME);

        if ($this->randomizeName) {
            $basename = uniqid() . '_' . time();
        }

        $targetName = $basename . '.' . $extension;
        $targetPath = $this->uploadDir . DIRECTORY_SEPARATOR . $targetName;

        // 处理文件名冲突
        $counter = 1;
        while (!$this->overwrite && file_exists($targetPath)) {
            $targetName = $basename . '_' . $counter . '.' . $extension;
            $targetPath = $this->uploadDir . DIRECTORY_SEPARATOR . $targetName;
            $counter++;
        }

        return $targetPath;
    }

    // 格式化字节大小
    private function formatBytes($bytes) {
        $units = ['B', 'KB', 'MB', 'GB'];
        $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 getUploadErrorMessage($errorCode) {
        switch ($errorCode) {
            case UPLOAD_ERR_INI_SIZE:
                return "文件大小超过php.ini中的限制";
            case UPLOAD_ERR_FORM_SIZE:
                return "文件大小超过HTML表单中的限制";
            case UPLOAD_ERR_PARTIAL:
                return "文件只有部分被上传";
            case UPLOAD_ERR_NO_FILE:
                return "没有文件被上传";
            case UPLOAD_ERR_NO_TMP_DIR:
                return "找不到临时文件夹";
            case UPLOAD_ERR_CANT_WRITE:
                return "文件写入失败";
            case UPLOAD_ERR_EXTENSION:
                return "上传被文件扩展停止";
            default:
                return "未知上传错误";
        }
    }
}

// 使用示例
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    try {
        $uploader = new FileUploader('uploads');

        // 设置上传限制
        $uploader->setAllowedExtensions(['jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx'])
                ->setMaxSize(10485760) // 10MB
                ->setRandomizeName(true)
                ->setOverwrite(false);

        // 上传单个文件
        if (isset($_FILES['single_file'])) {
            $result = $uploader->upload('single_file');
            echo "<h3>上传成功!</h3>";
            echo "<p>原文件名:" . $result['original_name'] . "</p>";
            echo "<p>保存路径:" . $result['file_path'] . "</p>";
            echo "<p>文件大小:" . $uploader->formatBytes($result['file_size']) . "</p>";
            echo "<p>上传时间:" . $result['upload_time'] . "</p>";
        }

        // 批量上传
        if (isset($_FILES['multiple_files'])) {
            $results = $uploader->uploadMultiple('multiple_files');
            echo "<h3>批量上传结果:</h3>";
            echo "<ul>";
            foreach ($results as $i => $result) {
                if (isset($result['error'])) {
                    echo "<li>错误:" . htmlspecialchars($result['error']) . "</li>";
                } else {
                    echo "<li>成功:" . htmlspecialchars($result['original_name']) . "</li>";
                }
            }
            echo "</ul>";
        }

    } catch (Exception $e) {
        echo "<h3>上传错误</h3>";
        echo "<p>" . htmlspecialchars($e->getMessage()) . "</p>";
    }
}
?>

<!-- HTML表单 -->
<!DOCTYPE html>
<html>
<head>
    <title>文件上传示例</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .upload-form { border: 1px solid #ddd; padding: 20px; margin: 20px 0; }
        .form-group { margin: 15px 0; }
        label { display: block; margin-bottom: 5px; font-weight: bold; }
        input[type="file"] { padding: 5px; }
        button { padding: 10px 20px; background-color: #007cba; color: white; border: none; cursor: pointer; }
        button:hover { background-color: #005a87; }
        .success { color: green; }
        .error { color: red; }
    </style>
</head>
<body>
    <h1>文件上传示例</h1>

    <div class="upload-form">
        <h3>单个文件上传</h3>
        <form method="POST" enctype="multipart/form-data">
            <div class="form-group">
                <label for="single_file">选择文件:</label>
                <input type="file" name="single_file" id="single_file" required>
            </div>
            <button type="submit">上传文件</button>
        </form>
    </div>

    <div class="upload-form">
        <h3>多个文件上传</h3>
        <form method="POST" enctype="multipart/form-data">
            <div class="form-group">
                <label for="multiple_files">选择多个文件:</label>
                <input type="file" name="multiple_files[]" id="multiple_files" multiple required>
            </div>
            <button type="submit">批量上传</button>
        </form>
    </div>
</body>
</html>

安全注意事项

1. 文件安全检查

<?php
// 文件安全检查类
class FileSecurityChecker {
    private $allowedExtensions = [];
    private $maxFileSize = 10485760; // 10MB
    private $allowedMimeTypes = [];

    public function __construct() {
        // 设置默认的安全配置
        $this->allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'txt', 'doc', 'docx'];
        $this->allowedMimeTypes = [
            'image/jpeg', 'image/png', 'image/gif',
            'application/pdf',
            'text/plain',
            'application/msword',
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        ];
    }

    // 验证上传文件
    public function validateUploadedFile($file) {
        // 检查上传错误
        if ($file['error'] !== UPLOAD_ERR_OK) {
            throw new Exception("上传错误:" . $this->getUploadErrorMessage($file['error']));
        }

        // 验证是否为上传文件
        if (!is_uploaded_file($file['tmp_name'])) {
            throw new Exception("文件不是通过HTTP POST上传的");
        }

        // 验证文件扩展名
        $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
        if (!in_array($extension, $this->allowedExtensions)) {
            throw new Exception("不允许的文件扩展名:{$extension}");
        }

        // 验证文件大小
        if ($file['size'] > $this->maxFileSize) {
            throw new Exception("文件大小超过限制");
        }

        // 验证MIME类型
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mimeType = finfo_file($finfo, $file['tmp_name']);
        finfo_close($finfo);

        if (!in_array($mimeType, $this->allowedMimeTypes)) {
            throw new Exception("不允许的MIME类型:{$mimeType}");
        }

        // 检查文件内容
        $this->validateFileContent($file['tmp_name'], $mimeType);

        return true;
    }

    // 验证文件内容
    private function validateFileContent($filePath, $mimeType) {
        // 检查文件是否为图片
        if (strpos($mimeType, 'image/') === 0) {
            $this->validateImage($filePath);
        }

        // 检查是否包含恶意代码
        $this->checkForMaliciousContent($filePath);

        return true;
    }

    // 验证图片文件
    private function validateImage($filePath) {
        // 使用getimagesize检查文件头
        $imageInfo = @getimagesize($filePath);
        if ($imageInfo === false) {
            throw new Exception("文件不是有效的图片");
        }

        // 检查图片尺寸
        list($width, $height) = $imageInfo;
        if ($width <= 0 || $height <= 0) {
            throw new Exception("无效的图片尺寸");
        }

        // 检查图片是否过大
        if ($width > 4000 || $height > 4000) {
            throw new Exception("图片尺寸过大");
        }

        // 重新保存图片以去除潜在的恶意代码
        $this->sanitizeImage($filePath, $imageInfo[2]);
    }

    // 清理图片文件
    private function sanitizeImage($filePath, $imageType) {
        switch ($imageType) {
            case IMAGETYPE_JPEG:
                $image = imagecreatefromjpeg($filePath);
                imagejpeg($image, $filePath, 90);
                break;
            case IMAGETYPE_PNG:
                $image = imagecreatefrompng($filePath);
                imagepng($image, $filePath, 9);
                break;
            case IMAGETYPE_GIF:
                $image = imagecreatefromgif($filePath);
                imagegif($image, $filePath);
                break;
        }

        if (isset($image)) {
            imagedestroy($image);
        }
    }

    // 检查恶意内容
    private function checkForMaliciousContent($filePath) {
        // 读取文件的前1KB内容检查
        $handle = fopen($filePath, 'r');
        $content = fread($handle, 1024);
        fclose($handle);

        // 检查常见的Web脚本标签
        $dangerousPatterns = [
            '/<\?php/i',
            '/<\?=/i',
            '/<script/i',
            '/javascript:/i',
            '/vbscript:/i',
            '/onload=/i',
            '/onerror=/i'
        ];

        foreach ($dangerousPatterns as $pattern) {
            if (preg_match($pattern, $content)) {
                throw new Exception("文件包含危险内容");
            }
        }

        return true;
    }

    // 获取上传错误消息
    private function getUploadErrorMessage($errorCode) {
        switch ($errorCode) {
            case UPLOAD_ERR_INI_SIZE:
                return "文件大小超过服务器限制";
            case UPLOAD_ERR_FORM_SIZE:
                return "文件大小超过表单限制";
            case UPLOAD_ERR_PARTIAL:
                return "文件只上传了部分内容";
            case UPLOAD_ERR_NO_FILE:
                return "没有选择文件";
            case UPLOAD_ERR_NO_TMP_DIR:
                return "服务器配置错误:缺少临时目录";
            case UPLOAD_ERR_CANT_WRITE:
                return "文件写入失败";
            case UPLOAD_ERR_EXTENSION:
                return "上传被扩展阻止";
            default:
                return "未知上传错误";
        }
    }

    // 生成安全的文件名
    public function generateSecureFilename($originalName) {
        // 获取文件扩展名
        $extension = pathinfo($originalName, PATHINFO_EXTENSION);
        $basename = pathinfo($originalName, PATHINFO_FILENAME);

        // 清理文件名
        $basename = preg_replace('/[^a-zA-Z0-9_-]/', '', $basename);
        $basename = substr($basename, 0, 50); // 限制长度

        // 生成随机前缀
        $prefix = bin2hex(random_bytes(8));
        $timestamp = time();

        return $prefix . '_' . $timestamp . '_' . $basename . '.' . $extension;
    }

    // 创建安全的上传目录结构
    public function createSecureUploadDir($baseDir) {
        // 按年月创建目录
        $year = date('Y');
        $month = date('m');
        $day = date('d');

        $dirPath = $baseDir . DIRECTORY_SEPARATOR . $year .
                   DIRECTORY_SEPARATOR . $month .
                   DIRECTORY_SEPARATOR . $day;

        if (!is_dir($dirPath)) {
            if (!mkdir($dirPath, 0755, true)) {
                throw new Exception("无法创建上传目录");
            }

            // 创建.htaccess文件防止直接访问
            $htaccessContent = "Options -Indexes\n";
            $htaccessContent .= "deny from all\n";
            file_put_contents($dirPath . DIRECTORY_SEPARATOR . '.htaccess', $htaccessContent);
        }

        return $dirPath;
    }
}

// 使用示例
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['secure_upload'])) {
    try {
        $securityChecker = new FileSecurityChecker();

        // 验证上传文件
        $file = $_FILES['secure_upload'];
        $securityChecker->validateUploadedFile($file);

        // 生成安全文件名
        $secureFilename = $securityChecker->generateSecureFilename($file['name']);

        // 创建安全上传目录
        $uploadDir = $securityChecker->createSecureUploadDir('secure_uploads');
        $targetPath = $uploadDir . DIRECTORY_SEPARATOR . $secureFilename;

        // 移动文件
        if (move_uploaded_file($file['tmp_name'], $targetPath)) {
            echo "<div class='success'>";
            echo "<h3>安全上传成功!</h3>";
            echo "<p>安全文件名:" . htmlspecialchars($secureFilename) . "</p>";
            echo "<p>存储路径:" . htmlspecialchars($targetPath) . "</p>";
            echo "</div>";
        } else {
            throw new Exception("文件移动失败");
        }

    } catch (Exception $e) {
        echo "<div class='error'>";
        echo "<h3>安全检查失败</h3>";
        echo "<p>" . htmlspecialchars($e->getMessage()) . "</p>";
        echo "</div>";
    }
}
?>

<!-- 安全上传表单 -->
<div class="upload-form">
    <h3>安全文件上传</h3>
    <form method="POST" enctype="multipart/form-data">
        <div class="form-group">
            <label for="secure_upload">选择文件(安全上传):</label>
            <input type="file" name="secure_upload" id="secure_upload" required>
            <small>允许的文件类型:JPG, PNG, GIF, PDF, DOC, DOCX(最大10MB)</small>
        </div>
        <button type="submit">安全上传</button>
    </form>
</div>

总结

文件操作是PHP开发中的核心技能,掌握文件操作对于构建功能完整的Web应用至关重要。

关键要点:

  1. 路径处理:正确处理相对路径和绝对路径,使用跨平台的路径函数
  2. 文件权限:理解文件权限,确保PHP进程有足够的权限
  3. 错误处理:始终检查文件操作函数的返回值
  4. 安全考虑:验证上传文件,防止路径遍历攻击
  5. 性能优化:对大文件使用流式处理,避免内存溢出

最佳实践:

  • 使用file_exists()检查文件是否存在
  • 对上传文件进行严格的验证和安全检查
  • 使用文件锁定机制防止并发写入问题
  • 为上传的文件生成安全的文件名
  • 及时关闭文件句柄释放资源
  • 使用异常处理机制管理文件操作错误

通过学习本章内容,你现在应该能够熟练处理PHP中的各种文件操作,为构建复杂的Web应用打下坚实基础。