最稳妥做法是先用is_file()判断文件存在且为普通文件,再用file_get_contents()读取并严格检查返回值是否为false;避免直接使用file_exists()(因对目录也返回true)和单独使用is_readable()(不保证文件存在),同时须处理竞态条件与大文件内存限制。

用 file_exists() 判断再读取最稳妥
直接用 file_get_contents() 读取不存在的文件会触发警告(Warning: file_get_contents(...): Failed to open stream),影响错误处理和日志清晰度。先判断再读取是标准做法,file_exists() 是专为这个场景设计的函数,它只检查路径是否存在且可访问,不关心内容或权限细节。
注意:file_exists() 对目录也返回 true,如果明确要读取文件,需额外排除目录:
- 用
is_file()替代file_exists()更精准(推荐) - 或组合使用:
file_exists($path) && !is_dir($path) - 避免用
is_readable()单独判断——它不保证文件存在,只测权限,且在某些 NFS 或 SELinux 环境下可能误判
file_get_contents() 失败时不要忽略返回值
file_get_contents() 在失败时返回 false,不是空字符串。如果只做 if ($content) 判断,当文件内容恰好是 "0" 或空字符串时会被误判为失败。
正确写法必须严格比较返回值类型:
立即学习“PHP免费学习笔记(深入)”;
$content = file_get_contents($path);
if ($content === false) {
// 处理读取失败:文件不存在、无权限、磁盘满等
error_log("Failed to read $path");
return null;
}
// 此时 $content 是 string 类型,可安全使用
- 永远用
=== false,不用== false或!$content - 即使已用
is_file()判断过,仍建议对file_get_contents()做失败处理——因为判断和读取之间存在竞态窗口(文件可能被其他进程删除)
大文件别用 file_get_contents(),改用 fopen() 流式读取
如果文件可能超过几 MB,file_get_contents() 会一次性把全部内容载入内存,容易触发 memory_limit 错误,尤其在共享主机或低配环境中。
替代方案是手动打开流并分块读取:
$handle = fopen($path, 'rb');
if ($handle === false) {
return null; // 文件不可读或不存在
}
$content = '';
while (!feof($handle)) {
$chunk = fread($handle, 8192); // 每次读 8KB
if ($chunk === false) break;
$content .= $chunk;
}
fclose($handle);
- 用
'rb'模式确保二进制安全,避免 Windows 下换行符干扰 -
fread()返回false表示读取错误(如磁盘 I/O 故障),需检查 - 如果只需处理某一行或特定位置,更推荐
file()(按行读成数组)或stream_get_line()
相对路径容易出错,优先用绝对路径
PHP 的当前工作目录(getcwd())不等于脚本所在目录(__DIR__),用相对路径如 "./data/config.json" 时,一旦在 CLI 下被其他目录调用,或通过 include 引入,就可能找不到文件。
安全做法是基于 __DIR__ 或 dirname(__FILE__) 构造绝对路径:
$path = __DIR__ . '/data/config.json';
if (is_file($path)) {
$content = file_get_contents($path);
}
- 避免拼接时漏掉
/:用__DIR__ . '/file.txt',不要写__DIR__ . 'file.txt' - Windows 下反斜杠不影响
is_file()和file_get_contents(),但统一用正斜杠更兼容 - 如果路径来自用户输入,必须用
realpath()规范化并校验是否仍在预期根目录内,防止路径遍历攻击



















