DEV Community

Cover image for Magic Methods in PHP: Why Programmers Need Magic Too
Ian Patrick
Ian Patrick

Posted on

Magic Methods in PHP: Why Programmers Need Magic Too

PHP, like many other programming languages, offers a number of special methods known as magic methods. These methods are identified by their double underscore (__) and are automatically called in certain situations during the execution of a program. They allow developers to implement custom behaviors in classes, making their code more flexible and powerful.

In this article, we will explore the main magic methods in PHP, explain how each one works, provide practical examples, and discuss common use cases.

  • __construct()
  • __destruct()
  • __get()
  • __set()
  • __call()
  • __callStatic()
  • __toString()
  • __invoke()
  • __sleep()
  • __wakeup()
  • __isset()
  • __serialize()
  • __unserialize()
  • __unset()
  • __clone()

These are the main magic methods in PHP, each with a specific purpose to customize the behavior of classes and objects.

__construct()

The __construct() method is the constructor for a class. It is automatically called when an object is instantiated. It is commonly used to initialize object properties or perform initial setup.

<?php

class User
{
    public function __construct(
        public string $name
    ) {
        $this->name = $name;
        echo "User {$this->name} was created!" . PHP_EOL;
    }
}

$user = new User('Jonas');  // Output: User Jonas was created!
Enter fullscreen mode Exit fullscreen mode

Use cases:

  • Initialize properties of an object.
  • Configure connections to databases or external services.
  • Perform initial validations.

__destruct()

The __destruct() method is the destructor for a class. It is called automatically when an object is destroyed or when the script finishes executing. It is useful for freeing up resources, such as closing connections to databases or files.

<?php

class User
{
    public function __destruct()
    {
        echo 'Object destructed!' . PHP_EOL;
    }
}

$user = new User();
unset($user);  // Output: Object destructed!
Enter fullscreen mode Exit fullscreen mode

Use Cases:

  • Free up allocated resources, such as memory or connections.
  • Perform cleanup before destroying an object.

__get()

The __get() method is called when we try to access an inaccessible or non-existent property of an object. It allows you to define custom behavior for these cases.

<?php

class User
{
    private $data = ['name' => 'Pedro'];

    public function __get($name)
    {
        if (array_key_exists($name, $this->data)) {
            return $this->data[$name];
        }
        return "Property {$name} does not exist!" . PHP_EOL;
    }
}

$user = new User();
echo $user->name . PHP_EOL;  // Output: Pedro
echo $user->age;  // Output: Property age does not exist!
Enter fullscreen mode Exit fullscreen mode

Use Cases:

  • Access private or protected properties in a controlled manner.
  • Implement custom logic for dynamic properties.

__set()

The __set() method is called when we try to assign a value to an inaccessible or non-existent property of an object. It allows you to handle the assignment of values ​​in a custom way.

<?php

class User
{
    private $data = [];

    public function __set($name, $value)
    {
        $this->data[$name] = $value;
    }

    public function __get($name)
    {
        return $this->data[$name] ?? "propery {$name} does not exist!" . PHP_EOL;
    }
}

$user = new User();
$user->age = 30;
echo $user->age . PHP_EOL;  // Output: 30
Enter fullscreen mode Exit fullscreen mode

Use Cases:

  • Assign values ​​to private or protected properties.
  • Implement validation logic when assigning values.

__call()

The __call() method is called when we attempt to invoke an inaccessible or nonexistent method on an object. It allows you to define custom behavior for dynamic method calls.

<?php

class User
{
    public function __call($name, $arguments)
    {
        echo "Method '{$name}' does not exist! Arguments: " . implode(', ', $arguments) . PHP_EOL;
    }
}

$user = new User();
$user->sayHello('Jonas');  // Output: Method 'sayHello' does not exist! Arguments: Jonas
Enter fullscreen mode Exit fullscreen mode

Use Cases:

  • Implement dynamic methods.
  • Create flexible APIs that respond to calls to undefined methods.

__callStatic()

The __callStatic() method is similar to __call(), but it is called when we try to invoke an inaccessible or non-existent static method.

<?php

class User
{
    public static function __callStatic($name, $arguments)
    {
        echo "Static method '{$name}' does not exists! Arguments: " . implode(', ', $arguments) . PHP_EOL;
    }
}

User::sayHello('Maria');  // Output: Static method 'sayHello' does not exist! Arguments: Maria
Enter fullscreen mode Exit fullscreen mode

Use Cases:

  • Implement dynamic static methods.
  • Create custom behaviors for static calls.

__toString()

The __toString() method is called when we try to treat an object as a string. It allows us to define how the object should be represented textually.

<?php

class User
{
    public $name = 'Jonas';

    public function __toString()
    {
        return "User: {$this->name}";
    }
}

$user = new User();
echo $user . PHP_EOL;  // Output: User: Jonas
Enter fullscreen mode Exit fullscreen mode

Use Cases:

  • Represent objects in a readable way in strings.
  • Make it easier to display object information.

__invoke()

The __invoke() method is called when we try to use an object as a function. It allows the object to be "invokable".

<?php

class User
{
    public function __invoke($name)
    {
        echo "Hello, {$name}!" . PHP_EOL;
    }
}

