Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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 USD

Without 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; // dark

From 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: Failure

The 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, 9

Such 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.01

Common 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.

Thanks for reading!

Donate to the site — we work hard for you.