Throwing from __toString was a fatal error, which meant a value object that could not render itself had to return a placeholder string and hope somebody noticed.
final class Money
{
public function __toString(): string
{
if ($this->currency === null) {
// 7.3: fatal. 7.4: a normal exception.
throw new LogicException('no currency');
}
return sprintf('%s %.2f', $this->currency, $this->cents / 100);
}
}
7.4 lifts the restriction, and the reason it existed is worth understanding before relying on it: string conversion happens in places with no error handling around them, including inside the engine’s own error message construction. An exception thrown while building an exception message is not a good afternoon. The safer position remains that __toString should be total — if a value can fail to render, an explicit method that can throw is clearer than a magic one that might.