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



PHP : Language Reference : Classes and Objects (PHP 5) : Magic Methods

Magic Methods

The function names __construct, __destruct (see Constructors and Destructors), __call, __get, __set, __isset, __unset (see Overloading), __sleep, __wakeup, __toString, __set_state, __clone and __autoload are magical in PHP classes. You cannot have functions with these names in any of your classes unless you want the magic functionality associated with them.

Caution:

PHP reserves all function names starting with __ as magical. It is recommended that you do not use function names with __ in PHP unless you want some documented magic functionality.

__sleep and __wakeup

serialize() checks if your class has a function with the magic name __sleep. If so, that function is executed 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 do not need to 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 the 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.

Example10.27.Sleep and wakeup

<?php
class Connection {
protected
$link;
private
$server, $username, $password, $db;

public function
__construct($server, $username, $password, $db)
{
$this->server = $server;
$this->username = $username;
$this->password = $password;
$this->db = $db;
$this->connect();
}

private function
connect()
{
$this->link = mysql_connect($this->server, $this->username, $this->password);
mysql_select_db($this->db, $this->link);
}

public function
__sleep()
{
return array(
'server', 'username', 'password', 'db');
}

public function
__wakeup()
{
$this->connect();
}
}
?>


__toString

The __toString method allows a class to decide how it will react when it is converted to a string.

Example10.28.Simple example

<?php
// Declare a simple class
class TestClass
{
public
$foo;

public function
__construct($foo) {
$this->foo = $foo;
}

public function
__toString() {
return
$this->foo;
}
}

$class = new TestClass('Hello');
echo
$class;
?>

The above example will output:

Hello


It is worth noting that before PHP 5.2.0 the __toString method was only called when it was directly combined with echo() or print(). Since PHP 5.2.0, it is called in any string context (e.g. in printf() with %s modifier) but not in other types contexts (e.g. with %d modifier). Since PHP 5.2.0, converting objects without __toString method to string would cause E_RECOVERABLE_ERROR.

__set_state

This static method is called for classes exported by var_export() since PHP 5.1.0.

The only parameter of this method is an array containing exported properties in the form array('property' => value, ...).

Code Examples / Notes » language.oop5.magic

martin dot goldinger

When you use sessions, its very important to keep the sessiondata small, due to low performance with unserialize. Every class shoud extend from this class. The result will be, that no null Values are written to the sessiondata. It will increase performance.
<?
class BaseObject
{
   function __sleep()
   {
    $vars = (array)$this;
    foreach ($vars as $key => $val)
    {
    if (is_null($val))
    {
    unset($vars[$key]);
    }
    }
    return array_keys($vars);
   }
};
?>


andrew dot minerd

