file_get_contents('notes.txt')はファイル全体を読んで、1つの文字列として返します。ファイルを読めなければfalseを返します。書き込むにはfile_put_contents('notes.txt', $text)を使い、ファイルを作るか、中身を置き換えます。
出力:
Buy milk
Call Ada
Write docs
29 bytes
3 lines
このブロックは、扱うファイルがあるよう先にnotes.txtを書き出してから読み戻します。'notes.txt'のようなパスは現在の作業ディレクトリからの相対なので、Webアプリでは__DIR__ . '/notes.txt'のようにスクリプト自身のフォルダーからパスを組み立てます。
file_put_contents()で書き込む、追記する
file_put_contents()は書き込んだバイト数を返し、失敗するとfalseを返します。デフォルトではファイルを上書きします。FILE_APPENDフラグを付けると代わりに末尾に追加し、これが簡単なログの仕組みです。LOCK_EXは2つのリクエストが同時に書き込むのを防ぎます。
出力:
Wrote 8 bytes
started
step 1 done
step 2 done
replaced
abc
5 bytes
配列を保存して配列として取り戻すには、json_encode()でJSONとして書き、json_decode()で読みます。
file()でファイルを1行ずつ読む
file()はファイルを1行1要素の配列に読み込みます。FILE_IGNORE_NEW_LINESを渡さない限り各行の末尾には改行が残り、FILE_SKIP_EMPTY_LINESは空行を取り除きます(FILE_IGNORE_NEW_LINESと一緒のときだけ働きます)。
出力:
string(9) "Buy milk
"
1. Buy milk
2. Call Ada
3. Write docs
3 tasks
file()とfile_get_contents()はファイル全体をメモリに読み込みます。設定ファイルや数メガバイトのログなら問題ありません。数百メガバイトのファイルは、代わりにfopen()とfgets()で1行ずつ読みましょう。
読む前にファイルが存在するか確認する
ファイルがないと、file_get_contents()はfalseを返し、PHPは警告を表示します。
Warning: file_get_contents(missing.txt): Failed to open stream: No such file or directory in /home/index.php on line 2
先にis_file()(存在していてディレクトリでないファイル)かfile_exists()(ファイルかディレクトリ)で確認し、結果は必ず=== falseと比較しましょう。空のファイルは""を返し、これもfalseと評価されるからです。
出力:
empty.txt exists file
missing.txt missing
. exists directory
Cannot read missing.txt, using defaults
bool(false)
bool(true)
0 bytes
@file_get_contents()で警告を隠さないでください。先に確認するかfalseを処理すれば、ほかの何かがおかしくなったときにエラーが見えるままになります。
ファイルの一部を読む
4つ目と5つ目の引数は、読み始めるバイトのオフセットと、読むバイト数です。負のオフセットはファイルの末尾から数えます。名前付き引数を使えば、間の2つの引数を飛ばせます。
出力:
HEADER
line 2
last line
オフセットは文字ではなくバイトを数えます。UTF-8では日本語の文字は3バイトなので、その途中のオフセットは文字を半分に切ってしまいます。文字単位で扱うには、文字列全体を読んでmb_substr()を使います。
テキストファイルをWebページに表示する
ファイルの中身はテキストで、HTMLに出力するテキストはエスケープしなければなりません。そうしないとファイルの中の<がタグになってしまいます。htmlspecialchars()がそれをエスケープし、nl2br()が改行を<br>タグに変えます。実行してPageタブを見てください。
htmlspecialchars()を外してもう一度実行すると、ページは「v1.2: Fixed」で終わってしまいます。ブラウザが<title>を本物のタグとして読み、そのあとのすべてをページのタイトルとして飲み込んでしまうからです。ユーザーが書けるあらゆるものにとってこれがなぜ重要かは、htmlspecialchars()のページで扱っています。
file_get_contents()でURLを読む
allow_url_fopenが有効なら(php.iniのデフォルト)、file_get_contents()はURLも読み、レスポンスの本文を返します。ストリームコンテキストで、メソッド、ヘッダー、本文、タイムアウトを加えられます。
<?php
$html = file_get_contents('https://example.com/');
$context = stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\n",
'content' => json_encode(['name' => 'Ada']),
'timeout' => 5,
'ignore_errors' => true, // return the body of 4xx/5xx responses too
],
]);
$response = file_get_contents('https://api.example.com/users', false, $context);
if ($response === false) {
// DNS failure, timeout, or a refused connection
}
再試行、上限付きのリダイレクト、わかりやすいエラーコードには、cURLやGuzzleのようなHTTPクライアントライブラリのほうが適した道具です。
file_get_contents、file()、fopen()の使い分け
| やりたいこと | 使うもの | メモリ |
|---|---|---|
| ファイル全体を1つの文字列で | file_get_contents() | ファイル全体 |
| 行を配列で | file($path, FILE_IGNORE_NEW_LINES) | ファイル全体 |
| 1回の呼び出しでファイルを書き込む、置き換える | file_put_contents() | 書き込むデータ |
| ログに1行追加する | file_put_contents($path, $line, FILE_APPEND | LOCK_EX) | 1行 |
| 大きなファイルやCSVの行を1行ずつ読む | fopen()とfgets()かfgetcsv() | 1行 |
| 開いた同じファイルを読み書きする | r+、a+、c+でのfopen() | 読んだ分 |
1回で済む関数は、ファイルを開き、読むか書き、閉じるところまでをやってくれるので、デフォルトの選択として正解です。ファイルがメモリに収まらないほど大きいときや、操作の間ファイルを開いたままにする必要があるときにfopen()を選びましょう。
よくある質問
PHPでファイルを文字列として読み込むには?
file_get_contents('path/to/file.txt')を呼びます。ファイル全体を文字列として返し、ファイルを読めなければfalseを返すので、使う前に結果を=== falseと比較しましょう。
PHPでファイルに追記するには?
FILE_APPENDフラグを渡します:file_put_contents('log.txt', "new line\n", FILE_APPEND);。これがないと、file_put_contents()はファイル全体を置き換えます。複数のリクエストが同時に書き込むかもしれないときはLOCK_EXを加えます(FILE_APPEND | LOCK_EX)。
PHPでファイルを1行ずつ読むには?
小さなファイルなら、file('data.txt', FILE_IGNORE_NEW_LINES)が1行1要素の配列を返します。大きなファイルなら、fopen()で開いてwhileループの中のfgets()で1行ずつ読みます。こうすればメモリに置くのは1行だけです。
file_get_contentsがfalseを返すのはなぜですか?
ファイルが存在しないか、パスが思っているのとは別のディレクトリからの相対になっているか、PHPに読む権限がないかです。PHPはFailed to open stream: No such file or directoryのような警告も表示します。is_file()とis_readable()で確認し、パスは__DIR__から組み立てましょう。
file_get_contentsでURLを読めますか?
php.iniでallow_url_fopenが有効なら読めます:file_get_contents('https://example.com/')はレスポンスの本文を返します。ヘッダー、POSTデータ、タイムアウトにはstream_context_create()のコンテキストを渡します。もっと込み入ったことには、cURLのほうがエラー処理に優れています。