PHP magic methods are easy to recognize. They start with two underscores, such as __construct(), __get(), and __toString(). The less obvious part is knowing exactly when PHP calls each one.
That automatic behavior is useful, but it can also make a class feel a little too magical. If you have ever followed a property access through a codebase only to discover that the property does not actually exist, you have probably met __get().
A magic method is a predefined method that PHP calls automatically when a particular operation is performed on an object. You define the method in your class, but normally you do not call it yourself.
For example, PHP calls __construct() when creating an object, __get() when reading an inaccessible property, and __toString() when an object is used as a string.
PHP magic methods at a glance
The following table shows the main PHP magic methods and what triggers them.
| Magic method | Called when |
|---|---|
__construct() |
An object is created. |
__destruct() |
An object is being destroyed. |
__get() |
An inaccessible or undefined property is read. |
__set() |
A value is assigned to an inaccessible or undefined property. |
__isset() |
isset() or empty() checks an inaccessible property. |
__unset() |
unset() is used on an inaccessible property. |
__call() |
An inaccessible or undefined instance method is called. |
__callStatic() |
An inaccessible or undefined static method is called. |
__toString() |
An object is treated as a string. |
__invoke() |
An object is called like a function. |
__serialize() |
An object is serialized. |
__unserialize() |
An object is restored from serialized data. |
__sleep() |
serialize() uses the older serialization mechanism. |
__wakeup() |
unserialize() uses the older serialization mechanism. |
__set_state() |
An object exported by var_export() is reconstructed. |
__clone() |
An object is cloned. |
__debugInfo() |
An object is inspected with var_dump(). |
Rules for PHP magic methods
Magic methods are part of PHP’s object model, so their names and expected signatures are defined by the language. A few rules are worth remembering before using them.
- Magic method names begin with two underscores (
__). - Names beginning with
__are reserved by PHP. Do not invent your own double-underscore method names. - Most magic methods must be declared
public.__construct(),__destruct(), and__clone()are exceptions to this visibility rule. - PHP 8 validates magic method signatures. If you declare parameter or return types, they must be compatible with the signature PHP expects.
- Magic methods should usually respond to PHP’s automatic behavior rather than being called directly from application code.
Magic methods are useful when they make an object’s behavior natural. They become harder to maintain when they hide too much logic, so explicit properties and methods are still the better choice when no special behavior is required.
__construct() and __destruct()
PHP __construct() and __destruct() run automatically during an object’s lifecycle. __construct() runs when you create an object and is normally used to initialize required properties or other object state.
<?php
class Product
{
public function __construct(
public string $name,
public float $price
) {
}
}
$product = new Product('Wireless Mouse', 24.99);
echo $product->name;
There is no need to call __construct() explicitly. PHP invokes it as part of new Product(...).
__destruct() runs when an object is destroyed, usually when there are no remaining references to it or when the script finishes.
<?php
class LogFile
{
private $handle;
public function __construct(string $filename)
{
$this->handle = fopen($filename, 'a');
}
public function write(string $message): void
{
fwrite($this->handle, $message . PHP_EOL);
}
public function __destruct()
{
if (is_resource($this->handle)) {
fclose($this->handle);
}
}
}
A destructor can be useful for cleanup, but avoid putting important business logic in it. The exact point at which destruction happens can depend on object references and script shutdown. For operations that must definitely happen at a known point, an explicit method is usually clearer.
__get() and __set() for inaccessible properties
PHP calls __get() when code tries to read a property that is inaccessible from the current scope. This includes undefined properties and private or protected properties accessed from outside the class.
__set() handles the matching write operation.
<?php
class UserProfile
{
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;
}
}
$user = new UserProfile();
$user->name = 'Maya';
$user->role = 'Editor';
echo $user->name;
The properties name and role are not declared on the class. Writing them triggers __set(), while reading them triggers __get().
This PHP property overloading pattern is sometimes useful for wrappers, configuration objects, and data-transfer layers. Do not use it merely to avoid declaring properties. Explicit properties give IDEs, static analysis tools, and other developers much more information about a class.
__isset() and __unset()
If a class uses inaccessible properties, __isset() and __unset() let it control how isset(), empty(), and unset() behave.
We can extend the previous class like this:
<?php
class UserProfile
{
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]);
}
public function __unset(string $name): void
{
unset($this->data[$name]);
}
}
$user = new UserProfile();
$user->name = 'Maya';
var_dump(isset($user->name));
unset($user->name);
var_dump(isset($user->name));
The output is:
bool(true)
bool(false)
A useful detail is that empty($object->property) can also involve __isset(). If your class implements dynamic property access, these methods should usually be designed together so their behavior stays consistent.
__call() and __callStatic()
PHP calls __call() when code invokes an inaccessible or undefined instance method. It receives the requested method name and an array containing the arguments.
<?php
class Formatter
{
public function __call(string $name, array $arguments): mixed
{
if ($name === 'uppercase') {
return strtoupper($arguments[0] ?? '');
}
throw new BadMethodCallException(
"Method {$name}() does not exist."
);
}
}
$formatter = new Formatter();
echo $formatter->uppercase('Hello PHP');
The result is:
HELLO PHP
Notice that the example throws an exception for unsupported method names. Returning null silently can hide spelling mistakes and make debugging unnecessarily painful.
__callStatic() does the same job for inaccessible static method calls.
<?php
class UnitConverter
{
public static function __callStatic(string $name, array $arguments): mixed
{
if ($name === 'kmToMiles') {
$kilometers = $arguments[0] ?? 0;
return $kilometers * 0.621371;
}
throw new BadMethodCallException(
"Static method {$name}() does not exist."
);
}
}
echo UnitConverter::kmToMiles(10);
Both methods can support proxy or fluent APIs, but they also hide methods from normal inspection. If the set of available operations is known in advance, real methods are usually easier to understand and maintain.
__toString() for string conversion
PHP calls __toString() when an object is used where a string is expected.
<?php
class Product
{
public function __construct(
private string $name,
private float $price
) {
}
public function __toString(): string
{
return $this->name . ' - $' . number_format($this->price, 2);
}
}
$product = new Product('Mechanical Keyboard', 89.50);
echo $product;
The output is:
Mechanical Keyboard - $89.50
__toString() must return a string. It is most useful when an object has one obvious textual representation, such as a value object, identifier, or display label.
Avoid putting expensive work inside __toString(). String conversion can happen in places that are easy to overlook, including logging, debugging, and interpolation.
__invoke() makes an object callable
If a class defines __invoke(), an instance of that class can be called like a function.
<?php
class DiscountCalculator
{
public function __construct(
private float $percentage
) {
}
public function __invoke(float $price): float
{
return $price - ($price * $this->percentage / 100);
}
}
$applyDiscount = new DiscountCalculator(15);
echo $applyDiscount(100);
The output is:
85
This pattern works well for small service objects that represent one operation. It is also useful when an API expects a callable but you want the logic to keep its own configuration or dependencies.
__serialize() and __unserialize()
For modern PHP code, __serialize() and __unserialize() are the preferred magic methods for controlling object serialization.
__serialize() returns the array of values that PHP should serialize. __unserialize() receives that array when the object is restored.
<?php
class ApiToken
{
public function __construct(
private string $token,
private DateTimeImmutable $createdAt
) {
}
public function __serialize(): array
{
return [
'token' => $this->token,
'createdAt' => $this->createdAt->format(DATE_ATOM),
];
}
public function __unserialize(array $data): void
{
$this->token = $data['token'];
$this->createdAt = new DateTimeImmutable($data['createdAt']);
}
}
$token = new ApiToken(
'abc123',
new DateTimeImmutable('2026-08-14 10:00:00')
);
$serialized = serialize($token);
$restored = unserialize($serialized);
These methods give the class direct control over its serialized representation without relying on internal property names.
Do not treat PHP serialization as a safe format for untrusted input. Calling unserialize() on untrusted input can instantiate objects and trigger object-related behavior. Use safer formats such as JSON for data received from users or external systems unless PHP serialization is specifically required.
__sleep() and __wakeup()
__sleep() and __wakeup() are older serialization hooks. They still exist, but new code should generally prefer __serialize() and __unserialize().
__sleep() returns a list of property names to serialize. __wakeup() runs after the object has been unserialized.
<?php
class LegacySession
{
private string $userId;
private string $temporaryValue;
public function __construct(string $userId)
{
$this->userId = $userId;
$this->temporaryValue = 'runtime-only';
}
public function __sleep(): array
{
return ['userId'];
}
public function __wakeup(): void
{
$this->temporaryValue = 'restored';
}
}
If a class defines both the modern and older serialization hooks, PHP uses __serialize() and __unserialize() instead of __sleep() and __wakeup().
__clone() for custom cloning behavior
PHP object cloning with the clone keyword creates a shallow copy of an object. After the copy is made, PHP calls its __clone() method if one is defined.
<?php
class Order
{
public function __construct(
public string $reference,
public DateTime $createdAt
) {
}
public function __clone()
{
$this->reference = uniqid('order-', true);
$this->createdAt = clone $this->createdAt;
}
}
$original = new Order(
'order-original',
new DateTime('2026-08-14')
);
$copy = clone $original;
echo $copy->reference;
The explicit clone of $createdAt is important. Object properties are copied by reference during a shallow clone, so without it both Order objects would still point to the same DateTime instance.
This is one of the more useful details about __clone(): it is not just a notification that cloning happened. It is where you can make nested mutable objects independent.
__set_state() with var_export()
__set_state() is used when PHP reconstructs an object from the output of var_export().
<?php
class Settings
{
public function __construct(
public string $theme,
public bool $notifications
) {
}
public static function __set_state(array $properties): Settings
{
return new Settings(
$properties['theme'],
$properties['notifications']
);
}
}
$settings = new Settings('dark', true);
$exported = var_export($settings, true);
echo $exported;
The exported value contains a call to Settings::__set_state(). That makes the output valid PHP code that can recreate the object when evaluated.
This method is mostly useful for configuration, debugging tools, code generation, and other cases where var_export() is part of the workflow.
__debugInfo() controls var_dump() output
PHP calls __debugInfo() when an object is inspected with var_dump(). It lets the class decide what information should appear in the debug output.
<?php
class DatabaseConnection
{
public function __construct(
private string $host,
private string $username,
private string $password
) {
}
public function __debugInfo(): array
{
return [
'host' => $this->host,
'username' => $this->username,
'password' => '[hidden]',
];
}
}
$connection = new DatabaseConnection(
'localhost',
'app_user',
'secret-password'
);
var_dump($connection);
This is useful when an object contains noisy internal state or sensitive values that should not appear in normal debug output.
It is still not a security boundary. Secrets can exist elsewhere in memory or logs, so __debugInfo() should be treated as a debugging convenience rather than protection for sensitive data.
Common mistakes with PHP magic methods
Magic methods often cause problems when they make code look simpler than it really is.
Using __get() and __set() for every property
Dynamic property access can save a few lines, but it also hides the shape of the object. If the properties are known, declare them normally.
Silently accepting unknown methods
A __call() method that quietly returns null can turn a typo into a much harder bug. Throw an exception when the requested method is not supported.
Putting important work in __destruct()
Do not depend on a destructor for operations such as saving a payment, committing essential data, or sending a required response. Use an explicit method when the timing matters.
Forgetting nested objects when cloning
clone makes a shallow copy. If a property contains another mutable object, clone that nested object inside __clone() when the copy must be independent.
Using unserialize() with untrusted data
Never pass arbitrary user-controlled data directly to unserialize(). Prefer JSON or another plain data format when object reconstruction is not required.
When should you use magic methods?
Magic methods are a good fit when they make an object behave naturally in PHP. Examples include converting a value object to a string, making a small service object callable, controlling cloning, or exposing a carefully designed dynamic API.
They are less useful when they merely hide ordinary methods and properties. A little magic can make an API elegant. Too much of it makes debugging feel like detective work.
For most application classes, explicit methods and PHP typed properties and type declarations should remain the default. Use a magic method when its automatic PHP behavior genuinely improves the design.
PHP magic methods FAQ
Why are they called magic methods?
They are called magic methods because PHP invokes them automatically in response to specific operations. Your code performs an action such as reading an inaccessible property, cloning an object, or converting an object to a string, and PHP calls the matching method behind the scenes.
Can I create my own magic method?
No. PHP recognizes only the magic methods defined by the language. Method names beginning with two underscores are reserved, so application-specific methods should use normal names.
Do all magic methods have to be public?
Most do. PHP expects magic methods to be public except for __construct(), __destruct(), and __clone(), which may use other visibility levels.
What is the difference between __call() and __invoke()?
__call() runs when an inaccessible or undefined instance method is called. __invoke() runs when the object itself is called like a function.
<?php
$object->missingMethod(); // May trigger __call()
$object(); // Triggers __invoke()
What is the difference between __sleep() and __serialize()?
__sleep() is the older serialization hook and returns property names to serialize. __serialize() returns the actual array representation to serialize, giving the class more control. For modern PHP code, prefer __serialize() and __unserialize().
Are PHP magic methods slow?
Magic methods add some runtime work compared with direct property or method access, but performance is rarely the main reason to avoid them. Readability, discoverability, and predictable behavior are usually more important concerns.
Conclusion
PHP magic methods let classes participate in built-in object behavior without requiring explicit calls everywhere. The most commonly useful ones are __construct(), __get(), __set(), __call(), __toString(), __invoke(), and __clone().
Use them when the automatic behavior makes the class clearer. If a normal method or typed property communicates the same idea more directly, prefer the ordinary version. Good PHP code should be understandable even when the magic is working.
Excellent article
Thank you Stefan