Магические методы

Имена методов __construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state() and __clone() зарезервированы для "магических" методов в PHP. Не стоит называть свои методы этими именами, если вы не хотите использовать их "магическую" функциональность.

Предостережение

PHP оставляет за собой право все методы, начинающиеся с __, считать "магическими". Не рекомендуется использовать имена методов с __ в PHP, если вы не желаете использовать соответствующий "магический" функционал.


__sleep() и __wakeup()

public array __sleep ( void )
void __wakeup ( void )

Функция serialize() проверяет, присутствует ли в вашем классе метод с "магическим" именем __sleep(). Если это так, то этот метод выполняется прежде любой операции сериализации. Он может очистить объект и предполагается, что будет возвращен массив с именами всех переменных объекта, который должен быть сериализован. Если метод ничего не возвращает кроме NULL, то это значит, что объект сериализован и выдается предупреждение E_NOTICE.

Замечание:

Недопустимо возвращать в __sleep() имена приватных свойств объекта в родительский класс. Это приведет к предупреждению E_NOTICE. Вместо этого вы можете использовать интерфейс Serializable.

Рекомендованное использование __sleep() состоит в завершении работы над данными, ждущими обработки или других подобных задач очистки. Кроме того, этот метод можно выполнять в тех случаях, когда нет необходимости сохранять полностью очень большие объекты.

С другой стороны, функция unserialize() проверяет наличие метода с "магическим" именем __wakeup(). Если такой имеется, то он может воссоздать все ресурсы объекта, принадлежавшие ему.

Обычно __wakeup() используется для восстановления любых соединений с базой данных, которые могли быть потеряны во время операции сериализации и выполнения других операций повторной инициализации.

Пример #1 Sleep и wakeup


Наша группа в телеграмм для обмена идеями, проектами, мыслями, людьми в сфере ИТ г.Ростова-на-Дону: @it_rostov
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()

public string __toString ( void )

Метод __toString() позволяет классу решать самостоятельно, как он должен реагировать при преобразовании в строку. Например, что напечатает echo $obj;. Этот метод должен возвращать строку, иначе выдастся неисправимая ошибка E_RECOVERABLE_ERROR.

Внимание

Нельзя бросить исключение из метода __toString(). Попытка это сделать закончится фатальной ошибкой.


Пример #2 Простой пример

// Объявление простого класса
class TestClass
{
    public $foo;
    public function __construct($foo)
    {
        $this->foo = $foo;
    }
    public function __toString()
    {
        return $this->foo;
    }
}
$class = new TestClass('Привет');
echo $class;

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


Привет

Ранее, до PHP 5.2.0, метод __toString() вызывался только непосредственно в сочетании с функциями echo или print. Начиная с PHP 5.2.0, он вызывается в любом строчном контексте (например, в printf() с модификатором %s), но не в контекстах других типов (например, с %d модификатором). Начиная с PHP 5.2.0, преобразование объекта в строку при отсутствии метода __toString() вызывает ошибку E_RECOVERABLE_ERROR.


__invoke()

mixed __invoke ([ $... ] )

Метод __invoke() вызывается, когда скрипт пытается выполнить объект как функцию.

Замечание:

Данный метод доступен начиная с PHP 5.3.0.

Пример #3 Использование __invoke()

class CallableClass
{
    public function __invoke($x)
    {
        var_dump($x);
    }
}
$obj = new CallableClass;
$obj(5);
var_dump(is_callable($obj));

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


int(5)
bool(true)

__set_state()

static object __set_state ( array $properties )

Этот статический метод вызывается для тех классов, которые экспортируются функцией var_export() начиная с PHP 5.1.0.

Параметр этого метода должен содержать массив, состоящий из экспортируемых свойств в виде array('property' => value, ...).

Пример #4 Использование __set_state() (начиная с PHP 5.1.0)

class A
{
    public $var1;
    public $var2;
    public static function __set_state($an_array) // С PHP 5.1.0
    {
        $obj = new A;
        $obj->var1 = $an_array['var1'];
        $obj->var2 = $an_array['var2'];
        return $obj;
    }
}
$a = new A;
$a->var1 = 5;
$a->var2 = 'foo';
eval('$b = ' . var_export($a, true) . ';'); // $b = A::__set_state(array(
                                            //    'var1' => 5,
                                            //    'var2' => 'foo',
                                            // ));
