1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Properly handle reference return value from __toString()

It's possible to return a reference from __toString(), but this is not
handled and results in a (confusing) error telling that the return value
must be a string.
Properly handle this by unwrapping the reference.

Closes GH-18810.
This commit is contained in:
Niels Dossche
2025-06-09 14:47:02 +02:00
parent 8f3e5553f3
commit eb151e39b0
6 changed files with 48 additions and 0 deletions

1
NEWS
View File

@@ -54,6 +54,7 @@ PHP NEWS
released on bailout). (DanielEScherzer and ilutov)
. Fixed AST printing for immediately invoked Closure. (Dmitrii Derepko)
. Properly handle __debugInfo() returning an array reference. (nielsdos)
. Properly handle reference return value from __toString(). (nielsdos)
. Added the pipe (|>) operator. (crell)
- Curl:

View File

@@ -0,0 +1,19 @@
--TEST--
Exception fatal uncaught error with reference __toString
--FILE--
<?php
class MyException extends Exception {
private $field = 'my string';
public function &__toString(): string {
return $this->field;
}
}
// Must not be caught to trigger the issue!
throw new MyException;
?>
--EXPECTF--
Fatal error: Uncaught my string
thrown in %s on line %d

View File

@@ -0,0 +1,17 @@
--TEST--
String cast with reference __toString
--FILE--
<?php
class MyClass {
private $field = 'my string';
public function &__toString(): string {
return $this->field;
}
}
echo new MyClass;
?>
--EXPECT--
my string

View File

@@ -969,6 +969,9 @@ ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *ex, int severit
zend_call_known_instance_method_with_0_params(ex->ce->__tostring, ex, &tmp);
if (!EG(exception)) {
if (UNEXPECTED(Z_ISREF(tmp))) {
zend_unwrap_reference(&tmp);
}
if (Z_TYPE(tmp) != IS_STRING) {
zend_error(E_WARNING, "%s::__toString() must return a string", ZSTR_VAL(ce_exception->name));
} else {

View File

@@ -2440,8 +2440,12 @@ ZEND_API zend_result zend_std_cast_object_tostring(zend_object *readobj, zval *w
zend_call_known_instance_method_with_0_params(ce->__tostring, readobj, &retval);
zend_object_release(readobj);
if (EXPECTED(Z_TYPE(retval) == IS_STRING)) {
is_string:
ZVAL_COPY_VALUE(writeobj, &retval);
return SUCCESS;
} else if (Z_ISREF(retval)) {
zend_unwrap_reference(&retval);
goto is_string;
}
zval_ptr_dtor(&retval);
if (!EG(exception)) {

View File

@@ -702,6 +702,10 @@ static inline void phpdbg_handle_exception(void) /* {{{ */
EG(exception) = NULL;
msg = ZSTR_EMPTY_ALLOC();
} else {
if (UNEXPECTED(Z_ISREF(tmp))) {
zend_unwrap_reference(&tmp);
}
ZEND_ASSERT(Z_TYPE(tmp) == IS_STRING);
zend_update_property_string(zend_get_exception_base(ex), ex, ZEND_STRL("string"), Z_STRVAL(tmp));
zval_ptr_dtor(&tmp);
msg = zval_get_string(zend_read_property_ex(zend_get_exception_base(ex), ex, ZSTR_KNOWN(ZEND_STR_STRING), /* silent */ true, &rv));