PHP Logo PHP cURL の使い方

PHP で外部 API と通信する際、最もよく使われる手段のひとつである cURL について、curl_init による初期化から、curl_setopt でのオプション指定、curl_exec の実行、エラー処理、JSONレスポンスの扱いまで、基本的な流れを解説します。また、具体的なリクエストのサンプルも掲載しています。

更新日:2025年05月11日

作成日:2025年05月10日

cURL の基本的な使い方

PHP の cURL(Client URL Library)は、HTTP リクエストを行うための拡張機能です。

cURL を使うと、他のサーバーに HTTP リクエストを送ってデータを取得・送信できます。

cURL の基本的な使い方は次の「4ステップ」です。

  1. curl_init() により cURL セッションを初期化
  2. curl_setopt() により転送時のオプションを設定
  3. curl_exec() により転送を実行
  4. curl_close() によりセッションを終了
// 1. セッションを初期化
$ch = curl_init();  // 戻り値 $ch は cURL ハンドル(セッションを管理するためのオブジェクト)

// 2. オプションを設定
curl_setopt($ch, CURLOPT_URL, "https://example.com"); // リクエスト先を指定
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 結果を文字列で取得(出力しない)

// 3. 実行して結果を取得
$response = curl_exec($ch);

// 4. セッションを終了
curl_close($ch);

上記を実行するとリクエスト先に指定した URL のソース(HTML)が $response に格納されます。

PHP マニュアル:基本的な curl の使用法

以下は cURL で使用する各関数の詳細です。

curl_init

curl_init() は新規セッションを初期化し、cURL ハンドル(セッションハンドル)を返します。

cURL ハンドルは、PHP 内部で cURL セッションを管理するためのオブジェクト(CurlHandle クラスのインスタンス)で、以降の curl_setopt() や curl_exec() などでこのセッションを操作するために使います。

以下が書式です。

curl_init(?string $url = null): CurlHandle|false
パラメータ 説明
$url url を指定した場合、オプション CURLOPT_URL がそのパラメータの値に設定されます。

書式の ?string $url = null における ? の意味は、その引数が null を許容する nullable 型であることを示していて、?string は、この引数は「文字列 または null」のどちらかを受け入れることを意味します。

戻り値

成功した場合に cURL ハンドル(セッションハンドル)、エラー時に false を返します。

// セッションを初期化(戻り値の cURL ハンドルを変数 $ch に代入)
$ch = curl_init();

var_dump($ch);  // object(CurlHandle)#1 (0) { }

(補足)PHP 8.0 以降

PHP 8.0 から curl_init() の戻り値はリソース型から CurlHandle オブジェクトに変更されています。但し、従来どおり $ch(セッションハンドル)として扱えます。また、コードの書き方も基本的に同じです。

curl_init() に URL を指定しない?

一般的には、curl_init() に URL を直接渡すのではなく、以下のような理由により後から curl_setopt() や curl_setopt_array() で CURLOPT_URL を設定することが多いです。

  1. 柔軟性が高くなる
    • curl_init() の引数で URL を渡してしまうと、URL の設定が「固定」になる。
    • curl_setopt() を使えば、状況に応じて後から URL を決定・変更できるため、より柔軟。
  2. リクエストの構成を分かりやすく整理できる
    • curl_setopt() を使ってまとめてオプションを設定する方がコードの見通しが良く、保守性も高い。
  3. 複数リクエストを 1 つのセッションで使いまわせる(但し注意点あり)
    • 同じ cURL ハンドルを使って複数のリクエストを送ることも理論上は可能です(それにより初期化コストを多少削減できるが、接続が再利用されるとは限らない)。
    • 但し、セッション情報(Cookie など)が引き継がれる可能性があるため、別セッションとして完全に独立させたい場合は curl_init() を分けたほうが安全です。もし、複数リクエストを効率よく扱いたい場合は cURL のマルチハンドル(curl_multi_* 系関数)を利用できます。
  4. 他のオプションとの兼ね合いを調整しやすい
    • 例えばヘッダーや POST データを送る場合など、URL を決めるタイミングとオプションを決めるタイミングを柔軟に管理できます。

curl_setopt()

curl_setopt() を使って転送用のオプションを設定します。以下が書式です。

curl_setopt(CurlHandle $handle, int $option, mixed $value): bool
パラメータ 説明
$handle curl_init() が返す cURL ハンドル
$option CURLOPT_URL や CURLOPT_RETURNTRANSFER などのオプション名(定数)
$value $option に設定する値。

以下はよく使うオプションです。

