Magic methods
Magic methods are called by PHP itself — in special situations: when an object is turned into a string, when a missing property is accessed, when an object is copied. Their names start with two underscores. You already know one of them — the constructor __construct().
__toString
The __toString() method defines how an object turns into a string:
<?php
class Money
{
public function __construct(private int $amount, private string $currency) {}
public function __toString(): string
{
return $this->amount . ' ' . $this->currency;
}
}
echo new Money(250, 'USD'); // 250 USDWithout this method, printing the object with echo would end in an error.
__get and __set
__get() is called when reading a property that does not exist or is not accessible, __set() when writing one:
<?php
class Settings
{
private array $data = [];
public function __get(string $name)
{
return $this->data[$name] ?? null;
}
public function __set(string $name, $value): void
{
$this->data[$name] = $value;
}
}
$settings = new Settings();
$settings->theme = 'dark';
echo $settings->theme; // darkFrom outside these look like regular properties, while inside the values live in an array.
__isset
For isset() and the ?? operator to work with such properties, add __isset():
<?php
class Settings
{
private array $data = ['theme' => 'dark'];
public function __isset(string $name): bool
{
return isset($this->data[$name]);
}
}
$settings = new Settings();
var_dump(isset($settings->theme)); // bool(true)
var_dump(isset($settings->size)); // bool(false)__call
__call() intercepts a call to a method that does not exist. It receives the method name and an array of arguments:
<?php
class Logger
{
public function __call(string $level, array $arguments): string
{
return strtoupper($level) . ': ' . $arguments[0];
}
}
$logger = new Logger();
echo $logger->info('Starting'), "\n"; // INFO: Starting
echo $logger->error('Failure'); // ERROR: FailureThe class has no info() or error() methods — __call() handled both calls.
__invoke
With an __invoke() method an object can be called like a function:
<?php
class Multiplier
{
public function __construct(private int $factor) {}
public function __invoke(int $number): int
{
return $number * $this->factor;
}
}
$triple = new Multiplier(3);
echo $triple(5), "\n"; // 15
echo implode(', ', array_map($triple, [1, 2, 3])); // 3, 6, 9Such an object fits anywhere a callable is expected.
Cloning
The clone operator creates a copy of an object. The __clone() method is called on the copy — it is where nested objects are copied, otherwise they stay shared:
<?php
class Order
{
public DateTime $createdAt;
public function __construct()
{
$this->createdAt = new DateTime('2025-01-30');
}
public function __clone()
{
$this->createdAt = clone $this->createdAt;
}
}
$first = new Order();
$second = clone $first;
$second->createdAt->modify('+1 day');
echo $first->createdAt->format('d.m'), ' ', $second->createdAt->format('d.m'); // 30.01 31.01Common mistake
__get() and __call() hide from the editor and the reader which properties and methods a class has: a typo in a name gives no error but returns null. Use them only where the list of names really is unknown in advance. Otherwise declare properties and methods explicitly.