PHP 7.4 allows Exceptions from __toString()
Introduction
Currently prohibited from __toString() throws an exception and will cause a fatal error. This makes calling arbitrary code dangerous and makes it a problematic general-purpose API. 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 everywhere are prepared to handle exceptions "correctly", i.e. as early as possible.
From a technical perspective, this restriction is ultimately ineffective, since exceptions during string conversion can still be triggered by error handlers that convert recoverable errors into exceptions:
set_error_handler(function() { throw new Exception(); }); try { (string) new stdClass; } catch (Exception $e) { echo "(string) threw an exception...\n"; }
In fact, Symfony exploits this vulnerability to bypass the current restrictions. Unfortunately, this relies on the $errcontext parameter, which disappeared in PHP 8.
However, until we do a full review of string conversion in this code base, past articles on this topic There has been no discussion of loosening this restriction. This has been done in an attached implementation request.
Recommendations
Allow exceptions to be thrown from __toString() and it will behave as usual. Fatal errors are no longer triggered.
Also, convert the "cannot be converted to a string" and "__toString() must return a string value" recoverable fatal errors into correct error exceptions, consistent with the error policy established in PHP 7 .
Extension Guidelines
Extension authors who want to gracefully handle exceptions from string conversions should consider the following guidelines:
● If zval_get_string( ), convert_to_string() and friends generate an exception, they still generate a string. This string is guaranteed to be temporary. This means there is no need to free it, but it is possible to do so. In context, you can choose the more convenient option.
● If the conversion from object to string fails, the result of the string conversion will be an empty string, or if the array is converted to a string and the error handler raises the result notification to an exception. "Array". (The behavior is the same as before.)
● Usually it is enough to check whether an exception was thrown using the usual if (EG(exception)) check:
zend_string *str = zval_get_string(val); if (EG(exception)) { // Possibly free other resources here. return; }
Except In addition, some helper APIs are provided that model conversions as error-prone operations:
// 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 if the conversion fails. Therefore, using it is safer than using convert_to_string() and exception checking.
While checking every string conversion will certainly put you on the safe side, ignoring these checks usually just results in a few unnecessary calculations and possibly redundant warnings. The main thing you should be aware of are operations that modify persistent structures (such as databases).
Backward-incompatible changes
The transition from a recoverable fatal error to an error exception is technically a BC break.
Translation: https://wiki.php.net/rfc/tostring_exceptions
The above is the detailed content of PHP 7.4 allows throwing exceptions from __toString(). For more information, please follow other related articles on the PHP Chinese website!