Magic methods are specially named methods PHP calls during particular object operations. __get() can handle reading an inaccessible or missing property. __call() can handle a missing or inaccessible method call. __toString() supplies a string representation, and __invoke() lets an object be called like a function.
These hooks can support adapters and framework infrastructure, but they can also make ordinary behavior difficult to discover. This guide explains the main triggers, shows a controlled read-only virtual field, and highlights signature and error rules. You will learn when an explicit property or method is a clearer public contract.
Property hooks respond to inaccessible access
__get(string $name) runs when code reads an inaccessible or nonexistent property. __set(string $name, mixed $value) responds to a corresponding write. __isset(string $name) participates in isset and empty checks on inaccessible properties, and __unset(string $name) responds to unset. Public accessible properties are normally handled directly instead.
Choose an explicit allowlist of virtual names. Do not let every typo silently become a new field, because mistakes then survive without a useful error. A virtual field should have a documented meaning and type. Unknown names can throw a clear exception, while __isset should report whether an approved field is meaningfully available.
Dynamic properties created by arbitrary assignment are deprecated for most classes from PHP 8.2, with specific exceptions. A deliberate __set implementation can manage its own internal map, but that is not a reason to accept unlimited unvalidated keys. Prefer declared properties for stable object state.
Missing method calls and controlled dispatch
__call(string $name, array $arguments) handles inaccessible or missing instance method calls. __callStatic(string $name, array $arguments) handles corresponding static calls. These hooks are useful for carefully designed proxies, but they can also hide misspelled methods and weaken discoverability.
Do not forward every unknown method name to an external service or execute it as an arbitrary function. Map approved operations and validate arguments. A proxy's public contract should describe which operations it supports and how failures are reported. Otherwise, callers have no reliable way to know what the object actually does.
Explicit interface methods are usually better for core domain operations such as approveOrder or chargePayment. Their names and signatures are visible, and static analysis can help verify callers. Magic dispatch is an integration mechanism rather than a shortcut around API design.
String conversion and callable objects
__toString(): string controls how an object behaves when converted to text. Keep that representation concise, predictable and free of secrets. A diagnostic string is not automatically a serialization format suitable for storing or reconstructing the object. Use a dedicated serialization contract for that purpose.
__invoke() lets an object be used as a callable. It is useful when the behavior has configuration or dependencies that deserve their own object. A configured formatter can then be passed where a callback is expected without exposing arbitrary method dispatch.
Other magic methods include __construct, __destruct, __clone, __serialize, __unserialize and __debugInfo. Each has a specific trigger and signature. Serialization hooks require especially careful input validation; do not unserialize untrusted external data merely because a class has custom methods.
Visibility, signatures and recursion
Most magic methods must be public, with documented exceptions for lifecycle methods such as constructors, destructors and cloning. Modern PHP reports signature mismatches when declarations do not follow the required forms. Adding a method with the right special name but the wrong signature is not a supported customization.
Avoid recursive access inside __get or __set. Reading the same virtual property while computing it can lead to confusing recursion behavior. Store real state in declared private properties and access that storage directly. When implementing several hooks, keep their meanings consistent: __isset should not claim a field is unavailable while __get always returns a normal value unless null semantics justify that policy.
The PHP magic methods manual lists exact names, signatures and version changes. Do not invent double-underscore methods and expect PHP to call them automatically. The convention is reserved for documented language features.
PHP Magic Methods Examples
Each example shows the code, the output it produces and what each part does.
A controlled virtual display name
The virtual name is explicitly supported, and unknown names do not disappear into an empty string. A plain displayName method would be easier for many editors and callers to discover. Use the magic form only when property-style integration provides a concrete benefit.
<?php
class PersonLabel {
public function __construct(private string $first, private string $last) {}
public function __get(string $name): mixed {
if ($name === 'displayName') {
return $this->first . ' ' . $this->last;
}
throw new OutOfBoundsException('Unknown property');
}
public function __isset(string $name): bool {
return $name === 'displayName';
}
}
$person = new PersonLabel('Amina', 'Khan');
echo $person->displayName . PHP_EOL;
var_export(isset($person->displayName));
echo PHP_EOL;Amina Khan
trueAn invokable formatter with a string identity
Calling the variable invokes __invoke. Casting to string invokes __toString. These are separate contracts even though they belong to one object. The text identity does not expose configuration or credentials, and the formatter operation accepts one clear input.
<?php
class BracketFormatter {
public function __invoke(string $text): string {
return '[' . $text . ']';
}
public function __toString(): string {
return 'bracket formatter';
}
}
$formatter = new BracketFormatter();
echo $formatter('Pen') . PHP_EOL;
echo (string) $formatter . PHP_EOL;[Pen]
bracket formatterCommon PHP Magic Methods Mistakes
Hiding ordinary behavior behind magic
Do not use __get to make every database lookup look like a cheap property read. A caller may access the same field repeatedly and trigger repeated I/O without realizing it. Make expensive operations explicit, cache deliberately where appropriate, and document lazy behavior when the integration requires it.
Do not let __toString perform persistence or network actions. String conversion can happen during logging or error handling, where unexpected external work is especially confusing. Keep magic methods narrow, predictable and aligned with their triggers.
PHP Magic Methods in Real Projects
Practical task: a configured invokable prefixer
Create a prefix formatter that stores one prefix at construction and implements __invoke to add it to text. Pass the object to array_map so the operation uses the callable-object contract. This is a good fit because configuration belongs to the object while each invocation performs one small transformation.
<?php
class Prefixer {
public function __construct(private string $prefix) {}
public function __invoke(string $text): string {
return $this->prefix . $text;
}
}
$prefixer = new Prefixer('Item: ');
$labels = array_map($prefixer, ['Pen', 'Book']);
echo implode(' | ', $labels) . PHP_EOL;Item: Pen | Item: BookThe object is callable because it defines __invoke. It does not need __call or virtual properties. Try a different prefix and verify that the input list remains unchanged. If the result is rendered in HTML, escape the text at that boundary; magic methods do not make output safe for arbitrary contexts.
PHP Magic Methods FAQ
Does __get run for every property?
No. Accessible declared properties are normally read directly. The hook handles inaccessible or missing access.
Can __call make an object implement an interface?
No. The class must satisfy declared interface methods. Generic missing-method handling is not a substitute for the required contract.
Why use an invokable object instead of a closure?
An object can make configured state and dependencies explicit, especially when behavior is reused or substantial.
Is __toString a JSON serializer?
No. It returns one string representation. Use an explicit JSON or serialization contract when that is the requirement.
Should unknown virtual properties return null?
Only if the API intentionally defines that behavior. A clear exception often catches spelling errors sooner.
PHP Magic Methods Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
An order approval is a stable domain operation. Which API is usually clearest?
PHP Magic Methods Quiz
2 questions to check what you learned.
Which magic method makes an object callable like a function?
- __invoke()
- __get()
- __LINE__
When does __get normally run?
- For every local variable
- When an inaccessible or missing property is read
- Only during database connection