Magic Methods
Learn how PHP magic methods like __toString, __get, __set, and __call let PHP call special behavior automatically.
Introduction
You have already used two special methods without necessarily thinking of them that way: `__construct()`, which PHP calls automatically when an object is created, and `__destruct()`, which PHP calls automatically when an object is destroyed. These are examples of a broader category called magic methods.
Magic methods are special methods, always prefixed with a double underscore, that PHP invokes automatically in specific situations — converting an object to a string, accessing an undefined property, or calling an undefined method. They let your objects respond intelligently to situations you cannot fully predict in advance.
- What magic methods are and why they start with `__`.
- A quick recap of `__construct` and `__destruct`.
- How `__toString()` controls converting an object to a string.
- How `__get` and `__set` intercept access to undefined properties.
- How `__call` intercepts calls to undefined methods.
What Are Magic Methods?
A magic method is a method with a reserved name, always beginning with two underscores, that PHP calls automatically at a specific moment — not because your code explicitly calls it, but because PHP's engine triggers it in response to something happening to the object.
| Magic Method | Triggered When |
|---|---|
| __construct() | An object is created with `new` |
| __destruct() | An object is destroyed or the script ends |
| __toString() | An object is used in a string context, like echo |
| __get($name) | Reading an inaccessible or undefined property |
| __set($name, $value) | Writing to an inaccessible or undefined property |
| __call($name, $args) | Calling an inaccessible or undefined method |
Recap: __construct and __destruct
You met these two in an earlier lesson. `__construct()` runs automatically right after an object is created, typically to initialize its properties. `__destruct()` runs automatically right before an object is removed from memory, often used for cleanup like closing a file handle.
<?php
class Connection
{
public function __construct(private string $name)
{
echo "Opening connection: {$this->name}\n";
}
public function __destruct()
{
echo "Closing connection: {$this->name}\n";
}
}
$conn = new Connection("Database");
echo "Using connection...\n";Opening connection: Database
Using connection...
Closing connection: Database__toString
By default, PHP does not know how to display an object as text. The `__toString()` magic method lets you define exactly what should happen whenever an object is used somewhere a string is expected, such as inside `echo` or string concatenation.
<?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("Wireless Mouse", 24.5);
echo $product;Wireless Mouse - $24.50Without `__toString()`, trying to `echo $product` directly would cause a fatal error, since PHP would not know how to represent the object as text.
__get and __set
`__get($name)` is called automatically when code tries to read a property that is either undefined or inaccessible (like a private property from outside the class). `__set($name, $value)` is called automatically when code tries to write to such a property. Together, they let you intercept and control property access.
<?php
class Settings
{
private array $data = [];
public function __get(string $name)
{
return $this->data[$name] ?? null;
}
public function __set(string $name, $value): void
{
echo "Setting '$name' to '$value'\n";
$this->data[$name] = $value;
}
}
$settings = new Settings();
$settings->theme = "dark";
echo $settings->theme;Setting 'theme' to 'dark'
dark__get and __set only trigger for properties that are undefined or not directly accessible from the calling context. If a public property with that name already exists, PHP accesses it directly and skips these magic methods entirely.
__call
`__call($name, $arguments)` is called automatically whenever code tries to call a method that does not exist (or is not accessible) on an object. It receives the method name that was attempted and an array of the arguments passed.
<?php
class ApiClient
{
public function __call(string $name, array $arguments)
{
$endpoint = strtolower($name);
return "Calling endpoint '/$endpoint' with " . count($arguments) . " argument(s)";
}
}
$client = new ApiClient();
echo $client->getUsers(1, 20);Calling endpoint '/getusers' with 2 argument(s)This is exactly the technique many frameworks use to build flexible, dynamic APIs — methods that were never explicitly written can still be "called" and handled generically.
Worked Example: Money Class
Here is a small `Money` class that uses `__toString()` to make displaying money values natural and readable anywhere a string is expected.
<?php
class Money
{
public function __construct(
private float $amount,
private string $currency = "USD"
) {}
public function add(Money $other): Money
{
return new Money($this->amount + $other->amount, $this->currency);
}
public function __toString(): string
{
return number_format($this->amount, 2) . " {$this->currency}";
}
}
$price = new Money(19.99);
$tax = new Money(1.60);
$total = $price->add($tax);
echo "Price: $price\n";
echo "Tax: $tax\n";
echo "Total: $total";Price: 19.99 USD
Tax: 1.60 USD
Total: 21.59 USDNotice `$price`, `$tax`, and `$total` are all `Money` objects, but embedding them directly into strings just works — `__toString()` handles the conversion automatically.
Common Mistakes
- Forgetting `__toString()` must return a string — returning anything else causes a fatal error.
- Overusing `__get`/`__set`/`__call` for everything instead of declaring real, explicit properties and methods.
- Assuming `__get`/`__set` fire for public properties too — they only trigger for undefined or inaccessible ones.
- Forgetting `__call` receives arguments as an array, not as individual parameters.
Best Practices
- Implement `__toString()` on any class that represents a value naturally shown as text, like Money or Product.
- Reserve `__get`/`__set` for genuine cases like lazy-loading or wrapping dynamic data, not as a substitute for real properties.
- Use `__call` sparingly, and document what dynamic method names it supports.
- Prefer explicit, declared methods and properties whenever possible — magic methods trade clarity for flexibility.
Frequently Asked Questions
Are there other magic methods besides the ones covered here?
Yes. PHP also has `__isset`, `__unset`, `__callStatic`, `__invoke`, `__clone`, and more, each triggered by a specific situation.
What happens if I call an undefined method and there is no __call?
PHP throws a fatal "Call to undefined method" error, since there is no magic method to intercept and handle the call.
Can __toString() be used in string concatenation, not just echo?
Yes, __toString() is triggered any time an object is used in any string context, including concatenation with the dot operator, not only echo.
Do magic methods slow down my code?
They add a small amount of overhead compared to direct property or method access, but for typical applications this difference is negligible.
Can a class define __get without __set, or vice versa?
Yes, you can implement just one of the pair. Without __set defined, attempting to write to an inaccessible property causes a fatal error.
Key Takeaways
- Magic methods are special methods starting with `__` that PHP calls automatically.
- __construct and __destruct run automatically on object creation and destruction.
- __toString() controls how an object is converted to a string.
- __get and __set intercept access to undefined or inaccessible properties.
- __call intercepts calls to undefined or inaccessible methods.
Summary
Magic methods give your classes hooks into moments PHP normally handles silently — object creation, string conversion, undefined property access, and undefined method calls. Used well, they make objects feel natural to work with; used carelessly, they can hide what your code is actually doing.
In this lesson, you reviewed __construct and __destruct, and learned __toString, __get, __set, and __call, finishing with a Money class example. Next, you will learn about static properties and methods, which belong to the class itself rather than to any individual instance.
- You understand what magic methods are and when PHP triggers them.
- You can implement __toString() to control string conversion.
- You can use __get and __set to intercept property access.
- You can use __call to intercept undefined method calls.