Lesson 83 of 100
Classes15 min5 checksPHP 8.5

Give Objects a String Representation with __toString

Learn how __toString lets an object decide what it looks like as text, and how Stringable and strict typing affect where that text can be used.

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.

Example
<?php
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";
Output
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().

Example
<?php
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);
Output
[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.

Example
<?php
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";
Output
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.

Before you start

Functions · Arrays · Types

You’ll practice
  • Implement __toString and return its deterministic string
  • Implement and test a deterministic PHP 8.4 function
Your task

Make renderMoney() pass all 5 checks. Run your code as often as you like, then check your answer.

exercise.phpSaved in this browserPHP 8.5
CONSOLE & CHECKS● Ready

Ready when you are

Your output will appear here.
Make it work. Then make it pass.

PHP loads on your first run. Each check starts fresh. Your code stays in this browser; clearing site data removes your progress. A check or the task looks wrong? Report a problem with this exercise.