Until __sleep is "fixed" (here's hoping), a function that will return ALL members of a given object -- public, protected, AND private:
<?php
public function getPropertyNames(array $filter = NULL)
{
$rc = new ReflectionObject($this);
$names = array();
while ($rc instanceof ReflectionClass)
{
foreach ($rc->getProperties() as $prop)
{
if (!$filter || !in_array($prop->getName(), $filter))
$names[] = $prop->getName();
}
$rc = $rc->getParentClass();
}
return $names;
}
?>


ksamvel

To copy base part of derived class appropriate method in base should be defined. E.g.:
 class A {
   public function setAVar( $oAVar) { $this->oAVar = $oAVar; }
   public function getAVar() { return $this->oAVar; }
   public function copyA( &$roDest) {
     if( $roDest instanceof A)
       $this->oAVar = $roDest->oAVar;
   }
   private $oAVar;
 }
 class B extends A {
   public function setBVar( $oBVar) { $this->oBVar = $oBVar; }
   public function getBVar() { return $this->oBVar; }
   private $oBVar;
 }
 $oA = new A();
 $oB = new B();
 $oA->setAVar( 4);
 $oB->setAVar( 5);
 $oB->setBVar( 6);
 echo "oA::oAVar " . $oA->getAVar() . "
";
 echo "oB::oAVar " . $oB->getAVar() . "
";
 echo "oB::oBVar " . $oB->getBVar() . "
";
 echo "
";
 $oB->copyA( $oA);
 echo "oA::oAVar " . $oA->getAVar() . "
";
 echo "oB::oAVar " . $oB->getAVar() . "
";
 echo "oB::oBVar " . $oB->getBVar() . "
";
Output:
oA::oAVar 4
oB::oAVar 5
oB::oBVar 6
oA::oAVar 4
oB::oAVar 4
oB::oBVar 6


domenic denicola

This small sentence tripped me up for a half an hour:
"It is worth noting that the __toString method will only be called when it is directly combined with echo() or print()."
So code like this will _not_ work, even though you might think it would:
<?
//$x is some variable with a __toString method defined.
$y = "x's value is: " . $x;
$y = "x's value is: " . (string)$x;
?>
In _em_ both situations, $y will contain "x's value is: Object id #42" (or whatever object ID). So, the only recourse I guess is this:
<?
$y = "x's value is: " . $x->__toString();
?>


travis swicegood

There is no need to use eval() to mimic mixins (i.e., multiple inheritance) within PHP 5.  You only need to:
<?php
class MyClass
{
   private $_obj = null;
   public function __construct($obj)
   {
       $this->_obj = $obj;
   }
   public function __call($method, $args)
   {
       if (!method_exists($this->_obj, $method)) {
           throw new Exception("unknown method [$method]");
       }
       return call_user_func_array(
           array($this->_obj, $method),
           $args
       );
   }
}
?>
You could just as easily add an addMixin() method that would allow you to add multiple objects to an array, and then iterate over that array until you found the right method.  As noted, these are referred to as a Mixins in other languages.


alejandro dot gama

There is a trick to store static attributes with the objects in the $_SESSION array:
<?
session_start();
class Classes{
 private $name;
 private $statics;
 function __construct($name){
   $this->name=$name;
   $this->statics=array();
 }
 function setStatic($key,$value){
   if(!is_resource($value))
     $this->statics[$key]=$value;
 }

 function __wakeup(){
   foreach($this->statics as $k=>$v)
     eval($this->name."::\$".$k."=\$this->statics['".$k."'];");
   unset($this);
 }
}
class Object{
 function __sleep(){
   $name=get_class($this);
   if(empty($_SESSION["_classes"]))
     $_SESSION["_classes"]=array();

   if(empty($_SESSION["_classes"][$name])){
     $_SESSION["_classes"][$name]=new Classes($name);

     $from_object=get_object_vars($this);
     $from_class=get_class_vars($name);
     if(count($from_object)>0)
       foreach($from_object as $k=>$value)
         if($clave=array_search($k,$from_class))
           unset($from_class[$clave]);
     if(count($from_class)>0)
       foreach($from_class as $k=>$v)
         $_SESSION["_classes"][$name]->setStatic($k,$v);
   }

 return $from_object;
 }

}
?>
You must inherit all your objects from Object and when you save them in the session the static method from their classes will be store and restore to their original state.
You must have one object from each class (that has static attributes) stored in the session.


b dot schoppmeier

The sequence of events regarding __sleep and __destruct is unusual __ as __destruct is called before __sleep. The following code snippet:
<?php
$sequence = 0;
class foo {
public $stuff;
public function __construct($param) {
global $sequence;
echo "Seq: ", $sequence++, " - constructor\n";
$this->stuff = $param;
}
public function __destruct() {
global $sequence;
echo "Seq: ", $sequence++, " - destructor\n";
}
public function __sleep() {
global $sequence;
echo "Seq: ", $sequence++, " - __sleep\n";
return array("stuff");
}
public function __wakeup() {
global $sequence;
echo "Seq: ", $sequence++, " - __wakeup\n";
}
}
session_start();
$_SESSION["obj"] = new foo("A foo");
?>
yields the output:
Seq: 0 - constructor
Seq: 1 - destructor
Seq: 2 - __sleep
Only when you end your script with a call to session_write_close() as in:
<?php
$sequence = 0;
class foo {
public $stuff;
public function __construct($param) {
global $sequence;
echo "Seq: ", $sequence++, " - constructor\n";
$this->stuff = $param;
}
public function __destruct() {
global $sequence;
echo "Seq: ", $sequence++, " - destructor\n";
}
public function __sleep() {
global $sequence;
echo "Seq: ", $sequence++, " - __sleep\n";
return array("stuff");
}
public function __wakeup() {
global $sequence;
echo "Seq: ", $sequence++, " - __wakeup\n";
}
}
session_start();
$_SESSION["obj"] = new foo("A foo");
session_write_close();
?>
the sequence is as common sense would expect it to be as the following output shows:
Seq: 0 - constructor
Seq: 1 - __sleep
Seq: 2 - destructor


elias

The default toString output is very useful for visual debuggin
because it shows the object id.
There is no function to resolve the id directly(?), but you
can do this:
<?php
function __toString()
{
sscanf((string)$this, "Object id #%d", $id);
return "Object(Template) id #$id";
}
?>
HTH,
elias


smoseley

Suggestion: add a __toArray() magic method that would default as exportation of an object's properties in an associative array.

dérico filho

Since PHP 5.2.0, you'll always get an error like this:
"Object of class foo could not be converted to string"
When one tries to use an object as string, for instance:
class Test{}
echo new Test();
Thus, one way to avoid this problem is to programme the magic method __toString.
However, in the older versions, it would output a string saying that it was an object together a unique obj id. Therefore, the __toString() method must comply with this behaviour.
My suggestion:
class Test{
function __toString(){
if(!isset($this->__uniqid))
$this->__uniqid = md5(uniqid(rand(), true));
return(get_class($this)."@".$this->__uniqid);
}
}
echo new Test();
would output something like this:
Test@6006ba04f5569544c10a588b04849cf7


alejandro dot gama

Referering my previus note: there was an error in the code. But i find a better way:
<?
session_start();
class Classes{
 private $name;
 private $statics;

 function __construct($name){
   $this->name=$name;
   $this->statics=array();
 }

 function setStatic($k,$v){
   if(!is_resource($v))
     $this->statics[$k]=$v;
 }


 function __wakeup(){
   foreach($this->statics as $k=>$v)
     eval($this->name."::\$".$k."=\$this->statics['".$k."'];");
 }
}
function storeStaticAttributes(){
 $classes=get_declared_classes();
 foreach($classes as $name){
   $reflect=new ReflectionClass($name);
   if($reflect->isUserDefined()){
     $statics=$reflect->getStaticProperties();
     if(empty($_SESSION["_classes"]))
       $_SESSION["_classes"]=array();

     if(empty($_SESSION["_classes"][$name]))
       $_SESSION["_classes"][$name]=new Classes($name);
     foreach($statics as $k=>$v)
       $_SESSION["_classes"][$name]->setStatic($k,$v);
   }
 }
}
register_shutdown_function('storeStaticAttributes');
?>


ddavenport

One of the principles of OOP is encapsulation--the idea that an object should handle its own data and no others'.  Asking base classes to take care of subclasses' data, esp considering that a class can't possibly know how many dozens of ways it will be extended, is irresponsible and dangerous.
Consider the following...
<?php
class SomeStupidStorageClass
{
 public function getContents($pos, $len) { ...stuff... }
}
class CryptedStorageClass extends SomeStupidStorageClass
{
 private $decrypted_block;
 public function getContents($pos, $len) { ...decrypt... }
}
?>
If SomeStupidStorageClass decided to serialize its subclasses' data as well as its own, a portion of what was once an encrypted thingie could be stored, in the clear, wherever the thingie was stored.  Obviously, CryptedStorageClass would never have chosen this...but it had to either know how to serialize its parent class's data without calling parent::_sleep(), or let the base class do what it wanted to.
Considering encapsulation again, no class should have to know how the parent handles its own private data.  And it certainly shouldn't have to worry that users will find a way to break access controls in the name of convenience.
If a class wants both to have private/protected data and to survive serialization, it should have its own __sleep() method which asks the parent to report its own fields and then adds to the list if applicable.  Like so....
<?php
class BetterClass
{
 private $content;
 public function __sleep()
 {
   return array('basedata1', 'basedata2');
 }
 public function getContents() { ...stuff... }
}
class BetterDerivedClass extends BetterClass
{
 private $decrypted_block;
 public function __sleep()
 {
   return parent::__sleep();
 }
 public function getContents() { ...decrypt... }
}
?>
The derived class has better control over its data, and we don't have to worry about something being stored that shouldn't be.


adar

Maybe not really new and all in all definitely not the best solution,but if you cant extend a class (if your class alreay extends an abstract or other things like that) you can 'fake' a extend.
<?php
class MyClass
       extends SomeAbstractUnknownClass {
   private $classObject;
   public function __construct ( classObject $classToExtend ) {
       $this->classObject = $classToExtend;
   }
   public function __call($func, $var) {
       if ( !count($var) ) {
           return $this->classObject->$func($var);
       } else {
           $str = '';
           $values = array_values($var);
           for ( $i=0; $i<count($values); $i++ ) {
               $str .= "'".$values[$i]."' ,";
           }  
           $str = substr($str, 0, -2);
           return eval('return $this->classObject->'.$func.'('.$str.');');
       }  
   }  
}
?>
So if you'll do a $myClass->unknownMethod() and it is found neither in MyClass nor in SomeAbstractUnknownClass, MyClass will try to call this method in $classObject.
I use this for 'extending' a UserObject-Class which already extends an other one.
Better solutions are always welcome ;)


