Delicious Bookmark this on Delicious Share on Facebook SlashdotSlashdot It! Digg! Digg



PHP : Language Reference : Classes and Objects (PHP 4) : The magic functions __sleep and __wakeup

The magic functions __sleep and __wakeup

serialize() checks if your class has a function with the magic name __sleep. If so, that function is being run prior to any serialization. It can clean up the object and is supposed to return an array with the names of all variables of that object that should be serialized. If the method doesn't return anything then NULL is serialized and E_NOTICE is issued.

The intended use of __sleep is to commit pending data or perform similar cleanup tasks. Also, the function is useful if you have very large objects which need not be saved completely.

Conversely, unserialize() checks for the presence of a function with the magic name __wakeup. If present, this function can reconstruct any resources that object may have.

The intended use of __wakeup is to reestablish any database connections that may have been lost during serialization and perform other reinitialization tasks.

Code Examples / Notes » language.oop.magic_functions

php

To cut down the code in jan at sorgalla dot com's solution:
<?php
class foo {
 function __sleep() {
    return( array_keys( get_object_vars( &$this ) ) );
 }
}
?>


darrylkuhn

This is pretty obvious, but as a note the __wakeup method cannot be used to require in the definition of the class for deserialization (although it would be nice... perhaps in future versions of php serialization might store the __wakeup method when an object is serialized to accomplish this).

06-jan-2005 04:09

There is a weird behavior occuring when serializing/unserializing a class derived from a (abstract) base class. The base may not unserialize itself if it has private members, because even if the derived class' __wakeup function is contains only parent::__wakeup() as its code, the unserialization takes place from the children's __wakeup() function and the parent's __wakeup() wont be called (as of php 5.0.3) and hence, private members from the parent wont ever be restored, even though they do serialize properly. This might/might not be a bug, but it is evil and is works not nicely with object oriented concepts. A workaround would be to put the base class' members protected or to (un)serialize manually the base class' via its derived class' __sleep() and __wakeup()

rkelly

The documentation above says this function is 'supposed' to return an array with the names of variables to be saved.  What this in reality means is that if you dont, bad things will happen.
I had forgotten to return the requested array after using __sleep to shutdown LDAP connections in an object, and was attempting to save the object as part of a session.
Since __sleep was not returning anything, the serialisation was evidently not returning anything either.  This broke the stored session data, as one of the fields was completely missing and the unserialize parsing then failed.  Only by hacking through the serialised session data by hand was I able to track down the error, and my silly mistake.
Perhaps PHP should have some way of dealing with this eventuality (treat it like an empty array?) so that it doesnt break sessions...?
Anyway, hopefully this might save a few people the hours of headache I just went through.  Make sure you return something from this function!


michael_muir

The description isn't particularly clear about this, but it appears that if any variables identified in the array returned by __sleep() are objects, __sleep() will in turn be called on those objects. This is particularly useful if you have many levels of subordinate objects (not subclasses) stored in (for example) arrays. Simply return the array name from the 'top level' object's __sleep() and define a __sleep() for the object's class within the subordinate arrays and it shall be called for each.

jacobmather

Something interesting to point out...
the magic __wakeup() function has access to global variables... such as... $_POST! How interesing... I think i'm going to use this to enhance template handeling. Interesting to think of though. Just make your template object referance the information it needs from inside the __wakeup routine, and make it display the initial page when the constructor is run... store it in a session variable.
Sorry I don't have any code examples at the time... but it's just another odd option you have, like variable variable functions ... (i.e. $c = 'count'; $NumOfEntries = $c($array);)


j dot gizmo

re: last post
are you sure the problem with global variables doesn't arise from the use of references?
<?
class test
{
 function __wakeup ()
 {
   global $link;
   
   //will not work
   $link =& $this;
   //will work
  $GLOBALS['link'] =& $this;
 }
}
?>
see the section on references for further details


mccann

