API 整合、配置檔處理常需在 XML 與 PHP 陣列間轉換。本文提供兩個獨立工具:xml2array() 函數 (基於 PHP XML 解析器) 與 Array2Xml 類 (陣列生成 XML),支援屬性、CDATA、巢狀結構、重複標籤轉索引陣列。
function xml2array($xmlStr, $getAttributes = true, $priority = 'tag') {
if (!$xmlStr) return [];
$parser = xml_parser_create();
xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
xml_parse_into_struct($parser, trim($xmlStr), $vals);
xml_parser_free($parser);
$result = [];
$parents = [];
$repeated = [];
foreach ($vals as $v) {
$tag = $v['tag'];
$type = $v['type'];
$level = $v['level'];
$attrs = $v['attributes'] ?? [];
$value = $v['value'] ?? null;
if ($type == 'open') {
$parents[$level-1] = &$result;
if (!isset($result[$tag])) {
$result[$tag] = [];
$repeated[$tag . '_' . $level] = 1;
} else {
// 重複標籤轉為索引陣列
if (!isset($result[$tag][0])) {
$result[$tag] = [$result[$tag]];
}
$result[$tag][] = [];
$repeated[$tag . '_' . $level]++;
}
$last = $repeated[$tag . '_' . $level] - 1;
$result = &$result[$tag][$last];
if ($getAttributes && $attrs) $result['@attributes'] = $attrs;
} elseif ($type == 'complete') {
$item = $value;
if ($getAttributes && $attrs) $item = ['@value' => $value, '@attributes' => $attrs];
if (!isset($result[$tag])) {
$result[$tag] = $item;
} else {
if (!isset($result[$tag][0])) $result[$tag] = [$result[$tag]];
$result[$tag][] = $item;
}
} elseif ($type == 'close') {
$result = &$parents[$level-1];
}
}
return $result;
}
class Array2Xml {
private $xml = '\n';
private $subItems = [];
public function __construct(array $data) {
$this->xml .= $this->build($data);
}
private function build(array $arr, $parentKey = '') {
$out = '';
foreach ($arr as $key => $val) {
if (is_array($val)) {
if (is_numeric($key)) {
$this->subItems = array_merge($this->subItems, [$parentKey]);
$out .= "<$parentKey>" . $this->build($val, $key) . "$parentKey>";
} else {
$out .= "<$key>" . $this->build($val, $key) . "$key>";
}
} else {
// 屬性處理:@attributes 鍵
if ($key === '@attributes') {
// 屬性應在父標籤輸出時處理,這裡簡化忽略
} elseif ($key === '@value') {
$out .= htmlspecialchars($val, ENT_QUOTES, 'UTF-8');
} else {
$out .= "<$key>" . htmlspecialchars($val, ENT_QUOTES, 'UTF-8') . "$key>\n";
}
}
}
return $out;
}
public function __toString() {
// 修正巢狀數字鍵導致的重複標籤
foreach ($this->subItems as $tag) {
$this->xml = str_replace("<$tag><$tag>", "<$tag>", $this->xml);
$this->xml = str_replace("$tag>$tag>", "$tag>", $this->xml);
}
return $this->xml;
}
}
$xml = ''; $arr = xml2array($xml); print_r($arr); /* Array ( [root] => Array ( [user] => Array ( [0] => Array ([@attributes]=>Array([id]=>1), [name]=>John, [role]=>admin) [1] => Array ([@attributes]=>Array([id]=>2), [name]=>Jane) ) ) ) */ $xmlObj = new Array2Xml($arr); echo $xmlObj; // 輸出格式化 XML John admin Jane
DOMDocument / SimpleXMLElement 原生操作。XMLReader,避免記憶體爆炸。spatie/array-to-xml 或 laminas/laminas-xml,支援屬性、CDATA、命名空間完整規格。