var_dump($b);

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


object(A)#2 (2) {
["var1"]=>
int(5)
["var2"]=>
string(3) "foo"
}

User Contributed Notes 42 notes



3
ddavenport at newagedigital dot com8 years ago
class SomeStupidStorageClass
{
  public function getContents($pos, $len) { ...stuff... }
}
class CryptedStorageClass extends SomeStupidStorageClass
{
  private $decrypted_block;
  public function getContents($pos, $len) { ...decrypt... }
}


2
staff at pro-unreal dot de6 months ago
class A {
    public static function __set_state($data) {
        return new static();
    }
}
class B extends A {
}
$instance = new B();
eval('$test = ' . var_export($instance, true) . ';');
var_dump($test);
// -> object(B)#2 (0) {
// }


2
jsnell at e-normous dot com5 years ago
class A
{
    public $var1; 
    public static function __set_state($an_array)
    {
        $obj = new A;
        $obj->var1 = $an_array['var1'];  
        return $obj;
    }
}
class B extends A {
}
$b = new B;
$b->var1 = 5;
eval('$new_b = ' . var_export($b, true) . ';'); 
var_dump($new_b);
/*
object(A)#2 (1) {
  ["var1"]=>
  int(5)
}
*/


1
hyponiq at gmail dot com2 years ago
class InvokeNoParams {
 function __invoke()
 {
 print __METHOD__ . PHP_EOL;
 $i = 1;
 foreach (func_get_args() as $arg) {
 print "The value of \$param{$i} is: " . $arg . PHP_EOL;
 ++$i;
 }
        print PHP_EOL;
 }
}
class InvokeSingleParam {
 function __invoke($param1)
 {
 print __METHOD__ . PHP_EOL;
 print "Value of \$param1 is: " . $param1 . PHP_EOL . PHP_EOL;
 }
}
class InvokeMultiParams {
 function __invoke($param1, $param2, $param3) {
 print __METHOD__ . PHP_EOL;
 print "Value of \$param1 is: " . $param1 . PHP_EOL;
 print "Value of \$param2 is: " . $param2 . PHP_EOL;
 print "Value of \$param3 is: " . $param3 . PHP_EOL . PHP_EOL;
 }
}
$no = new InvokeNoParams;
$single = new InvokeSingleParam;
$multi = new InvokeMultiParams;
$no(1, 2, 3);
$single('one param');
$multi('param 1', 'param 2', 'param 3');


1
Anonymous3 years ago
class A {
    protected $test_int = 2;
    protected $test_array = array('key' => 'test');
    protected $test_obj;
    
    function __construct() {
        $this->test_obj = new stdClass();
        }
        
    function __get($prop) {
        return $this->$prop;
        }
        
    function __set($prop, $val) {
        $this->$prop = $val;
        }
    }
$a = new A();


0
danillo dot paiva dot toledo at gmail dot com6 months ago
class a 
{
    public function __toString() {
        list($a) = func_get_args();
        return $a;
    }
}
$a = new a();
echo $a->__toString('PHP'); // PHP


0
daan dot broekhof at gmail dot com1 year ago
set_error_handler(array('My_ToStringFixer', 'errorHandler'));
error_reporting(E_ALL | E_STRICT);
class My_ToStringFixer
{
    protected static $_toStringException;
    public static function errorHandler($errorNumber, $errorMessage, $errorFile, $errorLine)
    {
        if (isset(self::$_toStringException))
        {
            $exception = self::$_toStringException;
            // Always unset '_toStringException', we don't want a straggler to be found later if something came between the setting and the error
            self::$_toStringException = null;
            if (preg_match('~^Method .*::__toString\(\) must return a string value$~', $errorMessage))
                throw $exception;
        }
        return false;
    }
    
    public static function throwToStringException($exception)
    {
        // Should not occur with prescribed usage, but in case of recursion: clean out exception, return a valid string, and weep
        if (isset(self::$_toStringException))
        {
            self::$_toStringException = null;
            return '';
        }
        self::$_toStringException = $exception;
        return null;
    }
}
class My_Class
{
    public function doComplexStuff()
    {
        throw new Exception('Oh noes!');
    }
    public function __toString()
    {
        try
        {
            // do your complex thing which might trigger an exception
            return $this->doComplexStuff();
        }
        catch (Exception $e)
        {
            // The 'return' is required to trigger the trick
            return My_ToStringFixer::throwToStringException($e);
        }
    }
}
$x = new My_Class();
try
{
    echo $x;
}
catch (Exception $e)
{
    echo 'Caught Exception! : '. $e;
}


0
osbertv at yahoo dot com1 year ago
class A
{
    public function __invoke()
    {
        echo "Invoking A() Class";
    }
}
class B 
{
    public $a;
    
    public function __construct()
    {
        $this->a = new A();
    }
    
    public function __invoke()
    {
        echo "Invoking B() Class";
    }
}
$a = new A();
$b = new B();
$a();
$b();
$b->a();


0
Wesley2 years ago
if(strstr(substr($obj,0,1024), 'somestuff')
    echo $obj;
return 'missing somestuff at the start, create container!';
substr() will trigger a __toString aswell as echo $obj;


0
tom3 years ago
class A {
 public $b;
 public $name;
}
class B extends A {
 public $parent;
 public function __wakeup() {
  var_dump($parent->name);
 }
}
$a = new A();
$a->name = "foo";
$a->b = new B();
$a->b->parent = $a;
$s = serialize($a);
$a = unserialize($s);


0
Anonymous3 years ago
class point {
    public $x;
    public $y;
    function __construct($x=0, $y=0) {
        $this->x = (int) $x;
        $this->y = (int) $y;
        }
        
    function __invoke() {
        $args = func_get_args();
        $total_distance = 0;
        $current_loc = $this;
        foreach ($args as $arg) {
            if (is_object($arg) and (get_class($arg) === get_class($this))) {
                $total_distance += sqrt(pow($arg->x - $current_loc->x, 2) + pow((int) $arg->y - $current_loc->y, 2));
                $current_loc = $arg;
                }
            else {
                trigger_error("Arguments must be objects of this class.");
                return;
                }
            }
        return $total_distance;
        }
    
    }
$p1 = new point(1,1);
$p2 = new point(23,-6);
$p3 = new point(15,20);
echo $p1($p2,$p3,$p1); // round trip 73.89


0
moechofe4 years ago
class CallableClass
{
    var $next;
    function __invoke($x)
    {
        var_dump($x);
        return $this;
   }
}
$obj = new CallableClass;
$obj->next = new CallableClass;
var_dump( $obj(5) ); // OK!
var_dump( $obj(5)(6) ); // Parse error
var_dump( $obj->next(7) ); // Fatal error: Call to undefined method CallableClass::next()
var_dump( {$obj->next}(7) ); // Parse error


0
rudie-de-hotblocks at osu1 dot php dot net4 years ago
class my_tag_A{
    public $id='';
    public $href='';
    public $target='';
    public $class='';
    
    public $label='';
    
    function __construct($href, $label){
        $this->href = $href;
        $this->label = $label;
    }
    
    public function __toString(){
        return '<a '.$this->nz_arr(array('id', 'href', 'target', 'class')). ' >' . $this->label . '</a>';
    }
    
    function nz_arr($attrib_arr){
        $s = '';
        foreach($attrib_arr as $attrib){
            $s .= $this->nz($attrib);
        }
        return $s;
    }
    /**
     * Print the tag attribute if it is not blank, such as id="$this->id"
     * @param string $attrib
     * @return string
     */
    function nz($attrib){
        $s = '';
        if($this->$attrib != '') $s = $attrib .' = "' . $this->$attrib . '"';
        return $s;
    }
    //This causes RECURSION because of parsing between double quotes. This is a very UNEXPECTED behaviour!
    function nz_($attrib){
        $s = '';
        if($this->$attrib != '') $s = "$attrib = \"$this->$attrib\"";
        return $s;
    }
    
}//end  class
//usage
$a = new my_tag_A('abc.php', 'ABC'); $a->target = '_blank';
echo $a;
//prints:
//    <a href="abc.php" target="_blank" >ABC</a>


0
patricknegri at gmail dot com5 years ago
class BaseClass
{
    var $__imported;
    var $__imported_functions;
    
    function __construct()
    {
        $__imported = Array();
        $__imported_functions = Array();
    }
    
    function Imports($object)
    {
        $new_imports = new $object();
        $imports_name = get_class($new_imports);
        array_push( $__imported, Array($imports_name,$new_imports) );
        $imports_function = get_class_methods($new_imports);
        foreach ($imports_function as $i=>$function_name)
        {
            $this->__imported_functions[$function_name] = &$new_imports;
        }        
    }
    
    function __call($m, $a)
    {    
        if (array_key_exists($m,$this->__imported_functions))
        {                    
            return call_user_func_array(Array($this->__imported_functions[$m],$m),$a);
        }
        throw new ErrorException ('Call to Undefined Method/Class Function', 0, E_ERROR);
    }
}
class ExternalFunc
{
    function TestB()
    {
        echo "External Imported!";
    }
}
class B extends BaseClass
{
    function __construct()
    {
        $this->Imports("ExternalFunc");
    }
    
    function Msg()
    {
        echo "Hello world<br />";
    }
}
$b = new B();
$b->Msg();
// or call $b->Imports("ExternalFunc");
$b->TestB();
//$b->TestB(1,3,4);


0
jon at webignition dot net5 years ago
class Point {
  protected $x, $y;
  public function __construct($xVal = 0, $yVal = 0) {
    $this->x = $xVal;
    $this->y = $yVal;
  }    
  public function __toString() {      // the function we're interested in...
    return "Point[x=$this->x, y=$this->y]";
  }
}
$point1 = new Point(10, 10);
$point2 = new Point(50, 50);
echo $point1 . '<br />';
echo $point2 . '<br /><br />';


0
rc @ nospam @ vorklift dot sea oh em5 years ago
class foo {
}
class a {
  private $var1;
  function __construct(foo &$obj = NULL) {
    $this->var1 = &$obj;
  }
  /** Return its variables array, if its parent exists and the __sleep method is accessible, call it and push the result into the array and return the whole thing. */
  public function __sleep() {
    $a = array_keys(get_object_vars(&$this));
    if (method_exists(parent, '__sleep')) {
      $p = parent::__sleep();
      array_push($a, $p);
    };
    return $a;
  }
}
class b extends a {
  function __construct(foo &$obj = NULL) {
    parent::__construct($obj);
  }
}
session_start();
$myfoo = &new foo();
$myb = &new b($myfoo);
$myb = unserialize(serialize(&$myb));


0
yanleech at gmail dot com5 years ago
class user {
   /**
    * @var int Gets and sets the user ID
    */
   public $UserID;
   private $_userID;
   public function __construct() {
      // All the magic is in single line:
      // We unset public property, so our setters and getters
      // are used and phpDoc and editors with code completition are happy
      unset($this->UserID);
   }
   public function __set($key, $value) {
      // assign value for key UserID to _userID property
   }
   public function __get($key) {
      // return value of _userID for UserID property
   }
}


0
dhuseby domain getback tld com5 years ago
class Foo {
    public $bar;
    public function __sleep() {
        return array("bar");
    }
}


0
andrew dot minerd at sellingsource dot com6 years ago
        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;
        }


0
amir_abiri at ipcmedia dot com6 years ago
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);


0
alejandro dot gama at gmail dot com6 years ago
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
        );
    }
}


0
adar at darkpoetry dot de6 years ago
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.');');
        }   
    }   
}


0
Drico Filho6 years ago
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


0
jstubbs at work-at dot co dot jp7 years ago
$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];
}


0
taylorbarstow at google's mail service7 years ago
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.
E

Смотрите также:
Описание на ru2.php.net
Описание на php.ru

.

Популярное:


Содержание: