Skip to content
Lesson 6 of 9

Step 1 of 6 · Reading · ~2 min

Learn

Code 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 string $name;
    public function __construct(string $name) { $this->name = $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) {
        return $this->data[$name] ?? null;
    }
    public function __set(string $name, $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) {
        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 {
    private int $by;
    public function __construct(int $by) { $this->by = $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 int $cents;
    public function __construct(int $cents) { $this->cents = $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

  • Writing PHP 8 signatures on a 7.4 grader: mixed return types and constructor property promotion both came in PHP 8. On 7.4 : mixed is read as a class name (Return value must be an instance of mixed), and a promoted parameter is a parse error. Declare the property, then assign it, and leave the loose return type off.
  • 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.
Up nextClosures and use ClauseFunctional Patterns

Discussion

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

Sign in to post a comment or reply.

Loading…