summaryrefslogtreecommitdiff
path: root/Zend
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2020-05-25 12:10:41 +0200
committerNikita Popov <nikita.ppv@gmail.com>2020-05-28 11:51:35 +0200
commit55dd3945fb32cf35e0385febc0114a0036d50190 (patch)
tree960e07d67952b934fb0b4503e8cbbeeae1a26599 /Zend
parentefbe96166dc32f85e57e2c23e0c1440488a42432 (diff)
downloadphp-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.
Diffstat (limited to 'Zend')
-rw-r--r--Zend/tests/exception_delayed_message.phpt41
-rw-r--r--Zend/zend_exceptions.c3
-rw-r--r--Zend/zend_object_handlers.c2
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;