为什么不能将PDO对象序列化?

时间:2014-08-21 20:38:00

标签: php pdo

我正在制作一个多线程的CLI-PHP应用程序,需要序列化PDO object以在线程内的工作之间传递它,并使用魔术方法__sleep()和{从睡眠线程中唤醒它{1}}。但是,__wakeup()PDO扩展程序也不支持它。旧mysqli api执行了此操作,但已弃用并删除。

mysql_*()

生成错误

  

PHP致命错误:未捕获的异常' PDOException'有消息'你   不能serialize或反序列化PDO实例'在   W:\ workspace \ Sandbox \ application.php:5堆栈跟踪:

     

#0 [内部功能]:PDO-> __ sleep()

     

#1 W:\ workspace \ Sandbox \ application.php(5):serialize(Object(PDO))

     在第5行的W:\ workspace \ Sandbox \ application.php中抛出

#2 {main}

2 个答案:

答案 0 :(得分:10)

PDO对象包含无法以序列化格式表示的状态。例如,PDO对象包含与数据库服务器的开放连接。

如果要尝试反序列化序列化PDO对象,则__wakeup()方法必须重新连接到数据库服务器。这将要求身份验证凭证以可读方式存储在序列化PDO对象中,这是一种安全禁忌。

我很久以前就研究过Zend Framework的Zend_Db组件,因此我故意将Zend_Db_Adapter对象设计为不可序列化。 Zend_Db_Table,Zend_Db_Table_Row等的实例可以是可序列化的,但不能是" live"在反序列化之后,直到为其分配了一个新连接的Zend_Db_Adapter实例。

此外,在您反序列化PDO对象时,无法保证数据库服务器可以访问。目前还不清楚这是否意味着反序列化将被视为失败。"

对序列化的相同限制适用于其他资源,如套接字或文件句柄。

另见Why isn't every type of object serializable?

答案 1 :(得分:2)

http://php.net/manual/en/language.oop5.magic.php正在做的是创建一个可以序列化的包装器,因为PDO链接本身不能。

<?php
class Connection
{
    protected $link;
    private $dsn, $username, $password;

    public function __construct($dsn, $username, $password)
    {
        $this->dsn = $dsn;
        $this->username = $username;
        $this->password = $password;
        $this->connect();
    }

    private function connect()
    {
        $this->link = new PDO($this->dsn, $this->username, $this->password);
    }

    public function __sleep()
    {
        return array('dsn', 'username', 'password');
    }

    public function __wakeup()
    {
        $this->connect();
    }
}?>

PDO对象显然在连接后不保留dsn,user,pwd,因此不能直接序列化。但是如果您创建了一个包装器,就像上面的例子中存储了这些信息一样,您可以序列化包装器。然后,当您反序列化时,它将创建一个 PDO对象,并通过将包装中的凭据传递给PDO来重新连接。