01 · THE IDEAREAD → TRY → LEARN
Why objects need a text form
An object is not a string, so PHP needs to be told what to do when you echo one, interpolate it into a double-quoted string, or concatenate it with .. The magic method __toString() provides that answer. If a class defines it, PHP calls it whenever the object is used where text is expected.
This keeps formatting rules next to the data they describe. Instead of repeating the same formatting code across a script, the class owns one definition of "what this value looks like as text".
The required shape
The method signature is public function __toString(): string. Three rules matter:
- It must be
public. Magic methods other than__construct(),__destruct()and__clone()must be public, or PHP emits a warning. - If you declare types, they must match the documented signature, so the return type is
string. - The method takes no parameters. It cannot be customised per call; if you need options, write a normal method such as
format().
Since PHP 8.0, any class that contains __toString() implicitly implements the Stringable interface. Writing implements Stringable explicitly is still recommended because it documents the intent.
A first example
The class below stores a number and chooses a single, predictable text form for it. Notice that the same method is used for echo, interpolation and concatenation.
class Temperature {
public function __construct(private float $degrees) {}
public function __toString(): string {
return number_format($this->degrees, 1) . " C";
}
}
$t = new Temperature(21.456);
echo $t, "\n";
echo "Now: $t\n";
echo "Now: " . $t . "\n";21.5 C Now: 21.5 C Now: 21.5 C
The formatting is deterministic because number_format() with explicit arguments does not depend on the environment.
Stringable and explicit casts
Because __toString() makes the class Stringable, you can test for that capability with instanceof. You can also request the text explicitly with (string) $object, which is useful when you want a real string, for example to pass to strlen().
class Label {
public function __construct(private string $text) {}
public function __toString(): string {
return "[" . $this->text . "]";
}
}
$label = new Label("new");
echo $label, "\n";
echo "Tag: $label\n";
echo strlen((string) $label), "\n";
var_dump($label instanceof Stringable);[new] Tag: [new] 5 bool(true)
Strict typing
With declare(strict_types=1);, a Stringable object is not accepted by a parameter typed as plain string. If you want to accept both, use a union type: string|Stringable. Inside the function, cast with (string) to get the text.
declare(strict_types=1);
class Tag {
public function __construct(private string $name) {}
public function __toString(): string {
return "#" . $this->name;
}
}
function shout(string|Stringable $text): string {
return strtoupper((string) $text) . "!";
}
echo shout("hi"), "\n";
echo shout(new Tag("php")), "\n";HI! #PHP!
Good habits
- Keep the output deterministic. Avoid clocks, randomness or locale-dependent formatting inside the method.
- Always return a string. Without strict typing PHP may coerce a compatible value, but relying on that hides mistakes.
- Keep it cheap and side-effect free; it may be called implicitly in many places.
- Since PHP 7.4 an exception can be thrown from
__toString(), but a plain, total conversion is easier to reason about.
Exercise contract
Write the function renderMoney(string $currency, int $cents): string. Inside your file, define a class with __toString() and have the function create an instance and return it as a string.
The text must be the uppercase currency code, one space, then the amount with exactly two decimals. Negative amounts put a - directly before the number. For example, 1205 cents of usd becomes USD 12.05, 7 cents of gbp becomes GBP 0.07, and -50 cents of USD becomes USD -0.50. Work with the absolute value, then use intdiv(), the % operator and str_pad() for the cents part, so no floating-point rounding is involved.
Functions · Arrays · Types
- Implement __toString and return its deterministic string
- Implement and test a deterministic PHP 8.4 function