Магические методы начинаются с __ и вызываются PHP автоматически при определённых действиях с объектом.
__construct и __destruct
<?php
declare(strict_types=1);
class Connection
{
private mixed $resource;
public function __construct(private string $dsn)
{
$this->resource = fopen('/tmp/conn.log', 'a');
fwrite($this->resource, "Connected to {$dsn}\n");
}
public function __destruct()
{
// Called when object is garbage collected
if (is_resource($this->resource)) {
fwrite($this->resource, "Disconnected from {$this->dsn}\n");
fclose($this->resource);
}
}
}
__get, __set, __isset, __unset
<?php
declare(strict_types=1);
class DynamicObject
{
private array $data = [];
// Called when accessing undefined/inaccessible property
public function __get(string $name): mixed
{
return $this->data[$name] ?? throw new RuntimeException("Property {$name} not found");
}
// Called when setting undefined/inaccessible property
public function __set(string $name, mixed $value): void
{
$this->data[$name] = $value;
}
// Called by isset() and empty() on undefined/inaccessible property
public function __isset(string $name): bool
{
return isset($this->data[$name]);
}
// Called by unset() on undefined/inaccessible property
public function __unset(string $name): void
{
unset($this->data[$name]);
}
}
$obj = new DynamicObject();
$obj->name = 'Alice'; // __set
echo $obj->name; // __get => 'Alice'
echo isset($obj->name); // __isset => true
unset($obj->name); // __unset
echo isset($obj->name); // __isset => false
Ловушка экзамена:
__getи__setвызываются ТОЛЬКО для недоступных или несуществующих свойств. Если свойство объявлено какpublic, магические методы НЕ вызываются. Дляprotected/privateсвойств — вызываются при обращении извне.
__call и __callStatic
<?php
declare(strict_types=1);
class QueryBuilder
{
private array $conditions = [];
// Called when undefined instance method is called
public function __call(string $name, array $arguments): static
{
if (str_starts_with($name, 'where')) {
$field = lcfirst(substr($name, 5));
$this->conditions[$field] = $arguments[0];
return $this;
}
throw new BadMethodCallException("Method {$name} does not exist");
}
// Called when undefined STATIC method is called
public static function __callStatic(string $name, array $arguments): mixed
{
if ($name === 'create') {
return new static();
}
throw new BadMethodCallException("Static method {$name} does not exist");
}
public function getConditions(): array
{
return $this->conditions;
}
}
$query = QueryBuilder::create() // __callStatic
->whereName('Alice') // __call
->whereAge(25) // __call
->whereCity('Moscow'); // __call
print_r($query->getConditions());
// ['name' => 'Alice', 'age' => 25, 'city' => 'Moscow']
__toString
<?php
declare(strict_types=1);
class Money
{
public function __construct(
private float $amount,
private string $currency,
) {
}
public function __toString(): string
{
return number_format($this->amount, 2) . ' ' . $this->currency;
}
}
$price = new Money(42.50, 'USD');
echo $price; // 42.50 USD
echo "Price: {$price}"; // Price: 42.50 USD
echo (string)$price; // 42.50 USD
Запомни:
__toString()ДОЛЖЕН возвращатьstring. Без реализации —echo $objectвызовет Fatal Error. С PHP 8.0__toStringможет выбрасывать исключения.
__invoke
<?php
declare(strict_types=1);
class Multiplier
{
public function __construct(
private float $factor,
) {
}
// Called when object is used as function
public function __invoke(float $value): float
{
return $value * $this->factor;
}
}
$double = new Multiplier(2);
echo $double(5); // 10.0
echo $double(3.14); // 6.28
// Useful in callbacks
$numbers = [1, 2, 3, 4, 5];
$tripled = array_map(new Multiplier(3), $numbers);
// [3, 6, 9, 12, 15]
// Check if callable
var_dump(is_callable($double)); // true
__clone
<?php
declare(strict_types=1);
class Document
{
private array $sections = [];
public function __construct(
private string $title,
private DateTimeImmutable $createdAt,
) {
}
public function addSection(object $section): void
{
$this->sections[] = $section;
}
// Called after clone to customize the copy
public function __clone(): void
{
// Deep clone nested objects
foreach ($this->sections as $key => $section) {
$this->sections[$key] = clone $section;
}
// Reset creation date for clone
$this->createdAt = new DateTimeImmutable();
}
}
__serialize / __unserialize (PHP 7.4+)
<?php
declare(strict_types=1);
class SecureUser
{
public function __construct(
private string $name,
private string $email,
private string $password, // Should NOT be serialized
) {
}
// Modern serialization (PHP 7.4+)
public function __serialize(): array
{
return [
'name' => $this->name,
'email' => $this->email,
// Intentionally omit password
];
}
public function __unserialize(array $data): void
{
$this->name = $data['name'];
$this->email = $data['email'];
$this->password = ''; // Reset
}
}
// Replaces old __sleep() / __wakeup()
__debugInfo
<?php
declare(strict_types=1);
class DatabaseConnection
{
public function __construct(
private string $host,
private string $username,
private string $password,
) {
}
// Customize var_dump output
public function __debugInfo(): array
{
return [
'host' => $this->host,
'username' => $this->username,
'password' => '***HIDDEN***',
];
}
}
$db = new DatabaseConnection('localhost', 'admin', 'secret123');
var_dump($db);
// object(DatabaseConnection) { host => "localhost", username => "admin", password => "***HIDDEN***" }
Полный список магических методов
| Метод | Когда вызывается |
|---|---|
__construct() |
При создании объекта |
__destruct() |
При уничтожении объекта |
__get($name) |
Чтение недоступного свойства |
__set($name, $val) |
Запись в недоступное свойство |
__isset($name) |
isset() / empty() на недоступном свойстве |
__unset($name) |
unset() на недоступном свойстве |
__call($name, $args) |
Вызов недоступного метода |
__callStatic($name, $args) |
Вызов недоступного статического метода |
__toString() |
Приведение к строке |
__invoke($args) |
Использование как функции |
__clone() |
После clone |
__serialize() |
При serialize() |
__unserialize($data) |
При unserialize() |
__debugInfo() |
При var_dump() |