Skip to content

Commit fedd5a5

Browse files
committed
Add throw_on_error declare
This declare promotes warnings automatically to Exceptions
1 parent e069032 commit fedd5a5

26 files changed

+417
-2
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
If throw_on_error declare statement is explicitly turned off do not promote warning
3+
--FILE--
4+
<?php
5+
declare(throw_on_error=0);
6+
7+
echo $undef;
8+
9+
--EXPECTF--
10+
Warning: Undefined variable $undef in %s on line %d
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
throw_on_error declare statement promote warning
3+
--FILE--
4+
<?php
5+
declare(throw_on_error=1);
6+
7+
try {
8+
echo $undef;
9+
} catch (\Exception $e) {
10+
var_dump($e);
11+
}
12+
13+
--EXPECTF--
14+
object(Exception)#1 (7) {
15+
["message":protected]=>
16+
string(25) "Undefined variable $undef"
17+
["string":"Exception":private]=>
18+
string(0) ""
19+
["code":protected]=>
20+
int(2)
21+
["file":protected]=>
22+
string(%d) "%s"
23+
["line":protected]=>
24+
int(5)
25+
["trace":"Exception":private]=>
26+
array(0) {
27+
}
28+
["previous":"Exception":private]=>
29+
NULL
30+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
Test throw_on_error declare statement is isolated to file
3+
--FILE--
4+
<?php
5+
include 'fixture/no_declare.inc';
6+
7+
try {
8+
include 'fixture/declare_on.inc';
9+
} catch (\Exception $e) {
10+
echo "Warning caught\n";
11+
}
12+
include 'fixture/declare_off.inc';
13+
?>
14+
DONE
15+
--EXPECTF--
16+
Warning: Undefined variable $undef in %sno_declare.inc on line 2
17+
Warning caught
18+
19+
Warning: Undefined variable $undef in %sdeclare_off.inc on line 4
20+
DONE
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
php_exception_or_warning_docref test when declare enabled
3+
--SKIPIF--
4+
<?php
5+
if (!class_exists('_ZendTestClass')) die('skip zend_test extension required');
6+
?>
7+
--FILE--
8+
<?php
9+
declare(throw_on_error=1);
10+
11+
try {
12+
zend_throw_on_error_declare_exception_or_warning();
13+
} catch (\Exception $e) {
14+
echo $e->getMessage() . \PHP_EOL;
15+
}
16+
17+
echo "OK";
18+
--EXPECT--
19+
Context dependent
20+
OK
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
php_exception_or_warning_docref test when declare explicitly disable
3+
--SKIPIF--
4+
<?php
5+
if (!class_exists('_ZendTestClass')) die('skip zend_test extension required');
6+
?>
7+
--FILE--
8+
<?php
9+
declare(throw_on_error=0);
10+
11+
zend_throw_on_error_declare_exception_or_warning();
12+
13+
echo "OK";
14+
--EXPECTF--
15+
Warning: zend_throw_on_error_declare_exception_or_warning(): Context dependent in %s on line %d
16+
OK
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
php_exception_or_warning_docref test when declare implicitly disable
3+
--SKIPIF--
4+
<?php
5+
if (!class_exists('_ZendTestClass')) die('skip zend_test extension required');
6+
?>
7+
--FILE--
8+
<?php
9+
10+
zend_throw_on_error_declare_exception_or_warning();
11+
12+
echo "OK";
13+
--EXPECTF--
14+
Warning: zend_throw_on_error_declare_exception_or_warning(): Context dependent in %s on line %d
15+
OK
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
declare(throw_on_error=0);
3+
4+
echo $undef;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
declare(throw_on_error=1);
3+
4+
echo $undef;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?php
2+
echo $undef;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
Promoted warning by throw_on_error must ignore error_reporting setting
3+
--FILE--
4+
<?php
5+
declare(throw_on_error=1);
6+
ini_set("error_reporting", 0);
7+
try {
8+
echo $undef;
9+
} catch(Exception $e) {
10+
var_dump($e);
11+
}
12+
13+
--EXPECTF--
14+
object(Exception)#1 (7) {
15+
["message":protected]=>
16+
string(25) "Undefined variable $undef"
17+
["string":"Exception":private]=>
18+
string(0) ""
19+
["code":protected]=>
20+
int(2)
21+
["file":protected]=>
22+
string(%d) "%s"
23+
["line":protected]=>
24+
int(5)
25+
["trace":"Exception":private]=>
26+
array(0) {
27+
}
28+
["previous":"Exception":private]=>
29+
NULL
30+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
Promoted warning by throw_on_error must ignore @ operator
3+
--FILE--
4+
<?php
5+
declare(throw_on_error=1);
6+
7+
try {
8+
echo @$undef;
9+
} catch (\Exception $e) {
10+
var_dump($e);
11+
}
12+
13+
--EXPECTF--
14+
object(Exception)#1 (7) {
15+
["message":protected]=>
16+
string(25) "Undefined variable $undef"
17+
["string":"Exception":private]=>
18+
string(0) ""
19+
["code":protected]=>
20+
int(2)
21+
["file":protected]=>
22+
string(%d) "%s"
23+
["line":protected]=>
24+
int(5)
25+
["trace":"Exception":private]=>
26+
array(0) {
27+
}
28+
["previous":"Exception":private]=>
29+
NULL
30+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
If throw_on_error declare statement is missing do not promote warning
3+
--FILE--
4+
<?php
5+
6+
echo $undef;
7+
8+
function foo() {
9+
echo $undef;
10+
}
11+
12+
foo();
13+
14+
--EXPECTF--
15+
Warning: Undefined variable $undef in %s on line %d
16+
17+
Warning: Undefined variable $undef in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
throw_on_error declare statement with strict_types declare after
3+
--FILE--
4+
<?php
5+
declare(throw_on_error=1);
6+
declare(strict_types=1);
7+
8+
echo "OK";
9+
--EXPECT--
10+
OK
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
throw_on_error declare statement with strict_types declare before
3+
--FILE--
4+
<?php
5+
declare(strict_types=1);
6+
declare(throw_on_error=1);
7+
8+
echo "OK";
9+
--EXPECT--
10+
OK
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--TEST--
2+
throw_on_error declare statement must promote warning emit in internal function
3+
--FILE--
4+
<?php
5+
declare(throw_on_error=1);
6+
7+
try {
8+
file_get_contents('not_found.txt');
9+
} catch (\Exception $e) {
10+
var_dump($e);
11+
}
12+
13+
--EXPECTF--
14+
object(Exception)#1 (7) {
15+
["message":protected]=>
16+
string(82) "file_get_contents(not_found.txt): Failed to open stream: No such file or directory"
17+
["string":"Exception":private]=>
18+
string(0) ""
19+
["code":protected]=>
20+
int(2)
21+
["file":protected]=>
22+
string(%d) "%s"
23+
["line":protected]=>
24+
int(5)
25+
["trace":"Exception":private]=>
26+
array(1) {
27+
[0]=>
28+
array(4) {
29+
["file"]=>
30+
string(%d) "%s"
31+
["line"]=>
32+
int(5)
33+
["function"]=>
34+
string(17) "file_get_contents"
35+
["args"]=>
36+
array(1) {
37+
[0]=>
38+
string(13) "not_found.txt"
39+
}
40+
}
41+
}
42+
["previous":"Exception":private]=>
43+
NULL
44+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
--TEST--
2+
throw_on_error declare statement must promote warning emit in user defined function
3+
--FILE--
4+
<?php
5+
declare(throw_on_error=1);
6+
7+
function foo() {
8+
echo $undef;
9+
}
10+
11+
try {
12+
foo();
13+
} catch (\Exception $e) {
14+
var_dump($e);
15+
}
16+
17+
--EXPECTF--
18+
object(Exception)#1 (7) {
19+
["message":protected]=>
20+
string(25) "Undefined variable $undef"
21+
["string":"Exception":private]=>
22+
string(0) ""
23+
["code":protected]=>
24+
int(2)
25+
["file":protected]=>
26+
string(%d) "%s"
27+
["line":protected]=>
28+
int(5)
29+
["trace":"Exception":private]=>
30+
array(1) {
31+
[0]=>
32+
array(4) {
33+
["file"]=>
34+
string(%d) "%s"
35+
["line"]=>
36+
int(9)
37+
["function"]=>
38+
string(3) "foo"
39+
["args"]=>
40+
array(0) {
41+
}
42+
}
43+
}
44+
["previous":"Exception":private]=>
45+
NULL
46+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
throw_on_error declare statement must be before anything else
3+
--FILE--
4+
<?php
5+
echo "Before";
6+
7+
declare(throw_on_error=1);
8+
9+
--EXPECTF--
10+
Fatal error: throw_on_error declaration must be the very first statement in the script in %s on line 4
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
--TEST--
2+
throw_on_error declare statement
3+
--FILE--
4+
<?php
5+
declare(throw_on_error=1);
6+
7+
echo "OK";
8+
--EXPECT--
9+
OK

Zend/zend.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,23 @@ static ZEND_COLD void zend_error_impl(
13051305
zend_stack delayed_oplines_stack;
13061306
int type = orig_type & E_ALL;
13071307

1308+
/* Promote E_WARNING to exception when throw_on_error declare is enabled */
1309+
if (orig_type == E_WARNING && EG(current_execute_data)) {
1310+
zend_execute_data *ex = EG(current_execute_data);
1311+
/* Find first non internal execute_data */
1312+
while (ex && (!ex->func || !ZEND_USER_CODE(ex->func->type))) {
1313+
ex = ex->prev_execute_data;
1314+
}
1315+
if (ex->func == NULL) {
1316+
goto normal;
1317+
}
1318+
if ((ex->func->common.fn_flags & ZEND_ACC_THROW_WARNING) != 0) {
1319+
zend_throw_exception(NULL, ZSTR_VAL(message), E_WARNING);
1320+
return;
1321+
}
1322+
}
1323+
normal:
1324+
13081325
/* Report about uncaught exception in case of fatal errors */
13091326
if (EG(exception)) {
13101327
zend_execute_data *ex;

0 commit comments

Comments
 (0)