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

PHP 以 POST 方式發送 XML 資料的兩種核心方法:cURL 與 fsockopen 完整實作

     編輯:PHP 相關知識

PHP 以 POST 方式發送 XML 資料的兩種核心方法:cURL 與 fsockopen 完整實作

在系統整合、API 串接或第三方支付回調中,常需以 Content-Type: text/xml 或 application/xml 格式 POST XML 資料。PHP 提供 cURL(推薦)與原生 Socket 兩種主流實作,本文提供完整、可直接部署的代碼範例,並封裝為可重用類別。

一、方法一:cURL 實作(推薦,功能豐富、錯誤處理完善)

<?php
function postXmlByCurl(string $url, string $xmlData, array $options = []): ?string {
    $ch = curl_init($url);
    $defaultOpts = [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => $xmlData,
        CURLOPT_HTTPHEADER => [
            'Content-Type: application/xml; charset=utf-8',
            'Content-Length: ' . strlen($xmlData),
        ],
        CURLOPT_TIMEOUT => 30,
        CURLOPT_SSL_VERIFYPEER => false, // 測試環境可關閉,生產請開啟並配置 CA
        CURLOPT_SSL_VERIFYHOST => 2,
    ];
    curl_setopt_array($ch, $defaultOpts + $options);
    $response = curl_exec($ch);
    if (curl_errno($ch)) {
        error_log('cURL Error: ' . curl_error($ch));
        return null;
    }
    curl_close($ch);
    return $response;
}

// 使用範例
$xml = '<?xml version="1.0" encoding="UTF-8"?><order><id>20240101</id></order>';
$result = postXmlByCurl('https://api.example.com/notify', $xml);
var_dump($result);
?>

二、方法二:fsockopen 實作(無需擴充套件、適合極簡環境)

<?php
function postXmlBySocket(string $host, string $path, string $xmlData, int $port = 80, bool $ssl = false): ?string {
    $target = ($ssl ? 'ssl://' : '') . $host;
    $fp = @fsockopen($target, $port, $errno, $errstr, 10);
    if (!$fp) {
        error_log("Socket Error [$errno]: $errstr");
        return null;
    }
    $request = "POST $path HTTP/1.1\r\n";
    $request .= "Host: $host\r\n";
    $request .= "Content-Type: application/xml; charset=utf-8\r\n";
    $request .= "Content-Length: " . strlen($xmlData) . "\r\n";
    $request .= "Connection: close\r\n\r\n";
    $request .= $xmlData;
    fwrite($fp, $request);
    $response = '';
    while (!feof($fp)) {
        $response .= fgets($fp, 1024);
    }
    fclose($fp);
    // 簡易移除 HTTP 標頭,僅回傳 Body
    list(, $body) = explode("\r\n\r\n", $response, 2) + [null, ''];
    return $body;
}

// 使用範例
$xml = '<data><item>test</item></data>';
$result = postXmlBySocket('api.example.com', '/endpoint', $xml, 443, true);
var_dump($result);
?>

三、進階封裝:可重用 XML 發送類別(含重試、日誌、SSL 憑證)

<?php
class XmlSender {
    private int $timeout = 30;
    private int $maxRetries = 2;
    private ?string $caCertPath = null;

    public function __construct(array $config = []) {
        if (!extension_loaded('curl')) {
            throw new RuntimeException('cURL extension required');
        }
        $this->timeout = $config['timeout'] ?? 30;
        $this->maxRetries = $config['retries'] ?? 2;
        $this->caCertPath = $config['ca_cert'] ?? null;
    }

    public function send(string $url, string $xml, array $extraHeaders = []): ?string {
        $headers = array_merge([
            'Content-Type: application/xml; charset=utf-8',
            'Content-Length: ' . strlen($xml),
            'Expect:', // 避免 100 Continue 延遲
        ], $extraHeaders);

        for ($attempt = 0; $attempt <= $this->maxRetries; $attempt++) {
            $ch = curl_init($url);
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_POST => true,
                CURLOPT_POSTFIELDS => $xml,
                CURLOPT_HTTPHEADER => $headers,
                CURLOPT_TIMEOUT => $this->timeout,
                CURLOPT_SSL_VERIFYPEER => $this->caCertPath !== null,
                CURLOPT_SSL_VERIFYHOST => 2,
                CURLOPT_CAINFO => $this->caCertPath,
            ]);
            $response = curl_exec($ch);
            $errno = curl_errno($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);

            if ($errno === 0 && $httpCode >= 200 && $httpCode < 300) {
                return $response;
            }
            if ($attempt < $this->maxRetries) {
                usleep(200000 * ($attempt + 1)); // 指數退避
            }
        }
        error_log("XmlSender failed after {$this->maxRetries} retries: $url");
        return null;
    }
}

// 使用
$sender = new XmlSender(['timeout' => 20, 'retries' => 3]);
$xml = '<request><action>sync</action></request>';
$resp = $sender->send('https://partner.api/gateway', $xml);
?>

四、常見坑點與最佳實踐

  • 編碼一致性: XML 宣告 encoding="UTF-8"、PHP 檔案儲存為 UTF-8 無 BOM、cURL 傳送前確認 mb_internal_encoding('UTF-8')。
  • 大資料流: 超過 10MB 建議啟用 CURLOPT_READFUNCTION 分塊讀取,避免記憶體暴增。
  • 驗證回應: 收到回應後務必用 simplexml_load_string 或 DOMDocument 解析驗證結構,防止惡意回應導致 XXE 攻擊(關閉 libxml_disable_entity_loader(true))。
  • 非同步發送: 高併發場景可搭配 Swoole、Guzzle Promise 或消息佇列(RabbitMQ/Kafka)異步處理。

總結: cURL 為首選,功能完整且效能佳;fsockopen 作為無擴充套件環境的備案。建議封裝統一發送類別,集中管理超時、重試、SSL、日誌,提升維護性與安全性。

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