DDC-314: Unserialization data model objects from "empty" string #387

Closed
opened 2026-01-22 12:36:46 +01:00 by admin · 9 comments
Owner

Originally created by @doctrinebot on GitHub (Feb 11, 2010).

Jira issue originally created by user cloun:

All of my data model classes contain a specified **weakup method. This method expects that object was serialized with primary key value. And all worked fine.
But begin from the latest revision (7131) class "ClassMetadata" unserializes my objects with invalid serialization data. As I know, Doctrine requires that all classes contain a public contructor without required parameters. Why do you use some kind of hack: unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name))? May be it would be better to use contructor?

Originally created by @doctrinebot on GitHub (Feb 11, 2010). Jira issue originally created by user cloun: All of my data model classes contain a specified **weakup method. This method expects that object was serialized with primary key value. And all worked fine. But begin from the latest revision (7131) class "ClassMetadata" unserializes my objects with invalid serialization data. As I know, Doctrine requires that all classes contain a public contructor without required parameters. Why do you use some kind of hack: unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name))? May be it would be better to use contructor?
admin added the Bug label 2026-01-22 12:36:46 +01:00
admin closed this issue 2026-01-22 12:36:47 +01:00
Author
Owner

@doctrinebot commented on GitHub (Feb 11, 2010):

Comment created by cloun:

I change your code, and it works fine, so far...

    /****
     * Creates a new instance of the mapped class, without invoking the constructor.
     * 
     * @return object
     */
    public function newInstance()
    {
        if ($this->_prototype === null) {
            $className = $this->name;
            $this->_prototype = new $className();
            //$this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
        }
        return clone $this->_prototype;
    }
@doctrinebot commented on GitHub (Feb 11, 2010): Comment created by cloun: I change your code, and it works fine, so far... ``` /**** * Creates a new instance of the mapped class, without invoking the constructor. * * @return object */ public function newInstance() { if ($this->_prototype === null) { $className = $this->name; $this->_prototype = new $className(); //$this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name)); } return clone $this->_prototype; } ```
Author
Owner

@doctrinebot commented on GitHub (Feb 11, 2010):

Comment created by @beberlei:

The JPA requirement of a no-arg constructor is quite a big burdon.

Because PHP allows us, we choose to go the way using unserialize. You can implement the "Serialize" interface and build your logic in there, which plays nicely with our "hack."

See the discussion in DDC-79 for more details, pro- and con arguments on this issue. We will update the documentation shortly reflecting this change.

@doctrinebot commented on GitHub (Feb 11, 2010): Comment created by @beberlei: The JPA requirement of a no-arg constructor is quite a big burdon. Because PHP allows us, we choose to go the way using unserialize. You can implement the "Serialize" interface and build your logic in there, which plays nicely with our "hack." See the discussion in [DDC-79](http://www.doctrine-project.org/jira/browse/DDC-79) for more details, pro- and con arguments on this issue. We will update the documentation shortly reflecting this change.
Author
Owner

@doctrinebot commented on GitHub (Feb 11, 2010):

Issue was closed with resolution "Won't Fix"

@doctrinebot commented on GitHub (Feb 11, 2010): Issue was closed with resolution "Won't Fix"
Author
Owner

@doctrinebot commented on GitHub (Feb 11, 2010):

Comment created by romanb:

Calling __wakeup() instead of __construct() on reconstitution of persistent objects is much better in many ways. Calling the constructor is just wrong, can lead to side-effects, slows down instantiation, and more. Please read the comments on DDC-79.

You should implement http://de3.php.net/manual/en/class.serializable.php instead of using __sleep/__wakeup.

@doctrinebot commented on GitHub (Feb 11, 2010): Comment created by romanb: Calling __wakeup() instead of __construct() on reconstitution of persistent objects is much better in many ways. Calling the constructor is just wrong, can lead to side-effects, slows down instantiation, and more. Please read the comments on [DDC-79](http://www.doctrine-project.org/jira/browse/DDC-79). You should implement http://de3.php.net/manual/en/class.serializable.php instead of using __sleep/__wakeup.
Author
Owner

@doctrinebot commented on GitHub (Feb 11, 2010):

Comment created by romanb:

Docs updated: http://www.doctrine-project.org/documentation/manual/2_0/en/architecture#entities

@doctrinebot commented on GitHub (Feb 11, 2010): Comment created by romanb: Docs updated: http://www.doctrine-project.org/documentation/manual/2_0/en/architecture#entities
Author
Owner

@doctrinebot commented on GitHub (Feb 13, 2010):

Comment created by cloun:

I have found simple workaround, may be it will be usefull.
If you have your own implementation of the **weakup method, and you do not want to implement custom serialization methods (to implement ISerializable interface), you can make the next steps:

  1. Define a protected field "_prototype" in your base class of all data model classes.
class Entity
{
  protected $_prototype = false;
  ...
}
  1. Make a little modification with Doctrine code in file \Doctrine\ORM\Mapping\ClassMetadata.php. Modify the public function 'newInstance()'.
    public function newInstance()
    {
        if ($this->_prototype === null) {
            //$this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
            //'O:4:"Test":1:{s:13:" * _prototype";b:1;}';
            $this->*prototype = unserialize(sprintf('O:%d:"%s":1:{s:13:"%s";b:1;}', strlen($this->name), $this->name, "\0*\0*prototype"));
        }
        return clone $this->_prototype;
    }

Where "\0_\0_prototype" is your protected field in base class of all data model classes.
3. Now, you have a flag that indicates whether the object was unserialized as a prototype in depths of the Doctrine or not. So, your *_weakup method should look like below:

class Entity
{
  ...
  public function  **wakeup()
  {
    if ($this->_prototype)
    {
      $this->_prototype = false;
    }
    else
    {
      // your custome implementation of **weakup method.
    }
}
@doctrinebot commented on GitHub (Feb 13, 2010): Comment created by cloun: I have found simple workaround, may be it will be usefull. If you have your own implementation of the **weakup method, and you do not want to implement custom serialization methods (to implement ISerializable interface), you can make the next steps: 1. Define a protected field "_prototype" in your base class of all data model classes. ``` class Entity { protected $_prototype = false; ... } ``` 1. Make a little modification with Doctrine code in file \Doctrine\ORM\Mapping\ClassMetadata.php. Modify the public function 'newInstance()'. ``` public function newInstance() { if ($this->_prototype === null) { //$this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name)); //'O:4:"Test":1:{s:13:" * _prototype";b:1;}'; $this->*prototype = unserialize(sprintf('O:%d:"%s":1:{s:13:"%s";b:1;}', strlen($this->name), $this->name, "\0*\0*prototype")); } return clone $this->_prototype; } ``` Where "\0_\0_prototype" is your protected field in base class of all data model classes. 3. Now, you have a flag that indicates whether the object was unserialized as a prototype in depths of the Doctrine or not. So, your *_weakup method should look like below: ``` class Entity { ... public function **wakeup() { if ($this->_prototype) { $this->_prototype = false; } else { // your custome implementation of **weakup method. } } ```
Author
Owner

@doctrinebot commented on GitHub (Feb 13, 2010):

Comment created by @beberlei:

Can you explain why you need this hack instead of using Serializable?

@doctrinebot commented on GitHub (Feb 13, 2010): Comment created by @beberlei: Can you explain why you need this hack instead of using Serializable?
Author
Owner

@doctrinebot commented on GitHub (Feb 13, 2010):

Comment created by romanb:

Modifying Doctrine is usually not a good idea and if you really want to use **wakeup there are other (better) ways to safely use it that are not so intrusive. You say "This method expects that object was serialized with primary key value" so why not just check for that? (see option 1 below).

Here are two methods to safely implement __wakeup. The same could be done for __clone for that matter. We should probably add an entry in the documentation or cookbook about how one can safely implement __wakeup and __clone. I think the simplest way for both is option 1, a simple identity check.

// Option 1, simple and preferred: identity check
class SafeWakeup1 {
  private $id;
  //...
  function **wakeup() {
    if ($this->id) {
      // ... object has identity ...
    }
  }
}
// usage as normal
$o1 = new SafeWakeup1;
$ser = serialize($o1);
$o1 = unserialize($ser);


// Option 2: Context-aware serialization

class SafeWakeup2 {
  //...
  function **wakeup() {
    if (Serializer::inContext()) {
      // ... unserialization in context of Serializer ...
    }
  }
  function **sleep() {
    if (Serializer::inContext()) {
      // ... serialization in context of Serializer ...
    }
  }
}

final class Serializer {
  private static $_inContext = false;

  private function **construct() {}

  public static function serialize($obj) {
    self::$_inContext = true;
    $serialized = serialize($obj);
    self::$_inContext = false;
    return $serialized;
  }

  public static function unserialize($serialized) {
    self::$_inContext = true;
    $obj = unserialize($serialized);
    self::$_inContext = false;
    return $obj;
  }

  public static function inContext() {
    return self::$_inContext;
  }
}
// usage through indirection
$o2 = new SafeWakeup2;
$ser = Serializer::serialize($o1);
$o2 = Serializer::unserialize($ser);
@doctrinebot commented on GitHub (Feb 13, 2010): Comment created by romanb: Modifying Doctrine is usually not a good idea and if you really want to use **wakeup there are other (better) ways to safely use it that are not so intrusive. You say "This method expects that object was serialized with primary key value" so why not just check for that? (see option 1 below). Here are two methods to safely implement __wakeup. The same could be done for __clone for that matter. We should probably add an entry in the documentation or cookbook about how one can safely implement __wakeup and __clone. I think the simplest way for both is option 1, a simple identity check. ``` // Option 1, simple and preferred: identity check class SafeWakeup1 { private $id; //... function **wakeup() { if ($this->id) { // ... object has identity ... } } } // usage as normal $o1 = new SafeWakeup1; $ser = serialize($o1); $o1 = unserialize($ser); // Option 2: Context-aware serialization class SafeWakeup2 { //... function **wakeup() { if (Serializer::inContext()) { // ... unserialization in context of Serializer ... } } function **sleep() { if (Serializer::inContext()) { // ... serialization in context of Serializer ... } } } final class Serializer { private static $_inContext = false; private function **construct() {} public static function serialize($obj) { self::$_inContext = true; $serialized = serialize($obj); self::$_inContext = false; return $serialized; } public static function unserialize($serialized) { self::$_inContext = true; $obj = unserialize($serialized); self::$_inContext = false; return $obj; } public static function inContext() { return self::$_inContext; } } // usage through indirection $o2 = new SafeWakeup2; $ser = Serializer::serialize($o1); $o2 = Serializer::unserialize($ser); ```
Author
Owner

@doctrinebot commented on GitHub (Feb 13, 2010):

Comment created by cloun:

Benjamin Eberlei
May be I'm wrong, but I think that if I implement ISerializable interface then I should write code for custom serialization.

Roman S. Borschel
Yes, you are absolutely right. But as a common solution if you know nothing about customer's model or how to this classes are used I suggested this. Thanks for examples!

@doctrinebot commented on GitHub (Feb 13, 2010): Comment created by cloun: **Benjamin Eberlei** May be I'm wrong, but I think that if I implement ISerializable interface then I should write code for custom serialization. **Roman S. Borschel** Yes, you are absolutely right. But as a common solution if you know nothing about customer's model or how to this classes are used I suggested this. Thanks for examples!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#387