It is difficult to store objects in sessions as PHP doesn't necessarily have the correct class definitions on hand to unserialize the objects. Below is a simple session class that can act as a container for objects, but you have to pass the path to the class definition for this to work...
Assume you have this class defined in Session.php:
class Session
{
# attributes
var $_objects; # hash of named objects- of anything you want! Woot!
var $_classes; # hash of classes- This should import all classes required for objects in the session
var $_saved_objects;
function Session()
{
if (isset($_SESSION['__SESSSION_GLOBAL_OBJ']))
{$this=$_SESSION['__SESSSION_GLOBAL_OBJ'];}
else
{
$this->_objects=array();
$this->_classes=array();
$this->_saved_objects=array();
$_SESSION['__SESSSION_GLOBAL_OBJ']=$this;
}
}
function setObject($name,&$object/*, Required Class Paths */)
{
for ($i=2;$i<func_num_args();$i++)
{
if (!in_array(func_get_arg($i),$this->_classes))
{$this->_classes[]=func_get_arg($i);}
}
$this->_objects[$name]=&$object;
}
function getObject($name)
{
if (isset($this->_objects[$name]))
{return $this->_objects[$name];}
return false;
}
function __sleep()
{
# Serialize all objects
$keys = array_keys($this->_objects);
for ($i=0;$i<count($keys);$i++)
{$this->_saved_objects[$keys[$i]] = serialize($this->_objects[$keys[$i]]);}
return array("_saved_forms","_saved_objects","_classes");
}
function __wakeup()
{
# include all classes needed to unserialize objects in this session
for ($i=0;$i<count($this->_classes);$i++)
{include_once($this->_classes[$i]);}
# unserialize objects
$keys = array_keys($this->_saved_objects);
for ($i=0;$i<count($keys);$i++)
{
$this->_objects[$keys[$i]] = unserialize($this->_saved_objects[$keys[$i]]);
unset($this->_saved_objects[$keys[$i]]);
}
}
}
session_start();
if (!isset($_SESSION['__SESSSION_GLOBAL_OBJ']))
{$_SESSION['__SESSSION_GLOBAL_OBJ'] = new MSSession();}
$SESSION = &$_SESSION['__SESSSION_GLOBAL_OBJ'];
=========================================================
Now assume you have another class called MyClass defined in MyClass.php.... you can store it in the session as such:
include_once('Session.php');
include_once('stuff/MyClass.php');
$joe = new MyClass(/*arguments*/);
$SESSION->setObject('joe',$joe,'stuff/MyClass.php');
=========================================================
On another page you can retrieve this object from the session...
include_once('Session.php');
$joe = &$SESSION->getObject('joe');
=========================================================
This works, but you HAVE to pass setObject the path of every class definition you need to use the object you're embedding.


eric

It appears, that although __wakeup() can read from global defined variables // global $variable; , changing those variables don't affect them outside __wakeup().
The same thing seems to happen when an object is being serialized in a session when you start your session. if your __wakeup() function tries to set something within your session it might get overwritten/lost. In my situation it happened when an object in an object in a session tried to change a session variable on __wakup().
In my case, putting the variable in $GLOBAL and moving the variable in the session at a later time, is a satisfying workaround.


jan

If you want to ensure that all object vars are saved completely, try this:
<?php
class Foo
{
   function __sleep() {
       
      $objectVars = get_object_vars($this);
       $serializeVars = array();
      foreach ($objectVars as $key => $val) {
          $serializeVars[] = $sKey;
      }
       return $serializeVars;
   }
}
?>


phreak

I just noticed the following:
if you use $this inside __sleep, it does not change the original instance, because its a copy. very naughty.
the only workaround for this reference - copy problem with __sleep i found is to use global variables, which is not very beautiful.
<?php
class A {
  var $e;
  function __sleep() {
      $this->e = 'sleeping...';
      return array('e');
  }
}
$a = new A();
$a->e = 'MyValue';
echo serialize($a)."\n";
echo $a->e; // echoes "MyValue" - not "sleeping..."
?>
interesting to note:
<?php
echo serialize(&$a); // does what is expected
?>
BUT: thats deprecated.


scott

Here is a sample class and some test statements to demonstrate how __sleep() is used.
If you do not return anything from __sleep(), your object will *not* be serialized.  You must return something from __sleep() to tell serialize what to serialize.
<?
// to test the class
$x = new Scott();
print_r($x);
$y = serialize($x);
$z = unserialize($y);
print_r($z);
// Simple class to test __sleep()
class Scott {
// class variables
var $error;
var $svar = array();
// constructor
function Scott() {
 $this->svar['Hello'] = "World";
}
function __sleep() {
 $this->svar['Hello'] = "Yawn";
 // return list of instance-variables to be serialized
 return array('error', 'svar');
}
function __wakeup() {
 $this->svar['test'] = "I'm here!";
}
}// end class
?>


cwfsp

Following up to rkelly at NO dot whitley dot unimelb dot SPAM dot edu dot au's note regarding __sleep()
__sleep expects you to return an array of object variables that are allowed to be serialized.
Not returning this array -will- result in your object not being serialized, and -will- cause headaches. If you need __sleep() to do cleanup:
1) do your cleanup
2) return the object variables in an array using the code from the comment from php at sharpdreams dot com (below)
search phrases to help people find this info:
php object will not (does not) work in session
session object will not work in subsequent page views
my object won't show up on next page


agland

echoing output (even debugging statements) seems to cause problems in the __sleep() magic function. I suspect this is because, in the case of sessions, PHP is saving the session information (and running __sleep() in your object) after all the output has theoretically all been sent.
This resulted in the serialized object appearing as an empty string in our session files.


adamh

Correction to the above:
If you want to make sure all variables in your class are serialized, don't write a __sleep() function.


Change Language


Follow Navioo On Twitter
class
extends
Constructors
Scope Resolution Operator (::)
parent
Serializing objects - objects in sessions
The magic functions __sleep and __wakeup
References inside the constructor
Comparing objects
eXTReMe Tracker