php无内置目录操作日志功能,需手动在mkdir/rmdir/rename等调用前后用file_put_contents($logpath, $line, file_append | lock_ex)写入归一化路径、iso时间戳、操作类型及用户标识的纯文本日志,并配合logrotate轮转。

PHP 本身不自动记录文件夹操作历史,必须手动实现日志写入逻辑;没有内置函数如 log_Directory_access() 这类功能。
为什么 fopen() + file_put_contents() 是最常用方案
PHP 没有类似 linux inotify 的内核级文件系统监听能力(除非用 pcntl + 扩展),所以“记录操作”本质是:在你调用 mkdir()、rmdir()、rename() 等函数前/后,主动写一条日志。
- 用
file_put_contents($logPath, $line, FILE_APPEND | LOCK_EX)最简可靠,避免并发写乱序 - 不要用
fopen()+fwrite()手动管理句柄,除非需长连接或特殊编码处理 - 日志路径建议用绝对路径,比如
/var/log/myapp/directory.log,避免相对路径在 CLI/Web 下行为不一致 - 注意 Web 服务器用户(如
www-data)是否有该日志目录的写权限,否则会静默失败
记录哪些操作才算“有效历史”
不是所有文件函数都需要记——重点覆盖真正改变目录结构的行为:
- 创建:
mkdir()、scandir()不用记,但mkdir($path, 0755, true)要记(尤其是递归创建) - 删除:
rmdir()和unlink()都要记,但unlink()属于文件操作,可单独分类 - 移动/重命名:
rename()必须记,它可能跨分区、改目录名,影响最大 - 权限变更:
chmod()、chown()视安全要求决定是否记录 - 避免记录:
is_dir()、file_exists()这类只读判断,否则日志爆炸
日志格式怎么设才方便排查
纯文本即可,关键字段缺一不可:
立即学习“PHP免费学习笔记(深入)”;
- 时间戳用
date('c')(ISO 8601),比microtime(true)更易读且时区可控 - 操作类型明确写
mkdir/rmdir/rename,别缩写 - 路径统一用
realpath($path)归一化,消除../、符号链接干扰 - 加操作者标识:CLI 下用
get_current_user(),Web 下用$_SERVER['REMOTE_ADDR']或登录用户 ID - 示例行:
[2024-05-22T14:30:22+08:00] mkdir /data/uploads/2024/05 [user:admin]
权限和轮转问题最容易被忽略
日志文件会长期存在,不处理会撑爆磁盘:
- 确保日志目录属主是 Web/PHP 进程用户,且权限为
0755,日志文件为0644 - 不要依赖 PHP 自动轮转——用系统
logrotate配置更稳,比如每周切分 + 压缩 + 保留 4 周 - 如果必须 PHP 内部轮转,检查
filesize($logPath) > 10 * 1024 * 1024后 rename 并 touch 新文件,但要注意并发 rename 冲突 - 敏感路径(如含用户 ID)别直接进日志,可哈希脱敏:
md5(basename($path))
真正难的不是写几行日志,而是确保每次目录操作都经过同一套封装函数——比如把 my_mkdir() 替换所有裸 mkdir() 调用。漏掉一个,历史就断一截。