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

PHP XML 與陣列雙向轉換:array2xml 與 xml2array 完整實作

     編輯:PHP 綜合知識

應用場景

API 介接(對接 Flex/Java/.NET 客戶端)、配置檔處理、資料交換格式轉換。核心需求:關聯陣列 ↔ XML 字串 雙向無損轉換,支援巢狀結構、重複標籤、屬性。

array2xml:陣列轉 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>
*/
?>

xml2array:XML 轉關聯陣列

<?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
                        )
                )
        )
)
*/
?>

進階:支援 XML 屬性與 CDATA

<?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 但效能更強

安全提醒

  • 解析外部 XML **必須** 關閉外部實體載入:LIBXML_NOENT | LIBXML_DTDLOAD(預設關閉)或 libxml_disable_entity_loader(true)(PHP 8.0+ 已移除,改用 LIBXML_NOENT 控制)。
  • 輸出 XML 時所有動態值必須 htmlspecialchars 防注入。
  • 限制 XML 深度與大小,防止 Billion Laughs 攻擊。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved