Перегрузка

Перегрузка в PHP означает возможность динамически создавать свойства и методы. Эти динамические сущности обрабатываются с помощью магических методов, которые можно создать в классе для различных видов действий.

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

Все методы перегрузки должны быть объявлены как public.

Замечание:

Ни один из аргументов этих магических методов не может быть передан по ссылке.

Замечание:

Интерпретация перегрузки в PHP отличается от большинства объектно-ориентированных языков. Традиционно перегрузка означает возможность иметь несколько одноимённых методов с разным количеством и типами аргументов.

Перегрузка свойств

public __set(string $name, mixed $value): void
public __get(string $name): mixed
public __isset(string $name): bool
public __unset(string $name): void

Метод __set() будет выполнен при записи данных в недоступные (защищённые или приватные) или несуществующие свойства.

Метод __get() будет выполнен при чтении данных из недоступных (защищённых или приватных) или несуществующих свойств.

Метод __isset() будет выполнен при использовании isset() или empty() на недоступных (защищённых или приватных) или несуществующих свойствах.

Метод __unset() будет выполнен при вызове unset() на недоступном (защищённом или приватном) или несуществующем свойстве.

Аргумент $name представляет собой имя вызываемого свойства. Метод __set() содержит аргумент $value, представляющий собой значение, которое будет записано в свойство с именем $name.

Перегрузка свойств работает только в контексте объекта. Данные магические методы не будут вызваны в статическом контексте. Поэтому эти методы не должны объявляться статическими. При объявлении любого магического метода как static будет выдано предупреждение.

Замечание:

Возвращаемое значение __set() будет проигнорировано из-за способа обработки в PHP оператора присваивания. Аналогично, __get() никогда не вызывается при объединении присваиваний, например, подобным образом:

 $a = $obj->b = 8;

Замечание:

PHP не будет вызывать перегруженный метод изнутри того же перегруженного метода. Это означает, что, например, написание return $this->foo внутри __get() вернёт null и вызовет ошибку уровня E_WARNING, если не определено свойство foo, вместо того, чтобы вызвать метод __get() во второй раз. Однако методы перегрузки могут неявно вызывать другие методы перегрузки (например, метод __set() вызывает метод __get()).

Пример #1 Перегрузка свойств с помощью методов __get(), __set(), __isset() и __unset()

<?php
class PropertyTest
{
/** Место хранения перегружаемых данных. */
private $data = array();

/** Перегрузка не применяется к объявленным свойствам. */
public $declared = 1;

/** Здесь перегрузка будет использована только при доступе вне класса. */
private $hidden = 2;

public function
__set($name, $value)
{
echo
"Установка '$name' в '$value'\n";
$this->data[$name] = $value;
}

public function
__get($name)
{
echo
"Получение '$name'\n";
if (
array_key_exists($name, $this->data)) {
return
$this->data[$name];
}

$trace = debug_backtrace();
trigger_error(
'Неопределённое свойство в __get(): ' . $name .
' в файле ' . $trace[0]['file'] .
' на строке ' . $trace[0]['line'],
E_USER_NOTICE);
return
null;
}

public function
__isset($name)
{
echo
"Установлено ли '$name'?\n";
return isset(
$this->data[$name]);
}

public function
__unset($name)
{
echo
"Уничтожение '$name'\n";
unset(
$this->data[$name]);
}

/** Не магический метод, просто для примера. */
public function getHidden()
{
return
$this->hidden;
}
}


echo
"<pre>\n";

$obj = new PropertyTest;

$obj->a = 1;
echo
$obj->a . "\n\n";

var_dump(isset($obj->a));
unset(
$obj->a);
var_dump(isset($obj->a));
echo
"\n";

echo
$obj->declared . "\n\n";

echo
"Давайте поэкспериментируем с закрытым свойством 'hidden':\n";
echo
"Закрытые свойства видны внутри класса, поэтому __get() не используется...\n";
echo
$obj->getHidden() . "\n";
echo
"Закрытые свойства не видны вне класса, поэтому __get() используется...\n";
echo
$obj->hidden . "\n";
?>

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

Установка 'a' в '1'
Получение 'a'
1

Установлено ли 'a'?
bool(true)
Уничтожение 'a'
Установлено ли 'a'?
bool(false)

1

Давайте поэкспериментируем с закрытым свойством 'hidden':
Закрытые свойства видны внутри класса, поэтому __get() не используется...
2
Закрытые свойства не видны вне класса, поэтому __get() используется...
Получение 'hidden'


Notice: Неопределённое свойство в __get(): hidden в <file> on line 70 in <file> on line 29

Перегрузка методов

public __call(string $name, array $arguments): mixed
public static __callStatic(string $name, array $arguments): mixed

__call() запускается при вызове недоступных методов в контексте объект.

__callStatic() запускается при вызове недоступных методов в статическом контексте.

Аргумент $name представляет собой имя вызываемого метода. Аргумент $arguments представляет собой нумерованный массив, содержащий параметры, переданные в вызываемый метод $name.

Пример #2 Перегрузка методов с помощью методов __call() и __callStatic()

<?php
class MethodTest {
public function
__call($name, $arguments) {
// Замечание: значение $name регистрозависимо.
echo "Вызов метода '$name' "
. implode(', ', $arguments). "\n";
}

public static function
__callStatic($name, $arguments) {
// Замечание: значение $name регистрозависимо.
echo "Вызов статического метода '$name' "
. implode(', ', $arguments). "\n";
}
}

$obj = new MethodTest;
$obj->runTest('в контексте объекта');

MethodTest::runTest('в статическом контексте');
?>

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

Вызов метода 'runTest' в контексте объекта
Вызов статического метода 'runTest' в статическом контексте