オプション名 内容 値の型 デフォルト
CURLOPT_URL リクエストする URL を指定 string なし
CURLOPT_RETURNTRANSFER true にすると curl_exec の結果を文字列として返す bool false
CURLOPT_POST POST メソッドを使用する bool false
CURLOPT_POSTFIELDS POST データを指定。配列を渡すと application/x-www-form-urlencoded になる string | array なし
CURLOPT_HTTPHEADER 任意の HTTP ヘッダーを送信。ヘッダーが1行だけでも配列で指定 array なし
CURLOPT_TIMEOUT 全体のリクエスト処理の最大時間(接続・送信・受信すべて含む)(秒) int 0(無制限)
CURLOPT_CONNECTTIMEOUT 接続が確立されるまでの最大待機時間(DNS解決など)(秒) int 0(無制限)
CURLOPT_FOLLOWLOCATION HTTP リダイレクトが発生した場合に、リダイレクトを自動的に追跡 bool false
CURLOPT_SSL_VERIFYPEER SSL 証明書の検証を行う bool true
CURLOPT_USERPWD ユーザー名とパスワード string なし

戻り値

成功した場合に true を、失敗した場合に false を返します。

注意点

不正なオプションや値を指定すると curl_setopt() は false を返しますが、エラーは発生しません。

また、CURLOPT_RETURNTRANSFER を true にしないと、取得内容が echo でそのまま出力されてしまうため注意が必要です。

curl_setopt_array()

curl_setopt_array() は、curl_setopt() を複数回使う代わりに、一度にまとめてオプションを設定できる関数です。可読性が高いため、複雑なリクエストでは便利です。以下が書式です。

curl_setopt_array(CurlHandle $handle, array $options): bool
パラメータ 説明
$handle curl_init() で作成した cURL セッションハンドル。
$options CURLOPT_定数 => 値 の連想配列(設定するオプションとその値を指定した配列)。

連想配列のキーは CURLOPT_ から始まる定数名(文字列ではなく定数)で指定する必要があります。

以下は使用例です。

curl_setopt_array($ch, [
  CURLOPT_URL => $url,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => [
    'Content-Type: application/json',
    'Accept: application/json'
  ],
  CURLOPT_POSTFIELDS => json_encode($data),
  CURLOPT_TIMEOUT => 10,
]);

上記は以下と同じことです。

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  'Content-Type: application/json; charset=UTF-8',
  'Accept: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
ヘッダー

CURLOPT_HTTPHEADER を使って HTTP ヘッダーを送信することができます。

ヘッダーが1行だけでも、CURLOPT_HTTPHEADER では配列で Name: value の形式で指定する必要があります。

// 1行だけでも配列で指定
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  'Content-Type: application/json'
]);
// または curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);

以下は代表的な HTTP ヘッダーとその基本的な役割です。

ヘッダー名 用途
Accept クライアントが受け取りたいレスポンスの形式を示す(例:application/json)
Content-Type クライアントが送信するデータの形式を示す(例:application/json や application/x-www-form-urlencoded)
Authorization 認証情報(APIキー、Bearer トークンなど)を送信する
User-Agent ブラウザやクライアントの名前をサーバーに伝える(任意)

カスタムヘッダー

HTTP 通信では「独自のヘッダー(カスタムヘッダー)」を自由に定義して送信することが可能です。例えば、X-API-Key はその一例で、API 認証などの目的でよく使われています。

カスタムヘッダーとは、標準で定義されていない HTTP ヘッダーで、独自の用途で送信されるものです。

例:

  • X-API-Key: abc123xyz
  • X-Client-Version: 1.2.3
  • X-User-Token: xyz789

昔は「X-」プレフィックスを付けるのが慣例でしたが、現在は RFC により X- は非推奨とされ、独自ヘッダーでも X- を省く場合が増えています。

curl_setopt($ch, CURLOPT_HTTPHEADER, [
  'X-API-Key: your_api_key_here',  // カスタムヘッダー
  'Content-Type: application/json',
]);

カスタムヘッダー使用の注意点

  • クライアントとサーバーの両方がそのヘッダーの意味を理解している必要があります
  • CORS(クロスオリジン)通信時には、カスタムヘッダーが「プリフライトリクエスト」に影響することがあるため、サーバー側でも明示的に許可する必要があります

JSON を POST する場合

以下は cURL でヘッダーを指定して JSON データを POST 送信する例です。

POST 送信するので CURLOPT_POST に true を設定し、CURLOPT_POSTFIELDS に JSON エンコードした JSON 文字列を指定して送信しています。

ヘッダーでは、Content-Type に送信するデータの形式として application/json を、Accept に受け取りたいレスポンスの形式として application/json を指定しています。

$data = [
  'title' => 'Foo',
  'body' => 'Lorem ipsum dolor sit amet consectetur adipisicing elit.',
  'userId' => 1,
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://jsonplaceholder.typicode.com/posts");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); // JSON エンコードして送信

// ヘッダーを設定
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  'Content-Type: application/json',        // 送信データの形式
  'Accept: application/json',              // 期待するレスポンス形式
]);

