Skip to content
Magic Methods
step 1/6

Reading — step 1 of 6

Learn

~2 min readCode Organization

Magic methods are special method names PHP calls automatically in certain situations. They power the dynamic feel of PHP frameworks.

The lifecycle pair

class User {
    public function __construct(public string $name) {}
    public function __destruct() {
        // called when last reference goes out of scope
        echo "$this->name destroyed\n";
    }
}

Property interception: __get, __set, __isset, __unset

Called when you access a property that DOESN'T EXIST (or isn't accessible):

class Config {
    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]);
    }
}

$c = new Config();
$c->theme = 'dark';            // __set called
echo $c->theme;                 // __get called
isset($c->theme);              // __isset called

Common trap: __get is also called when accessing a private property from outside.

Dynamic methods: __call, __callStatic

Called when an undefined method is invoked:

class Mock {
    public function __call(string $name, array $args): mixed {
        echo "called $name with " . count($args) . " args\n";
        return null;
    }
}

(new Mock())->whatever(1, 2, 3);   // called whatever with 3 args

Used by mock objects, query builders, ORM proxies — $user->where('id', 1)->first().

Callable as function: __invoke

Makes an object behave like a function:

class Adder {
    public function __construct(private int $by) {}
    public function __invoke(int $n): int {
        return $n + $this->by;
    }
}

$add5 = new Adder(5);
echo $add5(10);              // 15 — like calling a function
is_callable($add5);          // true

Great for closures-with-state, dependency-injectable callables.

String coercion: __toString

class Money {
    public function __construct(public int $cents) {}
    public function __toString(): string {
        return '$' . number_format($this->cents / 100, 2);
    }
}

echo new Money(12345);       // $123.45 — implicit string conversion

MUST return a string. Throwing from __toString was a fatal error pre-PHP 7.4.

Serialization: __sleep, __wakeup, __serialize, __unserialize

Called by serialize() and unserialize(). Useful for connection objects you can't store.

Common mistakes

  • Overusing __get/__set — kills IDE autocomplete and static analysis.
  • __call when a real method would do — confusing for callers reading the source.
  • Forgetting that __get is bypassed for defined properties — defining a property at runtime via __set then reading $obj->name later might re-trigger __get if the property wasn't actually stored.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…