當第三方系統或前端透過 HTTP POST 直接送出 XML 內容(Content-Type: text/xml 或 application/xml)時,PHP $_POST 無法自動解析,需從原始輸入流讀取並解析。
file_get_contents('php://input')(推薦)或 $GLOBALS['HTTP_RAW_POST_DATA'](PHP 5.6+ 棄用、7.0+ 移除)simplexml_load_string()(簡單結構)或 DOMDocument(複雜、需命名空間、XPath)libxml 錯誤報告,自行捕獲<?php
declare(strict_types=1);
// 僅允許 POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
exit('Method Not Allowed');
}
// 1. 讀取原始 XML
$rawXml = file_get_contents('php://input');
if (empty($rawXml)) {
http_response_code(400);
exit('Empty XML body');
}
// 2. 解析(關閉 libxml 內部錯誤,改由程式碼處理)
libxml_use_internal_errors(true);
$xml = simplexml_load_string($rawXml);
$errors = libxml_get_errors();
libxml_clear_errors();
if ($xml === false) {
$msg = 'XML 解析失敗: ' . implode('; ', array_map(fn($e) => $e->message, $errors));
error_log($msg);
http_response_code(400);
exit($msg);
}
// 3. 取得資料(範例結構對應原文)
$namespace = ''; // 若有命名空間需註冊:$xml->registerXPathNamespace('ns', 'uri');
$iata = (string)($xml->Agency->Iata ?? '');
$agent = (string)($xml->Agency->Agent ?? '');
$password = (string)($xml->Agency->Password ?? '');
$brand = (string)($xml->Agency->Brand ?? '');
$adults = [];
foreach ($xml->Passengers->Adult ?? [] as $adult) {
$adults[] = [
'id' => (string)($adult['ID'] ?? ''),
'age' => (string)($adult['AGE'] ?? ''),
];
}
$destCode = (string)($xml->HotelAvailReq1->DestCode ?? '');
$hotelCode = (string)($xml->HotelAvailReq1->HotelCode ?? '');
$checkIn = (string)($xml->HotelAvailReq1->CheckInDate ?? '');
$checkOut = (string)($xml->HotelAvailReq1->CheckOutDate ?? '');
$useField = (string)($xml->HotelAvailReq1->UseField ?? '');
// 4. 業務邏輯處理...
// $result = processBooking($iata, $agent, $adults, $destCode, $hotelCode, $checkIn, $checkOut);
// 5. 回應 XML
header('Content-Type: text/xml; charset=utf-8');
echo <<
$dom = new DOMDocument();
$dom->loadXML($rawXml, LIBXML_NOERROR | LIBXML_NOWARNING);
$xpath = new DOMXPath($dom);
// 註冊命名空間(若 XML 含 xmlns)
// $xpath->registerNamespace('ns', 'https://example.com/ns');
$iata = $xpath->evaluate('string(//Agency/Iata)');
$adultNodes = $xpath->query('//Passengers/Adult');
foreach ($adultNodes as $node) {
$id = $node->getAttribute('ID');
$age = $node->getAttribute('AGE');
// ...
}
<?php
$xmlData = '<AATAvailReq1><Agency><Iata>1234567890</Iata>...</AATAvailReq1>';
$ch = curl_init('https://your-api.example.com/endpoint');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $xmlData,
CURLOPT_HTTPHEADER => [
'Content-Type: text/xml; charset=utf-8',
'Content-Length: ' . strlen($xmlData),
],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => true, // 生產環境必須驗證
CURLOPT_TIMEOUT => 30,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo "HTTP $httpCode\n$response";
| 問題 | 原因 | 解決 |
|---|---|---|
$_POST 為空 | PHP 只解析 application/x-www-form-urlencoded 或 multipart/form-data | 用 php://input |
| 大檔案記憶體爆炸 | simplexml_load_string 全載入記憶體 | 用 XMLReader 流式解析或分塊處理 |
| 命名空間無法查詢 | SimpleXML 預設不處理命名空間 | 用 DOMDocument + registerXPathNamespace |
| 編碼亂碼 | XML 宣告 encoding 與實際不符 | 強制 mb_convert_encoding($rawXml, 'UTF-8', 'auto') |
| 外部實體注入 (XXE) | 解析器預設載入外部 DTD/實體 | libxml_disable_entity_loader(true) (PHP 8.0+ 預設關閉) |
Content-Type 標頭確為 text/xml 或 application/xmlnginx client_max_body_size、php.ini post_max_size掌握 php://input + simplexml_load_string/DOMDocument 組合,即可輕鬆處理各類 XML API 整合場景(旅館預訂、支付回調、物流追蹤、電子發票等)。