$user = new User();
$user('Jonas');  // Output: Hello, Jonas!
Enter fullscreen mode Exit fullscreen mode

Use Cases:

  • Create objects that behave like functions.
  • Implement design patterns such as Callable Object.

__sleep()

The __sleep() method is called when the object is serialized (for example, with serialize() ). It allows you to define which properties should be included in the serialization.

<?php

class User
{
    public $name = 'Jonas';
    private $password = '123456';

    public function __sleep()
    {
        return ['name'];  // Just the 'name' property will be serialized
    }
}

$user = new User();
echo serialize($user) . PHP_EOL;  // Output: O:4:"User":1:{s:4:"name";s:4:"Jonas";}
Enter fullscreen mode Exit fullscreen mode

Use Cases:

  • Control which properties are serialized.
  • Avoid serialization of sensitive data.

__wakeup()

The __wakeup() method is called when the object is deserialized (for example, with unserialize() ). It allows you to reinitialize the object after deserialization.

<?php

class User
{
    public $name;

    public function __wakeup()
    {
        $this->name = 'Jonas';
    }
}

$userSerialized = serialize(new User());
$user = unserialize($userSerialized);

echo $user->name . PHP_EOL;  // Output: Reactived user: Jonas
Enter fullscreen mode Exit fullscreen mode

Use Cases:

  • Reconstruct resources after deserialization.
  • Reestablish connections or object states.

__isset()

The __isset() method is called when the isset() or empty() function is used to check whether an inaccessible or nonexistent property is set.

<?php

class User
{
    private $data = ['name' => 'Jonas'];

    public function __isset($name)
    {
        return isset($this->data[$name]);
    }
}

$user = new User();
var_dump(isset($user->name));  // Output: bool(true)
var_dump(isset($user->age));  // Output: bool(false)
Enter fullscreen mode Exit fullscreen mode

Use Cases:

  • Check for the existence of dynamic or private properties.
  • Implement custom logic for isset() or empty().

__serialize()

The __serialize() method is called when the object is serialized (for example, with serialize() ). It allows you to define an array of data that will be serialized, overriding the default serialization behavior.

<?php

class User
{
    public $name = 'Jonas';
    private $password = '123456';

    public function __serialize(): array
    {
        return [
            'name' => $this->name,
            'password' => '***'  // Hide sensetive data
        ];
    }
}

$user = new User();
echo serialize($user) . PHP_EOL;

// Output: O:4:"User":2:{s:4:"name";s:4:"Jonas";s:8:"password";s:3:"***";}
Enter fullscreen mode Exit fullscreen mode

Use Cases:

  • Explicitly control what data is serialized.
  • Hide or transform sensitive data during serialization.

__unserialize()

The __unserialize() method is called when the object is deserialized (for example, with unserialize()). It allows you to reconstruct the object from an array of serialized data.

<?php

class User
{
    public $name;
    private $password;

    public function __unserialize(array $data): void
    {
        $this->name = $data['name'];
        $this->password = 'restored';  // Retore or redefine values
    }
}

$user = new User();
$user->name = 'Jonas';

$userSerialized = serialize($user);

$userRestored = unserialize($userSerialized);
echo $userRestored->name . PHP_EOL;  // Output: Jonas
Enter fullscreen mode Exit fullscreen mode

Use Cases:

  • Reconstruct the object with custom logic after deserialization.
  • Restore or reset values ​​that were changed during serialization.

__unset()

The __unset() method is called when the unset() function is used to remove an inaccessible or nonexistent property.

<?php

class User
{
    private $data = ['name' => 'Jonas'];

    public function __unset($name)
    {
        unset($this->data[$name]);
    }
}

$user = new User();
unset($user->name);  // Remove the 'name' property
var_dump(isset($user->name));  // Output: bool(false)
Enter fullscreen mode Exit fullscreen mode

Use Cases:

  • Implement custom logic when removing dynamic or private properties.
  • Clear or reset values ​​when using unset().

__clone()

The __clone() method is called when an object is cloned using the clone() keyword. It allows you to define custom behaviors during cloning.

<?php

class User
{
    public $name;

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

    public function __clone()
    {
        $this->name = 'Clone from ' . $this->name;
    }
}

$user = new User('Maria');
$userClone = clone $user;
echo $userClone->name . PHP_EOL;
Enter fullscreen mode Exit fullscreen mode

Conclusion

Magic methods in PHP are powerful tools that allow you to customize the behavior of classes and objects. They are essential for creating flexible, dynamic, and maintainable code. By mastering these methods, you can implement advanced design patterns, improve the usability of your classes, and make your code more expressive.

Remember to use these methods sparingly, as overusing them can lead to code that is difficult to understand and maintain. Use them when it makes sense in the context of your project, and always document the custom behavior you are implementing.

Top comments (1)

Collapse
 
xwero profile image
david duymelinck • Edited

Don't use any of the magic methods to access protected or private properties and methods.
That defeats the purpose of shielding those.

In a rare case when you need a dynamic object __get and __set can be replaced with property hooks to create properties in a more structured way. The magic methods can look like a mess pretty fast.
The only case I can think of for a dynamic object is a collection of inputs that a user can create and extend,

The should have called them override methods, because all those methods override PHP default behavior. There is nothing magical about it. But it inspires nice post titles like this one.