mirror of
https://github.com/php/doc-ru.git
synced 2026-03-27 01:02:19 +01:00
646 lines
22 KiB
XML
646 lines
22 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
||
<!-- EN-Revision: 5e15a6c3e4d5819102361ae78e73c90a06238c8a Maintainer: irker Status: ready -->
|
||
<!-- Reviewed: no -->
|
||
<sect1 xml:id="language.oop5.magic" xmlns="http://docbook.org/ns/docbook">
|
||
<title>Магические методы</title>
|
||
<para>
|
||
Магические методы — методы, которые переопределяют действие PHP по умолчанию,
|
||
когда над объектом выполняются отдельные действия.
|
||
</para>
|
||
<caution>
|
||
<simpara>
|
||
Имена методов, которые начинаются с двух символов подчёркивания <literal>__</literal>, зарезервированы PHP.
|
||
Не рекомендуется использовать имена методов с символами __ в PHP, если не требуется
|
||
магическая функциональность.
|
||
</simpara>
|
||
</caution>
|
||
<para>
|
||
Следующие названия методов считаются магическими:
|
||
<link linkend="object.construct">__construct()</link>,
|
||
<link linkend="object.destruct">__destruct()</link>,
|
||
<link linkend="object.call">__call()</link>,
|
||
<link linkend="object.callstatic">__callStatic()</link>,
|
||
<link linkend="object.get">__get()</link>,
|
||
<link linkend="object.set">__set()</link>,
|
||
<link linkend="object.isset">__isset()</link>,
|
||
<link linkend="object.unset">__unset()</link>,
|
||
<link linkend="object.sleep">__sleep()</link>,
|
||
<link linkend="object.wakeup">__wakeup()</link>,
|
||
<link linkend="object.serialize">__serialize()</link>,
|
||
<link linkend="object.unserialize">__unserialize()</link>,
|
||
<link linkend="object.tostring">__toString()</link>,
|
||
<link linkend="object.invoke">__invoke()</link>,
|
||
<link linkend="object.set-state">__set_state()</link>,
|
||
<link linkend="object.clone">__clone()</link> и
|
||
<link linkend="object.debuginfo">__debugInfo()</link>
|
||
</para>
|
||
|
||
<warning>
|
||
<simpara>
|
||
Магические методы, за исключением
|
||
<link linkend="object.construct">__construct()</link>,
|
||
<link linkend="object.destruct">__destruct()</link>
|
||
и <link linkend="object.clone">__clone()</link>,
|
||
<emphasis>ДОЛЖНЫ</emphasis> объявляться как <literal>public</literal>,
|
||
иначе PHP выдаст ошибку уровня <constant>E_WARNING</constant>.
|
||
До PHP 8.0.0 для магических методов
|
||
<link linkend="object.sleep">__sleep()</link>,
|
||
<link linkend="object.wakeup">__wakeup()</link>,
|
||
<link linkend="object.serialize">__serialize()</link>,
|
||
<link linkend="object.unserialize">__unserialize()</link>
|
||
и <link linkend="object.set-state">__set_state()</link>
|
||
не выполнялась проверка.
|
||
</simpara>
|
||
</warning>
|
||
<warning>
|
||
<para>
|
||
Если в определении магического метода указали объявления типа, они должны повторять сигнатуру,
|
||
которую описывает этот документ.
|
||
Иначе возникает фатальная ошибка.
|
||
До PHP 8.0.0 диагностические сообщения не отправлялись.
|
||
Однако методы <link linkend="object.construct">__construct()</link>
|
||
и <link linkend="object.destruct">__destruct()</link> не должны объявлять возвращаемый тип;
|
||
иначе возникает фатальная ошибка.
|
||
</para>
|
||
</warning>
|
||
|
||
<sect2 xml:id="language.oop5.magic.sleep">
|
||
<title>
|
||
Методы <link linkend="object.sleep">__sleep()</link>
|
||
и <link linkend="object.wakeup">__wakeup()</link>
|
||
</title>
|
||
|
||
<methodsynopsis xml:id="object.sleep">
|
||
<modifier>public</modifier> <type>array</type><methodname>__sleep</methodname>
|
||
<void/>
|
||
</methodsynopsis>
|
||
<methodsynopsis xml:id="object.wakeup">
|
||
<modifier>public</modifier> <type>void</type><methodname>__wakeup</methodname>
|
||
<void/>
|
||
</methodsynopsis>
|
||
|
||
<para>
|
||
Функция <function>serialize</function> проверяет, определили ли
|
||
в классе метод с магическим именем <link linkend="object.sleep">__sleep()</link>.
|
||
Если метод определили, он выполняется перед сериализацией. Метод может
|
||
очистить объект и должен вернуть массив с именами
|
||
всех переменных этого объекта, которые должны быть сериализованы.
|
||
Если метод ничего не возвращает, то сериализуется константа &null;
|
||
и выдаётся предупреждение <constant>E_NOTICE</constant>.
|
||
</para>
|
||
<note>
|
||
<para>
|
||
Методу <link linkend="object.sleep">__sleep()</link> нельзя
|
||
возвращать имена закрытых свойств в родительских классах. Это
|
||
приведёт к ошибке уровня <constant>E_NOTICE</constant>.
|
||
Вместо этого используйте метод
|
||
<link linkend="object.serialize">__serialize()</link>.
|
||
</para>
|
||
</note>
|
||
<note>
|
||
<para>
|
||
Начиная с PHP 8.0.0 возврат из метода <link linkend="object.sleep">__sleep()</link>
|
||
значения, которое не является массивом,
|
||
генерирует предупреждение. Раньше выдавалось уведомление.
|
||
</para>
|
||
</note>
|
||
<para>
|
||
Назначение метода <link linkend="object.sleep">__sleep()</link> —
|
||
зафиксировать отложенные данные или выполнить аналогичные задачи очистки.
|
||
Метод также будет полезным, когда требуется сохранить только часть объекта.
|
||
</para>
|
||
<para>
|
||
И наоборот, функция <function>unserialize</function> проверяет
|
||
наличие метода с магическим именем
|
||
<link linkend="object.wakeup">__wakeup()</link>.
|
||
Если метод определили, функция может восстанавливать любые ресурсы,
|
||
которые может иметь объект.
|
||
</para>
|
||
<para>
|
||
Назначение метода <link linkend="object.wakeup">__wakeup()</link> —
|
||
восстановить соединения с базой данных,
|
||
которые могли потеряться во время сериализации,
|
||
и выполнить другие задачи повторной инициализации.
|
||
</para>
|
||
<example>
|
||
<title>Пример засыпания и пробуждения</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?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();
|
||
}
|
||
}
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
</sect2>
|
||
|
||
<sect2 xml:id="language.oop5.magic.serialize">
|
||
<title>
|
||
Методы <link linkend="object.serialize">__serialize()</link>
|
||
и <link linkend="object.unserialize">__unserialize()</link>
|
||
</title>
|
||
|
||
<methodsynopsis xml:id="object.serialize">
|
||
<modifier>public</modifier> <type>array</type><methodname>__serialize</methodname>
|
||
<void/>
|
||
</methodsynopsis>
|
||
<methodsynopsis xml:id="object.unserialize">
|
||
<modifier>public</modifier> <type>void</type><methodname>__unserialize</methodname>
|
||
<methodparam><type>array</type><parameter>data</parameter></methodparam>
|
||
</methodsynopsis>
|
||
|
||
<para>
|
||
Функция <function>serialize</function> проверяет, определили ли в классе
|
||
метод с магическим именем <link linkend="object.serialize">__serialize()</link>. Если метод определили,
|
||
функция выполняется перед сериализацией. Метод должен создать и вернуть ассоциативный массив пар ключ и значение,
|
||
которые представляют сериализованную форму объекта.
|
||
Если метод не вернул массив, выбрасывается исключение <classname>TypeError</classname>.
|
||
</para>
|
||
<note>
|
||
<para>
|
||
Если в одном и том же объекте определили оба метода —
|
||
и <link linkend="object.serialize">__serialize()</link>, и <link linkend="object.sleep">__sleep()</link>,
|
||
PHP вызовет только метод <link linkend="object.serialize">__serialize()</link>.
|
||
Метод <link linkend="object.sleep">__sleep()</link> проигнорируется.
|
||
Если объект реализует интерфейс <link linkend="class.serializable">Serializable</link>,
|
||
PHP проигнорирует интерфейсный метод <literal>serialize()</literal>, и вместо него
|
||
будет использовать метод <link linkend="object.serialize">__serialize()</link>.
|
||
</para>
|
||
</note>
|
||
<para>
|
||
Назначение метода <link linkend="object.serialize">__serialize()</link> заключается в определении удобного для сериализации
|
||
произвольного представления объекта. Элементам массива разрешается соответствовать свойствам объекта, но это не обязательно.
|
||
</para>
|
||
<para>
|
||
И наоборот, функция <function>unserialize</function> проверяет
|
||
присутствие магического метода <link linkend="object.unserialize">__unserialize()</link>.
|
||
Если метод определили, PHP передаст методу массив, который восстановил и вернул метод
|
||
<link linkend="object.serialize">__serialize()</link>. Затем, если нужно,
|
||
метод восстанавливает свойства объекта из этого массива.
|
||
</para>
|
||
<note>
|
||
<para>
|
||
Если в одном и том же объекте определили оба метода —
|
||
и <link linkend="object.unserialize">__unserialize()</link>, и <link linkend="object.wakeup">__wakeup()</link>,
|
||
PHP вызовет только метод <link linkend="object.unserialize">__unserialize()</link>.
|
||
Метод <link linkend="object.wakeup">__wakeup()</link> проигнорируется.
|
||
</para>
|
||
</note>
|
||
<note>
|
||
<para>
|
||
Функция доступна с PHP 7.4.0.
|
||
</para>
|
||
</note>
|
||
<example>
|
||
<title>Пример сериализации и десериализации</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?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();
|
||
}
|
||
}
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
</sect2>
|
||
|
||
<sect2 xml:id="language.oop5.magic.tostring">
|
||
<title>
|
||
Метод <link linkend="object.tostring">__toString()</link>
|
||
</title>
|
||
<methodsynopsis xml:id="object.tostring">
|
||
<modifier>public</modifier> <type>string</type><methodname>__toString</methodname>
|
||
<void/>
|
||
</methodsynopsis>
|
||
<para>
|
||
Метод <link linkend="object.tostring">__toString()</link> разрешает классу выбирать,
|
||
как класс будет реагировать, когда с ним обращаются как со строкой.
|
||
Например, класс решает, что выведет выражение <literal>echo $obj;</literal>.
|
||
</para>
|
||
<warning>
|
||
<para>
|
||
С PHP 8.0.0 возвращаемое значение соответствует стандартной семантике типов PHP,
|
||
что означает, что значение приводится к строке (<type>string</type>), если возможно,
|
||
и если отключили <link linkend="language.types.declarations.strict">строгую типизацию</link>.
|
||
</para>
|
||
<para>
|
||
Объект, который реализует интерфейс <interfacename>Stringable</interfacename>,
|
||
<emphasis>не</emphasis> будет приниматься объявлением типа <type>string</type>,
|
||
если включили <link linkend="language.types.declarations.strict">строгую типизацию</link>.
|
||
Если такое поведение необходимо, то объявление типа должно принимать
|
||
интерфейс <interfacename>Stringable</interfacename> и строку (<type>string</type>)
|
||
через объединение типов.
|
||
</para>
|
||
<para>
|
||
С PHP 8.0.0 любой класс, в котором описали метод <link linkend="object.tostring">__toString()</link>,
|
||
также будет неявно реализовывать интерфейс <interfacename>Stringable</interfacename> и, таким образом,
|
||
будет проходить проверку типа для этого интерфейса.
|
||
В любом случае рекомендуется явно реализовать интерфейс.
|
||
</para>
|
||
<para>
|
||
В PHP 7.4 возвращаемое значение <emphasis>ДОЛЖНО</emphasis>
|
||
быть строкой (<type>string</type>), иначе выбрасывается исключение <classname>Error</classname>.
|
||
</para>
|
||
<para>
|
||
До PHP 7.4.0 возвращаемое значение <emphasis>должно</emphasis> было быть
|
||
строкой (<type>string</type>), иначе выдавалась фатальная
|
||
ошибка <constant>E_RECOVERABLE_ERROR</constant>.
|
||
</para>
|
||
</warning>
|
||
<warning>
|
||
<simpara>
|
||
До PHP 7.4.0 нельзя было выбрасывать исключение из метода
|
||
<link linkend="object.tostring">__toString()</link>.
|
||
Это приводило к фатальной ошибке.
|
||
</simpara>
|
||
</warning>
|
||
<example>
|
||
<title>Простой пример</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
// Объявление простого класса
|
||
class TestClass
|
||
{
|
||
public $foo;
|
||
|
||
public function __construct($foo)
|
||
{
|
||
$this->foo = $foo;
|
||
}
|
||
|
||
public function __toString()
|
||
{
|
||
return $this->foo;
|
||
}
|
||
}
|
||
|
||
$class = new TestClass('Привет');
|
||
echo $class;
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
Привет
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
</sect2>
|
||
|
||
<sect2 xml:id="language.oop5.magic.invoke">
|
||
<title>
|
||
Метод <link linkend="object.invoke">__invoke()</link>
|
||
</title>
|
||
<methodsynopsis xml:id="object.invoke">
|
||
<type>mixed</type><methodname>__invoke</methodname>
|
||
<methodparam rep="repeat"><parameter>values</parameter></methodparam>
|
||
</methodsynopsis>
|
||
<para>
|
||
Метод <link linkend="object.invoke">__invoke()</link>
|
||
вызывается, когда скрипт пытается выполнить объект как функцию.
|
||
</para>
|
||
<example>
|
||
<title>Пример использования метода <link linkend="object.invoke">__invoke()</link></title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
class CallableClass
|
||
{
|
||
public function __invoke($x)
|
||
{
|
||
var_dump($x);
|
||
}
|
||
}
|
||
|
||
$obj = new CallableClass();
|
||
|
||
$obj(5);
|
||
var_dump(is_callable($obj));
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
int(5)
|
||
bool(true)
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
<example>
|
||
<title>Пример использования метода <link linkend="object.invoke">__invoke()</link></title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
class Sort
|
||
{
|
||
private $key;
|
||
|
||
public function __construct(string $key)
|
||
{
|
||
$this->key = $key;
|
||
}
|
||
|
||
public function __invoke(array $a, array $b): int
|
||
{
|
||
return $a[$this->key] <=> $b[$this->key];
|
||
}
|
||
}
|
||
|
||
$customers = [
|
||
['id' => 1, 'first_name' => 'John', 'last_name' => 'Do'],
|
||
['id' => 3, 'first_name' => 'Alice', 'last_name' => 'Gustav'],
|
||
['id' => 2, 'first_name' => 'Bob', 'last_name' => 'Filipe']
|
||
];
|
||
|
||
// Сортировка клиентов по имени
|
||
usort($customers, new Sort('first_name'));
|
||
print_r($customers);
|
||
|
||
// Сортировка клиентов по фамилии
|
||
usort($customers, new Sort('last_name'));
|
||
print_r($customers);
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
Array
|
||
(
|
||
[0] => Array
|
||
(
|
||
[id] => 3
|
||
[first_name] => Alice
|
||
[last_name] => Gustav
|
||
)
|
||
|
||
[1] => Array
|
||
(
|
||
[id] => 2
|
||
[first_name] => Bob
|
||
[last_name] => Filipe
|
||
)
|
||
|
||
[2] => Array
|
||
(
|
||
[id] => 1
|
||
[first_name] => John
|
||
[last_name] => Do
|
||
)
|
||
|
||
)
|
||
Array
|
||
(
|
||
[0] => Array
|
||
(
|
||
[id] => 1
|
||
[first_name] => John
|
||
[last_name] => Do
|
||
)
|
||
|
||
[1] => Array
|
||
(
|
||
[id] => 2
|
||
[first_name] => Bob
|
||
[last_name] => Filipe
|
||
)
|
||
|
||
[2] => Array
|
||
(
|
||
[id] => 3
|
||
[first_name] => Alice
|
||
[last_name] => Gustav
|
||
)
|
||
|
||
)
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
</sect2>
|
||
|
||
<sect2 xml:id="language.oop5.magic.set-state">
|
||
<title>
|
||
Метод <link linkend="object.set-state">__set_state()</link>
|
||
</title>
|
||
<methodsynopsis xml:id="object.set-state">
|
||
<modifier>static</modifier> <type>object</type><methodname>__set_state</methodname>
|
||
<methodparam><type>array</type><parameter>properties</parameter></methodparam>
|
||
</methodsynopsis>
|
||
<para>
|
||
Этот <link linkend="language.oop5.static">статический</link> метод
|
||
вызывается для тех классов, которые экспортируются функцией
|
||
<function>var_export</function>.
|
||
</para>
|
||
<para>
|
||
Единственный параметр метода — массив, который содержит экспортируемые
|
||
свойства в виде <literal>['property' => value, ...]</literal>.
|
||
</para>
|
||
<example>
|
||
<title>Пример использования метода <link linkend="object.set-state">__set_state()</link></title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?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);
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
string(60) "A::__set_state(array(
|
||
'var1' => 5,
|
||
'var2' => 'foo',
|
||
))"
|
||
object(A)#2 (2) {
|
||
["var1"]=>
|
||
int(5)
|
||
["var2"]=>
|
||
string(3) "foo"
|
||
}
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
<note>
|
||
<simpara>
|
||
При экспорте объекта функция <function>var_export</function> не проверяет,
|
||
реализует ли класс объекта метод <link linkend="object.set-state">__set_state()</link>,
|
||
поэтому повторный импорт объектов выбросит исключение <classname>Error</classname>,
|
||
если метод __set_state() не реализовали. В частности, это относится к ряду внутренних классов.
|
||
</simpara>
|
||
<simpara>
|
||
Программист несет ответственность за то, чтобы повторно
|
||
импортировались только те объекты, класс которых реализует метод __set_state().
|
||
</simpara>
|
||
</note>
|
||
</sect2>
|
||
|
||
<sect2 xml:id="language.oop5.magic.debuginfo">
|
||
<title>
|
||
Метод <link linkend="object.debuginfo">__debugInfo()</link>
|
||
</title>
|
||
<methodsynopsis xml:id="object.debuginfo">
|
||
<type>array</type><methodname>__debugInfo</methodname>
|
||
<void/>
|
||
</methodsynopsis>
|
||
<para>
|
||
Этот метод вызывается функцией <function>var_dump</function>, когда
|
||
необходимо вывести список свойств объекта. Если этот метод не определили,
|
||
выводится каждое свойство объекта c модификаторами public, protected и private.
|
||
</para>
|
||
<example>
|
||
<title>Пример использования метода <link linkend="object.debuginfo">__debugInfo()</link></title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?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));
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
object(C)#1 (1) {
|
||
["propSquared"]=>
|
||
int(1764)
|
||
}
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
</sect2>
|
||
</sect1><!-- Keep this comment at the end of the file
|
||
Local variables:
|
||
mode: sgml
|
||
sgml-omittag:t
|
||
sgml-shorttag:t
|
||
sgml-minimize-attributes:nil
|
||
sgml-always-quote-attributes:t
|
||
sgml-indent-step:1
|
||
sgml-indent-data:t
|
||
indent-tabs-mode:nil
|
||
sgml-parent-document:nil
|
||
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
|
||
sgml-exposed-tags:nil
|
||
sgml-local-catalogs:nil
|
||
sgml-local-ecat-files:nil
|
||
End:
|
||
vim600: syn=xml fen fdm=syntax fdl=2 si
|
||
vim: et tw=78 syn=sgml
|
||
vi: ts=1 sw=1
|
||
-->
|