API 介接(對接 Flex/Java/.NET 客戶端)、配置檔處理、資料交換格式轉換。核心需求:關聯陣列 ↔ XML 字串 雙向無損轉換,支援巢狀結構、重複標籤、屬性。
<?php
/**
* 關聯陣列轉 XML 字串
*
* @param array $data 待轉換陣列
* @param string $root 根節點標籤(可選)
* @return string XML 字串
*/
function array2xml(array $data, string $root = ''): string {
$xml = '';
if ($root) {
$xml .= "<$root>";
}
foreach ($data as $key => $value) {
// 數字索引:視為重複標籤,使用父標籤名或 'item'
$tag = is_numeric($key) ? ($root ?: 'item') : $key;
if (is_array($value)) {
// 遞迴處理子陣列
$xml .= array2xml($value, $tag);
} elseif (is_object($value)) {
// 物件轉陣列再處理
$xml .= array2xml((array)$value, $tag);
} else {
// 標量值:直接輸出,特殊字元實體化
$xml .= "<$tag>" . htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8') . "</$tag>";
}
}
if ($root) {
$xml .= "</$root>";
}
return $xml;
}
// === 使用範例 ===
$arr = [
'rtxAccount' => 'aaron',
'ipAddr' => '192.168.0.12',
'conferenceList' => [
'conference' => [
['conferenceId' => 1212, 'conferenceTitle' => 'quanshi 444', 'smeAccount' => 'https://example.com'],
['conferenceId' => 454, 'conferenceTitle' => 'quanshi meeting', 'smeAccount' => 'https://example.com'],
]
]
];
echo array2xml($arr, 'RTXConferenceReqDTO');
/* 輸出:
<RTXConferenceReqDTO>
<rtxAccount>aaron</rtxAccount>
<ipAddr>192.168.0.12</ipAddr>
<conferenceList>
<conference>
<conferenceId>1212</conferenceId>
<conferenceTitle>quanshi 444</conferenceTitle>
<smeAccount>https://example.com</smeAccount>
</conference>
<conference>
<conferenceId>454</conferenceId>
<conferenceTitle>quanshi meeting</conferenceTitle>
<smeAccount>https://example.com</smeAccount>
</conference>
</conferenceList>
</RTXConferenceReqDTO>
*/
?>
<?php
/**
* XML 字串轉關聯陣列(基於 SimpleXML)
*
* @param string $xmlString XML 內容
* @return array 關聯陣列
*/
function xml2array(string $xmlString): array {
// 載入 XML,關閉外部實體載入(安全)
$xml = simplexml_load_string($xmlString, 'SimpleXMLElement', LIBXML_NOENT | LIBXML_NOCDATA);
if ($xml === false) {
throw new InvalidArgumentException('Invalid XML string');
}
return _xml2arrayRecursive($xml);
}
function _xml2arrayRecursive(SimpleXMLElement $xml): array {
$result = [];
// 處理屬性
$attributes = $xml->attributes();
if (count($attributes)) {
$result['@attributes'] = [];
foreach ($attributes as $key => $val) {
$result['@attributes'][$key] = (string)$val;
}
}
// 處理子節點
$children = $xml->children();
if (count($children) === 0) {
// 葉節點:直接回傳文字內容
$value = trim((string)$xml);
return $value !== '' ? $value : null;
}
// 群組相同標籤名稱的子節點
$groups = [];
foreach ($children as $key => $child) {
$groups[$key][] = $child;
}
foreach ($groups as $key => $group) {
if (count($group) === 1) {
// 單一子節點:直接遞迴
$result[$key] = _xml2arrayRecursive($group[0]);
} else {
// 多個同名子節點:陣列化
foreach ($group as $child) {
$result[$key][] = _xml2arrayRecursive($child);
}
}
}
return $result;
}
// === 使用範例 ===
$xmlStr = '<?xml version="1.0" encoding="UTF-8"?>
<RTXConferenceReqDTO>
<conferenceTitle>IT交流會</conferenceTitle>
<startTime>2024-01-15 12:00:00</startTime>
<invitees>
<invitee>
<rtxAccount>user1</rtxAccount>
<tel>13800000001</tel>
</invitee>
<invitee>
<rtxAccount>user2</rtxAccount>
<tel>13800000002</tel>
</invitee>
</invitees>
</RTXConferenceReqDTO>';
$arr = xml2array($xmlStr);
print_r($arr);
/* 輸出:
Array
(
[conferenceTitle] => IT交流會
[startTime] => 2024-01-15 12:00:00
[invitees] => Array
(
[invitee] => Array
(
[0] => Array
(
[rtxAccount] => user1
[tel] => 13800000001
)
[1] => Array
(
[rtxAccount] => user2
[tel] => 13800000002
)
)
)
)
*/
?>
<?php
// array2xml 擴充:支援 @attributes 鍵
function array2xml_advanced(array $data, string $root = ''): string {
$xml = '';
if ($root) $xml .= "<$root>";
foreach ($data as $key => $value) {
if ($key === '@attributes') continue; // 屬性在父標籤處理
$attrs = '';
if (is_array($value) && isset($value['@attributes'])) {
foreach ($value['@attributes'] as $ak => $av) {
$attrs .= " $ak=\"" . htmlspecialchars($av, ENT_QUOTES) . "\"";
}
}
$tag = is_numeric($key) ? ($root ?: 'item') : $key;
if (is_array($value)) {
// 分離屬性與子內容
$childData = $value;
unset($childData['@attributes']);
$xml .= array2xml_advanced($childData, "$tag$attrs");
} else {
$xml .= "<$tag$attrs>" . htmlspecialchars((string)$value, ENT_QUOTES) . "</$tag>";
}
}
if ($root) $xml .= "</$root>";
return $xml;
}
// 測試屬性
$arr = [
'book' => [
'@attributes' => ['id' => '101', 'lang' => 'zh'],
'title' => 'PHP 進階',
'author' => '張三'
]
];
echo array2xml_advanced($arr, 'library');
/* 輸出:
<library><book id="101" lang="zh"><title>PHP 進階</title><author>張三</author></book></library>
*/
?>
| 需求 | 推薦方案 | 備註 |
|---|---|---|
| 簡單陣列 ↔ XML,無屬性/CDATA | 上方基礎版 array2xml/xml2array | 輕量、無依賴 |
| 需支援屬性、CDATA、命名空間 | DOMDocument / XMLWriter / XMLReader | 標準擴展、功能完整 |
| 大型 XML 串流處理(>100MB) | XMLReader (SAX) | 記憶體常駐低 |
| 高效能序列化 | protobuf / MessagePack / igbinary | 非 XML 但效能更強 |
LIBXML_NOENT | LIBXML_DTDLOAD(預設關閉)或 libxml_disable_entity_loader(true)(PHP 8.0+ 已移除,改用 LIBXML_NOENT 控制)。htmlspecialchars 防注入。