$response = curl_exec($ch);
curl_close($ch);
echo $response;

オプションの設定は curl_setopt_array() を使って以下のように記述することもできます。

curl_setopt_array($ch, [
  CURLOPT_URL => "https://jsonplaceholder.typicode.com/posts",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST => true,
  // ヘッダーを設定
  CURLOPT_HTTPHEADER => [
    'Content-Type: application/json',   // 送信データの形式
    'Accept: application/json',  // 期待するレスポンス形式
  ],
  CURLOPT_POSTFIELDS => json_encode($data),  // JSON エンコードして送信
]);

フォーム形式で POST する場合

以下は送信するデータ(連想配列)を http_build_query() を使ってクエリ文字列(URL エンコード形式)に変換して送信する場合の例です。

ヘッダーでは、Content-Type に送信するデータの形式として application/x-www-form-urlencoded を指定します。

$data = [
  'title' => 'Foo',
  'body' => 'Lorem ipsum dolor sit amet consectetur adipisicing elit.',
  'userId' => 1,
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://jsonplaceholder.typicode.com/posts");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); // クエリ形式で送信

// ヘッダーを設定
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  'Content-Type: application/x-www-form-urlencoded',   // 送信データの形式
  'Accept: application/json',                          // 期待するレスポンス形式
]);

$response = curl_exec($ch);
curl_close($ch);
echo $response;
Content-Type に指定できる値(送信データの形式)

以下は代表的な Content-Type の値です。

用途・説明
application/json JSON形式のデータ(APIで最も一般的)
application/json; charset=UTF-8 JSON は UTF-8 が標準ですが、日本語を含むデータを POST する場合や、より堅牢な実装を目指す場合には付けておくのが安心
application/x-www-form-urlencoded フォーム形式のデータ(key=value&... 形式、POSTフォームに相当)
multipart/form-data ファイルアップロード(HTMLフォームの enctype="multipart/form-data")
text/plain プレーンテキスト形式
application/xml XML形式のデータ
application/octet-stream バイナリデータの送信(ファイルなど)
text/html HTML文書(通常は送信に使われない)
Accept に指定できる値(受け取りたいレスポンスの形式)

以下は代表的な Accept の値です。

