首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP file_get_contents POST 請求完整指南:Stream Context、JSON/表單、超時、錯誤處理與 Guzzle 替代

     編輯:PHP 相關知識

file_get_contents 配合 Stream Context 可發送 POST 請求,無需額外擴展。但功能有限、錯誤處理繁瑣。本文提供完整參數範例、JSON/表單雙模式、超時控制、重試邏輯、最後推薦生產環境使用 Guzzle 或 Http\Client (PSR-18)。

基本 POST (application/x-www-form-urlencoded)

$url = 'https://api.example.com/endpoint';
$data = ['name' => 'Joe', 'website' => 'www.example.com'];

$options = [
    'http' => [
        'method' => 'POST',
        'header' => "Content-Type: application/x-www-form-urlencoded\r\n",
        'content' => http_build_query($data),
        'timeout' => 10, // 秒
        'ignore_errors' => true, // 取得 4xx/5xx 回應內容
    ],
];

$context = stream_context_create($options);
$response = @file_get_contents($url, false, $context);

if ($response === false) {
    $error = error_get_last();
    throw new RuntimeException("Request failed: {$error['message']}");
}

$httpCode = 0;
if (isset($http_response_header)) {
    foreach ($http_response_header as $header) {
        if (stripos($header, 'HTTP/') === 0) {
            $httpCode = (int)explode(' ', $header)[1];
            break;
        }
    }
}

if ($httpCode >= 400) {
    throw new RuntimeException("HTTP $httpCode: $response");
}

$result = json_decode($response, true);

JSON POST (application/json)

$data = ['name' => 'Joe', 'website' => 'www.example.com'];
$json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

$options = [
    'http' => [
        'method' => 'POST',
        'header' => "Content-Type: application/json\r\nContent-Length: " . strlen($json),
        'content' => $json,
        'timeout' => 10,
        'ignore_errors' => true,
    ],
];

$context = stream_context_create($options);
$response = @file_get_contents($url, false, $context);
// ... 同樣錯誤處理

完整封裝函式 (支援重試、自訂 Header、代理)

function httpPost(string $url, array $data, array $options = []): array
{
    $defaults = [
        'timeout' => 10,
        'json' => false, // true=JSON, false=form
        'headers' => [],
        'proxy' => null, // 'tcp://proxy:8080'
        'retries' => 0,
        'retry_delay' => 100000, // microseconds
    ];
    $opts = array_merge($defaults, $options);

    $content = $opts['json'] ? json_encode($data, JSON_UNESCAPED_UNICODE) : http_build_query($data);
    $contentType = $opts['json'] ? 'application/json' : 'application/x-www-form-urlencoded';

    $headers = [
        "Content-Type: $contentType",
        "Content-Length: " . strlen($content),
    ];
    foreach ($opts['headers'] as $k => $v) {
        $headers[] = "$k: $v";
    }

    $contextOpts = [
        'http' => [
            'method' => 'POST',
            'header' => implode("\r\n", $headers) . "\r\n",
            'content' => $content,
            'timeout' => $opts['timeout'],
            'ignore_errors' => true,
        ],
    ];

    if ($opts['proxy']) {
        $contextOpts['http']['proxy'] = $opts['proxy'];
        $contextOpts['http']['request_fulluri'] = true;
    }

    $lastError = null;
    for ($attempt = 0; $attempt <= $opts['retries']; $attempt++) {
        $context = stream_context_create($contextOpts);
        $response = @file_get_contents($url, false, $context);

        if ($response !== false) {
            $httpCode = 0;
            if (isset($http_response_header)) {
                foreach ($http_response_header as $h) {
                    if (stripos($h, 'HTTP/') === 0) {
                        $httpCode = (int)explode(' ', $h)[1];
                        break;
                    }
                }
            }
            if ($httpCode < 400) {
                return [
                    'code' => $httpCode,
                    'body' => $response,
                    'headers' => $http_response_header ?? [],
                ];
            }
            $lastError = "HTTP $httpCode: $response";
        } else {
            $lastError = error_get_last()['message'] ?? 'Unknown error';
        }

        if ($attempt < $opts['retries']) {
            usleep($opts['retry_delay']);
        }
    }

    throw new RuntimeException("Request failed after {$opts['retries']} retries: $lastError");
}

// 使用
try {
    $result = httpPost('https://api.example.com/users', ['email' => 'test@example.com'], [
        'json' => true,
        'timeout' => 15,
        'headers' => ['Authorization' => 'Bearer ' . $token],
        'retries' => 2,
    ]);
    $user = json_decode($result['body'], true);
} catch (RuntimeException $e) {
    // 記錄日誌、回傳錯誤
}

常見坑點與解法

問題原因解法
HTTPS 憑證驗證失敗預設驗證 CA,本地缺憑證或自簽'ssl' => ['verify_peer' => false, 'verify_peer_name' => false] (僅測試用,生產必須驗證)
超時無效timeout 僅適用連線/讀取,不含 DNS使用 stream_context_set_option($ctx, 'http', 'timeout', 10) 或 Guzzle
大檔案上傳記憶體爆炸content 需完整載入記憶體改用 fopen('php://temp') 串流上傳或 Guzzle Multipart
無法取得回應 Header需依賴 $http_response_header 全域變數函式結束後立即讀取,或用 Guzzle 物件導向存取
並發請求阻塞同步阻塞 IO使用 Swoole/Fiber/Guzzle Promise 非同步

生產環境強烈推薦:Guzzle (PSR-18 相容)

composer require guzzlehttp/guzzle
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

$client = new Client([
    'timeout' => 10,
    'base_uri' => 'https://api.example.com/',
]);

try {
    $response = $client->post('endpoint', [
        'json' => ['name' => 'Joe'],
        'headers' => ['Authorization' => 'Bearer ' . $token],
    ]);

    $data = json_decode($response->getBody()->getContents(), true);
    $status = $response->getStatusCode();
} catch (RequestException $e) {
    if ($e->hasResponse()) {
        $status = $e->getResponse()->getStatusCode();
        $body = $e->getResponse()->getBody()->getContents();
    }
    // 記錄錯誤
}

// 非同步並發
$promises = [
    'users' => $client->getAsync('users'),
    'posts' => $client->getAsync('posts'),
];
$results = GuzzleHttp\Promise\Utils::unwrap($promises);

file_get_contents 適合「無 Composer 環境、極簡腳本、單次請求」。凡涉及「重試、並發、串流、驗證、中介軟體、測試模擬」,請直接使用 Guzzle 或 Symfony HttpClient,獲得完整 HTTP 客戶端功能與 PSR-7/18 互通性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved