當前位置: 首頁> 最新文章列表> 在PDOStatement::fetchObject 中實現對象的深度複製技巧

在PDOStatement::fetchObject 中實現對象的深度複製技巧

gitbox 2025-05-29

在使用PHP 的PDOStatement::fetchObject()方法時,我們通常可以方便地將數據庫查詢結果直接映射成對象。但是,有時候因為某些特殊需求(比如防止引用問題),我們希望拿到的對像是的副本,而不是直接映射的單實例對象。這篇文章將介紹一種簡單高效的方法,實現深度複製的技巧。

問題背景

正常情況下, fetchObject()返回的是查詢行的新對象,但如果對象內部含有引用屬性、或通過其他邏輯緩存了對象副本,可能導致數據洩露或修改衝突。

例如:

 $stmt = $pdo->query('SELECT id, name FROM users');
$user = $stmt->fetchObject(User::class);

// 後續對 $user 的修改可能影響到其他邏輯
$user->name = 'New Name';

為了避免這種副作用,我們希望每次取到的對像都是獨立無關的,哪怕內部屬性有引用,也能正確斷開。

深度複製的方法

PHP 中實現深度複製最直接的方式是通過序列化反序列化對象。示例如下:

 function deepCopy($object) {
    return unserialize(serialize($object));
}

這個deepCopy函數可以確保對象及其內部屬性都被完整復制,不再共享引用。

將深度複製集成到fetch 流程

我們可以稍微包裝一下fetchObject()的使用流程,使得每次獲取對像後,立即進行深度複製:

 class UserRepository
{
    protected PDO $pdo;
    
    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }
    
    public function findAllUsers(): array
    {
        $stmt = $this->pdo->query('SELECT id, name, profile FROM users');
        
        $users = [];
        while ($user = $stmt->fetchObject(User::class)) {
            $users[] = $this->deepCopy($user);
        }
        
        return $users;
    }
    
    protected function deepCopy(object $object): object
    {
        return unserialize(serialize($object));
    }
}

在這個例子裡,我們使用deepCopy()包裝了從數據庫取回的對象,確保$users數組裡的每個元素都是獨立的副本。

注意事項

雖然序列化-反序列化的方法簡單易用,但也有幾個注意點:

  • 性能開銷:序列化深度複製對性能有一定影響,尤其在處理大量數據時,要評估好影響。

  • 對象限制:如果對像中包含資源句柄(如數據庫連接、文件指針等不可序列化的屬性),使用序列化會失敗。

  • 自定義復制:如果對像比較複雜,可以考慮實現__clone()方法來手動深度複製邏輯。

一個更優雅的小工具類

如果項目中經常需要深度複製對象,可以封裝一個工具類:

 class ObjectHelper
{
    public static function deepCopy(object $object): object
    {
        return unserialize(serialize($object));
    }
}

然後調用時更加清晰:

 $copy = ObjectHelper::deepCopy($user);

如果你有興趣,還可以把這個工具發布成Composer 包,上傳到自己的私有倉庫,比如https://gitbox.net/your-username/object-helper ,方便在多個項目中復用。

總結

PDOStatement::fetchObject()中實現深度複製的最佳技巧,就是結合序列化來確保每個對象互不干擾。雖然需要注意性能和對象結構的限制,但在大多數中小型項目中,這種方式簡單、高效,非常實用。

如果你想進一步優化,還可以探索基於反射的自定義復制策略,但那就是另一個話題了!