diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2020-05-25 12:10:41 +0200 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2020-05-28 11:51:35 +0200 |
commit | 55dd3945fb32cf35e0385febc0114a0036d50190 (patch) | |
tree | 960e07d67952b934fb0b4503e8cbbeeae1a26599 | |
parent | efbe96166dc32f85e57e2c23e0c1440488a42432 (diff) | |
download | php-git-55dd3945fb32cf35e0385febc0114a0036d50190.tar.gz |
Convert Exception::getMessage() result to string
We specify that the return type of Exception::getMessage() is a
string. However, we don't currently ensure this, because
Exception::$message is a protected member that can be set to any
type. Fix this by performing an explicit type-cast.
This also requires a temporary refcount increment in the __toString()
object handler, because there is no additional owner of the object,
and it may get released prematurely as part of the __toString() call.
-rw-r--r-- | Zend/tests/exception_delayed_message.phpt | 41 | ||||
-rw-r--r-- | Zend/zend_exceptions.c | 3 | ||||
-rw-r--r-- | Zend/zend_object_handlers.c | 2 |
3 files changed, 44 insertions, 2 deletions
diff --git a/Zend/tests/exception_delayed_message.phpt b/Zend/tests/exception_delayed_message.phpt new file mode 100644 index 0000000000..e9f7a1fa21 --- /dev/null +++ b/Zend/tests/exception_delayed_message.phpt @@ -0,0 +1,41 @@ +--TEST-- +Exception with delayed message computation +--FILE-- +<?php + +class MyException extends Exception { + public $message; + public $messageCallback; + + public function __construct() { + $this->messageCallback = static function() { + return "Foobar"; + }; + $this->message = new class($this->message, $this->messageCallback) { + private $message; + private $messageCallback; + + public function __construct(&$message, &$messageCallback) + { + $this->message = &$message; + $this->messageCallback = &$messageCallback; + } + + public function __toString(): string + { + $messageCallback = $this->messageCallback; + $this->messageCallback = null; + return $this->message = $messageCallback(); + } + }; + } +} + +throw new MyException; + +?> +--EXPECTF-- +Fatal error: Uncaught MyException: Foobar in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index a039d18668..31738b6a86 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -414,8 +414,7 @@ ZEND_METHOD(Exception, getMessage) ZEND_PARSE_PARAMETERS_NONE(); prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_MESSAGE); - ZVAL_DEREF(prop); - ZVAL_COPY(return_value, prop); + RETURN_STR(zval_get_string(prop)); } /* }}} */ diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 7abc87d75e..ce5411b040 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1800,7 +1800,9 @@ ZEND_API int zend_std_cast_object_tostring(zend_object *readobj, zval *writeobj, zend_class_entry *ce = readobj->ce; if (ce->__tostring) { zval retval; + GC_ADDREF(readobj); zend_call_method_with_0_params(readobj, ce, &ce->__tostring, "__tostring", &retval); + zend_object_release(readobj); if (EXPECTED(Z_TYPE(retval) == IS_STRING)) { ZVAL_COPY_VALUE(writeobj, &retval); return SUCCESS; |