Take the State of PHP 2026 Survey →

Allow throwing exceptions from __toString() View as Markdown

Implemented (PHP 7.4)
Authors
Nikita Popov nikic@php.net
Date
2019-04-30
Target
PHP 7.4
Implementation
https://github.com/php/php-src/pull/3887
Source
wiki.php.net/rfc/tostring_exceptions

Vote

Allow exceptions from __toString() in PHP 7.4? closed
Yes
42
No
0
42 voters
ab Yes 2019-05-23 16:02
ajf Yes 2019-06-01 16:16
ashnazg Yes 2019-05-29 14:02
bishop Yes 2019-05-22 15:10
brzuchal Yes 2019-05-23 06:32
bwoebi Yes 2019-05-22 14:06
carusogabriel Yes 2019-05-27 14:03
cmb Yes 2019-05-26 15:48
danack Yes 2019-05-27 15:01
derick Yes 2019-05-22 14:14
didou Yes 2019-05-22 13:50
duncan3dc Yes 2019-05-26 12:46
galvao Yes 2019-05-22 19:53
gasolwu Yes 2019-05-22 15:11
girgias Yes 2019-05-22 21:08
jasny Yes 2019-05-25 18:19
jhdxr Yes 2019-05-29 13:44
kalle Yes 2019-05-22 13:46
kelunik Yes 2019-05-23 19:34
kguest Yes 2019-05-28 11:45
levim Yes 2019-05-22 14:26
marcio Yes 2019-05-22 16:11
mariano Yes 2019-05-23 02:59
mbeccati Yes 2019-05-24 08:07
mightyuhu Yes 2019-05-23 11:37
mike Yes 2019-06-05 08:10
nikic Yes 2019-05-22 13:42
ocramius Yes 2019-05-22 14:48
ramsey Yes 2019-05-22 19:36
rasmus Yes 2019-06-02 14:21
reywob Yes 2019-05-22 14:06
rtheunissen Yes 2019-05-31 00:31
salathe Yes 2019-05-30 15:26
sammyk Yes 2019-05-25 18:06
santiagolizardo Yes 2019-05-23 11:55
sebastian Yes 2019-05-22 16:46
sergey Yes 2019-05-22 14:52
stas Yes 2019-05-22 21:07
subjective Yes 2019-05-26 10:57
svpernova09 Yes 2019-05-24 16:46
trowski Yes 2019-05-22 16:22
yunosh Yes 2019-05-24 08:14

Discussions on the mailing list

[RFC] Allow throwing exceptions from __toString()
by Nikita Popov · 17 messages
[VOTE] Allow throwing exceptions from __toString()
by Nikita Popov · 3 messages

RFC text

Copied from wiki.php.net, last synchronized Oct 13, 2026 11:51 UTC

Introduction

Throwing exceptions from __toString() is currently forbidden and will result in a fatal error. This makes it dangerous to call arbitrary code inside __toString() and makes its use as a general API problematic. This RFC aims to remove this restriction.

The rationale for the current behavior is that string conversions are performed in many places throughout the engine and standard library, and not all places are prepared to deal with exceptions “correctly”, in the sense that the exception is handled as early as possible.

This limitation is ultimately futile from a technical perspective, because exceptions during string conversion can still be triggered by an error handler that converts recoverable errors to exceptions:

set_error_handler(function() {
    throw new Exception();
});
 
try {
    (string) new stdClass;
} catch (Exception $e) {
    echo "(string) threw an exception...\n";
}

In fact, Symfony uses this loophole to work around the current limitation. Unfortunately this relies on the $errcontext parameter, which is going away in PHP 8.

Despite this, the sentiment in past discussions on this topic has been to not relax this restriction until we have performed a comprehensive audit of string conversions across the codebase. This has been done in the attached implementation pull request.

Proposal

Allow throwing exceptions from __toString(), which will behave as usual. Do not trigger a fatal error anymore.

Additionally convert the “could not be converted to string” and “__toString() must return a string value” recoverable fatal errors into proper Error exceptions, in line with the error policy established in PHP 7.

Extension Guidelines

Extension authors who would like to ensure that they handle exceptions from string conversions gracefully, should take the following guidelines into account:

  • If zval_get_string(), convert_to_string() and friends generate an exception, they will still produce a string. This string is guaranteed to be interned. This means that it is not necessary to release it, but it possible to do so. You can pick whichever option is more convenient in context.
  • The result of the string conversion will be an empty string if an object to string conversion failed, and “Array” if an array is converted to string and the resulting notice is promoted to an exception by an error handler. (This behavior is as before.)
  • It is generally sufficient to check whether an exception has been thrown using the usual if (EG(exception)) check:
zend_string *str = zval_get_string(val);
if (EG(exception)) {
    // Possibly free other resources here.
    return;
}
  • In addition to this, a number of helper APIs are provided that model the conversion as a fallible operation:
// Like zval_get_string() but returns NULL on conversion failure.
zend_string *str = zval_try_get_string(val);
if (!str) {
    // Possibly free other resources here.
    return;
}
// Main code.
zend_string_release(str);
 
 
// Like zval_get_tmp_string() but returns NULL on conversion failure.
zend_string *tmp, *str = zval_try_get_tmp_string(val, &tmp);
if (!str) {
    // Possibly free other resources here.
    return;
}
// Main code.
zend_tmp_string_release(tmp);
 
 
// Like convert_to_string() but returns a boolean indicating conversion success/failure.
if (!try_convert_to_string(val)) {
    // Possibly free other resources here.
    return;
}
// Main code.
  • try_convert_to_string() will not modify the original value in case of conversion failure. For this reason it is safer to use it, instead of convert_to_string() and an exception check.
  • While checking every single string conversion certainly puts you on the safe side, leaving out these checks will usually only result in some unneeded computation and possibly redundant warnings. The main thing you should watch out for are operations modifying persistent structures such as databases.

Backward Incompatible Changes

The conversion from recoverable fatal errors to Error exceptions is technically BC breaking.

Vote

Voting started 2019-05-22 and ends 2019-06-05.

Vote: Allow exceptions from __toString() in PHP 7.4? — Yes: 42 · No: 0 (closed)