魔术函数 __sleep__wakeup

serialize() 检查类中是否有魔术名称 __sleep 的函数。如果这样,该函数将在任何序列化之前运行。它可以清除对象并应该返回一个包含有该对象中应被序列化的所有变量名的数组。

使用 __sleep 的目的是关闭对象可能具有的任何数据库连接,提交等待中的数据或进行类似的清除任务。此外,如果有非常大的对象而并不需要完全储存下来时此函数也很有用。

相反地,unserialize() 检查具有魔术名称 __wakeup 的函数的存在。如果存在,此函数可以重建对象可能具有的任何资源。

使用 __wakeup 的目的是重建在序列化中可能丢失的任何数据库连接以及处理其它重新初始化的任务。


add a note add a note User Contributed Notes
cwfsp at hizzotmail dizzot cizzom nospam
20-Sep-2005 09:35
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
07-Jan-2005 12: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()
j dot gizmo at aon dot at
28-Sep-2004 08:48
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
Eric at Footsteps dot nl
02-Jul-2004 06:29
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.
php at sharpdreams dot com
09-May-2004 03:11
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 ) ) );
  }
}
?>
jacobmather at dwsc dot net
21-Nov-2003 08:23
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);)
mccann at cs dot usm dot maine dot edu
12-Jul-2003 01:38
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.
darrylkuhn at hotmail dot com
12-May-2003 11:04
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).
adamh at densi dot com
18-Apr-2003 01:46
Correction to the above:

If you want to make sure all variables in your class are serialized, don't write a __sleep() function.
jan at sorgalla dot com
08-Apr-2003 12:31
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;
   }
}

?>
rkelly at NO dot whitley dot unimelb dot SPAM dot edu dot au
30-Jan-2003 01:56
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 at dstoutput dot com
08-Oct-2002 03:21
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.
scott at graphictype dot com
02-Aug-2001 02:33
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
?>
agland at squiz dot net
26-Jun-2001 01:38
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.