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

PHP Object 轉 Array 遞歸函數 (支援 StdClass、巢狀物件、循環引用)

     編輯:PHP 相關知識

調用第三方 API、ORM 實體、XML 解析結果常返回 `stdClass` 物件。直接 `json_decode(json_encode($obj), true)` 雖快但有缺陷:丟失 `private/protected` 屬性、無法處理循環引用、資源類型報錯。本文提供健壯的遞歸轉換函數,支援可見性過濾、循環引用檢測、資源類型跳過。

核心實現

<?php
/**
 * 將任意物件/陣列深度轉為純陣列
 *
 * @param mixed  $data        物件、陣列、標量
 * @param bool   $includePrivate 是否包含 private/protected 屬性 (需 Reflection)
 * @param array  &$seen       內部用:已訪問物件 spl_object_id 防循環
 * @return array
 */
function objectToArray($data, bool $includePrivate = false, array &$seen = []): array {
    if (is_object($data)) {
        // 循環引用檢測
        $oid = spl_object_id($data);
        if (isset($seen[$oid])) {
            return ['*CIRCULAR_REF*' => get_class($data)];
        }
        $seen[$oid] = true;

        // 取得屬性
        if ($includePrivate) {
            $refl = new ReflectionObject($data);
            $props = [];
            foreach ($refl->getProperties() as $prop) {
                $prop->setAccessible(true);
                $props[$prop->getName()] = $prop->getValue($data);
            }
        } else {
            $props = get_object_vars($data); // 僅 public
        }

        $result = [];
        foreach ($props as $key => $val) {
            $result[$key] = objectToArray($val, $includePrivate, $seen);
        }
        unset($seen[$oid]);
        return $result;
    }

    if (is_array($data)) {
        $result = [];
        foreach ($data as $key => $val) {
            $result[$key] = objectToArray($val, $includePrivate, $seen);
        }
        return $result;
    }

    // 資源類型 (stream, curl, mysqli, pdo...) 無法序列化,標記跳過
    if (is_resource($data)) {
        return ['*RESOURCE*' => get_resource_type($data)];
    }

    // 標量直接返回
    return $data;
}

// 便捷別名
function std_class_object_to_array($obj) {
    return objectToArray($obj, false);
}
?>

使用範例

<?php
class User {
    public $id = 1;
    public $name = 'Alice';
    protected $email = 'alice@example.com';
    private $passwordHash = 'bcrypt$...';
    public $profile = null; // 稍後賦值形成循環引用
}

$user = new User();
$user->profile = (object)['bio' => 'Hi', 'user' => $user]; // 循環引用

// 1. 僅 public (默認)
print_r(objectToArray($user));
/* 輸出:
Array
(
    [id] => 1
    [name] => Alice
    [profile] => Array
        (
            [bio] => Hi
            [user] => Array
                (
                    [*CIRCULAR_REF*] => User
                )
        )
)
*/

// 2. 包含 private/protected (需 PHP 8.1+ ReflectionProperty::getValue 無需 setAccessible)
print_r(objectToArray($user, true));
/* 輸出包含 email、passwordHash */
?>

對比:json_encode 技巧 vs 遞歸函數

特性json_encode/decode遞歸函數 objectToArray
速度極快 (C 實現)中等 (PHP 迴圈)
private/protected丟失可選保留 (Reflection)
循環引用報錯 `Recursion detected`檢測並標記 `*CIRCULAR_REF*`
資源類型報錯/轉 null標記 `*RESOURCE*` 類型名
DateTime 等物件轉 ISO8601 字串保持物件 (可擴展轉換)
記憶體雙倍 (中間 JSON 字串)單次遍歷

擴展:自定義類型轉換器

<?php
function objectToArrayAdvanced($data, array $converters = [], array &$seen = []): array {
    if (is_object($data)) {
        $oid = spl_object_id($data);
        if (isset($seen[$oid])) return ['*CIRCULAR_REF*' => get_class($data)];
        $seen[$oid] = true;

        // 自定義轉換器優先 (如 DateTime => timestamp)
        $class = get_class($data);
        if (isset($converters[$class])) {
            $result = $converters[$class]($data);
            unset($seen[$oid]);
            return $result;
        }

        $props = get_object_vars($data);
        $result = [];
        foreach ($props as $k => $v) {
            $result[$k] = objectToArrayAdvanced($v, $converters, $seen);
        }
        unset($seen[$oid]);
        return $result;
    }
    // ... 陣列/標量同前
}

// 使用
$converters = [
    DateTime::class => fn($d) => $d->getTimestamp(),
    DateTimeImmutable::class => fn($d) => $d->getTimestamp(),
    // 自定義 Entity => toArray()
];
$arr = objectToArrayAdvanced($entity, $converters);
?>

性能提示

  • 高頻調用 (如 API 響應轉換) 建議改用 `json_encode/decode` + 事後補充私有屬性,或使用 symfony/serializer、jms/serializer 成熟庫。
  • 遞歸深度超過 PHP 默認限制 (通常 100-200 層) 會棧溢出,極深結構請改寫為迭代棧。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved