Магические методы — это специальные методы, которые переопределяют действие PHP по умолчанию, когда над объектом выполняются определенные действия.

Осторожность

Все имена методов, начинающиеся с __, зарезервированы PHP. Поэтому не рекомендуется использовать такие имена методов, если только это не переопределяет поведение PHP.

Следующие имена методов считаются магическими: __construct() , __destruct() , __call( ) , __callStatic( ) , __get() , __set() , __isset() , __unset() , __sleep() , __wakeup() , __serialize( ) , __unserialize() , __toString() , __invoke() , __set_state() , __clone() и __debugInfo() .

Предупреждение

Все магические методы, за исключением __construct() , __destruct() и __clone() , должны быть объявлены как public, иначе E_WARNINGбудет сгенерировано сообщение an. До версии PHP 8.0.0 для магических методов __sleep() , __wakeup() , __serialize() , __unserialize() и __set_state() не выдавалась диагностика .

Предупреждение

Если в определении магического метода используются объявления типов, они должны быть идентичны сигнатуре, описанной в этом документе. В противном случае выдается фатальная ошибка. До версии PHP 8.0.0 никакие диагностические сообщения не выдавались. Однако __construct() и __destruct() не должны объявлять возвращаемый тип; в противном случае выдается фатальная ошибка.

__sleep() и __wakeup()

public __sleep(): array
public __wakeup(): void

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

Примечание :

__sleep () не может возвращать имена частных свойств в родительских классах. Это приведет к E_NOTICEошибке уровня. Вместо этого используйте __serialize() .

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

И наоборот, unserialize() проверяет наличие функции с магическим именем __wakeup() . Если она присутствует, эта функция может восстановить любые ресурсы, которые может иметь объект.

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

Пример #1 Сон и пробуждение

<?php
class Connection
{
    protected $link;
    private $dsn, $username, $password;
    
    public function __construct($dsn, $username, $password)
    {
        $this->dsn = $dsn;
        $this->username = $username;
        $this->password = $password;
        $this->connect();
    }
    
    private function connect()
    {
        $this->link = new PDO($this->dsn, $this->username, $this->password);
    }
    
    public function __sleep()
    {
        return array('dsn', 'username', 'password');
    }
    
    public function __wakeup()
    {
        $this->connect();
    }
}?>

__serialize() и __unserialize() 

public __serialize(): array
public __unserialize(array $data): void

serialize() проверяет, есть ли в классе функция с магическим именем __serialize() . Если это так, эта функция выполняется до любой сериализации. Он должен создать и вернуть ассоциативный массив пар ключ/значение, представляющий сериализованную форму объекта. Если массив не возвращается, будет выброшена ошибка TypeError .

Примечание :

Если и __serialize() , и __sleep() определены в одном и том же объекте, будет вызываться только __serialize() . __sleep() будет игнорироваться. Если объект реализует интерфейс Serializable , метод интерфейса serialize()будет проигнорирован и вместо него будет использоваться __serialize() .

Предполагаемое использование __serialize() состоит в том, чтобы определить удобное для сериализации произвольное представление объекта. Элементы массива могут соответствовать свойствам объекта, но это не обязательно.

И наоборот, unserialize() проверяет наличие функции с магическим именем __unserialize() . Если он присутствует, этой функции будет передан восстановленный массив, который был возвращен из __serialize() . Затем он может восстановить свойства объекта из этого массива по мере необходимости.

Примечание :

Если и __unserialize() , и __wakeup() определены в одном и том же объекте, будет вызываться только __unserialize() . __wakeup() будет игнорироваться.

Примечание :

Эта функция доступна начиная с PHP 7.4.0.

Пример #2 Сериализация и десериализация

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

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

    private function connect()
    {
        $this->link = new PDO($this->dsn, $this->username, $this->password);
    }

    public function __serialize(): array
    {
        return [
          'dsn' => $this->dsn,
          'user' => $this->username,
          'pass' => $this->password,
        ];
    }

    public function __unserialize(array $data): void
    {
        $this->dsn = $data['dsn'];
        $this->username = $data['user'];
        $this->password = $data['pass'];

        $this->connect();
    }
}?>

__toString()

public __toString(): string

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

Предупреждение

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

Начиная с PHP 8.0.0, любой класс, содержащий метод __toString() , также будет неявно реализовывать интерфейс Stringable и, таким образом, будет проходить проверку типа для этого интерфейса. В любом случае рекомендуется явно реализовать интерфейс.

В PHP 7.4 возвращаемое значение должно быть строкой , иначе выдается ошибка .

До PHP 7.4.0 возвращаемое значение должно быть строкой , в противном случае выдается фатальная E_RECOVERABLE_ERROR ошибка.

Предупреждение

До версии PHP 7.4.0 было невозможно создать исключение из метода __toString() . Это приведет к фатальной ошибке.

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

<?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;
?>

__invoke()

__invoke( ...$values): mixed

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

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

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

__set_state()

static __set_state(array $properties): object

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

Единственным параметром этого метода является массив, содержащий экспортируемые свойства в виде ['property' => value, ...].

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

<?php

class A
{
    public $var1;
    public $var2;

    public static function __set_state($an_array)
    {
        $obj = new A;
        $obj->var1 = $an_array['var1'];
        $obj->var2 = $an_array['var2'];
        return $obj;
    }
}

$a = new A;
$a->var1 = 5;
$a->var2 = 'foo';

$b = var_export($a, true);
var_dump($b);
eval('$c = ' . $b . ';');
var_dump($c);
?>

Приведенный выше пример выведет:

string(60) "A::__set_state(array( 
   'var1' => 5, 
   'var2' => 'foo', 
))" 
object(A)#2 (2) { 
  ["var1"]=> 
  int( 5) 
  ["var2"]=> 
  строка(3) "foo" 
}

Примечание . При экспорте объекта var_export() не проверяет, реализовано ли __set_state() классом объекта, поэтому повторный импорт объектов приведет к исключению ошибки , если __set_state() не реализовано. В частности, это влияет на некоторые внутренние классы. Программист должен убедиться, что повторно импортируются только те объекты, класс которых реализует __set_state().

__debugInfo()

__debugInfo (): array

Этот метод вызывается функцией var_dump() при сбросе объекта для получения свойств, которые должны быть показаны. Если метод не определен для объекта, будут показаны все общедоступные, защищенные и частные свойства.

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

<?php
class C {
    private $prop;

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

    public function __debugInfo() {
        return [
            'propSquared' => $this->prop ** 2,
        ];
    }
}

var_dump(new C(42));
?>