Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UnexpectedValueException in session_start() php failing SPLObjectStorage serialization

Why UnexpectedValueException is thrown in session_start()?

I have object which has property of SPLObjectstorage. That object is assigned to session like

$_SESSION['foo'] = $barObject;

I suspect that internal session serializing facing problem to decode it. I store the session in database and it looks like it is serializing the objectStorage but can not decode it.

Sample session data

self|O:4:"User":8:{s:5:"�*�id";N;s:7:"�*�nick";N;s:13:"�*�reputation";i:1;s:11:"�*�password";N;s:8:"�*�email";N;s:7:"�*�crud";O:10:"CRUDobject":2:{s:13:"�*�fieldCache";a:0:{}s:13:"�*�dependency";r:1;}s:7:"�*�auth";N;s:11:"�*�roleList";C:11:"RoleStorage":23:{x:i:1;N;,r:13;;m:a:0:{}}}

Rolestorage is extends of SPLObjectstorage session_decode() on above string also returns false any ideas?

removing the roleList attribute makes it serialize properly.

If I separately do

$sr = serialize($roles); // $roles is RoleStorage object
var_dump($sr);
var_dump(unserialize($sr));

It prints string 'C:11:"RoleStorage":22:{x:i:1;N;,r:3;;m:a:0:{}}' (length=46) and then fails with same message while unserializing. I have no clue why this is happening.

Note: while attaching object to RoleStorage I used the object itself as data. Means it is stored as reference. I don't know how (if) does serialize() handles internally this.

like image 864
varuog Avatar asked Jul 31 '13 06:07

varuog


3 Answers

Objects with the name RoleStorage raises a couple of flags for me. Often, this object does contain a resource of sorts, or a reference to a built-in PHP object. Resources can't be serialized, nor can some PHP built-in types be serialized. Consider implementing the magic __sleep and __wakeup methods in those cases.
Say you have a PDO reference somewhere in the RoleStorage object, then these magic properties might look something like this:

public function __sleep()
{
    $this->pdo->commit();//commit && close
    $this->pdo = array($dsn, $user, $pwd, array(
                                              PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
                                          )
    );
    return serialize($this);
}
public function __wakeup()
{
    $this->pdo = new PDO($this->pdo[0], $this->pdo[1], $this->pdo[2], $this->pdo[3]);
}

But since you say the RoleStorage objects is a child of SPLObjectStorage, you'd be better off overriding the SPLObjectStorage's implementation of the Serializable interface:

It is not possible for __sleep() to return names of private properties in parent classes. Doing this will result in an E_NOTICE level error. Instead you may use the Serializable interface.

I'd suggest declaring iterating over all properties in the child class's serialize method, and store that data into an array. Return that array serialized, and unserialize that string back in the unserialize method, reassigning every property in a loop.
If SPLObjectStorage has private properties, you can get to them like so:

class RoleStorage extends SPLObjectStorage
      implements Serializable
{
    public function serialize()
    {
        return serialize((array) $this);
    }
    public function unserialize($string)
    {
        $array = unserialize($string);
        foreach($array as $property => $value)
        {
            $property = explode("\0", $property);//private & protected properties
            $this->{end($property)} = $value;
        }
    }
}

For details on the explode("\0",$property);, refer to the manual, or check this question

like image 166
Elias Van Ootegem Avatar answered Nov 20 '22 17:11

Elias Van Ootegem


I have no clue why this is happening

In your PHP version and with your concrete script it is not possible to serialize an object based on SPLObjectStorage unless you take care of the serialization your own. If you see this part of your serialized string:

C:11:"RoleStorage":23:{x:i:1;N;,r:13;;m:a:0:{}}

This represents the RoleStorage object. The big C at the beginning stands for:

C - Object implementing serializeable Interface

So the object itself is responsible here about the serialization and unserialization. You can normally expect this works, however not all software is without bugs.

In your case it looks like PHP made a mistake. The internal format here is:

x:i:1;N;,r:13;;m:a:0:{} 
      ^^^^^^^

And the problem is at the highlighted position, this requires a serialized object, not NULL. And it's not comma-terminated with a reference (r:13 here), but with null (N) to be working.

So looks like a hick-up triggered by referencing some earlier object (take care that this referencing is not the same as references / variable aliases in userland PHP).

So how to go on?

It's time you start to isolate your problem and create a self-contained, reproduceable example out of it. This is necessary to further look into the issue as you see it. This is important for two reasons:

  1. If this is a bug in PHP, it should be reported, a regression test written and added to PHP Q&A and then the bug fixed (if not yet fixed).
  2. If you're looking for a workaround, reproducing the original problem is necessary to create a workaround quickly and easily.

I did run some tests for a work-around, however so far I'm not able to reproduce your issue so I can not really suggest how to work around the issue as I don't have it here.

like image 21
hakre Avatar answered Nov 20 '22 17:11

hakre


There has been a bug closed recently regarding an issue similar to this. Depending on the php version you are running, you may still be affected. The affected version is 5.3.15.

Excerpt:

[2012-07-27 16:08 UTC] j dot henge-ernst at interexa dot de

The problem is that the unserialize of ArrayIterator (and also maybe ArrayObject or other SPL classes) can not dereference object references.

If you are affected by this bug, then you may be right that it is related to dereferencing. Perhaps try a newer version of PHP to see if it still happens.

like image 1
Travis Hegner Avatar answered Nov 20 '22 18:11

Travis Hegner