otsu dot ni

Looks like __toString() method doesn't have to be declared as public (at least for php 5.2.1).
<?php
class A{
   protected function __toString(){
       return "Nya!\n";
   }
}
class O{
   private function __toString(){
       return "Nyo!\n";
   }
}
$nya=new A; $nyo=new O;
echo $nya,$nyo;
?>


jeffxlevy

Intriguing what happens when __sleep() and __wakeup() and sessions() are mixed. I had a hunch that, as session data is serialized, __sleep would be called when an object, or whatever, is stored in _SESSION. true. The same hunch applied when session_start() was called. Would __wakeup() be called? True. Very helpful, specifically as I'm building massive objects (well, lots of simple objects stored in sessions), and need lots of automated tasks (potentially) reloaded at "wakeup" time. (for instance, restarting a database session/connection).

mastabog

In reply to krisj1010 at gmail.com below:
__sleep() handles protected/private properties very well. You should never rely on get_class_vars() to retrieve property names since this function only returns the public properties. Use the Reflection API instead for that purpose. Better yet, if you know which ones you want to save it is always faster to specify the return array manually.


rayro

If you use the Magical Method '__set()', be shure that the call of
<?php
$myobject->test['myarray'] = 'data';
?>
will not appear!
For that u have to do it the fine way if you want to use __set Method ;)
<?php
$myobject->test = array('myarray' => 'data');
?>
If a Variable is already set, the __set Magic Method already wont appear!
My first solution was to use a Caller Class.
With that, i ever knew which Module i currently use!
But who needs it... :]
There are quiet better solutions for this...
Here's the Code:
<?php
class Caller {
public $caller;
public $module;
function __call($funcname, $args = array()) {
$this->setModuleInformation();
if (is_object($this->caller) && function_exists('call_user_func_array'))
$return = call_user_func_array(array(&$this->caller, $funcname), $args);
else
trigger_error("Call to Function with call_user_func_array failed", E_USER_ERROR);

$this->unsetModuleInformation();
return $return;
}
function __construct($callerClassName = false, $callerModuleName = 'Webboard') {
if ($callerClassName == false)
trigger_error('No Classname', E_USER_ERROR);
$this->module = $callerModuleName;
if (class_exists($callerClassName))
$this->caller = new $callerClassName();
else
trigger_error('Class not exists: \''.$callerClassName.'\'', E_USER_ERROR);
if (is_object($this->caller))
{
$this->setModuleInformation();
if (method_exists($this->caller, '__init'))
$this->caller->__init();
$this->unsetModuleInformation();
}
else
trigger_error('Caller is no object!', E_USER_ERROR);
}
function __destruct() {
$this->setModuleInformation();
if (method_exists($this->caller, '__deinit'))
$this->caller->__deinit();
$this->unsetModuleInformation();
}
function __isset($isset) {
$this->setModuleInformation();
if (is_object($this->caller))
$return = isset($this->caller->{$isset});
else
trigger_error('Caller is no object!', E_USER_ERROR);
$this->unsetModuleInformation();
return $return;
}
function __unset($unset) {
$this->setModuleInformation();
if (is_object($this->caller)) {
if (isset($this->caller->{$unset}))
unset($this->caller->{$unset});
}
else
trigger_error('Caller is no object!', E_USER_ERROR);
$this->unsetModuleInformation();
}
function __set($set, $val) {
$this->setModuleInformation();
if (is_object($this->caller))
$this->caller->{$set} = $val;
else
trigger_error('Caller is no object!', E_USER_ERROR);
$this->unsetModuleInformation();
}
function __get($get) {
$this->setModuleInformation();
if (is_object($this->caller)) {
if (isset($this->caller->{$get}))
$return = $this->caller->{$get};
else
$return = false;
}
else
trigger_error('Caller is no object!', E_USER_ERROR);
$this->unsetModuleInformation();
return $return;
}

function setModuleInformation() {
$this->caller->module = $this->module;
}
function unsetModuleInformation() {
$this->caller->module = NULL;
}
}
// Well this can be a Config Class?
class Config {
public $module;
public $test;
function __construct()
{
print('Constructor will have no Module Information... Use __init() instead!<br />');
print('--> '.print_r($this->module, 1).' <--');
print('<br />');
print('<br />');
$this->test = '123';
}

function __init()
{
print('Using of __init()!<br />');
print('--> '.print_r($this->module, 1).' <--');
print('<br />');
print('<br />');
}

function testFunction($test = false)
{
if ($test != false)
$this->test = $test;
}
}
echo('<pre>');
$wow = new Caller('Config', 'Guestbook');
print_r($wow->test);
print('<br />');
print('<br />');
$wow->test = '456';
print_r($wow->test);
print('<br />');
print('<br />');
$wow->testFunction('789');
print_r($wow->test);
print('<br />');
print('<br />');
print_r($wow->module);
echo('</pre>');
?>
Outputs something Like:
Constructor will have no Module Information... Use __init() instead!
-->  <--
Using of __init()!
--> Guestbook <--
123
456
789
Guestbook


