【2026年9月最新】PHPのfopenでファイルを読み書きする方法完全ガイド|モード指定子とパストラバーサル対策
「PHPのfopenでファイルを読み書きしたい」「モード指定子のr, w, aの違いがよく分からない」——この記事にたどり着いた方は、PHPでのファイル操作の基本でつまずいているはずです。
結論から言うと、ファイルを開くにはfopen($filename, $mode)、書き込みはfwrite()、読み込みはfread()やfgets()、そして最後に必ずfclose()で閉じる、という流れが基本形です。ただし、モード指定子の使い分けを誤ると意図せずファイルの中身が消えたり、開き方によってはセキュリティ上のリスクを抱えたりすることがあります。
この記事では、fopenの基本構文からモード指定子の一覧、読み書きのコード例、file_get_contents等の簡易関数との使い分けまで整理します。あわせて、弊社(株式会社GENAI)がClaude Codeを全社で運用している立場から、「ファイル操作のようなコード周辺リスクを、非エンジニアの経営者がどう管理するか」という視点も加えて解説します。
この記事を最後まで読むと、次の6つが明確になります。
01 BASICS fopenとは何か|基本の流れを理解する 非エンジニアにも分かる例え話で理解する
fopenは、PHPでファイルを操作するための「最初の入り口」となる関数です。ファイルというキャビネットの引き出しを開ける動作だとイメージすると分かりやすくなります。引き出しを開けたら、中身を読む・書く・追記するといった操作を行い、最後に必ず引き出しを閉じます。
📚 用語解説
fopen:PHPでファイルを開くための関数。指定したファイルへの「操作用の窓口(リソース)」を返し、以降のfwrite・fread等の関数はこの窓口を経由してファイルにアクセスします。引数には、開きたいファイルのパスと、どんな目的で開くかを示す「モード指定子」を渡します。
基本構文は以下の通りです。
$handle = fopen($filename, $mode);
// ファイル操作
fclose($handle);
📚 用語解説
リソース(resource):PHPが外部の資源(ファイル・データベース接続など)を操作するために内部で管理する特殊なデータ型。fopenが返す値はこのリソース型で、直接中身を確認することはできず、fwriteやfreadなど専用の関数に渡して操作します。
この一連の流れを図解すると、以下のようになります。
ファイルを開く
モードを指定
書き込み
または読み込み
ファイルを
閉じる
02 MODE OPTIONS モード指定子の一覧と使い分け r・w・a・x・cの違いを正確に理解する
fopenの第2引数(モード)は、「どんな目的でファイルを開くか」を指定するものです。ここを誤ると、意図しない動作(既存データの消失など)につながるため、正確な理解が欠かせません。
| モード | 意味 | ファイルが存在しない時 | 既存の内容 |
|---|---|---|---|
| r | 読み込み専用 | エラー(falseを返す) | 保持される |
| r+ | 読み書き両方 | エラー(falseを返す) | 保持される |
| w | 書き込み専用 | 新規作成される | 即座に空になる(消える) |
| w+ | 読み書き両方 | 新規作成される | 即座に空になる(消える) |
| a | 追記専用 | 新規作成される | 保持される(末尾に追記) |
| a+ | 読み書き+追記 | 新規作成される | 保持される(末尾に追記) |
| x | 新規作成専用 | 新規作成される | 既存ファイルがあればエラー |
| c | 書き込み(上書き) | 新規作成される | 保持される(上書き位置は先頭) |
wやw+でファイルを開いた瞬間、ファイルの中身は書き込みを行う前から空になります。「既存の内容に追記するつもりだったのに、うっかりwモードで開いて中身を消してしまった」という事故は、初心者に限らず頻繁に起こるミスです。追記したい場合は必ずaを使ってください。
📚 用語解説
モード指定子:fopenでファイルを開く際に、読み込み・書き込み・追記のどれを行うか、ファイルが存在しない場合どうするかを指定する文字。r(読込)・w(書込・上書き)・a(追記)が代表的で、末尾に+を付けると読み書き両対応になります。
2-1. バイナリモード(b)の役割
画像ファイルなど、テキストではないデータを扱う場合は、モード指定子の末尾にbを付けます(例: rb、wb)。
$handle = fopen('image.png', 'rb');
Linux・macOSではbを付けても付けなくても動作に違いはありませんが、Windows環境では改行コードの自動変換が行われる場合があるため、バイナリデータを扱う際は必ずbを付けることが推奨されています。環境を問わず動くコードにするため、習慣的に付けておくのが安全です。
2-2. xモードとcモードの実務での使い分け
あまり使う機会は多くありませんが、xとcは、特定の場面で役立つモードです。
xモードは、「まだ存在しないファイルだけを新規作成したい」場合に使います。既に同名のファイルが存在する場合はエラーになるため、意図せず既存ファイルを上書きしてしまう事故を防げます。
$handle = fopen('new_report.txt', 'x');
if ($handle === false) {
echo "既にファイルが存在します";
}
一方cモードは、ファイルが存在しなければ新規作成し、存在すれば中身を消さずに先頭から上書きできる位置で開きます。ファイルサイズを制御しながら特定の位置だけを書き換えたい、といった特殊な用途で使われます。日常的なログ書き込みではa、単純な新規作成の重複防止にはx、と覚えておけば十分です。
2-3. 文字コードを指定して書き込む
レガシーなシステムとの連携で、UTF-8以外の文字コード(Shift-JIS等)でファイルを書き出す必要がある場合は、mb_convert_encoding()で変換してから書き込みます。
$text = "処理が完了しました";
$converted = mb_convert_encoding($text, 'SJIS-win', 'UTF-8');
$handle = fopen('report_sjis.txt', 'w');
fwrite($handle, $converted);
fclose($handle);
ファイルに書き込む前の文字コードと、書き込み先で期待されている文字コードが一致していないと、いわゆる「文字化け」が発生します。連携先のシステムがどの文字コードを前提にしているか、事前に確認しておくことがトラブル回避の近道です。
03 WRITING FILES ファイルに書き込む方法 fwriteの基本から、追記・ロックまで
ファイルに書き込むには、fwrite()(エイリアス: fputs())を使います。
$handle = fopen('log.txt', 'w');
fwrite($handle, "処理を開始しました\n");
fclose($handle);
3-1. 追記で書き込む
既存の内容を残したまま末尾に追記したい場合は、モードをaにします。
$handle = fopen('log.txt', 'a');
fwrite($handle, "追加の処理ログです\n");
fclose($handle);
3-2. 書き込み中に他のプロセスと衝突しないようにする
複数のプログラムが同時に同じファイルへ書き込む可能性がある場合、flock()でファイルロックをかけておくと、書き込みの競合による内容の破損を防げます。
$handle = fopen('log.txt', 'a');
if (flock($handle, LOCK_EX)) {
fwrite($handle, "排他制御ありの書き込み\n");
flock($handle, LOCK_UN);
}
fclose($handle);
📚 用語解説
ファイルロック(flock):複数のプログラムが同時に同じファイルを書き換えようとした際に、内容が壊れないよう排他的にアクセス権を制御する仕組み。アクセスログのように複数のリクエストから同時に書き込まれる可能性があるファイルでは、ロックをかけることが推奨されます。
04 READING FILES ファイルを読み込む方法 freadと1行ずつ読むfgetsの使い分け
ファイル全体を一気に読み込みたい場合はfread()、1行ずつ読み込みたい場合はfgets()を使います。
4-1. ファイル全体を読み込む
$handle = fopen('data.txt', 'r');
$content = fread($handle, filesize('data.txt'));
fclose($handle);
echo $content;
freadは「何バイト読み込むか」を第2引数で明示する必要があります。ファイル全体を読みたい場合はfilesize()でファイルサイズを取得して渡すのが定番のパターンです。
4-2. 1行ずつ読み込む
大きなファイルを扱う場合や、ログファイルのように行単位で処理したい場合は、feof()(ファイル終端の判定)と組み合わせて1行ずつ読み込みます。
$handle = fopen('data.txt', 'r');
while (!feof($handle)) {
$line = fgets($handle);
echo $line;
}
fclose($handle);
📚 用語解説
feof(end of file):ファイルの読み込み位置が末尾(終端)に到達したかどうかを判定する関数。ループ処理と組み合わせて「ファイルの最後まで読み終わるまで繰り返す」という処理を書く際に使われます。
fopenで開いたファイルは、処理が終わったら必ずfclose()で閉じてください。閉じ忘れると、同時に開けるファイル数の上限に達してエラーになったり、書き込み内容がディスクに反映されないまま処理が終わってしまったりする可能性があります。
4-3. CSVファイルを読み込む
業務でよく扱うCSVファイルについては、専用のfgetcsv()関数が用意されています。カンマ区切りの解析を自前で書く必要がなく、1行ずつ配列として取得できます。
$handle = fopen('members.csv', 'r');
while (($row = fgetcsv($handle)) !== false) {
echo $row[0] . ' / ' . $row[1] . "\n";
}
fclose($handle);
ファイルの終端に達するとfgetcsv()はfalseを返します。空行と区別するため、比較には!== falseのように型まで含めた厳密比較を使うのが安全です。単純な!=比較だと、空配列などを誤って終端と判定してしまう可能性があります。
05 QUICK REFERENCE fopen関連の関数早見表と使い分け file_get_contents等の簡易関数との比較
fopen・fwrite・freadを都度組み合わせる方法のほかに、PHPにはファイル全体を一括で読み書きできる簡易関数も用意されています。
| 関数 | 役割 | fopen系との違い |
|---|---|---|
| file_get_contents() | ファイル全体を文字列として一括取得 | fopen+fread+fcloseを1行で完結 |
| file_put_contents() | ファイル全体を一括で書き込み | fopen+fwrite+fcloseを1行で完結。FILE_APPENDで追記も可能 |
| file() | ファイルを1行ごとの配列として取得 | fopen+fgetsのループを1行で完結 |
| is_readable() / is_writable() | 読み込み・書き込み権限の事前確認 | 実際に開く前にチェックできる |
「ファイル全体を一括で読み書きするだけ」であれば、file_get_contents / file_put_contentsの方がコードがシンプルで済みます。fopen系を使うべきなのは、大きなファイルを少しずつ処理したい、複数回に分けて書き込みたい、といった細かい制御が必要な場面に限られます。
// file_put_contentsなら1行で書き込める
file_put_contents('log.txt', "追記します\n", FILE_APPEND);
// file_get_contentsなら1行で読み込める
$content = file_get_contents('data.txt');
単純な読み書きであれば、まずfile_get_contents・file_put_contentsを検討してください。fopenを直接使うのは、ファイルを少しずつ処理する必要がある場合や、ロック制御など細かい挙動を制御したい場合に限定するのが、コードをシンプルに保つコツです。
5-1. オブジェクト指向で扱うSplFileObject
PHPには、fopen系の関数群とは別に、ファイルをオブジェクトとして扱うSplFileObjectクラスも標準で用意されています。foreachで1行ずつ処理できるなど、コードの見通しが良くなる場面があります。
$file = new SplFileObject('data.txt');
foreach ($file as $line) {
echo $line;
}
関数ベースのfopen系と、オブジェクト指向のSplFileObject、どちらも最終的にできることは同じです。チームの既存コードのスタイルに合わせて選ぶのが実務的な判断になります。
06 BUSINESS RISK なぜファイル操作の実装ミスは経営リスクなのか 技術的な話を、経営者が判断できる言葉に翻訳する
ここまで解説してきたファイル操作は、「ユーザーが指定したファイル名をそのままfopenに渡す」実装になっていると、深刻なセキュリティリスクを抱えることになります。ここでは非エンジニアの視点で、なぜこれが問題になるのかを整理します。
ユーザー入力から
直接組み立てる
検証なしで
fopenに渡す
入力を送られる
想定外の
パス指定
アクセスされる
情報漏えい・
改ざんのリスク
📚 用語解説
パストラバーサル(ディレクトリトラバーサル):ファイル名にユーザーが自由に入力できる箇所がある場合、「../」(親ディレクトリへの移動)を含む文字列を送り込むことで、本来アクセスさせるはずのないファイルを読み書きさせる攻撃手法。ファイルアップロード機能やダウンロード機能で特に注意が必要な脆弱性です。
また、PHPの設定(allow_url_fopen)が有効な環境では、fopen()にURLをそのまま渡すと外部サーバーへの通信が発生します。この挙動を悪用されると、攻撃者が指定した外部サーバーに対して、自社のサーバー経由でアクセスさせられてしまう(SSRF)リスクにもつながります。
特に、ユーザーが入力した「画像URL」や「Webhook送信先URL」のような値をそのままfopen()やfile_get_contents()に渡す実装は要注意です。一見便利な機能ですが、社内ネットワークにしかアクセスできないはずの管理画面URLなどを指定され、想定外の情報が読み取られてしまうケースが実際に報告されています。
📚 用語解説
SSRF(サーバーサイドリクエストフォージェリ):本来外部からは直接アクセスできないはずの社内システムやサービスに対し、脆弱なサーバーを踏み台にして不正なリクエストを送らせる攻撃手法。ファイル名やURLをユーザー入力から組み立てる処理に、この種のリスクが潜んでいることがあります。
こうしたリスクは、開発時点でのテストケースに「正常な入力」しか含まれていないと発見できません。「意地悪なテスト(異常系のテスト)」をどれだけ組み込めているかが、実務での品質を大きく左右します。納期に追われるほど、この異常系のテストは後回しにされがちな工程でもあります。
07 GENAI CASE STUDY Claude Codeでファイル操作のリスクを減らすという選択肢 弊社GENAIの実運用データで見る、コードレビューの実践
弊社(株式会社GENAI)ではClaude Max 20xプラン(月額$200・約30,000円)を契約し、経営・営業・広告・開発・経理・秘書業務まで社内のあらゆる業務でClaude Codeを活用しています。開発領域では、こうしたファイル操作のリスクチェックもその一つです。
Claude Codeは、「このコードにパストラバーサルのリスクがないか確認して」と日本語で頼むだけで、該当箇所を指摘し、修正案(basename()での無害化やホワイトリスト方式への変更等)まで提示してくれます。モード指定子の選び間違いのような細かい見落としも、機械的にチェックできる領域です。
| チェック内容 | 人力レビューのみの場合 | Claude Codeを併用する場合 |
|---|---|---|
| パストラバーサルの検出 | コード全体を目視で探す必要 | 該当パターンを機械的に洗い出す |
| wモードでの意図しない上書き | 実際に事故が起きて発覚しがち | モード指定の意図を確認・指摘 |
| fclose漏れの検出 | 見た目では気づきにくい | リソースの開閉ペアを機械的に照合 |
| レビューにかかる時間 | 担当者の稼働に依存 | 即座に初期チェックが完了 |
弊社の開発領域(WordPress・LP制作・各種スクリプトの書き捨て等)では、ファイルアップロード・ダウンロード機能を含むコードのレビューをClaude Codeに任せることで、都度数時間かかっていた確認作業がその場で完結する肌感で削減できています。
本章の削減時間は弊社の肌感ベースの参考値であり、業種・業態・担当者のスキルによって変動します。「完全自動化」ではなく、最終確認は引き続き人の判断が必要です。
7-1. 「コードが読めない経営者」でも判断できる体制づくり
弊社がClaude Codeを導入して感じているのは、「コードを読めるかどうか」と「コードのリスクを判断できるかどうか」は別物になったということです。専門知識がなくても、「このファイル操作は安全か確認して」とClaude Codeに投げれば、要点を日本語で説明してもらえます。
これにより、経営者やプロジェクト管理者がコードの中身を直接読まなくても、リスクの有無を把握したうえで意思決定できる体制に近づきます。最終的な技術判断はエンジニアが担うべきですが、「そもそも確認する仕組みがあるかどうか」という一段階目のガバナンスは、非エンジニアの経営者でも十分に設計できる領域です。
7-2. 外注先とのやり取りにも活用できる
外部の開発会社やフリーランスにファイルアップロード機能・ダウンロード機能の開発を依頼した場合も、「このアップロード機能にパストラバーサルのリスクがないか確認して」とClaude Codeに依頼することで、専門知識がなくても一次チェックを済ませられます。「セキュリティ対策済みです」という報告を鵜呑みにせず、自社でも簡易的な検証を行える体制は、発注者側のリスク管理として有効です。
7-3. ライブラリのバージョンアップ対応も相談できる
PHP自体もバージョンアップによってファイル操作関連の関数の挙動や非推奨化が発生することがあります。「このコードは最新のPHPバージョンでも問題なく動くか確認して」とClaude Codeに尋ねるだけで、変更点を踏まえた回答が得られます。社内に専任のインフラ担当がいない中小企業ほど、こうした地味だが放置すると危険な作業が後回しになりがちです。
08 CONCLUSION まとめ ── 「開けた」と「安全に使えた」は別の基準 基本を正しく押さえたうえで、組織としてのリスク管理を両立させる
この記事では、PHPのfopenでファイルを読み書きする基本から、モード指定子の使い分け、file_get_contents等との比較、そしてそれが経営リスクにつながる構造までを整理しました。最後にポイントを振り返ります。
fopen($filename, $mode)→操作→fclose()wモードは開いた瞬間に既存の中身が消える。追記したいならaを使うbを付けると環境差異を回避できるfile_get_contents / file_put_contentsの方がシンプルPHPのfopenは、書き方自体はシンプルですが、「動く」と「安全に動く」の間にギャップがある典型例です。このギャップを個人の注意力だけで埋め続けるのではなく、機械的なチェックを組み合わせて組織としてリスクを減らす、という発想が重要になります。
ファイル操作に限らず、外部からの入力をそのまま処理に使う実装は、どのプログラミング言語・フレームワークでも起こり得るリスクです。「入力値は信用しない」という基本方針を徹底したうえで、機械的なチェックを日常の開発フローに組み込んでおくことが、長期的に事故を防ぐ一番の近道になります。
次にfopenを使ったコードを書く、あるいはレビューする機会があれば、この記事で紹介したモード指定子の対応表と、パストラバーサル・SSRFのチェック項目を見返してみてください。細部の積み重ねが、動くコードと安全なコードの差になります。
ファイル操作は、業務システムのほとんどに登場する基本機能だからこそ、実装の質が事故の頻度を大きく左右します。基本を正しく押さえ、確認の仕組みを整えることが、遠回りに見えて最も確実な近道です。
この記事で紹介したモード指定子の一覧、file_get_contentsとの使い分け、パストラバーサル・SSRFのリスクは、いずれもPHPの入門書で必ず触れられるにもかかわらず、実務のコードでも繰り返し見つかる典型的な論点です。基礎を押さえたうえで、確認体制まで含めて整備しておくことが、事故を未然に防ぐ一番の近道になります。
コードのリスクチェックを、Claude Codeで仕組み化しませんか
パストラバーサルのような細かいリスクを、個人の注意力だけに頼らない体制づくりを支援します。
弊社の実運用ノウハウをベースに、個別に導入設計のご相談を承ります。
ここから先の進め方は、大きく2つあります。
自社で回せるようになりたい方は、AI鬼管理でClaude Code/Codexの使い方から業務設計・社内定着まで伴走を受けながら、社内に仕組みを作る道があります。
覚えるより任せたい方は、AI社員AIKATAでこの記事のような定型業務を丸ごと預ける道があります。料金は月30万円の月額定額、追加費用は0円です。
どちらが合うかは、業務量と社内体制次第です。無料相談・無料適合診断で、貴社の場合はどちらが向くかからご相談いただけます。
NEXT STEP
この記事の内容を、あなたのビジネスで
実践してみませんか?
よくある質問
Q. fopenで"w"モードを使ったら中身が消えました。元に戻せますか?
A. 元に戻すことはできません。wモードはファイルを開いた瞬間に中身を空にする仕様のため、追記したい場合は必ずaモードを使う必要があります。重要なファイルを扱う前にバックアップを取る習慣も有効です。
Q. fopenとfile_get_contentsはどちらを使うべきですか?
A. ファイル全体を一括で読み書きするだけならfile_get_contents・file_put_contentsの方がシンプルです。大きなファイルを少しずつ処理したい、ロック制御が必要といった細かい制御が求められる場合にfopen系を検討してください。
Q. fcloseを忘れるとどんな問題が起きますか?
A. 同時に開けるファイル数の上限に達してエラーになったり、書き込んだ内容がディスクに確実に反映されない可能性があります。処理の最後には必ずfcloseを呼ぶ習慣をつけてください。
Q. パストラバーサルを防ぐ具体的な方法は何ですか?
A. basename()でパスの区切り文字やディレクトリ移動の指定を除去する、許可されたファイル名の一覧(ホワイトリスト)と照合する、realpath()で解決後のパスが想定ディレクトリ内かを確認する、といった対策が有効です。
Q. allow_url_fopenは無効にした方がいいですか?
A. 外部URLを直接開く必要がない場合は無効化を検討する価値があります。ただし、ライブラリによっては外部URL取得を前提にしている場合もあるため、無効化する際は既存の機能に影響がないか確認してください。
Q. 非エンジニアの経営者は、こうしたファイル操作の細部をどこまで理解すべきですか?
A. コードを1行ずつ読む必要はありませんが、「ユーザー入力をそのまま使うと危険」という構造は理解しておくべきです。そのうえで、Claude Codeなどによる機械的なレビュー体制を組織としてどう作るかを判断するのが経営者の役割です。
Q. fopenとSplFileObject、どちらを使うべきですか?
A. できることはほぼ同じです。既存コードが関数ベースであればfopen系に、オブジェクト指向のコードベースであればSplFileObjectに合わせるのが実務的です。新規プロジェクトであれば、foreachで扱えるSplFileObjectの方がコードが読みやすくなるケースもあります。
Q. fgetcsvを使う際に気をつけることはありますか?
A. 戻り値が false かどうかを === で厳密に比較することが重要です。空行や特殊なデータが含まれる場合、緩い比較(==や!=)では意図しない判定になることがあるため、型まで含めた厳密比較を徹底してください。
Claude Codeで業務自動化を90日で叩き込む
経営者向けの伴走型パーソナルトレーニング
AI鬼管理/AI社員AIKATAへのお問い合わせ
この記事を読んで気になった方へ。
専門スタッフが、御社に最適な
業務自動化・業務代行プランを無料でご提案します。




