]> granicus.if.org Git - php/commitdiff
Convert Exception::getMessage() result to string
authorNikita Popov <nikita.ppv@gmail.com>
Mon, 25 May 2020 10:10:41 +0000 (12:10 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Thu, 28 May 2020 09:51:35 +0000 (11:51 +0200)
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.

Zend/tests/exception_delayed_message.phpt [new file with mode: 0644]
Zend/zend_exceptions.c
Zend/zend_object_handlers.c

diff --git a/Zend/tests/exception_delayed_message.phpt b/Zend/tests/exception_delayed_message.phpt
new file mode 100644 (file)
index 0000000..e9f7a1f
--- /dev/null
@@ -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
index a039d186683880af563835a23c438ae7d5c6fa27..31738b6a862b5e23f04b9dd75e6f810c71eb3e41 100644 (file)
@@ -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));
 }
 /* }}} */
 
index 7abc87d75ec7c477dd77f274c69c690efa117426..ce5411b0406bd15a478f2d5fa48635f98af1e35a 100644 (file)
@@ -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;