krisj1010

If you are attempting to write an abstract/base class which automates the __sleep process in PHP5 you will run into some trouble if the subclasses which are being serialized have private/protected variables you need to be serialized.  
The reason is, even though get_class($this) within the base class will return the subclass -- get_class_vars(get_class($this)) will *not* return the subclass' protected/private variables.  Which makes sense -- using OO principles.  
However, when automating __sleep it becomes necissary to have access to the private/protected subclass variables because their names have to be returned by __sleep.
So here is the work around:
<?php
public function __sleep()
{
... code ...
$sleepVars    = array_keys((array)$this);
return $sleepVars;
}
?>
Even though array_keys includes more information about the variable names than just the variable names -- it still seems to work appropriately.


taylorbarstow

I've just come accross something interesting relating to storing PHP5 objects in a session.  If you don't provide an __autoload(), then you MUST load the class definition before calling session_start().  I guess that when you call session_start(), any objects in the session are unserialized then and there and placed into $_SESSION.  If you don't provide the class definition before calling session_start(), your object will get the class __PHP_Incomplete_Class, and you won't be able to use it for anything.
Examples:
<?php
session_start();
require_once 'MyClass.php';
$obj = new MyClass;
$_SESSION['obj'] = $obj;
?>
Works fine.  Then on a subsequent page load:
<?php
session_start();
require_once 'MyClass.php';
$_SESSION['obj']->callSomeMethod();
?>
Fatal error:  The script tried to execute a method or access a property of an incomplete object. Please ensure that the class definition "MyClass" of the object you are trying to operate on was loaded _before_ unserialize() gets called or provide a __autoload() function to load the class definition.
But if you do this instead, it works fine:
<?php
require_once 'MyClass.php';
session_start();
$_SESSION['obj']->callSomeMethod();
?>
Hopefully in some future release of PHP, __PHP_Incomplete_Class will be smart enough to check for a class definition at time of use (method call or property operation), and, if the class exists, magically "complete" itself and turn into the desired object.


