【PHP】ファイル存在チェックを極める!file_existsとis_fileの使い分け

【PHP】ファイル存在チェックを極める!file_existsとis_fileの使い分け PHP

どうも、Webエンジニアのkazuです。

SES時代から数多くの現場を見てきましたが、ファイル操作のような基礎的な処理ほど、エンジニアの「詰め」の甘さが出やすいと感じています。

「ファイルがあるはずなのに見つからない」
「ディレクトリをファイルとして処理してエラーになった」

こうしたミスは、言語の仕様を深く理解し、常に最善の効率を追求していれば防げるはずです。

エンジニアの世界は厳しいものです。
動けばいいという甘えを捨て、常にストイックに技術を追求すべきだと僕は思います。

今回は、PHPにおけるファイル存在チェックの決定版として、現場で使うべき関数とその背後にある論理について解説します。

【執筆者の簡易プロフィール】
kazu 執筆者:kazu
  • 38歳男性、既婚
  • Webエンジニア
  • 新卒でSESに入社し、Web系企業に常駐、その後Web系スタートアップに転職
  • 主な使用言語はRuby、PHP、JavaScript、HTML/CSS
  • モットーは「人に厳しく、自分にはもっと厳しく」

「file_exists」は万能ではない

PHPで存在チェックと聞いて、真っ先に file_exists を使う人は多いでしょう。
確かにこの関数は、ファイルでもディレクトリでも、そこに「何か」があれば真(true)を返します。

しかし、実務において「何かがあること」だけを確認すれば良いケースは稀です。

$path = 'data/user_log.txt';

// file_existsはファイルでもディレクトリでもtrueを返す
if (file_exists($path)) {
echo "対象が存在します。";
}

このコードの挙動を解説します。

file_exists は、指定したパスがファイルであれば当然 true ですが、もし data/user_log.txt という名前の「ディレクトリ」が存在していても true を返してしまいます。

これをそのままファイルとして読み込もうとすれば、当然エラーになります。

「対象がファイルであること」を期待しているなら、この関数に頼るべきではありません。
エンジニアなら、もっと厳密な関数を選択すべきです。

ファイルかディレクトリか。厳格な関数選択がバグを防ぐ

「ファイルの中身を読み込みたい」のであれば is_file を、「ディレクトリの中を走査したい」のであれば is_dir を使う。
これが効率的でミスのないプログラミングの基本です。

$file_path = 'config/app.json';
$dir_path  = 'storage/uploads';

// ファイルであることを厳密にチェック
if (is_file($file_path)) {
$content = file_get_contents($file_path);
echo "ファイルを読み込みました。";
}

// ディレクトリであることを厳密にチェック
if (is_dir($dir_path)) {
$files = scandir($dir_path);
echo "ディレクトリを走査しました。";
}

コードの内容を説明します。

is_file は、パスが存在し、かつそれが「通常のファイル」である場合のみ true を返します。
シンボリックリンクであっても、リンク先がファイルなら true です。
同様に、is_dir はディレクトリであることだけを確認します。

このように関数の役割を限定することで、後続の処理(読み込みやスキャン)の安全性が担保されます。
曖昧な file_exists で済ませるのではなく、用途に合わせて使い分ける。
これがプロの流儀です。

【重要】ファイルステータスキャッシュの罠を回避する

ここが中級者以上でも見落としがちな、PHPの仕様です。

PHPはパフォーマンスを稼ぐために、一度 file_existsis_file で確認した結果をメモリに保存(キャッシュ)します。
これを「ファイルステータスキャッシュ」と呼びます。

同じリクエスト内で「ファイルを削除した直後に存在確認をする」といった場合、キャッシュのせいで結果が狂うことがあります。

$file = 'test.txt';

if (is_file($file)) {
unlink($file); // ファイルを削除
}

// キャッシュをクリアしないと、削除したはずなのにtrueが返る可能性がある
clearstatcache();

if (!is_file($file)) {
echo "ファイルは正しく削除されました。";
}

unlink でファイルを消したとしても、直前の is_file の結果がキャッシュされていると、その後の存在チェックが古い情報を返してしまいます。
そこで clearstatcache() を呼び出します。

この関数は、PHPが保持しているファイル情報のキャッシュを物理的にクリアします。

ループ処理の中でファイルを頻繁に作成・削除・確認するようなプログラムでは、これを書かないエンジニアは信頼に値しないと僕は思います。

常に一貫性を保つ。
それがシステム構築の鉄則です。

パーミッションの考慮。「存在すること」と「アクセスできること」は別

最後に、ファイルが存在したとしても、PHPの実行ユーザーに読み書きの権限(パーミッション)がなければ、実質的には存在しないのと同じです。

$secure_file = '/etc/hosts';

if (is_file($secure_file) && is_readable($secure_file)) {
echo "ファイルは存在し、読み込みも可能です。";
} else {
echo "アクセスできないか、ファイルが存在しません。";
}

コードの意味を説明します。

is_readable 関数を使うことで、「読み込み権限があるか」を同時にチェックしています。
スタートアップのようなスピード感が求められる環境でも、こうした権限チェックを怠ると、本番環境のデプロイ後にだけエラーが出るという最悪の事態を招きます。

常にエラーの可能性を先回りして潰す。
自分にも他人にも厳しく、完璧なコードを目指すべきです。

まとめ。基礎への執着がエンジニアの質を決める

PHPでのファイル存在チェックについて、僕の流儀をまとめました。

「何かがある」の確認は file_exists でいいが、通常は is_file を使うべき。
ファイル情報のキャッシュに注意し、必要に応じて clearstatcache() を実行する。
存在確認だけでなく、is_readable 等で権限までセットで確認する。

一つひとつの関数の挙動を疎かにせず、効率的で整合性の取れたコードを書き続けてください。
エンジニアとしての評価は、こうした地味な部分の正確さで決まります。

努力を怠らないように。

以上、kazuでした。

コメント

タイトルとURLをコピーしました