目录操作
目录操作是文件系统管理的重要组成部分。在PHP中,我们可以创建、删除、遍历和管理目录,这对于构建文件管理系统、内容管理系统和其他需要组织文件的应用程序非常重要。
学习目标
完成本节学习后,你将能够:
- 创建和删除目录
- 遍历目录内容
- 管理目录权限
- 递归操作目录树
- 构建目录管理工具
目录的基本操作
1. 创建目录
使用 mkdir() 函数可以创建新目录。
<?php
// 基本目录创建
$dirName = 'new_directory';
if (mkdir($dirName)) {
echo "目录 '{$dirName}' 创建成功<br>";
} else {
echo "目录 '{$dirName}' 创建失败<br>";
}
// 创建多层目录(递归创建)
$nestedDir = 'path/to/nested/directory';
if (mkdir($nestedDir, 0755, true)) {
echo "多层目录 '{$nestedDir}' 创建成功<br>";
} else {
echo "多层目录 '{$nestedDir}' 创建失败<br>";
}
// 设置权限创建目录
$secureDir = 'secure_directory';
$permissions = 0755; // rwxr-xr-x (所有者可读写执行,组和其他用户可读执行)
if (mkdir($secureDir, $permissions)) {
echo "安全目录 '{$secureDir}' 创建成功,权限:" . decoct($permissions) . "<br>";
}
?>
2. 检查目录
<?php
// 目录检查函数
function checkDirectory($path) {
echo "检查路径:{$path}<br>";
echo "是否存在:" . (file_exists($path) ? "是" : "否") . "<br>";
if (file_exists($path)) {
echo "是否为目录:" . (is_dir($path) ? "是" : "否") . "<br>";
echo "是否可读:" . (is_readable($path) ? "是" : "否") . "<br>";
echo "是否可写:" . (is_writable($path) ? "是" : "否") . "<br>";
if (is_dir($path)) {
$permissions = fileperms($path);
echo "权限:" . substr(sprintf('%o', $permissions), -4) . "<br>";
echo "修改时间:" . date('Y-m-d H:i:s', filemtime($path)) . "<br>";
}
}
echo "<hr>";
}
// 测试不同路径
checkDirectory('new_directory');
checkDirectory('nonexistent_directory');
checkDirectory('.');
checkDirectory(__DIR__);
?>
3. 删除目录
<?php
// 删除空目录
$emptyDir = 'empty_directory';
mkdir($emptyDir);
echo "目录 '{$emptyDir}' 是否存在:" . (file_exists($emptyDir) ? "是" : "否") . "<br>";
if (rmdir($emptyDir)) {
echo "目录 '{$emptyDir}' 删除成功<br>";
} else {
echo "目录 '{$emptyDir}' 删除失败(可能不为空或权限不足)<br>";
}
// 递归删除目录的函数
function removeDirectory($dir) {
if (!file_exists($dir)) {
return true;
}
if (!is_dir($dir)) {
return unlink($dir);
}
foreach (scandir($dir) as $item) {
if ($item == '.' || $item == '..') {
continue;
}
$itemPath = $dir . DIRECTORY_SEPARATOR . $item;
if (!removeDirectory($itemPath)) {
return false;
}
}
return rmdir($dir);
}
// 测试递归删除
$testDir = 'test_remove_dir';
mkdir($testDir, 0755, true);
file_put_contents($testDir . '/file1.txt', 'test content 1');
mkdir($testDir . '/subdir');
file_put_contents($testDir . '/subdir/file2.txt', 'test content 2');
echo "递归删除目录 '{$testDir}':<br>";
if (removeDirectory($testDir)) {
echo "删除成功<br>";
} else {
echo "删除失败<br>";
}
?>
遍历目录内容
1. 使用 scandir()
<?php
// 基本目录扫描
function listDirectoryContents($dir) {
if (!is_dir($dir)) {
echo "路径不是有效目录:{$dir}<br>";
return;
}
echo "<h3>目录内容:{$dir}</h3>";
$items = scandir($dir);
echo "<ul>";
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$itemPath = $dir . DIRECTORY_SEPARATOR . $item;
$isDir = is_dir($itemPath);
$icon = $isDir ? "📁" : "📄";
$type = $isDir ? "目录" : "文件";
$size = $isDir ? "-" : number_format(filesize($itemPath)) . " 字节";
$modified = date('Y-m-d H:i:s', filemtime($itemPath));
echo "<li>";
echo "{$icon} <strong>" . htmlspecialchars($item) . "</strong> - {$type}";
echo " (大小: {$size}, 修改: {$modified})";
echo "</li>";
}
echo "</ul>";
}
// 列出当前目录内容
listDirectoryContents('.');
?>
2. 使用 DirectoryIterator
<?php
// 使用 DirectoryIterator 遍历目录
function listDirectoryWithIterator($dir) {
if (!is_dir($dir)) {
echo "路径不是有效目录:{$dir}<br>";
return;
}
echo "<h3>使用 DirectoryIterator 遍历:{$dir}</h3>";
$iterator = new DirectoryIterator($dir);
echo "<table border='1' style='border-collapse: collapse; width: 100%;'>";
echo "<tr><th>名称</th><th>类型</th><th>大小</th><th>修改时间</th><th>权限</th></tr>";
foreach ($iterator as $fileInfo) {
if ($fileInfo->isDot()) {
continue;
}
$name = htmlspecialchars($fileInfo->getFilename());
$type = $fileInfo->isDir() ? '目录' : '文件';
$size = $fileInfo->isFile() ? number_format($fileInfo->getSize()) . ' 字节' : '-';
$modified = $fileInfo->getMTime() ? date('Y-m-d H:i:s', $fileInfo->getMTime()) : '-';
$permissions = substr(sprintf('%o', $fileInfo->getPerms()), -4);
echo "<tr>";
echo "<td>{$name}</td>";
echo "<td>{$type}</td>";
echo "<td>{$size}</td>";
echo "<td>{$modified}</td>";
echo "<td>{$permissions}</td>";
echo "</tr>";
}
echo "</table>";
}
// 使用迭代器列出当前目录
listDirectoryWithIterator('.');
?>
3. 递归遍历目录树
<?php
// 递归遍历目录树
class DirectoryTreeWalker {
private $maxDepth;
private $showHidden;
private $results;
public function __construct($maxDepth = 5, $showHidden = false) {
$this->maxDepth = $maxDepth;
$this->showHidden = $showHidden;
$this->results = [];
}
public function walk($dir, $currentDepth = 0) {
if ($currentDepth >= $this->maxDepth) {
return;
}
if (!is_dir($dir)) {
return;
}
$items = scandir($dir);
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
if (!$this->showHidden && strpos($item, '.') === 0) {
continue;
}
$itemPath = $dir . DIRECTORY_SEPARATOR . $item;
$isDir = is_dir($itemPath);
$this->results[] = [
'path' => $itemPath,
'name' => $item,
'type' => $isDir ? 'directory' : 'file',
'size' => $isDir ? 0 : filesize($itemPath),
'depth' => $currentDepth,
'modified' => filemtime($itemPath)
];
if ($isDir) {
$this->walk($itemPath, $currentDepth + 1);
}
}
}
public function getResults() {
return $this->results;
}
public function displayTree($dir) {
echo "<h3>目录树:{$dir}</h3>";
echo "<pre style='background: #f5f5f5; padding: 10px; font-family: monospace;'>";
$this->displayTreeRecursive($dir, '', 0);
echo "</pre>";
}
private function displayTreeRecursive($dir, $prefix, $currentDepth) {
if ($currentDepth >= $this->maxDepth) {
return;
}
if (!is_dir($dir)) {
return;
}
$items = scandir($dir);
$dirs = [];
$files = [];
// 分别处理目录和文件
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
if (!$this->showHidden && strpos($item, '.') === 0) {
continue;
}
$itemPath = $dir . DIRECTORY_SEPARATOR . $item;
if (is_dir($itemPath)) {
$dirs[] = $item;
} else {
$files[] = $item;
}
}
// 先显示目录,再显示文件
$allItems = array_merge($dirs, $files);
$count = count($allItems);
foreach ($allItems as $index => $item) {
$itemPath = $dir . DIRECTORY_SEPARATOR . $item;
$isLast = ($index === $count - 1);
$currentPrefix = $prefix . ($isLast ? '└── ' : '├── ');
echo $currentPrefix . $item;
if (is_dir($itemPath)) {
echo "/<br>";
$nextPrefix = $prefix . ($isLast ? ' ' : '│ ');
$this->displayTreeRecursive($itemPath, $nextPrefix, $currentDepth + 1);
} else {
$size = number_format(filesize($itemPath));
echo " ({$size} 字节)<br>";
}
}
}
}
// 使用目录树遍历器
$walker = new DirectoryTreeWalker(3, true);
$walker->walk('.');
$allItems = $walker->getResults();
echo "<h3>统计信息:</h3>";
$fileCount = 0;
$dirCount = 0;
$totalSize = 0;
foreach ($allItems as $item) {
if ($item['type'] === 'file') {
$fileCount++;
$totalSize += $item['size'];
} else {
$dirCount++;
}
}
echo "文件数量:{$fileCount}<br>";
echo "目录数量:{$dirCount}<br>";
echo "总大小:" . number_format($totalSize) . " 字节<br>";
// 显示目录树
$walker->displayTree('.');
?>
目录管理工具
1. 目录复制
<?php
// 目录复制类
class DirectoryCopier {
private $overwrite;
private $preservePermissions;
public function __construct($overwrite = false, $preservePermissions = true) {
$this->overwrite = $overwrite;
$this->preservePermissions = $preservePermissions;
}
public function copy($source, $destination) {
if (!is_dir($source)) {
throw new Exception("源目录不存在:{$source}");
}
// 创建目标目录
if (!is_dir($destination)) {
if (!mkdir($destination, 0755, true)) {
throw new Exception("无法创建目标目录:{$destination}");
}
}
return $this->copyRecursive($source, $destination);
}
private function copyRecursive($source, $destination) {
$items = scandir($source);
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$sourcePath = $source . DIRECTORY_SEPARATOR . $item;
$destPath = $destination . DIRECTORY_SEPARATOR . $item;
if (is_dir($sourcePath)) {
// 复制子目录
if (!is_dir($destPath)) {
if (!mkdir($destPath, 0755, true)) {
throw new Exception("无法创建子目录:{$destPath}");
}
}
$this->copyRecursive($sourcePath, $destPath);
// 保持权限
if ($this->preservePermissions) {
chmod($destPath, fileperms($sourcePath));
}
} else {
// 复制文件
if (file_exists($destPath) && !$this->overwrite) {
continue;
}
if (!copy($sourcePath, $destPath)) {
throw new Exception("无法复制文件:{$sourcePath} -> {$destPath}");
}
// 保持权限
if ($this->preservePermissions) {
chmod($destPath, fileperms($sourcePath));
}
}
}
return true;
}
public function getCopyStats($source, $destination) {
$stats = [
'files_copied' => 0,
'dirs_created' => 0,
'bytes_copied' => 0
];
$this->calculateStats($source, $destination, $stats);
return $stats;
}
private function calculateStats($source, $destination, &$stats) {
if (!is_dir($source)) {
return;
}
$items = scandir($source);
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$sourcePath = $source . DIRECTORY_SEPARATOR . $item;
$destPath = $destination . DIRECTORY_SEPARATOR . $item;
if (is_dir($sourcePath)) {
if (!is_dir($destPath)) {
$stats['dirs_created']++;
}
$this->calculateStats($sourcePath, $destPath, $stats);
} else {
if (file_exists($destPath)) {
$sourceSize = filesize($sourcePath);
$destSize = filesize($destPath);
if ($sourceSize === $destSize) {
continue; // 文件已存在且大小相同
}
}
$stats['files_copied']++;
$stats['bytes_copied'] += filesize($sourcePath);
}
}
}
}
// 使用目录复制工具
try {
// 创建测试源目录
$sourceDir = 'test_source';
if (!is_dir($sourceDir)) {
mkdir($sourceDir, 0755, true);
}
// 创建一些测试文件
file_put_contents($sourceDir . '/file1.txt', 'Test file 1 content');
file_put_contents($sourceDir . '/file2.txt', 'Test file 2 content');
mkdir($sourceDir . '/subdir');
file_put_contents($sourceDir . '/subdir/file3.txt', 'Test file 3 content');
$copier = new DirectoryCopier(true, true);
$destinationDir = 'test_copy';
// 执行复制
$copier->copy($sourceDir, $destinationDir);
echo "目录复制成功<br>";
// 显示统计信息
$stats = $copier->getCopyStats($sourceDir, $destinationDir);
echo "<h3>复制统计:</h3>";
echo "复制的文件:{$stats['files_copied']}<br>";
echo "创建的目录:{$stats['dirs_created']}<br>";
echo "复制的字节:" . number_format($stats['bytes_copied']) . "<br>";
} catch (Exception $e) {
echo "复制错误:" . $e->getMessage();
}
?>
2. 目录同步
<?php
// 目录同步类
class DirectorySynchronizer {
private $source;
private $destination;
private $deleteExtra;
private $log;
public function __construct($source, $destination, $deleteExtra = false) {
$this->source = rtrim($source, '/\\');
$this->destination = rtrim($destination, '/\\');
$this->deleteExtra = $deleteExtra;
$this->log = [];
}
public function sync() {
if (!is_dir($this->source)) {
throw new Exception("源目录不存在:{$this->source}");
}
// 确保目标目录存在
if (!is_dir($this->destination)) {
mkdir($this->destination, 0755, true);
$this->addLog("创建目标目录:{$this->destination}");
}
$this->syncRecursive($this->source, $this->destination);
return $this->log;
}
private function syncRecursive($sourceDir, $destDir) {
// 确保目标目录存在
if (!is_dir($destDir)) {
mkdir($destDir, 0755, true);
$this->addLog("创建目录:" . $this->getRelativePath($destDir));
}
$sourceItems = scandir($sourceDir);
$destItems = is_dir($destDir) ? scandir($destDir) : [];
// 同步源目录中的项目
foreach ($sourceItems as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$sourcePath = $sourceDir . DIRECTORY_SEPARATOR . $item;
$destPath = $destDir . DIRECTORY_SEPARATOR . $item;
if (is_dir($sourcePath)) {
// 递归同步子目录
$this->syncRecursive($sourcePath, $destPath);
} else {
// 同步文件
$this->syncFile($sourcePath, $destPath);
}
}
// 删除目标目录中多余的项目
if ($this->deleteExtra) {
foreach ($destItems as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$sourcePath = $sourceDir . DIRECTORY_SEPARATOR . $item;
$destPath = $destDir . DIRECTORY_SEPARATOR . $item;
if (!file_exists($sourcePath)) {
if (is_dir($destPath)) {
$this->deleteDirectoryRecursive($destPath);
$this->addLog("删除多余目录:" . $this->getRelativePath($destPath));
} else {
unlink($destPath);
$this->addLog("删除多余文件:" . $this->getRelativePath($destPath));
}
}
}
}
}
private function syncFile($sourceFile, $destFile) {
$needsUpdate = false;
if (!file_exists($destFile)) {
$needsUpdate = true;
$action = "复制新文件";
} elseif (filemtime($sourceFile) > filemtime($destFile)) {
$needsUpdate = true;
$action = "更新文件";
} elseif (filesize($sourceFile) !== filesize($destFile)) {
$needsUpdate = true;
$action = "同步文件大小";
}
if ($needsUpdate) {
if (copy($sourceFile, $destFile)) {
touch($destFile, filemtime($sourceFile)); // 保持修改时间
$this->addLog($action . ":" . $this->getRelativePath($destFile));
} else {
$this->addLog("同步失败:" . $this->getRelativePath($destFile));
}
}
}
private function deleteDirectoryRecursive($dir) {
if (!is_dir($dir)) {
return;
}
$items = scandir($dir);
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$itemPath = $dir . DIRECTORY_SEPARATOR . $item;
if (is_dir($itemPath)) {
$this->deleteDirectoryRecursive($itemPath);
} else {
unlink($itemPath);
}
}
rmdir($dir);
}
private function getRelativePath($path) {
$basePath = dirname($this->destination);
return str_replace($basePath . DIRECTORY_SEPARATOR, '', $path);
}
private function addLog($message) {
$this->log[] = date('Y-m-d H:i:s') . ' - ' . $message;
}
public function getLog() {
return $this->log;
}
}
// 使用目录同步工具
try {
// 创建测试环境
$sourceDir = 'sync_source';
$destDir = 'sync_dest';
if (!is_dir($sourceDir)) {
mkdir($sourceDir, 0755, true);
file_put_contents($sourceDir . '/test.txt', 'Original content');
mkdir($sourceDir . '/subdir');
file_put_contents($sourceDir . '/subdir/nested.txt', 'Nested content');
}
if (!is_dir($destDir)) {
mkdir($destDir, 0755, true);
file_put_contents($destDir . '/old_file.txt', 'Old content');
}
$synchronizer = new DirectorySynchronizer($sourceDir, $destDir, true);
$log = $synchronizer->sync();
echo "<h3>同步日志:</h3>";
echo "<ul>";
foreach ($log as $entry) {
echo "<li>" . htmlspecialchars($entry) . "</li>";
}
echo "</ul>";
} catch (Exception $e) {
echo "同步错误:" . $e->getMessage();
}
?>
实用工具
1. 目录大小分析器
<?php
// 目录大小分析器
class DirectoryAnalyzer {
private $excludePatterns;
private $maxDepth;
private $results;
public function __construct($excludePatterns = [], $maxDepth = 10) {
$this->excludePatterns = array_merge([
'/^\./', // 隐藏文件
'/\.(git|svn)$/', // 版本控制目录
'/node_modules/', // Node.js依赖
'/vendor/', // Composer依赖
], $excludePatterns);
$this->maxDepth = $maxDepth;
$this->results = [];
}
public function analyze($directory) {
if (!is_dir($directory)) {
throw new Exception("目录不存在:{$directory}");
}
$this->results = [
'total_size' => 0,
'file_count' => 0,
'dir_count' => 0,
'extensions' => [],
'largest_files' => [],
'directory_sizes' => []
];
$this->analyzeRecursive($directory, 0);
return $this->results;
}
private function analyzeRecursive($directory, $currentDepth) {
if ($currentDepth >= $this->maxDepth) {
return 0;
}
$dirSize = 0;
$items = scandir($directory);
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
// 检查排除模式
if ($this->shouldExclude($item)) {
continue;
}
$path = $directory . DIRECTORY_SEPARATOR . $item;
if (is_dir($path)) {
$this->results['dir_count']++;
$subDirSize = $this->analyzeRecursive($path, $currentDepth + 1);
$dirSize += $subDirSize;
$relativePath = $this->getRelativePath($path);
$this->results['directory_sizes'][$relativePath] = $subDirSize;
} elseif (is_file($path)) {
$fileSize = filesize($path);
$dirSize += $fileSize;
$this->results['file_count']++;
// 统计扩展名
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
if ($extension) {
if (!isset($this->results['extensions'][$extension])) {
$this->results['extensions'][$extension] = [
'count' => 0,
'size' => 0
];
}
$this->results['extensions'][$extension]['count']++;
$this->results['extensions'][$extension]['size'] += $fileSize;
}
// 记录大文件
$this->results['largest_files'][] = [
'path' => $this->getRelativePath($path),
'size' => $fileSize
];
}
}
return $dirSize;
}
private function shouldExclude($name) {
foreach ($this->excludePatterns as $pattern) {
if (preg_match($pattern, $name)) {
return true;
}
}
return false;
}
private function getRelativePath($path) {
$basePath = getcwd();
return str_replace($basePath . DIRECTORY_SEPARATOR, '', $path);
}
public function displayResults() {
echo "<h2>目录分析报告</h2>";
// 总体统计
echo "<h3>总体统计:</h3>";
echo "<ul>";
echo "<li>总大小:" . $this->formatBytes($this->results['total_size']) . "</li>";
echo "<li>文件数量:" . $this->results['file_count'] . "</li>";
echo "<li>目录数量:" . $this->results['dir_count'] . "</li>";
echo "<li>平均文件大小:" . $this->formatBytes($this->results['total_size'] / max(1, $this->results['file_count'])) . "</li>";
echo "</ul>";
// 文件类型统计
if (!empty($this->results['extensions'])) {
echo "<h3>文件类型统计:</h3>";
echo "<table border='1' style='border-collapse: collapse;'>";
echo "<tr><th>扩展名</th><th>文件数</th><th>总大小</th><th>平均大小</th><th>占比</th></tr>";
// 按大小排序
uasort($this->results['extensions'], function($a, $b) {
return $b['size'] - $a['size'];
});
foreach ($this->results['extensions'] as $ext => $stats) {
$percentage = round(($stats['size'] / $this->results['total_size']) * 100, 2);
$avgSize = $this->formatBytes($stats['size'] / $stats['count']);
echo "<tr>";
echo "<td>.{$ext}</td>";
echo "<td>{$stats['count']}</td>";
echo "<td>" . $this->formatBytes($stats['size']) . "</td>";
echo "<td>{$avgSize}</td>";
echo "<td>{$percentage}%</td>";
echo "</tr>";
}
echo "</table>";
}
// 最大文件
if (!empty($this->results['largest_files'])) {
echo "<h3>最大文件(前10个):</h3>";
// 按大小排序并取前10个
usort($this->results['largest_files'], function($a, $b) {
return $b['size'] - $a['size'];
});
$topFiles = array_slice($this->results['largest_files'], 0, 10);
echo "<table border='1' style='border-collapse: collapse;'>";
echo "<tr><th>文件</th><th>大小</th></tr>";
foreach ($topFiles as $file) {
echo "<tr>";
echo "<td>" . htmlspecialchars($file['path']) . "</td>";
echo "<td>" . $this->formatBytes($file['size']) . "</td>";
echo "</tr>";
}
echo "</table>";
}
// 目录大小
if (!empty($this->results['directory_sizes'])) {
echo "<h3>目录大小(前10个):</h3>";
// 按大小排序并取前10个
arsort($this->results['directory_sizes']);
$topDirs = array_slice($this->results['directory_sizes'], 0, 10, true);
echo "<table border='1' style='border-collapse: collapse;'>";
echo "<tr><th>目录</th><th>大小</th></tr>";
foreach ($topDirs as $dir => $size) {
echo "<tr>";
echo "<td>" . htmlspecialchars($dir) . "</td>";
echo "<td>" . $this->formatBytes($size) . "</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];
}
}
// 使用目录分析器
try {
$analyzer = new DirectoryAnalyzer([], 5);
$results = $analyzer->analyze('.');
// 设置结果(analyze方法已经更新了results)
$analyzer->results = $results;
$analyzer->displayResults();
} catch (Exception $e) {
echo "分析错误:" . $e->getMessage();
}
?>
2. 目录清理工具
<?php
// 目录清理工具
class DirectoryCleaner {
private $rules;
private $dryRun;
private $deleted;
private $errors;
public function __construct($dryRun = true) {
$this->dryRun = $dryRun;
$this->rules = [];
$this->deleted = [];
$this->errors = [];
}
// 添加清理规则
public function addRule($name, $callback) {
$this->rules[$name] = $callback;
}
// 清理目录
public function clean($directory) {
if (!is_dir($directory)) {
throw new Exception("目录不存在:{$directory}");
}
$this->deleted = [];
$this->errors = [];
$this->cleanRecursive($directory);
return [
'deleted' => $this->deleted,
'errors' => $this->errors,
'dry_run' => $this->dryRun
];
}
private function cleanRecursive($directory) {
$items = scandir($directory);
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$path = $directory . DIRECTORY_SEPARATOR . $item;
if (is_dir($path)) {
$this->cleanRecursive($path);
// 尝试删除空目录
$this->tryDeleteEmptyDirectory($path);
} else {
$this->tryDeleteFile($path);
}
}
}
private function tryDeleteFile($path) {
foreach ($this->rules as $ruleName => $rule) {
try {
if ($rule($path)) {
if ($this->dryRun) {
$this->deleted[] = [
'path' => $path,
'rule' => $ruleName,
'action' => 'would_delete'
];
} else {
if (unlink($path)) {
$this->deleted[] = [
'path' => $path,
'rule' => $ruleName,
'action' => 'deleted'
];
} else {
$this->errors[] = [
'path' => $path,
'message' => '无法删除文件'
];
}
}
break; // 一旦匹配一个规则就停止
}
} catch (Exception $e) {
$this->errors[] = [
'path' => $path,
'message' => $e->getMessage()
];
}
}
}
private function tryDeleteEmptyDirectory($path) {
$items = scandir($path);
if (count($items) === 2) { // 只有 . 和 ..
if ($this->dryRun) {
$this->deleted[] = [
'path' => $path,
'rule' => 'empty_directory',
'action' => 'would_delete'
];
} else {
if (rmdir($path)) {
$this->deleted[] = [
'path' => $path,
'rule' => 'empty_directory',
'action' => 'deleted'
];
}
}
}
}
public function setDryRun($dryRun) {
$this->dryRun = $dryRun;
}
public function displayResults() {
$action = $this->dryRun ? '将要删除' : '已删除';
echo "<h3>清理结果({$action})</h3>";
if (!empty($this->deleted)) {
echo "<p>文件/目录数量:" . count($this->deleted) . "</p>";
echo "<ul>";
foreach ($this->deleted as $item) {
$rule = $item['rule'] === 'empty_directory' ? '空目录' : $item['rule'];
echo "<li>" . htmlspecialchars($item['path']) . " (规则: {$rule})</li>";
}
echo "</ul>";
} else {
echo "<p>没有文件或目录被标记为删除</p>";
}
if (!empty($this->errors)) {
echo "<h4>错误:</h4>";
echo "<ul>";
foreach ($this->errors as $error) {
echo "<li>" . htmlspecialchars($error['path']) . ": " . htmlspecialchars($error['message']) . "</li>";
}
echo "</ul>";
}
}
}
// 创建清理工具并添加规则
$cleaner = new DirectoryCleaner(true); // 默认为试运行模式
// 添加常见清理规则
$cleaner->addRule('temp_files', function($path) {
$name = basename($path);
$tempPatterns = ['~$', '.tmp', '.temp', '.bak', '.swp', '.log'];
foreach ($tempPatterns as $pattern) {
if (strpos($name, $pattern) !== false) {
return true;
}
}
return false;
});
$cleaner->addRule('old_logs', function($path) {
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
if ($ext === 'log') {
// 删除7天前的日志文件
$mtime = filemtime($path);
return ($mtime < (time() - 7 * 24 * 60 * 60));
}
return false;
});
$cleaner->addRule('old_cache', function($path) {
$path = strtolower($path);
if (strpos($path, 'cache') !== false || strpos($path, 'temp') !== false) {
$mtime = filemtime($path);
// 删除30天前的缓存文件
return ($mtime < (time() - 30 * 24 * 60 * 60));
}
return false;
});
$cleaner->addRule('php_sessions', function($path) {
$name = basename($path);
// 删除旧的PHP会话文件
return (strpos($name, 'sess_') === 0 && filemtime($path) < (time() - 24 * 60 * 60));
});
// 运行清理(试运行模式)
echo "<h2>目录清理(试运行模式)</h2>";
echo "<p>注意:这是试运行模式,没有实际删除任何文件</p>";
try {
$results = $cleaner->clean('.');
$cleaner->displayResults();
echo "<p>要实际删除文件,请将 dryRun 参数设置为 false</p>";
} catch (Exception $e) {
echo "清理错误:" . $e->getMessage();
}
?>
总结
目录操作是PHP文件系统编程的重要组成部分。通过本节学习,你应该掌握:
关键技能:
-
基本目录操作:
- 创建、删除、检查目录
- 设置和管理目录权限
- 处理嵌套目录结构
-
目录遍历:
- 使用
scandir()进行基本遍历 - 使用
DirectoryIterator进行面向对象遍历 - 实现递归目录树遍历
- 使用
-
实用工具:
- 目录复制和同步
- 目录大小分析
- 自动化清理工具
-
最佳实践:
- 错误处理和异常管理
- 权限检查和安全考虑
- 性能优化和内存管理
这些技能将帮助你构建功能完整的文件管理系统和内容管理平台。