В PHP магические методы __get() и __set() позволяют перехватывать обращение к несуществующим или недоступным свойствам объекта. Давайте разберем их работу подробно.
Метод __get()
Вызывается при чтении значения недоступного свойства
public mixed __get(string $name)
Пример использования:
class User {
private $data = [];
public function __get($name) {
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
return "Свойство {$name} не существует";
}
}
$user = new User();
echo $user->name; // Вызовет __get('name')
Метод __set()
Вызывается при записи значения в недоступное свойство
public void __set(string $name, mixed $value)
Пример использования:
class User {
private $data = [];
public function __set($name, $value) {
$this->data[$name] = $value;
}
public function __get($name) {
return $this->data[$name] ?? null;
}
}
$user = new User();
$user->name = 'John'; // Вызовет __set('name', 'John')
echo $user->name; // Вызовет __get('name') → 'John'
Практический пример
class Config {
private $settings = [];
public function __set($name, $value) {
$this->settings[$name] = $value;
}
public function __get($name) {
return $this->settings[$name] ?? null;
}
public function getAll() {
return $this->settings;
}
}
$config = new Config();
$config->db_host = 'localhost';
$config->db_user = 'root';
$config->db_pass = 'password';
echo $config->db_host; // localhost
print_r($config->getAll());
Более сложный пример с валидацией
class Product {
private $data = [];
private $allowedProperties = ['name', 'price', 'category'];
public function __set($name, $value) {
if (!in_array($name, $this->allowedProperties)) {
throw new Exception("Свойство {$name} не разрешено");
}
// Валидация для price
if ($name === 'price' && (!is_numeric($value) || $value < 0)) {
throw new Exception("Цена должна быть положительным числом");
}
$this->data[$name] = $value;
}
public function __get($name) {
if (!array_key_exists($name, $this->data)) {
throw new Exception("Свойство {$name} не установлено");
}
return $this->data[$name];
}
}
$product = new Product();
$product->name = "Телефон";
$product->price = 1000;
// $product->invalid = "test"; // Выбросит исключение
Когда использовать __get() и __set()
- Динамические свойства — когда свойства определяются во время выполнения
- Обертка для массивов — для объектного доступа к элементам массива
- Прокси-объекты — для делегирования вызовов другим объектам
- ORM и Active Record — для доступа к данным базы как к свойствам объекта
Важные особенности
- Методы вызываются только для недоступных свойств (private, protected или несуществующих)
- Не работают для статических свойств
- Могут негативно влиять на производительность при частом использовании
- Усложняют статический анализ кода
Альтернативный подход с __call()
Для методов существует аналогичная пара магических методов — __call() и __callStatic().
class Magic {
public function __call($name, $arguments) {
echo "Вызван метод: {$name} с аргументами: ";
print_r($arguments);
}
}
$obj = new Magic();
$obj->undefinedMethod('test'); // Вызовет __call()
Магические методы предоставляют мощный инструмент для создания гибких и динамических объектов, но их следует использовать осознанно, так как они могут сделать код менее предсказуемым.