Skip to content

Commit a31f464

Browse files
committed
Allow exceptions in __toString()
RFC: https://wiki.php.net/rfc/tostring_exceptions And convert some object to string conversion related recoverable fatal errors into Error exceptions. Improve exception safety of internal code performing string conversions.
1 parent 528aa79 commit a31f464

File tree

113 files changed

+1389
-473
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

113 files changed

+1389
-473
lines changed

UPGRADING

+5
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,11 @@ PHP 7.4 UPGRADE NOTES
187187
. Support for WeakReferences has been added.
188188
RFC: https://wiki.php.net/rfc/weakrefs
189189

190+
. Throwing exceptions from __toString() is now permitted. Previously this
191+
resulted in a fatal error. Existing recoverable fatals in string conversions
192+
have been converted to Error exceptions.
193+
RFC: https://wiki.php.net/rfc/tostring_exceptions
194+
190195
- CURL:
191196
. CURLFile now supports stream wrappers in addition to plain file names, if
192197
the extension has been built against libcurl >= 7.56.0. The streams may

UPGRADING.INTERNALS

+10
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ PHP 7.4 INTERNALS UPGRADE NOTES
1919
p. ZEND_EXT_FCALL_BEGIN can access arguments
2020
q. ZEND_COMPILE_IGNORE_USER_FUNCTIONS and ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS
2121
r. TSRM environment locking
22+
s. Typed references support
23+
t. Exceptions thrown by string conversions.
2224

2325
2. Build system changes
2426
a. Abstract
@@ -198,6 +200,14 @@ PHP 7.4 INTERNALS UPGRADE NOTES
198200
behave as if they are safe, care should still be taken in multi-threaded
199201
environments.
200202

203+
s. Correct support for typed properties requires the use of new macros to
204+
assign values to references. For more information see
205+
https://wiki.php.net/rfc/typed_properties_v2#impact_on_extensions.
206+
207+
t. convert_to_string() and zval_get_string() are now more likely to result in
208+
an exception. For instructions on how to gracefully handle this see
209+
https://wiki.php.net/rfc/tostring_exceptions#extension_guidelines.
210+
201211
========================
202212
2. Build system changes
203213
========================

Zend/tests/bug26166.phpt

+11-14
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,18 @@ echo $o;
3131

3232
echo "===NONE===\n";
3333

34-
function my_error_handler($errno, $errstr, $errfile, $errline) {
35-
var_dump($errstr);
36-
}
37-
38-
set_error_handler('my_error_handler');
39-
4034
class NoneTest
4135
{
4236
function __toString() {
4337
}
4438
}
4539

4640
$o = new NoneTest;
47-
echo $o;
41+
try {
42+
echo $o;
43+
} catch (Error $e) {
44+
echo $e->getMessage(), "\n";
45+
}
4846

4947
echo "===THROW===\n";
5048

@@ -58,17 +56,16 @@ class ErrorTest
5856
$o = new ErrorTest;
5957
try {
6058
echo $o;
61-
}
62-
catch (Exception $e) {
63-
echo "Got the exception\n";
59+
} catch (Exception $e) {
60+
echo $e->getMessage(), "\n";
6461
}
6562

6663
?>
6764
===DONE===
68-
--EXPECTF--
65+
--EXPECT--
6966
Hello World!
7067
===NONE===
71-
string(%d) "Method NoneTest::__toString() must return a string value"
68+
Method NoneTest::__toString() must return a string value
7269
===THROW===
73-
74-
Fatal error: Method ErrorTest::__toString() must not throw an exception, caught Exception: This is an error! in %sbug26166.php on line %d
70+
This is an error!
71+
===DONE===

Zend/tests/bug28444.phpt

+8-11
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,6 @@ Bug #28444 (Cannot access undefined property for object with overloaded property
33
--FILE--
44
<?php
55

6-
function my_error_handler($errno, $errstr, $errfile, $errline) {
7-
var_dump($errstr);
8-
}
9-
10-
set_error_handler('my_error_handler');
11-
126
class ObjectOne
137
{
148
public $x;
@@ -17,6 +11,10 @@ class ObjectOne
1711
{
1812
$this->x = $x;
1913
}
14+
15+
function __toString() {
16+
return "Object";
17+
}
2018
}
2119

2220
class Overloaded
@@ -55,8 +53,8 @@ var_dump($y->z->x = 6);
5553

5654
?>
5755
===DONE===
58-
--EXPECTF--
59-
object(ObjectOne)#%d (1) {
56+
--EXPECT--
57+
object(ObjectOne)#2 (1) {
6058
["x"]=>
6159
int(2)
6260
}
@@ -66,9 +64,8 @@ Overloaded::__set(y,3)
6664
int(3)
6765
Overloaded::__get(y)
6866
int(3)
69-
string(58) "Object of class ObjectOne could not be converted to string"
70-
Overloaded::__set(z,)
71-
object(ObjectOne)#%d (1) {
67+
Overloaded::__set(z,Object)
68+
object(ObjectOne)#3 (1) {
7269
["x"]=>
7370
int(4)
7471
}

Zend/tests/bug30791.phpt

+9-16
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,25 @@ Bug #30791 (magic methods (__sleep/__wakeup/__toString) call __call if object is
33
--FILE--
44
<?php
55

6-
function my_error_handler($errno, $errstr, $errfile, $errline) {
7-
var_dump($errstr);
8-
}
9-
10-
set_error_handler('my_error_handler');
11-
126
class a
137
{
14-
public $a = 4;
15-
function __call($a,$b) {
16-
return "unknown method";
17-
}
8+
public $a = 4;
9+
function __call($name, $args) {
10+
echo __METHOD__, "\n";
11+
}
1812
}
1913

