5

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.

hakre
  • 193,403
  • 52
  • 435
  • 836
varuog
  • 3,031
  • 5
  • 28
  • 56
  • Please also add a [hexdump of your session data](http://stackoverflow.com/questions/1057572/how-can-i-get-a-hex-dump-of-a-string-in-php). Also the `UnexpectedValueException` has a message. Your question does not contain that message so far, please add it. It often contains important information about the problem when unserializing. – hakre Aug 13 '13 at 11:19

3 Answers3

3

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

Community
  • 1
  • 1
Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
  • +1 Looks like something is removed from the SPLObjectStorage when it serializes. Good nose. BTW SPLObjectStorage already implements Serializable. However it still might be too late in the shutdown sequence if you implement it your own. I suspect the problem lies deeper in the object graph of that `RoleStorage`. – hakre Aug 13 '13 at 22:29
2

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.

hakre
  • 193,403
  • 52
  • 435
  • 836
  • +1 I had tried to find resource on Serialization notation but could not find it in PHP DOC. They should have add it there. – varuog Aug 16 '13 at 22:11
  • Currently i have replaced SPLObjectStorage with and abstract class which uses array to store the data and follows same interface as SPLobjectStorage as a temporary workaround. Once i isolate and fix the problem as you said I would just Just switch that Base Abstract Class with SPLObjectStorage – varuog Aug 16 '13 at 22:17
1

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.

Travis Hegner
  • 2,465
  • 1
  • 12
  • 11
  • I am using php 5.4.3 that bug has been reported for `Same behaviour with PHP 5.4.5` too. I guess i might be affected. Though http://php.net/ChangeLog-5.php changelog does not fix the bug.I don't understand why? Do you know which version would be safe to use it? – varuog Aug 08 '13 at 14:08
  • I missed the reference to 5.4.5. Good Catch. It appears that the fix was committed only about 6 weeks ago, so there may not be an official release yet with the fix. You may have to build from source if you truly need this functionality. http://git.php.net/?p=php-src.git;a=commit;h=04db57066deb73ef9c960a2c5bebad49195bc1bb – Travis Hegner Aug 08 '13 at 14:20