flock() 报“expects parameter 1”警告是因为传入了无效资源(如fopen()失败返回的false),必须先用is_resource($fp)验证资源有效性再调用flock()。

为什么 flock() 会报 “expects parameter 1” 警告
这个警告不是锁本身失败,而是你传给了 flock() 一个无效的资源——最常见的是 fopen() 失败后返回 false,你却直接拿它去加锁。PHP 不会自动把 false 当成文件指针,flock() 看到非 resource 类型就立刻报错。
-
fopen()失败原因很多:目录不存在、权限不足(如 web 用户无法写入/var/log/)、磁盘满、SELinux 限制 - 错误示例:
$fp = fopen('/tmp/log.txt', 'w'); flock($fp, LOCK_EX);—— 如果fopen()返回false,flock()就崩了 - 注意:
flock()只作用于文件指针(resource),不能对路径字符串或已关闭的句柄调用
正确检查文件锁前的资源有效性
必须在调用 flock() 前确认 $fp 是有效 resource。不要依赖“文件存在就一定能打开”——写权限、umask、父目录可执行位都可能拦住你。
- 用
is_resource($fp)判断,比$fp !== false更严谨(某些旧 PHP 版本中失败 fopen 可能返回 null) - 加上错误上下文输出,例如:
if (!is_resource($fp)) { error_log("fopen failed for /tmp/log.txt: " . error_get_last()['message']); } - 临时文件建议用
sys_get_temp_dir()而非硬编码路径,避免环境差异
并发写入场景下 flock() 的典型误用
flock() 是 advisory lock(建议性锁),不阻塞其他进程的 open/write 操作——它只对同样调用 flock() 的代码起作用。很多人以为加了锁就万事大吉,结果发现日志还是乱序或丢失。
- 必须在
fwrite()前加锁,写完立即flock($fp, LOCK_UN),不能等到fclose()自动释放(因为 fclose 不保证原子性) - 写入量大时避免长时持锁:先拼好完整内容再一次性
fwrite(),别边读边写边锁 - 注意
flock()在 NFS 上不可靠,某些容器环境(如 rootless Podman)也默认禁用,需改用fcntl()或外部锁服务
替代方案:当 flock() 不可用或不适用时
如果你在无文件系统权限、NFS 共享卷、或 serverless 环境(如 AWS Lambda)中运行,flock() 会静默失效或直接报错。这时候得换思路。
立即学习“PHP免费学习笔记(深入)”;
- 用原子重命名模拟锁:
file_put_contents($tmp_file, $data); rename($tmp_file, $real_file);——rename()在同一文件系统上是原子的 - 简单计数器类场景,改用 Redis 的
INCR+GETSET,天然支持分布式并发控制 - 日志聚合需求,推荐走
syslog()或标准输出,由 systemd/journald 统一落盘,避开 PHP 层锁逻辑
真正麻烦的不是锁函数怎么写,而是你是否清楚自己锁的是什么资源、谁会和你争、以及失败后有没有 fallback 路径。多数线上问题出在没验证 fopen() 结果,而不是 flock() 本身。



















