当前位置: 首页> 最新文章列表> 在 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() 中实现深度复制的最佳技巧,就是结合序列化来确保每个对象互不干扰。虽然需要注意性能和对象结构的限制,但在大多数中小型项目中,这种方式简单、高效,非常实用。

如果你想进一步优化,还可以探索基于反射的自定义复制策略,但那就是另一个话题了!