fopen() + fwrite() 是最可控可靠的文件写入方式,需检查返回值、权限、编码、关闭资源;file_put_contents() 简便但隐藏细节,大文件易内存溢出,错误诊断依赖 error_get_last()。

用 fopen() + fwrite() 写入文件最可控
直接覆盖或追加内容时,fopen() 是底层最可靠的入口。它能显式控制打开模式、错误处理和资源释放,比 file_put_contents() 更容易定位问题。
常见错误现象:fopen() 返回 false 但没检查,后续 fwrite() 直接报 Warning: fwrite() expects parameter 1 to be resource, bool given;或者权限不足却没给明确提示。
- 写入前务必用
is_writable()检查目录可写性,尤其在 Linux 上,Web 用户(如www-data)可能无权写入父目录 - 打开模式选对:
'w'覆盖写,'a'追加写,'x'独占创建(失败不覆盖,适合防止误写) - 写完必须调用
fclose(),否则内容可能缓存未落盘,多进程下还易引发竞态 - 中文内容要确保传入的是 UTF-8 字节流,PHP 不自动转编码;若源字符串是 GBK,得先
mb_convert_encoding($str, 'UTF-8', 'GBK')
if ($fp = fopen('/tmp/log.txt', 'a')) {
fwrite($fp, date('Y-m-d H:i:s') . " - OK\n");
fclose($fp);
} else {
error_log("Cannot open /tmp/log.txt for writing");
}
file_put_contents() 简单写入但默认不报错
适合单次写入、无需精细控制的场景,比如保存配置快照、写临时 token。但它把打开、写、关闭全包了,出问题时堆栈浅,难定位是权限、磁盘满还是编码崩了。
使用场景:脚本内快速 dump 数组为 JSON 文件,或记录单行调试日志。
立即学习“PHP免费学习笔记(深入)”;
- 默认不创建父目录,路径中任意一级不存在就会失败,得提前用
mkdir(..., 0755, true) - 第二个参数如果是数组,会自动
print_r()格式化,不是 JSON;要存结构化数据,得手动json_encode() - 加
FILE_APPEND标志才追加,不加就是覆盖;加LOCK_EX可避免并发写乱序,但会阻塞,高并发日志慎用 - 返回实际写入字节数,等于 0 不代表成功(空字符串也返回 0),得用
=== false判断失败
$ret = file_put_contents('/var/log/app.json', json_encode($data), FILE_APPEND | LOCK_EX);
if ($ret === false) {
// 注意:这里不能只判断 !$ret
trigger_error('Failed to write app.json');
}写入失败时,error_get_last() 比 try/catch 更有用
PHP 文件函数基本不抛异常,而是触发 warning 或 notice,try/catch 捕不到。真要诊断,得靠 error_get_last() 看最后一条错误,或者开启 track_errors(不推荐)。
容易踩的坑:本地开发时 display_errors=On 看到报错,上线后关了就静默失败;或者错误被自定义错误处理器吞掉,没记录。
- 写操作后立刻调
error_get_last(),检查['type']是否为E_WARNING或E_ERROR,['message']里常含“Permission denied”或“No such file or directory” - 不要依赖
@抑制符来“静默处理”,它同时屏蔽了error_get_last()的捕获能力 - Linux 下注意 SELinux 或 AppArmor 限制,
ls -Z查看文件上下文,有时 chmod 正确仍写不了
大文件写入别用 file_put_contents() 一次性加载
往几百 MB 日志里追加一行,如果用 file_put_contents($file, $line, FILE_APPEND),PHP 会把整个文件读进内存再写回——内存爆掉、超时、卡死全可能发生。
性能影响明显:100MB 文件追加 1KB,内存占用瞬时飙升 100MB+,响应时间从毫秒变秒级。
- 大文件一律走
fopen()+fwrite()流式写,不加载原内容 - 写敏感数据(如密钥)后,可用
ftruncate()清空并重写,再unlink()原文件,避免残留 - 频繁小写建议缓冲:累积 1KB 再
fwrite()一次,减少系统调用次数
写文件看着简单,但权限、编码、并发、大文件这四块,漏查任何一项都可能让程序在某个环境突然哑火。特别是生产服务器上,错误日志关了、SELinux 开着、磁盘只剩 10MB —— 这些细节比语法更决定成败。