用途・説明
application/json JSONレスポンスを要求(APIで一般的)
application/xml XMLレスポンスを要求
text/html HTMLレスポンス(ブラウザのデフォルト)
text/plain プレーンテキストレスポンスを要求
*/* すべての形式を受け入れる(何でもOK)

curl_exec()

curl_exec() は、初期化して設定済みの cURL セッションを実行する関数で、実際の HTTP リクエストを送信し、レスポンスを受け取る処理を行います。

この関数は、cURL セッションを初期化し、 オプションを全て設定した後にコールする必要があります。

curl_exec(CurlHandle $handle): string|bool
  • $handle は curl_init() で生成した cURL セッションハンドル。
  • 実行されたリクエストの結果は、設定により次のように返されます:
    • CURLOPT_RETURNTRANSFER が true → レスポンス文字列。失敗時は false
    • CURLOPT_RETURNTRANSFER が false → 直接出力され、戻り値は true/false

戻り値の解釈

  • 成功時: レスポンス文字列、または true を返す
  • 失敗時: false を返す

curl_exec() が false を返す理由

curl_exec() が false を返す主な理由は以下の 3 つのケースに分類されます。

  1. 通信レベルのエラー(cURL がサーバーにアクセスできなかった)
    • DNS 解決失敗:CURLE_COULDNT_RESOLVE_HOST (6)
    • 接続失敗:CURLE_COULDNT_CONNECT (7)
    • タイムアウト
      • 接続:CURLE_OPERATION_TIMEOUTED (28)
      • 応答読み込み:CURLE_RECV_ERROR (56) など
    • SSL エラー:CURLE_SSL_CONNECT_ERROR (35), CURLE_PEER_FAILED_VERIFICATION (51)
  2. 通信前の設定ミス(リクエストの構成が不正)
    • URL 書式不正:CURLE_URL_MALFORMAT (3)
    • POST フィールドの不正、読み込みエラー:CURLE_READ_ERROR (26)
    • ログイン失敗(FTP/SMTP など):CURLE_LOGIN_DENIED (67)
    • ヘッダーサイズオーバー:CURLE_TOO_MANY_REDIRECTS (47) など
  3. CURLOPT_FAILONERROR による false(意図的な false)※以下参照
    • curl_setopt() で CURLOPT_FAILONERROR を true に設定すると、通信自体は成功しても、HTTP ステータスコード(例: 404, 500)が 400 以上の場合に false を返します。この場合のエラーコードは、CURLE_HTTP_RETURNED_ERROR (22)

※ 404 Not Found や 500 Internal Server Error

デフォルトでは、HTTP ステータスコードが 400 以上(例: 404, 500)の場合や 200 未満の場合、通信自体は成功したとみなして、curl_exec() は false を返しません。

そのため、エラー判定をする際は、curl_exec() が false を返すかだけでなく、ステータスコードも確認する必要があります。詳細は エラーハンドリング を参照。

curl_close()

curl_close() は、cURL セッションを閉じてリソースを解放する関数で、cURL ハンドルも削除されます。

curl_init() で作成した $ch を使い終わったら、curl_close($ch) を呼ぶのが基本です(※ PHP 7.x 以前)。

curl_close(CurlHandle $handle): void
  • $handle は curl_init() で生成した cURL セッションハンドル。
  • 戻り値:なし(void)

PHP 8.0 以降の場合

バージョン curl_init() の戻り値(cURL ハンドル) curl_close() の役割
PHP 7.x 以前 リソース(resource 型) リソースを明示的に開放する
PHP 8.0 以降 オブジェクト(CurlHandle クラス) 不要。オブジェクト破棄時に自動で開放される

PHP 8.0 以降では、cURL ハンドルはオブジェクトなので、PHP のガベージコレクション(自動メモリ解放)により、自動的に解放されます。そのため、curl_close() を呼んでも実際には何も処理されません。

curl_close() は今でも書くべきか?

以下の理由から書いておくのが推奨されます。

  • PHP 7.x 以前との互換性のため
  • コードの明示性・明快さのため(意図が読みやすい)

PHP 8 以降では curl_close() は実質「何もしない関数」ですが、書いても害はなく、保守性が上がるため推奨されます。PHP 8.0 以降専用のコードを書くケースでは省略も可能です。

エラーハンドリング

cURL でエラーハンドリングを行うには、「通信エラー」や「HTTP ステータスコードによるエラー」をキャッチして対応します。以下が基本的なエラーハンドリングの流れになります。

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://example.com/api");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);

// curl_exec() の戻り値で通信成功かどうか判定(ネットワーク、タイムアウトなど)
if ($response === false) {
  // 明確な失敗。cURLレベルの通信障害があった可能性が高い
  echo "cURL エラー番号: " . curl_errno($ch) . "<br>";
  echo "エラーメッセージ: " . curl_error($ch);
} else {
  // 成功しても HTTP エラーの可能性があるのでステータスコード(整数値)をチェック
  $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  if ($httpCode < 200 || $httpCode >= 400) {
    // 200 未満または400以上はエラーとみなす
    echo "HTTP エラーまたは予期しないステータス: $httpCode";
  } else {
    echo "成功: $response";
  }
}

curl_close($ch);
  1. curl_exec() の戻り値でエラーが起きたか確認。
  2. curl_error() や curl_errno() でエラー内容やエラー番号を確認(表示)
  3. curl_getinfo() で HTTP ステータスコードを確認し、200 未満または400以上なら HTTP エラー

まず、curl_exec() の戻り値を確認し、false ならエラーと判定できます。

但し、curl_exec() が false でなくても、HTTP 的には 404 Not Found や 500 Internal Server Error などのエラーである可能性があるので、別途 curl_getinfo() でステータスコードをチェックする必要があり、ステータスコードが 400 以上の場合は HTTP エラーと判定します。

curl_getinfo($ch, CURLINFO_HTTP_CODE) で返されるステータスコードは整数(int)です。

HTTP ステータスコード分類表
範囲 名称 概要
100-199 Informational(情報) リクエストは受け取ったが処理は未完了
200-299 Success(成功) リクエストは正常に処理された
300-399 Redirection(リダイレクト) 別URLへ移動するよう指示
400-499 Client Error(クライアントエラー) リクエストに問題あり
500-599 Server Error(サーバーエラー) サーバー側のエラー
よく使うステータスコードの例(代表的なもの)
コード 意味 用途・備考
200 OK 一般的な成功
201 Created リソース作成成功(POST)
204 No Content 成功だが本文なし(DELETE等)
301 Moved Permanently 恒久的なリダイレクト
302 Found 一時的なリダイレクト
400 Bad Request 構文ミスや不正パラメータ
401 Unauthorized 認証が必要
403 Forbidden アクセス権がない
404 Not Found リソースが存在しない
500 Internal Server Error サーバー内部エラー
502 Bad Gateway ゲートウェイやプロキシのエラー
503 Service Unavailable 一時的な利用不可

curl_errno()

curl_errno() は PHP の cURL 関数でエラーが発生した際に、エラー番号(int型)を取得する関数です。

curl_errno(CurlHandle $handle): int
  • $ch は curl_init() で取得した cURL セッションハンドルです。
  • 戻り値は整数のエラーコード。0 はエラーなし。それ以外は何らかのエラーを意味します。
  • エラーの内容(メッセージ)を知りたい場合は curl_error($ch) を使います。
代表的なエラーコード一覧(一部)
コード 定数名 意味
0 CURLE_OK 正常終了
6 CURLE_COULDNT_RESOLVE_HOST ホスト名解決失敗
7 CURLE_COULDNT_CONNECT 接続に失敗
28 CURLE_OPERATION_TIMEDOUT 処理がタイムアウト
35 CURLE_SSL_CONNECT_ERROR SSL 接続エラー
60 CURLE_SSL_CACERT CA証明書の検証に失敗
67 CURLE_LOGIN_DENIED FTP/SMTP などのログイン失敗

curl_error()

curl_error() は PHP の cURL 関数において、直近のエラーのメッセージ(文字列)を取得する関数です。

curl_error(CurlHandle $handle): string
  • $ch は curl_init() で作成した cURL セッションハンドル。
  • 戻り値はエラーメッセージ(string)。エラーがなければ空文字 '' を返します。

curl_errno($ch) が 0 以外(=何らかのエラー)だったときに、その具体的な原因を知るために使います。

curl_getinfo()

curl_getinfo() は PHP の cURL セッションに関する実行結果の詳細情報を取得するための関数です。

curl_getinfo(CurlHandle $handle, ?int $option = null): mixed
  • $ch は curl_init() で作成した cURL セッションハンドル。
  • $option:
    • 省略:すべての情報を連想配列で取得
    • 指定:特定の情報のみ取得(例:CURLINFO_HTTP_CODE)
  • 戻り値は $option を指定した場合はその値を返し、省略した場合はすべての情報の要素をもつ連想配列を返します。失敗した場合は false を返します。

情報は curl_exec() 実行後 でなければ取得できません。

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://example.com/");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);

// HTTP ステータスコードを取得
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// レスポンスヘッダーの Content-Type を取得
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
// DNS解決にかかった時間
$dnsTime = curl_getinfo($ch, CURLINFO_NAMELOOKUP_TIME);
// 接続にかかった時間
$connectTime = curl_getinfo($ch, CURLINFO_CONNECT_TIME);
// リクエスト全体にかかった時間
$totalTime = curl_getinfo($ch, CURLINFO_TOTAL_TIME);

// 結果出力(例)
echo "HTTP ステータス: $httpCode<br>";  // HTTP ステータス: 200
echo "レスポンス Content-Type: $contentType<br>";  // レスポンス Content-Type: text/html
echo "DNS解決にかかった時間: $dnsTime<br>";  // DNS解決にかかった時間: 0.000887
echo "接続にかかった時間: $connectTime<br>";  // 接続にかかった時間: 0.13797
echo "リクエスト全体にかかった時間: $totalTime<br>";  // リクエスト全体にかかった時間: 0.640814

以下は curl_getinfo() で取得できる代表的な情報一覧です。

これらの情報を使ってレスポンスのデータ形式(Content-Type)を調べたり、パフォーマンス測定、リダイレクト検知などにも使えます

定数 意味 戻り値の型
CURLINFO_HTTP_CODE HTTPステータスコード int 200, 201, 404, 500 など
CURLINFO_EFFECTIVE_URL 実際にアクセスされたURL(リダイレクト後を含む) string https://example.com/final
CURLINFO_CONTENT_TYPE レスポンスのContent-Type string | null application/json; charset=utf-8
CURLINFO_TOTAL_TIME リクエスト全体にかかった時間(秒) float 0.235
CURLINFO_NAMELOOKUP_TIME DNS解決にかかった時間(秒) float 0.005
CURLINFO_CONNECT_TIME 接続(DNS解決後のTCP接続確立)にかかった時間(秒) float 0.012
CURLINFO_STARTTRANSFER_TIME 転送開始までの時間(秒) float 0.020
CURLINFO_REDIRECT_COUNT リダイレクト回数 int 2
CURLINFO_REDIRECT_URL リダイレクト先URL(もしあれば) string | null https://example.com/next
CURLINFO_SIZE_DOWNLOAD ダウンロードされたデータのサイズ(バイト) float 5123.0
CURLINFO_HEADER_SIZE レスポンスヘッダーのバイト数 int 342

CURLINFO_HTTP_CODE の値は、成功時は 200, 201, 404, 500 など、HTTP のステータスコード(int型)が返りますが、DNS 解決失敗時やネットワーク接続失敗時(タイムアウトなど)には 0 になります。また、意図せず curl_close() した後に取得すると null になることがあります。

レスポンスが JSON の場合

以下はレスポンスが JSON の場合の GET リクエストの例で、レスポンスの JSON 文字列を json_decode() で第2引数に true を指定して PHP の配列に変換しています。

json_decode() はエラーが起きても警告や例外を出さず、false を返すのみなので、必ず json_last_error() で検証するのが推奨される手順です。json_last_error_msg() を使うとエラー文字列を取得できます。

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://jsonplaceholder.typicode.com/posts/1");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);

if ($response === false) {
  echo "cURL エラー番号: " . curl_errno($ch) . "<br>";
  echo "エラーメッセージ: " . curl_error($ch);
} else {
  $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  // 成功時の処理
  if ($httpCode === 200) {
    // JSON 文字列を PHP の配列に変換
    $json = json_decode($response, true);
    // JSON を検証
    if (json_last_error() === JSON_ERROR_NONE) {
      // JSON 解析でエラーなし
      print_r($json); // または echo $json['body']; など
    } else {
      echo "JSONの解析に失敗しました: " . json_last_error_msg();
    }
  } else {
    echo "HTTP エラーまたは予期しないステータス: $httpCode";
  }
}

curl_close($ch);

JSON が不正になる可能性(典型例)

  • レスポンスが HTML やテキスト(API 側のエラーメッセージなど)だった。
  • 文字コードに問題があった。
  • ネットワーク障害などで部分的な JSON が返った。

リクエストサンプル

cURL を使った GET リクエストと POST リクエストのサンプルです。

GET リクエスト

以下は、{JSON} Placeholder というテスト用の JSON データを返す API にアクセスして取得したレスポンスを出力する例です。「/posts/1」にアクセスすると ID が1の投稿のデータ(JSON)を取得できます。

この例の場合、{JSON} Placeholder は Accept ヘッダーが未指定でも JSON を返すように設計されているので、Accept ヘッダーは省略可能ですが、本番環境・汎用的なコードでは Accept ヘッダーを付けておくのが無難です。

$url = 'https://jsonplaceholder.typicode.com/posts/1';

$ch = curl_init($url);

// オプション設定
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  // 結果を文字列で受け取る
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Accept: application/json']); // ヘッダー指定
curl_setopt($ch, CURLOPT_TIMEOUT , 10);  // タイムアウト秒数

$response = curl_exec($ch);

// ステータスコード取得
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

// エラー確認
if ($response === false) {
  echo 'cURL エラー: ' . curl_error($ch);
} elseif ($httpCode === 200) {
  // JSON 文字列を PHP の配列に変換
  $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ステータス " . $httpCode . "\n";
}

curl_close($ch);
URL パラメータ

以下は http_build_query() を使ってパラメータをクエリ文字列に変換して URL に追加してリクエストする例です。また、カスタムヘッダー(X-API-Key)で API キーを指定しています。

$baseUrl = "https://example.com/wp/wp-json/wp/v2/posts";

// URL に追加するクエリパラメータ
$params = [
  '_fields' => 'title,link',
  'per_page' => 3,
];

// パラメータをクエリ文字列に変換して URL に追加
$url = $baseUrl . '?' . http_build_query($params);

$api_key = 'abc123def456'; // API キー(実際の運用ではAPIキーを別ファイルで管理する)

// cURL セッションを初期化
$ch = curl_init($url);

// オプションをまとめて設定
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    "X-API-Key: $api_key",
    "Content-Type: application/json",
  ],
  CURLOPT_TIMEOUT => 10,
]);

// 実行とエラーチェック
$response = curl_exec($ch);
$curlError = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

// セッション終了
curl_close($ch);

// 結果処理
if ($response === false) {
  echo "cURLエラー: $curlError";
} elseif ($httpCode === 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ステータス $httpCode\n";
}

API キーを Authorization ヘッダーで指定する場合は、例えば以下のように CURLOPT_HTTPHEADER オプションを変更します。以下は Bearer 認証形式の場合の例です。

CURLOPT_HTTPHEADER => [
  "Authorization: Bearer $api_key",  // "X-API-Key: $api_key" から変更
  "Content-Type: application/json",
],
APIキーを別ファイルで管理

前述の例では、API キーをコードに直接記述していましたが、以下は config というフォルダを作成し、その中に .htaccess と API キーを定義した config.php というファイルを作成して管理する例です。

/public_html/xxxx/
  ├── index.php  // PHP を記述するファイル
  ├── config/
  │   ├── .htaccess   // 外部アクセス禁止にする
  │   └── config.php  // APIキーなどを含む

.htaccess に以下を記述し、Web 経由では config.php にアクセスできないようにします。

# /config/.htaccess
Order deny,allow
Deny from all

Apache 2.4 以降では以下のように記述します。

# Apache 2.4+
Require all denied

config.php に API キーを定義します。

<?php
// API キーを定義(絶対に何も出力しない)
define('API_KEY', 'abc123def456');

API キーを require_once で読み込みます。

以下では curl_setopt_array() の代わりに curl_setopt() を使っていますが、特に意味はありません。

// API キーを読み込む
require_once __DIR__ . '/config/config.php';

$baseUrl = "https://example.com/wp/wp-json/wp/v2/posts";

$params = [
  '_fields' => 'title,link',
  'per_page' => 3,
];

$url = $baseUrl . '?' . http_build_query($params);

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER,  [
  "X-API-Key: " . API_KEY,  // 読み込んだ API キーを指定
  "Content-Type: application/json",
]);
curl_setopt($ch, CURLOPT_TIMEOUT , 10);

$response = curl_exec($ch);
$curlError = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($response === false) {
  echo "cURLエラー: $curlError";
} elseif ($httpCode === 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ステータス $httpCode\n";
}

POST リクエスト

以下は JSON 形式、URL エンコード形式でデータを送信する POST リクエストのサンプルです。

JSON 形式

以下は JSON 形式(application/json)でデータを送信するサンプルです。

JSON 形式でデータを送信するので、Content-Type に application/json を指定し、Accept ヘッダー(受け取りたいレスポンスの形式)には application/json を指定しています。

また、JSON は UTF-8 が標準ですが、この例では Content-Type: application/json; charset=UTF-8 のように charset を明示的に指定しています(charset=UTF-8 は省略可能)。

成功判定はステータスコードが 201 Created かどうかで確認しています。

$url = 'https://jsonplaceholder.typicode.com/posts';

$data = [
  'title' => 'Foo',
  'body' => 'Lorem ipsum dolor sit amet consectetur adipisicing elit.',
  'userId' => 1,
];

// JSON に変換
$jsonData = json_encode($data);

// cURL 初期化
$ch = curl_init();

// オプション設定
curl_setopt_array($ch, [
  CURLOPT_URL => $url,
  CURLOPT_POST => true,  // POST メソッドを使用
  CURLOPT_POSTFIELDS => $jsonData,  // JSON を指定
  CURLOPT_HTTPHEADER => [
    'Content-Type: application/json; charset=UTF-8',  // Content-Type: application/json を指定
    'Accept: application/json',
  ],
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_TIMEOUT => 10,
]);

// 実行
$response = curl_exec($ch);

// ステータスコード取得
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

// エラーハンドリング
if ($response === false) {
  echo "cURL エラー: " . curl_error($ch);
} elseif ($httpCode === 201) { // JSONPlaceholder は成功時に 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ステータス $httpCode\n";
  echo "レスポンス内容:\n$response";
}

curl_close($ch);
URL エンコード形式

以下は URL エンコード形式でデータを送信する POST リクエストのサンプルです。

送信するデータを http_build_query() で URL エンコード形式に変換して content に指定しています。この例では RFC3986準拠のエンコードでクエリ文字列を生成しています。

CURLOPT_POSTFIELDS に URL エンコードされた文字列を渡すと、自動で Content-Type を application/x-www-form-urlencoded に設定するケースもありますが、明示的に設定するのが安全です。

また、Content-Length も省略可能ですが、サーバーによっては必要な場合があるため指定してあります。

https://httpbin.org/ はテスト用のサービスです。

$url = 'https://httpbin.org/post'; // テスト用エコーサービス

// 送信データ(連想配列)
$data = [
  'name' => 'John Doe',
  'email' => 'john@example.com',
  'message' => 'こんにちは',
];

// 送信データを URL エンコード形式に変換
$postData = http_build_query($data, '', '&', PHP_QUERY_RFC3986);

// cURL 初期化
$ch = curl_init($url);

// オプション設定
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST => true,
  CURLOPT_POSTFIELDS => $postData,
  CURLOPT_HTTPHEADER => [
    'Content-Type: application/x-www-form-urlencoded',
    'Content-Length: ' . strlen($postData),
  ],
  CURLOPT_TIMEOUT => 10,
]);

// 実行
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

// エラーチェック
if ($response === false) {
  echo "cURL エラー: " . curl_error($ch);
} elseif ($httpCode >= 200 && $httpCode < 300) {
  echo "送信成功:\n";
  echo $response;
} else {
  echo "送信失敗: HTTPステータス $httpCode\n";
  echo "レスポンス:\n$response";
}

// 終了処理
curl_close($ch);

補足

CURLOPT_POSTFIELDS に URL エンコードされた文字列 を渡した場合、PHP の cURL は自動的に Content-Type: application/x-www-form-urlencoded を付与します。

同様に、Content-Length も自動で計算されます。

つまり、オプション設定を以下のように CURLOPT_HTTPHEADER を省略しても問題なく動作します。

curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST => true,
  CURLOPT_POSTFIELDS => $postData,
  CURLOPT_TIMEOUT => 10,
]);

但し、以下の場合は CURLOPT_HTTPHEADER を自分で指定する必要があります。

  • Content-Type を application/json や multipart/form-data などに変更したい場合
  • 特定の API キーや認証トークンを Authorization ヘッダーなどで送信したい場合
レスポンスの Content-Type を取得

$response のデータ形式(文字列の構造や内容)は、サーバー側の実装に依存します。

この例の https://httpbin.org/post のエンドポイントの場合、POST された内容を JSON 形式で返すことがわかっているので、$response を json_decode() すれば、配列として扱えます。

必要に応じて curl_getinfo() でレスポンスのデータ形式を確認することができます。

以下はレスポンスの Content-Type を取得するコード例です。

curl_getinfo($ch, CURLINFO_CONTENT_TYPE) でレスポンスの Content-Type を取得し、JSON 形式かどうかを strpos($contentType, 'application/json') でチェックしています。

$ch = curl_init('https://httpbin.org/post');

$data = [
  'name' => 'John Doe',
  'email' => 'john@example.com',
  'message' => 'こんにちは',
];

$postData = http_build_query($data, '', '&', PHP_QUERY_RFC3986);

curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST => true,
  CURLOPT_POSTFIELDS => $postData,
  CURLOPT_TIMEOUT => 10,
]);

$response = curl_exec($ch);

// レスポンスヘッダーの Content-Type を取得
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);

$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

curl_close($ch);

// 結果出力(確認用)
echo "HTTP ステータス: $httpCode<br>";
echo "レスポンス Content-Type: $contentType<br>";

if ($httpCode >= 200 && $httpCode < 300) {
  // JSON 形式の場合はデコードして出力
  if (strpos($contentType, 'application/json') !== false) {
    $json = json_decode($response, true);
    if (json_last_error() === JSON_ERROR_NONE) {
      echo "<pre>";
      print_r($json);
      echo "</pre>";
    } else {
      echo "JSON解析エラー: " . json_last_error_msg();
    }
  } else {
    echo "レスポンス:\n$response";
  }
} else {
  echo "送信失敗";  // エラーレスポンスも表示する場合は echo "送信失敗:\n$response";
}

httpbin.org

https://httpbin.org は、HTTP リクエストのテストとデバッグ用に使える無料の Web サービスです。開発者が HTTP リクエストの送信や受信内容を確認するために利用することができます。

以下は主な特徴です。

  • リクエスト内容のエコー(反映)
    送信したデータ(ヘッダー、クエリパラメータ、ボディなど)をそのまま返します。
  • さまざまな HTTP メソッドのテスト
    GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS などすべてに対応。
  • 任意のステータスコードを返すエンドポイント
    /status/404 や /status/500 のように、指定したステータスコードで応答させることができます。
  • レスポンスの遅延テスト
    /delay/3 のように遅延させたレスポンスを返すことで、タイムアウト処理のテストが可能です。

以下は curl 経由のリクエストで API キーが正しく送信されているか確認する例です。

$apiKey = 'abc123xyz456'; // API キーを指定
$url = 'https://httpbin.org/headers'; // 送信されたヘッダーをそのまま返してくれる

$ch = curl_init($url);

curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'X-API-Key: ' . $apiKey,
    'Accept: application/json',
  ],
]);

$response = curl_exec($ch);

if ($response === false) {
  echo "cURL エラー: " . curl_error($ch);
} else {
  $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  echo "HTTP ステータス: $httpCode\n";
  // JSON 文字列を整形(整形済みの形式で見やすく)して出力(必要に応じて)
  $prettyJson = json_encode(json_decode($response), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
  echo "<pre>$prettyJson</pre>";
}

curl_close($ch);

以下は実行結果の例です。レスポンスに "X-Api-Key": "abc123xyz456" が含まれていれば、正しく送信できていると判断できます。但し、送信したものをそのまま返しているだけなので、内容が正しいかどうかを判定できるわけではありません。

HTTP ステータス: 200
{
  "headers": {
    "Accept": "application\/json",
    "Host": "httpbin.org",
    "X-Amzn-Trace-Id": "Root=1-681821cc-0e26ea8e70345faa1b45bcaf",
    "X-Api-Key": "abc123xyz456"
  }
}

リクエスト先に https://httpbin.org/anything を使っても以下のようにヘッダー+ボディが返ります。

HTTP ステータス: 200
{
  "args": {},
  "data": "",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "application\/json",
    "Host": "httpbin.org",
    "X-Amzn-Trace-Id": "Root=1-68182307-30f0c5e45d0d0df34c07c9d0",
    "X-Api-Key": "abc123xyz456"
  },
  "json": null,
  "method": "GET",
  "origin": "12.3.45.678",
  "url": "https:\/\/httpbin.org\/anything"
}

自分の IP アドレス

また、ブラウザで https://httpbin.org/ip にアクセスすると自分の IP アドレスが返されます。

{
  "origin": "12.3.45.678"
}