我正在制作一个多线程的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}
答案 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对象时,无法保证数据库服务器可以访问。目前还不清楚这是否意味着反序列化将被视为失败。"
对序列化的相同限制适用于其他资源,如套接字或文件句柄。
答案 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来重新连接。