gedoon-s

I just thought it was worth mentioning that __toString works also with <?= $class ?>. It calls __toString and echoes what was returned just like <?php echo $class; ?> does, but it's shorter.

amir_abiri

Another small thing that is important to note about __sleep() and privte member variables:
<?php
class A
{
 private $a;
 
 public function __construct()
 {
   $this->a = 1;
 }
}
class B extends A
{
 protected $b;
 
 public function __construct()
 {
   parent::__construct();
   $this->b = 2;
 }
 
 function __sleep()
 {
   return array('a', 'b');
 }
}
serialize(new B);
?>
result:
Notice: serialize(): "a" returned as member variable from __sleep() but does not exist in ...
To summerize: in a given class hierarchy in which parent classes contain private member variables, those variables are serialized when __sleep() is not defined. However, once __sleep() is defined, there is no way to make those private member variables serialized as well. From that point on, serialization is performed from the visibility scope of the subclass.
It is particularly important to note this little quirk when designing base classes that their derivables may be serialized, or when subclassing an external library class.


docey

about __sleep and _wakeup, consider using a method like this:
class core
{
var $sub_core; //ref of subcore
var $_sleep_subcore; // place where serialize version of sub_core will be stored
function core(){
 $this->sub_core = new sub_core();
 return true;
}
function __wakeup()
{
 // on wakeup of core, core unserializes sub_core
 // wich it had stored when it was serialized itself
 $this->sub_core = unserialize($this->_sleep_subcore);
 return true;
}
function __sleep()
{
 // sub_core will be serialized when core is serialized.
 // the serialized subcore will be stored as a string inside core.
  $this->_sleep_subcore = serialize($this->sub_core);
  $return_arr[] = "_sleep_subcore";
  return $return_arr;
}
}
class sub_core
{
var $info;
function sub_core()
{
 $this->info["somedata"] = "somedata overhere"
}
function __wakeup()
{
 return true;
}
function __sleep()
{
 $return_arr[] = "info"
 return $return_arr;
}
}
this way subcore is being serialized by core when core is being serialized. subcore handles its own data and core stores it as a serialize string inside itself. on wakeup core unserializes subcore.
this may have a performance cost, but if you have many objects connected this way this is the best way of serializing them. you only need to serialize the the main object wich will serialize all those below which will serialize all those below them again. in effect causing a sort of chainreaction in wich each object takes care of its own info.
offcoarse you always need to store the eventualy serialized string in a safe place. somebody got experience with this way of __wakeup and __sleep.
works in PHP4&5


