unlink() 函数用于删除文件。这个函数对应于Unix/Linux系统中的unlink命令和Windows系统中的del命令。
与rmdir()函数不同,unlink()只能删除文件,不能删除目录。要删除目录,需要使用rmdir()函数。
unlink ( string $filename [, resource $context ] ) : bool
参数说明:
$filename:要删除的文件的路径(必需)$context:上下文资源,用于指定流的上下文参数(可选)| 参数 | 描述 |
|---|---|
filename |
要删除的文件的路径。必需参数。
|
context |
上下文资源。可选参数。
|
truefalse,通常是由于以下原因:
删除一个文件并检查结果:
<?php
$filename = 'old_file.txt';
if (file_exists($filename)) {
if (unlink($filename)) {
echo "文件删除成功: $filename";
} else {
echo "文件删除失败: $filename";
// 获取错误信息
$error = error_get_last();
if ($error) {
echo " - " . $error['message'];
}
}
} else {
echo "文件不存在: $filename";
}
?>
安全地删除文件,包含更多验证:
<?php
function safeUnlink($filename) {
// 验证输入
if (!is_string($filename) || empty($filename)) {
return ['success' => false, 'error' => '无效的文件名'];
}
// 检查文件是否存在
if (!file_exists($filename)) {
return ['success' => false, 'error' => '文件不存在'];
}
// 检查是否是文件(不是目录)
if (!is_file($filename)) {
return ['success' => false, 'error' => '路径不是文件'];
}
// 检查文件是否可写
if (!is_writable($filename)) {
return ['success' => false, 'error' => '文件不可写'];
}
// 尝试删除文件
if (unlink($filename)) {
// 验证文件是否真的被删除
clearstatcache(true, $filename);
if (!file_exists($filename)) {
return ['success' => true, 'error' => null];
} else {
return ['success' => false, 'error' => '文件删除失败(可能仍在缓存中)'];
}
} else {
$error = error_get_last();
return ['success' => false, 'error' => $error['message'] ?? '未知错误'];
}
}
// 使用示例
$result = safeUnlink('data.txt');
if ($result['success']) {
echo "文件安全删除成功";
} else {
echo "删除失败: " . $result['error'];
}
?>
批量删除匹配特定模式的文件:
<?php
function deleteFilesByPattern($directory, $pattern = '*') {
if (!is_dir($directory) || !is_readable($directory)) {
return ['success' => false, 'error' => '目录不可访问'];
}
$files = glob($directory . '/' . $pattern);
$results = [
'total' => count($files),
'deleted' => 0,
'failed' => 0,
'errors' => []
];
foreach ($files as $file) {
// 跳过目录
if (is_dir($file)) {
continue;
}
if (unlink($file)) {
$results['deleted']++;
} else {
$results['failed']++;
$error = error_get_last();
$results['errors'][$file] = $error['message'] ?? '未知错误';
}
}
return $results;
}
// 使用示例:删除所有.txt文件
$result = deleteFilesByPattern('/tmp', '*.txt');
echo "批量删除结果:<br>";
echo "找到文件: {$result['total']} 个<br>";
echo "成功删除: {$result['deleted']} 个<br>";
echo "删除失败: {$result['failed']} 个<br>";
if (!empty($result['errors'])) {
echo "<br>错误详情:<br>";
foreach ($result['errors'] as $file => $error) {
echo "- {$file}: {$error}<br>";
}
}
?>
删除超过指定时间的旧文件:
<?php
function deleteOldFiles($directory, $maxAgeDays = 30) {
if (!is_dir($directory)) {
return false;
}
$maxAge = $maxAgeDays * 24 * 60 * 60; // 转换为秒
$now = time();
$deleted = 0;
$files = scandir($directory);
foreach ($files as $file) {
if ($file === '.' || $file === '..') {
continue;
}
$filepath = $directory . '/' . $file;
// 只处理文件
if (!is_file($filepath)) {
continue;
}
// 获取文件修改时间
$filemtime = filemtime($filepath);
$fileAge = $now - $filemtime;
// 如果文件太旧,删除它
if ($fileAge > $maxAge) {
if (unlink($filepath)) {
$deleted++;
echo "删除旧文件: {$file} (年龄: " . round($fileAge / 86400, 1) . " 天)<br>";
}
}
}
return $deleted;
}
// 使用示例:删除超过7天的文件
$deletedCount = deleteOldFiles('/var/log/myapp', 7);
echo "共删除了 {$deletedCount} 个旧文件";
?>
使用上下文资源删除文件(虽然不常用):
<?php
// 创建上下文(虽然unlink()通常不需要特殊的上下文)
$context = stream_context_create([
'http' => [
'method' => 'GET',
'header' => "User-Agent: PHP\r\n"
]
]);
$filename = 'temp_file.txt';
// 先创建测试文件
file_put_contents($filename, '测试内容');
// 使用上下文删除文件
if (unlink($filename, $context)) {
echo "使用上下文删除文件成功";
} else {
echo "删除失败";
// 如果失败,尝试不使用上下文删除
if (file_exists($filename)) {
unlink($filename);
}
}
?>
unlink() 函数可能被滥用,特别是当文件名来自用户输入时。需要特别注意以下安全问题:
防止用户输入导致删除系统文件:
<?php
// 不安全的做法
$user_input = $_GET['file']; // 用户可能输入 '../../etc/passwd'
unlink($user_input); // 危险!可能删除系统文件
// 安全的做法
function safeDeleteUserFile($user_input, $allowed_dir) {
// 获取规范化路径
$real_path = realpath($allowed_dir . '/' . basename($user_input));
// 验证路径是否在允许的目录内
if (strpos($real_path, realpath($allowed_dir)) !== 0) {
return false; // 路径不在允许的目录内
}
// 验证是否是文件且存在
if (!is_file($real_path)) {
return false;
}
return unlink($real_path);
}
// 使用示例
$result = safeDeleteUserFile($_GET['file'], '/var/www/uploads');
?>
防止通过符号链接删除敏感文件:
<?php
function safeUnlinkWithSymlinkCheck($filename) {
// 检查是否是符号链接
if (is_link($filename)) {
// 如果是符号链接,获取实际路径
$realpath = realpath($filename);
// 验证实际路径是否在安全范围内
$safe_dirs = ['/var/www/uploads', '/tmp/myapp'];
$is_safe = false;
foreach ($safe_dirs as $safe_dir) {
if (strpos($realpath, $safe_dir) === 0) {
$is_safe = true;
break;
}
}
if (!$is_safe) {
return false;
}
}
return unlink($filename);
}
?>
在执行删除前检查适当权限:
<?php
function checkDeletePermission($filename) {
// 检查文件是否存在
if (!file_exists($filename)) {
return false;
}
// 检查是否是文件
if (!is_file($filename)) {
return false;
}
// 检查当前用户是否有权限
$perms = fileperms($filename);
// 如果是当前用户拥有的文件
if (fileowner($filename) === getmyuid()) {
// 检查所有者写权限
return ($perms & 0x0080) !== 0; // 所有者写权限位
}
// 如果是当前用户所在组的文件
if (filegroup($filename) === getmygid()) {
// 检查组写权限
return ($perms & 0x0010) !== 0; // 组写权限位
}
// 其他情况:检查其他用户写权限
return ($perms & 0x0002) !== 0; // 其他用户写权限位
}
?>
<?php
$filename = 'test.txt';
// 方法1:使用错误控制运算符和错误获取函数
if (@unlink($filename)) {
echo "删除成功";
} else {
$error = error_get_last();
if ($error) {
echo "删除失败: " . $error['message'];
}
}
// 方法2:使用try-catch(PHP 7.0+)
try {
if (!unlink($filename)) {
throw new Exception("无法删除文件: $filename");
}
echo "删除成功";
} catch (Exception $e) {
echo "错误: " . $e->getMessage();
}
?>
| 错误情况 | 可能的原因 | 解决方案 |
|---|---|---|
| 权限被拒绝 | 文件只读、没有写权限、被其他进程锁定 | 检查文件权限,确保文件未被占用 |
| 文件不存在 | 文件已被删除、路径错误、符号链接断裂 | 使用file_exists()检查文件是否存在 |
| 路径是目录 | 尝试使用unlink()删除目录 | 使用is_file()验证,使用rmdir()删除目录 |
| 磁盘I/O错误 | 磁盘故障、文件系统错误、磁盘满 | 检查磁盘状态和可用空间 |
<?php
function unlinkWithDetailedError($filename) {
// 清除状态缓存
clearstatcache(true, $filename);
// 检查文件是否存在
if (!file_exists($filename)) {
return ['success' => false, 'error' => '文件不存在', 'code' => 'ENOENT'];
}
// 检查是否是文件
if (!is_file($filename)) {
if (is_dir($filename)) {
return ['success' => false, 'error' => '路径是目录,请使用rmdir()', 'code' => 'EISDIR'];
}
return ['success' => false, 'error' => '路径不是普通文件', 'code' => 'ENOTFILE'];
}
// 尝试删除
if (unlink($filename)) {
// 验证删除成功
clearstatcache(true, $filename);
if (!file_exists($filename)) {
return ['success' => true, 'error' => null, 'code' => null];
} else {
return ['success' => false, 'error' => '文件删除后仍然存在(可能是缓存问题)', 'code' => 'ESTALE'];
}
} else {
// 获取系统错误
$error = error_get_last();
$error_msg = $error['message'] ?? '未知错误';
// 根据错误消息判断错误类型
$error_code = 'EUNKNOWN';
if (strpos($error_msg, 'Permission denied') !== false) {
$error_code = 'EACCES';
} elseif (strpos($error_msg, 'No such file') !== false) {
$error_code = 'ENOENT';
}
return ['success' => false, 'error' => $error_msg, 'code' => $error_code];
}
}
// 使用示例
$result = unlinkWithDetailedError('/path/to/file.txt');
if ($result['success']) {
echo "删除成功";
} else {
echo "删除失败 [{$result['code']}]: {$result['error']}";
}
?>
自动清理应用程序生成的临时文件:
<?php
class TempFileCleaner {
private $tempDir;
private $patterns;
public function __construct($tempDir = null) {
$this->tempDir = $tempDir ?: sys_get_temp_dir();
$this->patterns = ['*.tmp', '*.temp', 'cache_*', 'temp_*'];
}
public function cleanup($maxAgeHours = 24) {
if (!is_dir($this->tempDir) || !is_readable($this->tempDir)) {
return ['success' => false, 'error' => '临时目录不可访问'];
}
$maxAge = $maxAgeHours * 3600;
$now = time();
$stats = ['deleted' => 0, 'failed' => 0, 'skipped' => 0];
foreach ($this->patterns as $pattern) {
$files = glob($this->tempDir . '/' . $pattern);
foreach ($files as $file) {
// 跳过目录
if (is_dir($file)) {
$stats['skipped']++;
continue;
}
// 检查文件年龄
$fileAge = $now - filemtime($file);
if ($fileAge > $maxAge) {
if (unlink($file)) {
$stats['deleted']++;
} else {
$stats['failed']++;
}
} else {
$stats['skipped']++;
}
}
}
return ['success' => true, 'stats' => $stats];
}
public function getDiskUsage() {
$totalSize = 0;
$fileCount = 0;
foreach ($this->patterns as $pattern) {
$files = glob($this->tempDir . '/' . $pattern);
foreach ($files as $file) {
if (is_file($file)) {
$totalSize += filesize($file);
$fileCount++;
}
}
}
return [
'total_size' => $totalSize,
'file_count' => $fileCount,
'total_size_mb' => round($totalSize / 1024 / 1024, 2)
];
}
}
// 使用示例
$cleaner = new TempFileCleaner();
$usage = $cleaner->getDiskUsage();
echo "临时文件使用情况: {$usage['file_count']} 个文件,{$usage['total_size_mb']} MB<br>";
$result = $cleaner->cleanup(12); // 清理超过12小时的文件
if ($result['success']) {
$stats = $result['stats'];
echo "清理完成: 删除 {$stats['deleted']} 个,失败 {$stats['failed']} 个,跳过 {$stats['skipped']} 个";
}
?>
处理文件上传失败或完成后的清理:
<?php
class UploadHandler {
private $uploadDir;
private $maxFileSize;
private $allowedTypes;
public function __construct($uploadDir, $maxFileSize = 10485760, $allowedTypes = []) {
$this->uploadDir = $uploadDir;
$this->maxFileSize = $maxFileSize;
$this->allowedTypes = $allowedTypes;
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
}
public function handleUpload($fileField) {
if (!isset($_FILES[$fileField])) {
return ['success' => false, 'error' => '没有上传文件'];
}
$file = $_FILES[$fileField];
$tempPath = $file['tmp_name'];
// 检查上传错误
if ($file['error'] !== UPLOAD_ERR_OK) {
$this->cleanupTempFile($tempPath);
return ['success' => false, 'error' => $this->getUploadError($file['error'])];
}
// 检查文件大小
if ($file['size'] > $this->maxFileSize) {
$this->cleanupTempFile($tempPath);
return ['success' => false, 'error' => '文件太大'];
}
// 检查文件类型
if (!empty($this->allowedTypes)) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $tempPath);
finfo_close($finfo);
if (!in_array($mimeType, $this->allowedTypes)) {
$this->cleanupTempFile($tempPath);
return ['success' => false, 'error' => '不允许的文件类型'];
}
}
// 生成安全文件名
$filename = $this->generateSafeFilename($file['name']);
$destination = $this->uploadDir . '/' . $filename;
// 移动文件
if (move_uploaded_file($tempPath, $destination)) {
return ['success' => true, 'filename' => $filename, 'path' => $destination];
} else {
$this->cleanupTempFile($tempPath);
return ['success' => false, 'error' => '文件移动失败'];
}
}
private function cleanupTempFile($tempPath) {
if (file_exists($tempPath) && is_file($tempPath)) {
@unlink($tempPath);
}
}
private function getUploadError($errorCode) {
$errors = [
UPLOAD_ERR_INI_SIZE => '文件超过php.ini中upload_max_filesize限制',
UPLOAD_ERR_FORM_SIZE => '文件超过表单MAX_FILE_SIZE限制',
UPLOAD_ERR_PARTIAL => '文件只有部分被上传',
UPLOAD_ERR_NO_FILE => '没有文件被上传',
UPLOAD_ERR_NO_TMP_DIR => '找不到临时文件夹',
UPLOAD_ERR_CANT_WRITE => '文件写入失败',
UPLOAD_ERR_EXTENSION => 'PHP扩展阻止了文件上传'
];
return $errors[$errorCode] ?? '未知上传错误';
}
private function generateSafeFilename($originalName) {
$extension = pathinfo($originalName, PATHINFO_EXTENSION);
$basename = pathinfo($originalName, PATHINFO_FILENAME);
// 清理文件名
$safeBasename = preg_replace('/[^a-zA-Z0-9_-]/', '_', $basename);
$safeBasename = substr($safeBasename, 0, 100); // 限制长度
return $safeBasename . '_' . uniqid() . '.' . $extension;
}
public function deleteUploadedFile($filename) {
$filepath = $this->uploadDir . '/' . basename($filename);
if (file_exists($filepath) && is_file($filepath)) {
return unlink($filepath);
}
return false;
}
}
// 使用示例
$uploader = new UploadHandler('/var/www/uploads', 5 * 1024 * 1024, ['image/jpeg', 'image/png']);
$result = $uploader->handleUpload('userfile');
if ($result['success']) {
echo "上传成功: " . $result['filename'];
// ... 处理上传的文件
// 如果需要删除
// $uploader->deleteUploadedFile($result['filename']);
} else {
echo "上传失败: " . $result['error'];
}
?>