unlink() 删除失败主因是父目录无写权限、文件被占用或路径错误,而非函数误用;需检查父目录w权限、释放文件对象、验证真实路径并分层错误处理。

unlink() 删除失败,90% 不是函数写错了,而是权限、占用或路径校验没做对。
父目录没写权限,文件再 777 也删不掉
Linux/macOS 下 unlink() 的成败关键不在文件自身权限,而在它所在目录是否对 PHP 进程用户开放了 w 权限。即使 is_writable($filepath) 返回 true,unlink() 仍可能报 Permission denied。
- 用
ls -ld /path/to/dir查父目录权限,确认 PHP 进程用户(如www-data)有w位 - 在 PHP 中执行
echo posix_getpwuid(posix_geteuid())['name'];确认真实运行用户 - 临时调试:用
sudo -u www-data ls -l /path/to/dir模拟 PHP 视角 - 修复方式优先改目录权限:
chmod u+w /path/to/dir,而非反复 chmod 文件本身
文件被 PHP 自己占着,unset() 才是解药
ThinkPHP5/6 或原生上传后直接 unlink() 失败,常见于 $file->move() 返回的 $info 对象未释放。该对象内部持有资源句柄,系统认为文件正被使用,拒绝删除。
- 上传完成后立刻执行
unset($info),不是“可选”,是必须 - Windows 下尤其明显,哪怕加了
sleep(1)也无效,只有释放对象才真正解锁 - 若用
fopen()读过该文件,务必配对调用fclose() - 避免在循环中反复创建未释放的文件操作对象
路径看着对,其实根本没落到文件系统上
相对路径、DS 拼接、__DIR__ 使用不当,会导致 unlink() 找不到目标——但 file_exists() 却返回 true,这是最迷惑人的陷阱。
立即学习“PHP免费学习笔记(深入)”;
- 打印完整路径并用
realpath($filepath)验证是否解析成功 - ThinkPHP 中
ROOT_PATH . 'public' . DS . 'upload' . DS . $name很容易漏掉开头斜杠,变成非法路径 - 统一用正斜杠
/,PHP 在 Windows 下完全兼容,比DS更少出错 - 用户输入参与路径拼接时,必须过
basename(),禁止../类跳转
错误处理不能只靠 @ 符号压 warning
@unlink() 只是屏蔽错误输出,掩盖问题本质。真要健壮,得靠主动反馈和分层判断。
- 先
if (!file_exists($filepath)) { /* 提示不存在 */ },别让 unlink() 承担存在性校验 - 删前加
if (!is_dir($filepath)) { unlink($filepath); },防止误删目录引发致命错误 - 删后检查返回值:
if (unlink($filepath) === false) { error_log('unlink failed: ' . error_get_last()['message']); } - 并发场景下,
file_exists()和unlink()之间存在竞态,需配合flock()或原子重命名规避
真正卡住的点,往往藏在“删之前那个没关掉的对象”或“删不掉的那个父目录权限”里。盯着错误信息本身没用,得顺着 PHP 进程的视角,一层层查它到底看见了什么、能碰着什么、被谁锁住了。



