alastair dot cooke

<?php
echo phpversion();
class moose { function __toString() { return 'a moose'; } }
$foo = new moose();
echo " i have $foo \n" ;
echo $foo;
?>
will give you:
5.1.6
i have Object id #278
a moose
ie __toString does not happen when you interpolate things into a string...


jstubbs

$myclass->foo['bar'] = 'baz';
When overriding __get and __set, the above code can work (as expected) but it depends on your __get implementation rather than your __set. In fact, __set is never called with the above code. It appears that PHP (at least as of 5.1) uses a reference to whatever was returned by __get. To be more verbose, the above code is essentially identical to:
$tmp_array = &$myclass->foo;
$tmp_array['bar'] = 'baz';
unset($tmp_array);
Therefore, the above won't do anything if your __get implementation resembles this:
function __get($name) {
   return array_key_exists($name, $this->values)
       ? $this->values[$name] : null;
}
You will actually need to set the value in __get and return that, as in the following code:
function __get($name) {
   if (!array_key_exists($name, $this->values))
       $this->values[$name] = null;
   return $this->values[$name];
}


Change Language


Follow Navioo On Twitter
Introduction
The Basics
Autoloading Objects
Constructors and Destructors
Visibility
Scope Resolution Operator (::)
Static Keyword
Class Constants
Class Abstraction
Object Interfaces
Overloading
Object Iteration
Patterns
Magic Methods
Final Keyword
Object cloning
Comparing objects
Reflection
Type Hinting
Late Static Bindings
eXTReMe Tracker