PHP file_get_contents の使い方
file_get_contents() は、PHPでファイルやURLの内容を簡単に取得できる便利な関数です。特に設定が少なく、シンプルな用途には最適ですが、細かな制御やエラーハンドリングには注意が必要です。
以下では、基本的なパラメータの解説から、ファイルの存在チェックや読み込みエラーへの対処法、URL読み込み時の挙動、マルチバイト対応、POSTリクエストへの応用まで、幅広いユースケースをカバーします。
また、php://input や http_build_query() を使ったJSONデータの受信・送信の具体例も紹介しています。
更新日:2025年05月10日
作成日:2025年05月10日
file_get_contents()
file_get_contents() は、指定したファイルや URL の内容を文字列として取得する関数で、「ファイルを開いて→読んで→閉じる」という処理をまとめて行ってくれます。
以下は、ローカルファイル(例: file.txt)を読み込んで内容を表示する、最もシンプルな例です。
$content = file_get_contents('path/to/file.txt');
echo $content;
基本的な書式
以下は file_get_contents の基本的な書式(構文)です。
file_get_contents(
string $filename,
bool $use_include_path = false,
resource $context = null,
int $offset = 0,
int $length = null
): string|false
戻り値
この関数は、成功すれば「読み込んだ内容(文字列)」を返し、失敗すれば false を返します。
そのため、普通は読み込んだ後にエラーを確認します。
$content = file_get_contents('path/to/file.txt');
if ($content === false) {
echo 'ファイルの読み込みに失敗しました。';
}
パラメータ
パラメータ(引数)は $filename のみが必須で、その他はオプション(省略可能)です。
パラメータ | 説明 |
---|---|
$filename | 読み込み対象のファイルパス、または URL を文字列で指定します(必須)。 |
$use_include_path | true にすると、PHP の include_path も検索対象に含めます。 |
$context | HTTP ヘッダーやタイムアウトなどを制御するための「コンテキスト」を指定します。 |
$offset | ファイルの先頭からの読み取り開始位置(バイト数)を指定します。 |
$length | 読み込むバイト数を制限します。省略すると、ファイルの最後まで読み込みます。 |
以下はパラメータの詳細と使い方です。
$filename
読み込み対象のファイルパス、または URL を文字列で指定します(必須)。
file_get_contents('data.txt'); // ファイルパス
file_get_contents('https://example.com/data.json') // URL
相対パスで指定した場合は、実行時のカレントディレクトリ(スクリプトの実行場所)が基準になります。__DIR__ を使うとスクリプトのディレクトリを基準にできます。
file_get_contents('data/info.txt'); // 実行時のカレントディレクトリが基準
file_get_contents('/var/www/html/data/info.txt'); // 絶対パス
file_get_contents(__DIR__ . '/data/info.txt'); // __DIR__ を使ってスクリプトのディレクトリを基準に
$use_include_path
第2引数を true にすると、php.ini の include_path にあるファイルも検索対象になります。
include_path は、PHP がファイルを検索するディレクトリのリストです。
通常、file_get_contents('sample.txt') と書いた場合、PHP は現在のスクリプトと同じディレクトリや相対パス指定された場所しか見に行きませんが、以下のように第2引数の $use_include_path を true にすると、include_path に設定されたディレクトリも探すようになります。
file_get_contents('config.ini', true);
現在の include_path は以下で確認できます。
または、phpinfo(); でも確認できます(Configuration → Core セクション)。
echo get_include_path();
必要に応じて、set_include_path() などを使って、スクリプト内で動的に変更することもできます。
$context
PHP の file_get_contents() や fopen(), stream_socket_client() などのストリーム系関数では、第3引数(または第2引数)に「ストリームコンテキスト($context)」を渡すことができます。
この $context は、通信方法や読み込み動作の詳細なオプションを指定する仕組みで、実際は stream_context_create() 関数で作成したリソースを渡します。
// stream_context_create() で作成
$context = stream_context_create(array $options);
$options の構造は以下のようになります。
[
'wrapper名' => [
'オプション名' => 値,
...
]
]
ストリーム wrapper(プロトコル)に応じて、以下のような指定できるコンテキストがあります。
wrapper 名 | 用途 | 主なコンテキストオプション |
---|---|---|
http | HTTP 通信(GET/POST など) | method, header, content, timeout, ignore_errors |
https | HTTPS 通信(SSL 含む) | verify_peer, cafile, local_cert など(+ http) |
ftp | FTP アクセス | overwrite, resume_pos など |
file | ローカルファイルアクセス | notification(プログレス通知など) |
php | php://memory, php://input 等 | 特殊ストリーム |
ssl | 暗号化通信の設定 | verify_peer, cafile, allow_self_signed など |
socket | ソケット通信 | bindto(バインドアドレス) |
wrapper ごとのコンテキストオプション
PHP公式ドキュメントに各 wrapper ごとのコンテキストオプションが記載されています。
以下は HTTP の POST リクエストの例です。
$options = [
'http' => [
'method' => 'POST',
'header' => "Content-type: application/x-www-form-urlencoded",
'content' => http_build_query(['name' => 'Taro', 'age' => 25]),
'timeout' => 5,
]
];
$context = stream_context_create($options);
$response = file_get_contents('https://example.com/api', false, $context);
- 配列形式でプロトコル別にオプションを指定
- stream_context_create() 関数で作成
$offset
ファイルの先頭からの読み取り開始位置(バイト数)を指定します(デフォルト:0)。
日本語などマルチバイト文字列の場合、指定したバイトが文字の途中に当たると文字化けします。
// 最初の10バイトをスキップして読み込む
file_get_contents('data.txt', false, null, 10);
$length
読み込むバイト数を制限します。省略すると、ファイルの最後まで読み込みます。
ファイルサイズが大きい場合など、この第4引数 $length を使って、読み込む最大バイト数を制限することができます。但し、$offset 同様、マルチバイト文字列の場合、指定したバイトが文字の途中に当たると文字化けするので注意が必要です。
// 10バイト目から20バイト分だけ読む
file_get_contents('data.txt', false, null, 10, 20);
ファイルサイズが大きい場合の代替手法
大量のデータを一度に file_get_contents() でメモリに読み込むのは負担が大きいです。代わりに fopen() と fread() を使って分割読み込みが可能です。
if (file_exists('sample.txt')) {
$fp = fopen('sample.txt', 'r');
while (!feof($fp)) {
echo fread($fp, 1024); // 1024バイトずつ読み込む
}
fclose($fp);
}
エラーハンドリング
file_get_contents() は、読み込み失敗時に false を返すだけでなく、Warning(警告)も出力します。
これをエラーハンドリングで適切に回避したい場合は、次のような方法があります。
@ エラー抑制演算子を使う(簡易)
@ はエラー表示を抑制しますが、完全に非推奨ではないものの、デバッグを難しくするため注意が必要です。そのため、開発中は使わない方が無難ですが、「本番環境で Warning を表示しないことが重要」なときに有効です。URL 読み込みにも使えます。
$content = @file_get_contents('sample.txt');
if ($content !== false) {
// 成功時の処理
echo $content;
} else {
// エラー処理
echo 'ファイルが見つかりませんでした。';
}
file_exists() を使って確認
file_exists() を使って、事前にファイルが存在するかを確認する方法です。
但し、file_exists() はローカルファイルシステム限定の関数なので(URL 読み込みでは常に false になるため)、この方法は使えません。
$filename = 'sample.txt';
if (file_exists($filename)) {
$content = file_get_contents($filename);
// 成功時の処理
echo $content;
} else {
// ファイルが存在しないときの処理
echo 'ファイルが見つかりませんでした。';
}
この方法の場合、ファイルまたはディレクトリが存在するかだけを確認します(ディレクトリでも true を返します)。また、読み取り可能か、ファイルかどうかは確認しません。
is_file() と is_readable() で確認
以下は、is_file() で本当に「ファイルかどうか」を確認し(ディレクトリは除外)、is_readable() でそのファイルに「読み取り権限があるか」確認します。
但し、file_exists() 同様、ローカルファイルシステム限定の関数なので、URL 読み込みでは使えません。
$filename = 'sample.txt';
if (is_file($filename) && is_readable($filename)) {
$content = file_get_contents($filename);
// 成功時の処理
echo $content;
} else {
echo '読み込めるファイルが見つかりません。';
}
単に「存在」だけ確認したい場合は、file_exists() を使い、読み込む処理を安全に行いたい場合はこちらの方法が確実です。
URL 読み込みのエラーハンドリング
URL 読み込みのエラーハンドリングとしては以下のような方法があります。
@ エラー抑制演算子を使う
URL 読み込みの場合も、@ エラー抑制演算子を使うことができます。以下は file_get_contents() を使って URL を読み込む際に、HTTP ステータスコードもチェックする例です。
$context の HTTP コンテキストで ignore_errors => true を指定すると、404 や 500 などの HTTP エラーでも $content にレスポンス本文が返ってくるようになります。これにより、$http_response_header が常にセットされ、ステータスコードの判定が可能になります。
$http_response_header は、その同じ HTTP リクエストのレスポンスの一部として PHP に格納される変数です。$http_response_header は file_get_contents などの HTTP ストリーム関数を呼び出したスコープ内でのみ利用可能な特殊なローカル変数です。
但し、PHP では file_get_contents() が失敗した場合に $http_response_header が作られないため、必ず isset() で存在チェックを行ってから使うのが安全です。
@ によって PHP の Warning を抑制し、$context を通してエラー無視オプションを有効にします。
$http_response_header[0] は HTTP/1.1 200 OK のようなステータスラインなので、preg_match() によりステータスコードだけを抽出します。
$statusCode = $matches[1] ?? null は NULL 合体演算子(??)を使ってステータスコードが取得できていればその値を、取得できなければ null を変数 $statusCode に代入しています。
$statusCode に代入されている値は文字列なので $statusCode !== '200' のように判定し、また (int)$statusCode >= 200 のように明示的に (int) キャストしていますが、$statusCode !=200 で判定したり、 (int) キャストを省略することもできます。
$context = stream_context_create([
'http' => ['ignore_errors' => true]
]);
// @ によって PHP の Warning を抑制し、$context を通してエラー無視オプションを有効に
$content = @file_get_contents('https://example.com/file.txt', false, $context);
if ($content === false) {
echo '読み込みエラー';
} else {
// HTTPレスポンスコード確認(必ず isset() で存在チェックを行ってから使う)
if (isset($http_response_header)) {
preg_match('#HTTP/\d+\.\d+ (\d+)#', $http_response_header[0], $matches);
$statusCode = $matches[1] ?? null;
if ($statusCode !== '200') {
echo "HTTPステータス異常: $statusCode";
error_log("HTTPエラー: $statusCode\nレスポンス: $content"); // 必要に応じて(省略可能)
}
if ($statusCode !== null && (int)$statusCode >= 200 && (int)$statusCode < 300) {
// 成功時の処理
echo $content;
}
}
}
上記の $statusCode を数値として扱いたい場合は、例えば、以下のように最初にキャストしておくこともできます。null 合体演算子を使って記述することもできますが、$matches[1] が未定義だった場合、(int)($matches[1] ?? null) は 0 になるので注意が必要です。
$statusCode = null;
if (isset($matches[1]) && is_numeric($matches[1])) {
$statusCode = (int)$matches[1];
}
get_headers() で HTTP ステータスコードをチェック
get_headers() はリモートサーバーに対して HEAD リクエストを行い、ステータスコードを含むヘッダーを取得します。$headers[0] にステータスコード 200 が含まれていれば OK と判定しています。
$url = 'https://example.com/file.txt';
$headers = @get_headers($url, 1);
if ($headers && strpos($headers[0], '200') !== false) {
$content = file_get_contents($url);
echo $content;
} else {
echo 'URL からファイルを読み込めませんでした。';
}
@ エラー抑制演算子で get_headers() での警告を抑える代わりに、PHP のエラーハンドラ set_error_handler() を使う方法もあります。
// URL が有効かどうかを判定する関数
function check_url_exists($url) {
$error = null;
// カスタムエラーハンドラを登録
set_error_handler(function($errno, $errstr) use (&$error) {
$error = $errstr;
});
$headers = get_headers($url, 1);
// 元のエラーハンドラに戻す
restore_error_handler();
// エラーが発生していれば false を返す
if ($error !== null || $headers === false) {
return false;
}
// 200 OK を含むかで判断
return strpos($headers[0], '200') !== false;
}
// 使用例
$url = 'https://example.com/file.txt';
if (check_url_exists($url)) {
$content = file_get_contents($url);
echo $content;
} else {
echo 'ファイルがURLに存在しないか、アクセスできません。';
}
但し、上記のどちらの方法でも、get_headers() と file_get_contents() で通信が2回発生しているため、効率的ではありません。
cURL を使う
以下のように cURL 関数を使えば、一度の curl_exec() で内容取得とステータスチェックも済ませることができるので、効率的です。
function fetch_url_content($url) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // リダイレクト対応
curl_setopt($ch, CURLOPT_TIMEOUT, 10); // タイムアウト設定
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); // SSL検証(必要に応じて false に)
$content = curl_exec($ch);
$http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http_status >= 200 && $http_status < 400 && $content !== false) {
return $content;
}
return false;
}
// 使用例
$url = 'https://jsonplaceholder.typicode.com/users/1';
$content = fetch_url_content($url);
if ($content !== false) {
echo $content;
} else {
echo "ファイルが取得できませんでした。";
}
エラーが発生したときにエラーログに出力するには、curl_error() を使ってログ出力します。
function fetch_url_content($url) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
$content = curl_exec($ch);
$http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// エラー処理
if ($content === false || $http_status < 200 || $http_status >= 400) {
$error_msg = curl_error($ch);
error_log("curl error: {$error_msg} (HTTP {$http_status}) for URL: {$url}");
curl_close($ch);
return false;
}
curl_close($ch);
return $content;
}
URL からデータを取得する目的であれば、file_get_contents() よりも cURL を使う方が一般的に「より安全で柔軟」です。
安全なマルチバイト文字列の制御方法
$offset や $length を使ってバイト数を指定する場合、日本語などマルチバイト文字列では、文字を途中で切ってしまい文字化けするリスクがあります。
対策としては以下のような方法があります。
mb_substr() を使う
file_get_contents() で取得したデータに対して mb_substr() で文字数を制限することができます。この方法なら、「文字が途中で切れて文字化けする」ことを避けられます。
mb_substr() の引数に 'UTF-8' を指定するのが重要です。
$contents = file_get_contents('example.txt');
// UTF-8 として、先頭から 100 文字だけ取得(バイトではなく「文字」)
$substring = mb_substr($contents, 0, 100, 'UTF-8');
echo $substring;
ファイルサイズが大きい場合
ファイルサイズが大きい場合は、一部だけ読み込んでから文字変換することもできます。
fread() を使って読み込む際には、少し多めにバイト数を指定します。UTF-8などのマルチバイト文字列では、1文字あたりのバイト数が1〜4バイトで可変なので、以下では 100文字 × 最大4バイト = 400バイト以上を確保しています。
$fp = fopen('sample.txt', 'r');
$partial = fread($fp, 512); // 仮に 512バイトだけ読み込む(少し多めに確保)
fclose($fp);
// バイト列を UTF-8 文字列として安全に切り出す
$text = mb_convert_encoding($partial, 'UTF-8', 'UTF-8');
// UTF-8 として、先頭から 30 文字だけ取得
$safe_substr = mb_substr($text, 0, 30, 'UTF-8');
echo $safe_substr;
URL からデータを取得
file_get_contents() を使って URL からデータを取得する基本的な方法は以下の通りです。
$content = file_get_contents('https://example.com/data.json');
これだけで、指定した URL に対して GET リクエストが1回送信され、レスポンスボディ(HTML や JSON などの本文)が $content に格納されます。
URLを使うには、php.ini で allow_url_fopen = On に設定されている必要がありますが、多くの PHP インストールで初期状態として有効(On)になっています。以下で確認できます。
var_dump(ini_get('allow_url_fopen')); // "1" または "On" で有効
無効の場合は、php.ini で次のように設定します(CLI や Apache 等の環境によって異なります)。
allow_url_fopen = On
HTTP コンテキスト
file_get_contents() で URL を読み込む場合、第3引数にストリームコンテキスト($context)を指定できますが、HTTP プロトコル用に指定できるオプションには以下のようなものがあります。
オプション名 | 説明 |
---|---|
method | 使用する HTTP メソッド(GET や POST など)。省略時は GET。※ content を使う場合は必ず method もセットで明示すべき(POST, PUT, PATCH など) |
header | 追加の HTTP ヘッダー(Content-Type など)を文字列で指定。複数行なら \r\n 区切り |
content | POST や PUT の場合に送信するリクエストボディ(文字列) |
timeout | 通信のタイムアウト(秒数、float 可)。デフォルトは無制限(default_socket_timeout に従う) |
ignore_errors | HTTP ステータスが 4xx / 5xx の場合でもレスポンスを取得するなら true(デフォルトは false) |
max_redirects | リダイレクトを何回まで許容するか(デフォルトは 20) |
protocol_version | HTTP のバージョン(デフォルトは 1.0、指定可能例: 1.1) |
ヘッダーの複数行記述
ヘッダー(header オプション)は \r\n 区切りで文字列として渡す必要があります。
複数行で記述する方法としては、以下のように implode() を利用できます。
'header' => implode("\r\n", [
"User-Agent: MyAgent/1.0",
"Accept: application/json"
])
クエリパラメータを生成
http_build_query() を使って連想配列を URL のクエリ文字列(URL エンコード形式)に変換することができます。JavaScript の new URLSearchParams(obj).toString() に相当します。
以下は連想配列で定義したパラメータを http_build_query() を使ってクエリ文字列に変換して URL を作成する例です。
// パラメータを連想配列で定義
$params = [
'_fields' => 'title,link',
'per_page' => 5,
'order' => 'asc'
];
// クエリ文字列に変換して URL に追加
$url = 'https://example.com/api?' . http_build_query($params);
echo $url;
//https://example.com/api?_fields=title%2Clink&per_page=5&order=asc
http_build_query()
以下は http_build_query() の書式です。
http_build_query(
array $data, // 第1引数:連想配列またはオブジェクト
string $numeric_prefix = "", // 第2引数:数値キーの前につける接頭辞(省略可能)
string $arg_separator = "&", // 第3引数:キーと値のペア同士をつなぐ区切り文字
int $encoding_type = PHP_QUERY_RFC1738 // 第4引数:エンコード方式
)
パラメータ | 説明 |
---|---|
$data | クエリ文字列に変換したい連想配列またはオブジェクト |
$numeric_prefix | 数値キーがある場合に付ける接頭辞 |
$arg_separator | クエリ文字列内の各キーと値のペアを結ぶ区切り文字。デフォルト(省略時)は & (セキュリティ的理由で & を使うこともありますが、通常の送信では &)。 |
$encoding_type | エンコード方式
|
以下は同じことです。
http_build_query($data); // オプションのパラメータを省略
http_build_query($data, '', '&', PHP_QUERY_RFC1738); // デフォルト値を指定
配列の値が含まれている場合
配列(連想配列含む)の値が含まれている場合、その配列は自動的にインデックス付きのクエリパラメータとして展開されます。
$data = [
'name' => 'Taro',
'colors' => ['red', 'green', 'blue']
];
echo http_build_query($data);
// 出力 name=Taro&colors%5B0%5D=red&colors%5B1%5D=green&colors%5B2%5D=blue
// 上記をデコードすると name=Taro&colors[0]=red&colors[1]=green&colors[2]=blue
入れ子の配列も問題なく展開されます。
$data = [
'user' => [
'name' => 'Taro',
'age' => 30
]
];
echo http_build_query($data);
// 出力 user%5Bname%5D=Taro&user%5Bage%5D=30
// 上記をデコードすると user[name]=Taro&user[age]=30
リクエストのサンプル
file_get_contents() を使った GET と POST の送信サンプルです。
GET サンプル
以下は、{JSON} Placeholder というテスト用の JSON データを返す API にアクセスして取得したレスポンスを出力する例です。「/posts/1」にアクセスすると ID が1の投稿のデータ(JSON)を取得できます。
$options の http コンテキストで、Accept ヘッダー(受け取りたいレスポンスの形式)を指定していますが、この例の場合、{JSON} Placeholder は Accept ヘッダーが未指定でも JSON を返すように設計されているので省略可能です。
'ignore_errors' => true は HTTP エラーでもレスポンスを取得してステータスコードを確認できるようにするための指定です。ステータスコードの取得では isset() で $http_response_heade の存在チェックを行ってから、preg_match() によりステータスコードだけを抽出します(エラーハンドリング)。
また、$statusCod に格納されているステータスコードは文字列(または null)になります。
$response には file_get_contents() で取得した JSON 形式の文字列 が入っています。
json_decode() 関数はその JSON 文字列を PHP の配列やオブジェクトに変換します。第2引数に true を渡すことで、連想配列としてデコードされます。
$url = 'https://jsonplaceholder.typicode.com/posts/1';
$options = [
'http' => [
'method' => 'GET', // 省略可能(デフォルト)
'header' => "Accept: application/json\r\n", // この例の場合は省略可能
'ignore_errors' => true, // ステータス 400 以上(エラー)でもレスポンス取得
]
];
$context = stream_context_create($options);
$response = @file_get_contents($url, false, $context);
// ステータスコードの取得
$statusCode = null;
if (isset($http_response_header)) {
preg_match('#HTTP/\d+\.\d+ (\d+)#', $http_response_header[0], $matches);
$statusCode = $matches[1] ?? null;
}
// ステータスコードが200(成功)の場合
if ($statusCode === '200') {
// JSON 文字列を PHP の配列に変換
$json = json_decode($response, true);
// 出力
print_r($json);
} else {
echo "GET失敗: HTTPステータス " . ($statusCode ?? '不明') . "\n";
}
レスポンスが JSON でないときに json_decode() を使うと null が返る可能性があるので、以下のように json_last_error() を使った json_decode() のエラーハンドリングを追加うとより安全(確実)です。
if ($statusCode === '200') {
// JSON 文字列を PHP の配列に変換
$json = json_decode($response, true);
// JSON を検証
if (json_last_error() === JSON_ERROR_NONE) {
// JSON 解析でエラーなし(問題ないので出力)
print_r($json);
} else {
echo "JSONの解析に失敗しました: " . json_last_error_msg();
}
} else {
echo "GET失敗: HTTPステータス " . ($statusCode ?? '不明') . "\n";
}
GET サンプル(URL パラメータ)
以下は http_build_query() を使ってパラメータをクエリ文字列に変換して URL に追加してリクエストする例です。また、この例では header の X-API-Key で API キーを指定しています。
$baseUrl = "https://example.com/wp/wp-json/wp/v2/posts";
// URL に追加するパラメータ
$params = [
'_fields' => 'title,link',
'per_page' => 3,
];
// http_build_query() でクエリパラメータを構築して URL に付加
$url = $baseUrl . '?' . http_build_query($params);
$api_key = 'abc123def456'; // 必要に応じて別ファイルで管理
$context = stream_context_create( [
'http' => [
'method' => 'GET',
'header' => "X-API-Key: $api_key\r\nContent-Type: application/json\r\n",
'ignore_errors' => true, // ステータス 400 以上(エラー)でもレスポンス取得
]
]);
$response = @file_get_contents($url, false, $context);
// ステータス確認
$statusCode = null;
if (isset($http_response_header)) {
preg_match('#HTTP/\d+\.\d+ (\d+)#', $http_response_header[0], $matches);
$statusCode = $matches[1] ?? null;
}
// 結果処理
if ($statusCode === '200') {
$json = json_decode($response, true);
if (json_last_error() === JSON_ERROR_NONE) {
print_r($json);
} else {
echo "JSONの解析に失敗しました: " . json_last_error_msg();
}
} else {
echo "GET失敗: HTTPステータス " . ($statusCode ?? '不明') . "\n";
}
POST サンプル(JSON 形式)
以下は JSON 形式(application/json)でデータを送信する POST リクエストのサンプルです。
JSON 形式でデータを送信するので、Content-Type は application/json になります。また、Accept ヘッダー(受け取りたいレスポンスの形式)には application/json を指定しています。
結果判定では、成功ステータス 201(Created)を確認しています。
$url = 'https://jsonplaceholder.typicode.com/posts';
$data = [
'title' => 'foo',
'body' => 'bar',
'userId' => 1,
];
// JSON に変換
$jsonData = json_encode($data);
// HTTP コンテキストオプション
$options = [
'http' => [
'method' => 'POST',
'header' => implode("\r\n", [
'Content-Type: application/json; charset=UTF-8',
'Accept: application/json',
]),
'content' => $jsonData,
'ignore_errors' => true, // ステータス 400 以上(エラー)でもレスポンス取得
'timeout' => 10,
]
];
$context = stream_context_create($options);
$response = @file_get_contents($url, false, $context);
// ステータスコードを取得
$statusCode = null;
if (isset($http_response_header)) {
preg_match('#HTTP/\d+\.\d+ (\d+)#', $http_response_header[0], $matches);
$statusCode = $matches[1] ?? null;
}
// 結果判定
if ($statusCode === '201') { // jsonplaceholder は POST 成功で 201 を返す
$json = json_decode($response, true);
if (json_last_error() === JSON_ERROR_NONE) {
echo "送信成功:\n";
print_r($json);
} else {
echo "JSON解析エラー: " . json_last_error_msg();
}
} else {
echo "送信失敗: HTTPステータス " . ($statusCode ?? '不明') . "\n";
echo "レスポンス内容:\n$response";
}
POST サンプル(URL エンコード形式)
以下は URL エンコード形式でデータを送信する POST リクエストのサンプルです。
送信するデータを http_build_query() で URL エンコード形式に変換して content に指定しています。この例では RFC3986準拠のエンコードでクエリ文字列を生成しています。
Content-Type は application/x-www-form-urlencoded になります。
httpbin.org は送信データをそのまま JSON で返してくれるテスト用 API(エコーサービス)です。
$url = 'https://httpbin.org/post'; // テスト用エコーサービス
// 送信データ(連想配列)
$data = [
'name' => 'John Doe',
'email' => 'john@example.com',
'message' => 'こんにちは'
];
// URL エンコード形式に変換 (key1=value1&key2=value2...)
$postData = http_build_query($data, '', '&', PHP_QUERY_RFC3986);
// HTTP コンテキスト
$options = [
'http' => [
'method' => 'POST',
'header' => implode("\r\n", [
'Content-Type: application/x-www-form-urlencoded',
'Content-Length: ' . strlen($postData),
]),
'content' => $postData,
'ignore_errors' => true,
'timeout' => 10,
],
];
$context = stream_context_create($options);
$response = @file_get_contents($url, false, $context);
// HTTP ステータスコード取得
$statusCode = null;
if (isset($http_response_header)) {
preg_match('#HTTP/\d+\.\d+ (\d+)#', $http_response_header[0], $matches);
$statusCode = $matches[1] ?? null;
}
// 結果処理
if ($statusCode && (int)$statusCode >= 200 && (int)$statusCode < 300) {
echo "送信成功:\n";
echo $response;
} else {
echo "送信失敗: HTTPステータス " . ($statusCode ?? '不明') . "\n";
echo "レスポンス:\n" . ($response ?: 'レスポンスなし');
}
上記のコードでは、成功判定を 200〜299 の範囲で成功とみなしています(REST APIなどで 201 や 204 の成功も受け入れたい場合など)。また、上記ではレスポンスをそのまま echo しています。
以下は、ステータスコードの判定では200のみを成功とみなし、レスポンスを JSON として解析して取り出す例です。
// 結果処理
if ($statusCode === '200') {
$json = json_decode($response, true);
if (json_last_error() === JSON_ERROR_NONE) {
echo "送信成功:\n";
print_r($json['form']); // httpbin.org の場合 'form' に入ってくる
} else {
echo "JSON解析エラー: " . json_last_error_msg();
}
} else {
echo "送信失敗: HTTPステータス " . ($statusCode ?? '不明') . "\n";
echo "レスポンス内容:\n$response";
}
フロントエンドが JSON を POST してくる場合(サーバー側)
サーバー側の処理として、フロントエンド(例えば fetch() や axios)が Content-Type: application/json で JSON を POST してくる場合、PHP の $_POST が使えません。
PHP の $_POST はリクエストの Content-Type に応じて動作が変わり、application/x-www-form-urlencoded や multipart/form-data のときのみ機能します。
Content-Type | $_POST に入る? | 解説 |
---|---|---|
application/x-www-form-urlencoded | ✅ 入る | HTMLフォームの標準形式(通常の form) |
multipart/form-data | ✅ 入る | ファイルアップロード含む form |
application/json | ❌ 入らない | PHP は自動で解析しないため、手動処理が必要 |
Content-Type が application/json の場合、PHP は JSON を自動で $_POST に変換しないため、生のリクエストボディを自分で取得してデコードする必要があります。
file_get_contents('php://input') を使用すると、クライアント(ブラウザや JavaScript)から送信されたリクエストボディの生データを取得することができます。
以下は典型的なやり取りの流れです。
fetch('/api/receive.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: 'Taro', age: 20 })
});
// receive.php
$json = file_get_contents('php://input'); // 生のリクエストボディを取得
$data = json_decode($json, true); // JSON をデコード(連想配列に変換)
echo "受信した名前: " . $data['name']; // Taro
また、PHPでは $_POST は POST メソッド専用です。PUT・PATCH・DELETE メソッドで送信されたデータは php://input を使って取得します。
// PUT された JSON データを取得
if ($_SERVER['REQUEST_METHOD'] === 'PUT') {
$rawData = file_get_contents('php://input');
$data = json_decode($rawData, true);
}
php://input
php://input は、現在のリクエストに対して送信された「生のリクエストボディ」を読み取るためのストリームです(php:// — さまざまな入出力ストリームへのアクセス)
次のような特徴があります。
特性 | 説明 |
---|---|
対象 | POST(または PUT)などで送信された「ボディ部」全体 |
内容 | エンコード処理される前の生データ(文字列) |
用途 | JSON や XML などのカスタムペイロードの受信に使う |
読み取り制限 | 一度しか読み取れない(再読不可) |
「HTTP リクエストのボディを丸ごと取得するための読み取り専用ストリーム」が php://input です。
注意点
- php://input は「一度しか読めない」ため、複数回の読み取りが必要な場合は最初の読み取り時に変数に保存する必要があります。
- 書き込みはできません(読み取り専用)。
file_get_contents() 以外で扱う
php://input は PHP の「ストリームラッパー」なので、ファイルストリームのように他の関数でも読み取り可能です。
$fp = fopen('php://input', 'r');
$input = '';
while (!feof($fp)) {
$input .= fread($fp, 1024); // 1024バイトずつ読み取る
}
fclose($fp);
- 利点: 大きなデータを分割して処理できる(メモリ節約)
- 欠点: 少し冗長。通常は file_get_contents() の方が簡潔
$fp = fopen('php://input', 'r');
$input = stream_get_contents($fp);
fclose($fp);
以下はそれぞれの方法の比較です。file_get_contents() は1行で書けるのでシンプルですが、場合によっては他の方法も検討できます。
方法 | 推奨用途 |
---|---|
file_get_contents() | 一括で読み取って問題ないサイズのデータ(通常の JSON 投稿など)。 |
fopen()+fread() | 大量データやストリーミング処理(例:大きなファイルアップロードの独自処理) |
stream_get_contents() | fopen() を使う必要がある状況下で簡潔に読みたい場合 |
JSON 受信サンプル
以下は、file_get_contents('php://input') を使って JSON 形式の POST データを受け取るサンプルです。
この例では以下のように送信側の JavaScript は HTML ファイルに直接記述して、レスポンスを HTML 上とコンソールに出力します。fetch() に指定するパスは適宜調整します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fetch Test</title>
</head>
<body>
<h1>Fetch API Test</h1>
<script>
// JSON 形式で POST リクエスト
fetch('path/to/receive.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Taro',
age: 30
}),
})
.then((response) => {
if (response.ok) {
//ステータスが ok ならばレスポンスボディを取得して変換
return response.json();
} else {
//ok でなければ例外を発生
throw new Error(`リクエスト失敗: ${response.status} ${response.statusText}`);
}
})
.then((json) => {
//データを HTML とコンソールに出力
document.body.insertAdjacentHTML('beforeend', `<pre>${JSON.stringify(json, null, 2)}</pre>`);
console.log(json);
})
.catch((error) => {
console.warn(error);
});
</script>
</body>
</html>
以下は JSON POST リクエスト受信処理を行うサーバー側 PHP ファイル(receive.php)です。
受け取ったデータを検証し、問題がなければレスポンスを作成して返しています。
name と age が存在し、型や値が正しいかを検証し、問題がなければ、ステータスコードとヘッダー(Content-Type)を設定してデータを JSON 形式にして返します。
<?php
// 生のリクエストボディを取得
$rawJson = file_get_contents('php://input');
// JSON をデコード(連想配列として取得)
$data = json_decode($rawJson, true);
// JSONエラーの確認
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
echo json_encode(['error' => '無効なJSONデータです']);
exit;
}
// 必須フィールドのバリデーション
if (!isset($data['name']) || !isset($data['age'])) {
http_response_code(422); // Unprocessable Entity
echo json_encode(['error' => '必要な項目が不足しています']);
exit;
}
// 型や値のチェック(例:年齢が正の整数か)
if (!is_string($data['name']) || !is_int($data['age']) || $data['age'] <= 0) {
http_response_code(422);
echo json_encode(['error' => 'データ形式が正しくありません']);
exit;
}
// 成功時の処理
$response = [
'message' => 'データを受け取りました',
'received' => $data
];
http_response_code(200); // 成功ステータスを明示(デフォルトは 200 なので省略可能)
header('Content-Type: application/json'); // ヘッダーを設定
// JSON 形式にして返す
echo json_encode($response);
ブラウザの開発ツールのコンソールで確認すると以下のように出力されます(HTML にも表示されます)。
{
"message": "データを受け取りました",
"received": {
"name": "Taro",
"age": 30
}
}