Magic Methods: __get, __call, __invoke

Hook into property access, undefined method calls, and using an object like a function. Powerful — and best used sparingly.

Magic methods let a class intercept operations that would otherwise be errors. The workhorses:

  • __get($name) / __set($name, $value) — run when you read/write an inaccessible or non-existent property.
  • __call($name, $args) — runs when you call a method that doesn't exist.
  • __invoke(...) — lets you call the object itself like a function.

Easy example

<?php
class Multiplier {
    public function __construct(private int $factor) {}
    public function __invoke(int $n): int {
        return $n * $this->factor;
    }
}

$double = new Multiplier(2);
echo $double(21); // 42 — the object is callable

They're the machinery behind ORMs and config bags. But they hide structure from your IDE and readers, so keep them for genuine dynamic cases.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Use __get and __set on a Model class to proxy property access to an $attributes array for flexible data mapping.
  • Implement __invoke on a middleware class so it can be used as a callable without exposing an explicit method name.
  • Use __call to forward unknown method calls to an underlying decorated object for a transparent proxy.

More examples

__get and __set for dynamic properties

__get, __set, and __isset intercept property access on undefined properties, enabling flexible dynamic attribute storage.

Example · php
<?php
class DynamicModel {
    private array $data = [];

    public function __get(string $name): mixed {
        return $this->data[$name] ?? null;
    }
    public function __set(string $name, mixed $value): void {
        $this->data[$name] = $value;
    }
    public function __isset(string $name): bool {
        return isset($this->data[$name]);
    }
}

$m = new DynamicModel();
$m->name  = 'Alice';
$m->email = '[email protected]';

echo $m->name;              // Alice
echo isset($m->email) ? 'set' : 'not set'; // set

__invoke makes objects callable

__invoke lets an object be called like a function; here the Multiplier becomes a reusable, configurable callable.

Example · php
<?php
class Multiplier {
    public function __construct(private float $factor) {}

    public function __invoke(float $value): float {
        return $value * $this->factor;
    }
}

$double = new Multiplier(2);
$triple = new Multiplier(3);

echo $double(5);  // 10
echo $triple(5);  // 15

$prices = [10, 20, 30];
$result = array_map($double, $prices); // objects as callbacks
print_r($result);

__call for transparent delegation

__call intercepts calls to undefined methods and forwards them to the wrapped object, adding cross-cutting concerns transparently.

Example · php
<?php
class LoggingProxy {
    public function __construct(private object $target) {}

    public function __call(string $method, array $args): mixed {
        echo "Calling $method\n";
        return $this->target->$method(...$args);
    }
}

class Calculator {
    public function add(float $a, float $b): float { return $a + $b; }
}

$proxy = new LoggingProxy(new Calculator());
echo $proxy->add(3, 4); // logs 'Calling add', returns 7

Discussion

  • Be the first to comment on this lesson.