4

I am making a multi-threaded CLI-PHP application and need to serialize PDO object to pass it between work inside the thread, and wake it up from a sleeping thread using magic methods __sleep() and __wakeup(). However nor the PDO or mysqli extension supports it. The old mysql_*() api did this but it has been deprecated and removed.

<?php
    // Application
    $link = new PDO('mysql:host=localhost;port=3306;dbname=testdatabase', 'root', '');

    $obj = serialize($link);

well generate error

PHP Fatal error: Uncaught exception 'PDOException' with message 'You cannot ser ialize or unserialize PDO instances' in W:\workspace\Sandbox\application.php:5 Stack trace:

#0 [internal function]: PDO->__sleep()

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

#2 {main} thrown in W:\workspace\Sandbox\application.php on line 5

Community
  • 1
  • 1
Junior
  • 671
  • 3
  • 9
  • 24
  • 3
    Serialization only captures the superfical PDO object properties (which there aren't much of), but not the behind-the-scenes socket connection and buffers et al. – mario Aug 21 '14 at 20:40
  • 1
    This is not a duplicate as the accepted answer on the question doesn't answer anything. @mario – Junior Aug 21 '14 at 20:42
  • 1
    I agree. The answer fails to explain why PDO cannot be serialized, it just uses circular reasoning (`You can not serialize objects that can not be serialized.`). I feel the correct answer would be -> http://php.net/manual/en/intro.session.php `Because session data is serialized, resource variables cannot be stored in the session.`. So, because PDO is a external resource, it cannot be serialized. – Dave Chen Aug 21 '14 at 20:45
  • 2
    I really doubt there's any explanation that can be given beyond "It doesn't make sense to serialize database connections." – developerwjk Aug 21 '14 at 20:46
  • @developerwjk Whats the points in magic methods __sleep() and __wakeup() then? http://php.net/manual/en/language.oop5.magic.php `The intended use of __wakeup() is to reestablish any database connections that may have been lost during serialization and perform other reinitialization tasks.` – Junior Aug 21 '14 at 20:47
  • 1
    perhaps is the OP explained why he wanted to serlise in the first place ... –  Aug 21 '14 at 20:48
  • 1
    No, the linked dupe doesn't go into details. That would entail a super lengthy roundup of PDOs internal data structures, that of PDO_SQlite and PDO_MySQL to completely answer that. Too broad. In the end, it amounts to "it's a resource, not a plain object". Which a comment covers sufficiently. The question is, why do you ask for the why? How do expect to use the information. It's not like you could somehow make the PDO handle serializable after all. – mario Aug 21 '14 at 20:50
  • @Ole Kristian Aanensen, What's being done at http://php.net/manual/en/language.oop5.magic.php looks more like keeping a reference to the dsn, user, pwd, to reconnect, not true serialization. This is also done on a wrapper not the PDO object itself. – developerwjk Aug 21 '14 at 20:52

2 Answers2

13

A PDO object contains state that cannot be represented in the serialization format. For example, the PDO object contains an open connection to a database server.

If you were to try to deserialize a serialized PDO object, the __wakeup() method would have to reconnect to the database server. This would require that authentication credentials be stored in a readable manner in the serialized PDO object, which is a security no-no.

I worked on the Zend Framework's Zend_Db component a long time ago, and I deliberately designed the Zend_Db_Adapter object to not be serializable for this reason. Instances of Zend_Db_Table, Zend_Db_Table_Row, etc. could be serializable, but could not be "live" after deserialization until you assigned it a freshly connected Zend_Db_Adapter instance.

Also, there's no guarantee that the database server would be reachable at the time you deserialize the PDO object. It's unclear whether this means the deserialization would be considered "failed."

The same restrictions on serialization apply to other resources such as sockets or file handles.

See also Why isn't every type of object serializable?

Community
  • 1
  • 1
Bill Karwin
  • 538,548
  • 86
  • 673
  • 828
  • Hey Bill, just an idea: Is there anything interesting extractable from a PDO instance using Reflection classes/methods (information which is not present otherwise)? – Daniel W. Aug 21 '14 at 21:07
  • @DanFromGermany: You can test this with `ReflectionObject::export($pdo)`. Reflection only accesses public and private constants, properties, and methods that would be accessible via PHP anyway. Not any part of the internals of the PDO extension. – Bill Karwin Aug 21 '14 at 21:22
2

What's being done at http://php.net/manual/en/language.oop5.magic.php is creating a wrapper that can be serialized since the PDO link itself cannot be.

<?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();
    }
}?>

The PDO object apparently does not keep the dsn, user, pwd after connecting, and so cannot be directly serialized. But if you created a wrapper like in the example above, where you stored this information, you could serialize the wrapper. Then when you unserialize, it will create a new PDO object and reconnect by passing the credentials in from the wrapper to PDO.

developerwjk
  • 8,619
  • 2
  • 17
  • 33