2014
$b = new a;
21-
echo $b,"\n";
15+
var_dump($b);
2216
$c = unserialize(serialize($b));
23-
echo $c,"\n";
2417
var_dump($c);
2518

2619
?>
2720
--EXPECT--
28-
string(50) "Object of class a could not be converted to string"
29-
30-
string(50) "Object of class a could not be converted to string"
31-
21+
object(a)#1 (1) {
22+
["a"]=>
23+
int(4)
24+
}
3225
object(a)#2 (1) {
3326
["a"]=>
3427
int(4)

Zend/tests/bug60909_2.phpt

+6-3
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,21 @@ Bug #60909 (custom error handler throwing Exception + fatal error = no shutdown
33
--FILE--
44
<?php
55
register_shutdown_function(function(){echo("\n\n!!!shutdown!!!\n\n");});
6-
set_error_handler(function($errno, $errstr, $errfile, $errline){throw new Exception("Foo");});
76

87
class Bad {
98
public function __toString() {
10-
throw new Exception('Oops, I cannot do this');
9+
throw new Exception('I CAN DO THIS');
1110
}
1211
}
1312

1413
$bad = new Bad();
1514
echo "$bad";
1615
--EXPECTF--
17-
Fatal error: Method Bad::__toString() must not throw an exception, caught Exception: Oops, I cannot do this in %sbug60909_2.php on line %d
16+
Fatal error: Uncaught Exception: I CAN DO THIS in %s:%d
17+
Stack trace:
18+
#0 %s(%d): Bad->__toString()
19+
#1 {main}
20+
thrown in %s on line %d
1821

1922

2023
!!!shutdown!!!

Zend/tests/bug70967.phpt

+5-1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,8 @@ class A {
1111
echo (new A);
1212
?>
1313
--EXPECTF--
14-
Fatal error: Method A::__toString() must not throw an exception, caught Error: Call to undefined function undefined_function() in %sbug70967.php on line %d
14+
Fatal error: Uncaught Error: Call to undefined function undefined_function() in %s:%d
15+
Stack trace:
16+
#0 %s(%d): A->__toString()
17+
#1 {main}
18+
thrown in %s on line %d

Zend/tests/bug72162.phpt

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,8 @@ $var11 = new StdClass();
77
$var16 = error_reporting($var11);
88
?>
99
--EXPECTF--
10-
Recoverable fatal error: Object of class stdClass could not be converted to string in %sbug72162.php on line %d
10+
Fatal error: Uncaught Error: Object of class stdClass could not be converted to string in %s:%d
11+
Stack trace:
12+
#0 %s(%d): error_reporting(Object(stdClass))
13+
#1 {main}
14+
thrown in %s on line %d

Zend/tests/call_with_refs.phpt

+9-8
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22
Check call to non-ref function with call-time refs
33
--FILE--
44
<?php
5-
function my_errorhandler($errno,$errormsg) {
6-
global $my_var;
7-
$my_var=0x12345;
8-
echo $errormsg."\n";
9-
return true;
5+
class Test {
6+
public function __toString() {
7+
global $my_var;
8+
$my_var=0x12345;
9+
return "";
10+
}
1011
}
11-
$oldhandler = set_error_handler("my_errorhandler");
12+
1213
$my_var = str_repeat("A",64);
13-
$data = call_user_func_array("substr_replace",array(&$my_var, new StdClass(),1));
14+
$data = call_user_func_array("substr_replace",array(&$my_var, new Test(), 1));
1415
echo "OK!";
16+
?>
1517
--EXPECT--
16-
Object of class stdClass could not be converted to string
1718
OK!

Zend/tests/class_properties_const.phpt

+4-1
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,7 @@ NULL
2222
Notice: Undefined property: A::$1 in %sclass_properties_const.php on line %d
2323
NULL
2424

25-
Recoverable fatal error: Object of class Closure could not be converted to string in %sclass_properties_const.php on line %d
25+
Fatal error: Uncaught Error: Object of class Closure could not be converted to string in %s:%d
26+
Stack trace:
27+
#0 {main}
28+
thrown in %s on line %d

Zend/tests/closure_015.phpt

+15-13
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@
22
Closure 015: converting to string/unicode
33
--FILE--
44
<?php
5-
set_error_handler('myErrorHandler', E_RECOVERABLE_ERROR);
6-
function myErrorHandler($errno, $errstr, $errfile, $errline) {
7-
echo "Error: $errstr at $errfile($errline)\n";
8-
return true;
9-
}
5+
106
$x = function() { return 1; };
11-
print (string) $x;
12-
print "\n";
13-
print $x;
14-
print "\n";
15-
?>
16-
--EXPECTF--
17-
Error: Object of class Closure could not be converted to string at %sclosure_015.php(8)
7+
try {
8+
print (string) $x;
9+
} catch (Error $e) {
10+
echo $e->getMessage(), "\n";
11+
}
12+
try {
13+
print $x;
14+
} catch (Error $e) {
15+
echo $e->getMessage(), "\n";
16+
}
1817

19-
Error: Object of class Closure could not be converted to string at %sclosure_015.php(10)
18+
?>
19+
--EXPECT--
20+
Object of class Closure could not be converted to string
21+
Object of class Closure could not be converted to string

Zend/tests/exception_009.phpt

+5-1
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,8 @@ throw new my_exception;
2525

2626
?>
2727
--EXPECT--
28-
Recoverable fatal error: Object of class stdClass could not be converted to string in Unknown on line 0
28+
Fatal error: Uncaught Error: Object of class stdClass could not be converted to string in [no active file]:0
29+
Stack trace:
30+
#0 [internal function]: Exception->__toString()
31+
#1 {main}
32+
thrown in [no active file] on line 0

0 commit comments

Comments
 (0)