1
0
mirror of https://github.com/php/doc-ru.git synced 2026-03-27 01:02:19 +01:00
Files
archived-doc-ru/language/oop5/magic.xml
Mikhail Alferov 69f47a3342 Update magic.xml
2024-05-29 12:38:04 +03:00

646 lines
22 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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
-->