PHP: Magic Methods - Manual

downloads | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | conferences | my php.net

search for in the

Ключевое слово "final" Patterns

Last updated: Sat, 27 Jan 2007

view this page in

# 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.

Предостережение
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.

The intended use of `__sleep` is to close any database connections that the object may have, 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.

Пример 19-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()
{
mysql_close($this->link);
}
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.

Пример 19-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;
?>`

Результат выполнения данного примера:

    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().

## `__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, ...)`.

add a note User Contributed Notes
Magic Methods
Dérico Filho
23-Jan-2007 07:33
`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`
alastair dot cooke at transport dot nsw dot gov dot au
22-Oct-2006 09:00
`<?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 at work-at dot co dot jp
02-Sep-2006 12:32
`$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];
}`
taylorbarstow at google's mail service
15-May-2006 01:54
`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.`
rayRO
02-Apr-2006 09:55
`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`
ksamvel at gmail dot com
10-Feb-2006 09:29
`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() . "<br>";
echo "oB::oAVar " . $oB->getAVar() . "<br>";
echo "oB::oBVar " . $oB->getBVar() . "<br>";
echo "<br>";
$oB->copyA( $oA);
echo "oA::oAVar " . $oA->getAVar() . "<br>";
echo "oB::oAVar " . $oB->getAVar() . "<br>";
echo "oB::oBVar " . $oB->getBVar() . "<br>";
Output:
oA::oAVar 4
oB::oAVar 5
oB::oBVar 6
oA::oAVar 4
oB::oAVar 4
oB::oBVar 6`
b dot schoppmeier at bas-consult dot de
26-Jan-2006 03:18
`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`
docey
09-Dec-2005 08:44
`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`
martin dot goldinger at netserver dot ch
15-Aug-2005 04:47
`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);
}
};
?>`
jeffxlevy at gmail dot com
13-Aug-2005 06:26
`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 at hotmail dot com
12-Aug-2005 05:06
`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.`
Domenic Denicola
23-Jun-2005 08:52
`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();
?>`
elias
11-Apr-2005 09:48
`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`
Gedoon-S
07-Apr-2005 11:26
`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.`
ddavenport at newagedigital dot com
26-Jan-2005 11:09
`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.`
krisj1010 at gmail.com
09-Jan-2005 12:09
`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.`
add a note
Ключевое слово "final" Patterns

Last updated: Sat, 27 Jan 2007
show source | credits | stats | sitemap | contact | advertising | mirror sites
Copyright © 2001-2007 The PHP Group
All rights reserved.
This mirror generously provided by: Yahoo! Inc.
Last updated: Sat Mar 10 09:32:46 2007 PST