Skip to Content
Course content
Click on the "Edit" button in the top corner of the screen to edit your slide content.

By now, you've spent plenty of time writing standard classes and methods. But PHP has this set of "magic" methods—functions that start with a double underscore—that allow you to hook into the internal workings of the engine. They aren't called by you directly; instead, PHP triggers them automatically when certain events happen to an object.

What's the deal with the double underscores and why use them?

The double underscore __ is just PHP's way of saying, "This isn't a normal method; this is a system hook." Think of them as event listeners for your objects. I've seen a lot of junior devs avoid them because they feel like "black magic," but when used correctly, they remove a massive amount of boilerplate code.

For example, instead of writing a dozen different getters and setters for a data-heavy object, you can use magic methods to handle those requests dynamically. It keeps your class definitions clean and your code more flexible.

How do I stop writing twenty different getters and setters?

This is where __get() and __set() come in. These are triggered whenever you try to access or assign a property that is either private or doesn't exist at all. I usually use this pattern when I'm building a "Settings" or "Session" class where the properties are stored in an internal array rather than as defined class members.

class UserProfile {
    private array $data = [];

    public function __set($name, $value) {
        echo "Setting $name to $value\n";
        $this->data[$name] = $value;
    }

    public function __get($name) {
        return $this->data[$name] ?? "Property $name not found";
    }
}

$user = new UserProfile();
$user->firstName = "Jane"; // Triggers __set
echo $user->firstName;      // Triggers __get
echo $user->age;            // Triggers __get (returns "Property age not found")

Can I make an object act like a string or a function?

Yes, and this is actually incredibly useful for logging or creating specialized callback objects. __toString() is called when you try to echo an object or treat it as a string. __invoke() is called when you try to call the object as if it were a function.

I personally love __invoke() for creating "Action" classes—single-purpose classes that do one thing. It makes the intent of the class very clear.

class Logger {
    public function __toString() {
        return "[Log Entry: " . date('Y-m-d H:i:s') . "]";
    }

    public function __invoke($message) {
        echo "Logging message: $message\n";
    }
}

$log = new Logger();
echo $log;       // Triggers __toString: [Log Entry: 2023-...]
$log("Error!");  // Triggers __invoke: Logging message: Error!

What happens if I call a method that doesn't even exist?

Normally, PHP would throw a fatal error. But if you define __call(), you can intercept that failure. This is a powerful tool for creating "wrappers." If you're building a class that wraps a third-party API, you can use __call() to forward any method call directly to that API without having to manually map every single possible endpoint in your own code.

class ApiWrapper {
    private $apiClient;

    public function __construct($client) {
        $this->apiClient = $client;
    }

    public function __call($method, $args) {
        echo "Forwarding call to $method...\n";
        return call_user_func_array([$this->apiClient, $method], $args);
    }
}

Just a word of caution: don't overdo it. If you make everything "magic," your IDE will stop being able to provide autocomplete, and other developers (or you, six months from now) will have a hard time figuring out where a method is actually defined.




📋 Practical Task

Exercise: Building a Dynamic Configuration Wrapper

You need to create a class called AppConfig that stores configuration settings in a private array. Instead of creating a method for every possible setting, implement the following:

  • Use __set($name, $value) to allow adding settings to the internal array.
  • Use __get($name) to retrieve settings. If the setting doesn't exist, return the string "Setting not defined".
  • Implement __toString() so that when the object is echoed, it returns a comma-separated list of all the keys currently stored in the configuration (e.g., "db_host, db_user, api_key").

Test your class by setting three different configuration values and then echoing the object itself to verify the list of keys.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.