タイトル: curl HTTP通信によるリクエスト
SEOタイトル: PHP cURL の使い方総まとめ — GET / POST / ヘッダ / タイムアウト / エラー処理
| この記事の要点 |
|
最小の GET リクエスト
$ch = curl_init('https://api.example.com/users/1');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 戻り値で受け取る (echo しない)
curl_setopt($ch, CURLOPT_TIMEOUT, 10); // 全体 10 秒で中断
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); // 接続段階 5 秒
$body = curl_exec($ch);
if ($body === false) {
$err = curl_error($ch);
$errno = curl_errno($ch);
error_log("cURL error ($errno): $err");
curl_close($ch);
throw new RuntimeException("通信失敗: $err");
}
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status >= 400) {
throw new RuntimeException("HTTP $status: $body");
}
$data = json_decode($body, true);
POST リクエスト(フォーム送信)
$ch = curl_init('https://api.example.com/login');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'email' => 'taro@example.com',
'password' => 'secret',
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/x-www-form-urlencoded',
'Accept: application/json',
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
JSON を POST する
$payload = json_encode([
'title' => 'Hello',
'body' => 'World',
'tags' => ['php', 'curl'],
], JSON_UNESCAPED_UNICODE);
$ch = curl_init('https://api.example.com/posts');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . $token,
'Accept: application/json',
],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
主要オプション一覧
| オプション | 意味 | 推奨値 |
|---|---|---|
CURLOPT_RETURNTRANSFER | 戻り値で受け取る | true 必須 |
CURLOPT_TIMEOUT | 全体タイムアウト | 10〜30 秒 |
CURLOPT_CONNECTTIMEOUT | 接続タイムアウト | 5 秒 |
CURLOPT_FOLLOWLOCATION | 3xx リダイレクト追従 | true |
CURLOPT_MAXREDIRS | リダイレクト最大回数 | 5 |
CURLOPT_HTTPHEADER | リクエストヘッダ | 配列 |
CURLOPT_USERAGENT | UA 文字列 | サービス名 + URL |
CURLOPT_SSL_VERIFYPEER | SSL 証明書検証 | true(本番) |
CURLOPT_HEADER | レスポンスヘッダ込で取得 | 必要時 true |
CURLOPT_POST | POST モード | true |
CURLOPT_POSTFIELDS | POST 本文 | 文字列 or 配列 |
CURLOPT_CUSTOMREQUEST | PUT / DELETE 等の指定 | 'PUT' |
レスポンスヘッダを取得する
$ch = curl_init('https://example.com/');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true, // ヘッダもボディに含めて返す
]);
$response = curl_exec($ch);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$rawHeader = substr($response, 0, $headerSize);
$body = substr($response, $headerSize);
curl_close($ch);
// ヘッダを連想配列に
$headers = [];
foreach (explode("\r\n", trim($rawHeader)) as $line) {
if (strpos($line, ':') !== false) {
[$k, $v] = explode(':', $line, 2);
$headers[strtolower(trim($k))] = trim($v);
}
}
$contentType = $headers['content-type'] ?? null;
エラー処理の網羅版
function httpGet(string $url): array
{
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
CURLOPT_USERAGENT => 'MyApp/1.0',
]);
$body = curl_exec($ch);
if ($body === false) {
$msg = sprintf('cURL %d: %s', curl_errno($ch), curl_error($ch));
curl_close($ch);
throw new RuntimeException($msg);
}
$status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status >= 400) {
throw new RuntimeException("HTTP $status returned");
}
return ['status' => $status, 'body' => $body];
}
リトライロジック
function httpGetWithRetry(string $url, int $maxRetry = 3): string
{
$attempt = 0;
while (true) {
$attempt++;
try {
return httpGet($url)['body'];
} catch (RuntimeException $e) {
if ($attempt >= $maxRetry) throw $e;
usleep(min(2 ** $attempt, 8) * 1_000_000); // 指数バックオフ
}
}
}
Guzzle / Http ファサードとの比較
// === 同じことを Guzzle で ===
use GuzzleHttp\Client;
$client = new Client(['timeout' => 10]);
$res = $client->post('https://api.example.com/posts', [
'json' => ['title' => 'Hello'],
'headers' => ['Authorization' => "Bearer $token"],
]);
$body = (string) $res->getBody();
$status = $res->getStatusCode();
// === Laravel の Http ファサード ===
use Illuminate\Support\Facades\Http;
$res = Http::withToken($token)
->timeout(10)
->retry(3, 100)
->post('https://api.example.com/posts', ['title' => 'Hello']);
$res->json(); // 配列化
$res->status(); // 200
$res->successful(); // true/false
FAQ
Q: PUT / DELETE はどう送る?
A: CURLOPT_CUSTOMREQUEST => 'PUT'(または DELETE)。PUT で本文を送りたい場合は CURLOPT_POSTFIELDS も併用。
Q: HTTPS 検証エラー(cURL error 60)
A: 古い CA バンドル。CURLOPT_CAINFO に 最新の cacert.pem のパスを指定するか、PHP の curl.cainfo を設定。verify=false にしての回避は本番では絶対やらない。
Q: マルチパート(ファイル)送信
A: CURLOPT_POSTFIELDS に ['file' => new CURLFile('/path/to/x.png')] を渡す。Content-Type ヘッダは自動付与に任せる。