Skip to content

Commit 66523d5

Browse files
committed
Make throw statement an expression
1 parent 826a745 commit 66523d5

File tree

7 files changed

+322
-16
lines changed

7 files changed

+322
-16
lines changed

Zend/tests/throw/001.phpt

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
--TEST--
2+
throw expression
3+
--FILE--
4+
<?php
5+
6+
try {
7+
$result = true && throw new Exception("true && throw");
8+
var_dump($result);
9+
} catch (Exception $e) {
10+
var_dump($e->getMessage());
11+
}
12+
13+
try {
14+
$result = false && throw new Exception("false && throw");
15+
var_dump($result);
16+
} catch (Exception $e) {
17+
var_dump($e->getMessage());
18+
}
19+
20+
try {
21+
$result = true and throw new Exception("true and throw");
22+
var_dump($result);
23+
} catch (Exception $e) {
24+
var_dump($e->getMessage());
25+
}
26+
27+
try {
28+
$result = false and throw new Exception("false and throw");
29+
var_dump($result);
30+
} catch (Exception $e) {
31+
var_dump($e->getMessage());
32+
}
33+
34+
try {
35+
$result = true || throw new Exception("true || throw");
36+
var_dump($result);
37+
} catch (Exception $e) {
38+
var_dump($e->getMessage());
39+
}
40+
41+
try {
42+
$result = false || throw new Exception("false || throw");
43+
var_dump($result);
44+
} catch (Exception $e) {
45+
var_dump($e->getMessage());
46+
}
47+
48+
try {
49+
$result = true or throw new Exception("true or throw");
50+
var_dump($result);
51+
} catch (Exception $e) {
52+
var_dump($e->getMessage());
53+
}
54+
55+
try {
56+
$result = false or throw new Exception("false or throw");
57+
var_dump($result);
58+
} catch (Exception $e) {
59+
var_dump($e->getMessage());
60+
}
61+
62+
try {
63+
$result = null ?? throw new Exception("null ?? throw");
64+
var_dump($result);
65+
} catch (Exception $e) {
66+
var_dump($e->getMessage());
67+
}
68+
69+
try {
70+
$result = "foo" ?? throw new Exception('"foo" ?? throw');
71+
var_dump($result);
72+
} catch (Exception $e) {
73+
var_dump($e->getMessage());
74+
}
75+
76+
try {
77+
$result = null ?: throw new Exception("null ?: throw");
78+
var_dump($result);
79+
} catch (Exception $e) {
80+
var_dump($e->getMessage());
81+
}
82+
83+
try {
84+
$result = "foo" ?: throw new Exception('"foo" ?: throw');
85+
var_dump($result);
86+
} catch (Exception $e) {
87+
var_dump($e->getMessage());
88+
}
89+
90+
try {
91+
$callable = fn() => throw new Exception("fn() => throw");
92+
var_dump("not yet");
93+
$callable();
94+
} catch (Exception $e) {
95+
var_dump($e->getMessage());
96+
}
97+
98+
$result = "bar";
99+
try {
100+
$result = throw new Exception();
101+
} catch (Exception $e) {}
102+
var_dump($result);
103+
104+
try {
105+
var_dump(
106+
throw new Exception("exception 1"),
107+
throw new Exception("exception 2")
108+
);
109+
} catch (Exception $e) {
110+
var_dump($e->getMessage());
111+
}
112+
113+
try {
114+
$result = true ? true : throw new Exception("true ? true : throw");
115+
var_dump($result);
116+
} catch (Exception $e) {
117+
var_dump($e->getMessage());
118+
}
119+
120+
try {
121+
$result = false ? true : throw new Exception("false ? true : throw");
122+
var_dump($result);
123+
} catch (Exception $e) {
124+
var_dump($e->getMessage());
125+
}
126+
127+
try {
128+
throw new Exception() + 1;
129+
} catch (Throwable $e) {
130+
var_dump($e->getMessage());
131+
}
132+
133+
try {
134+
throw $exception = new Exception('throw $exception = new Exception();');
135+
} catch (Exception $e) {}
136+
var_dump($exception->getMessage());
137+
138+
try {
139+
$exception = null;
140+
throw $exception ??= new Exception('throw $exception ??= new Exception();');
141+
} catch (Exception $e) {}
142+
var_dump($exception->getMessage());
143+
144+
try {
145+
throw null ?? new Exception('throw null ?? new Exception();');
146+
} catch (Exception $e) {
147+
var_dump($e->getMessage());
148+
}
149+
150+
?>
151+
--EXPECTF--
152+
string(13) "true && throw"
153+
bool(false)
154+
string(14) "true and throw"
155+
bool(false)
156+
bool(true)
157+
string(14) "false || throw"
158+
bool(true)
159+
string(14) "false or throw"
160+
string(13) "null ?? throw"
161+
string(3) "foo"
162+
string(13) "null ?: throw"
163+
string(3) "foo"
164+
string(7) "not yet"
165+
string(13) "fn() => throw"
166+
string(3) "bar"
167+
string(11) "exception 1"
168+
bool(true)
169+
string(20) "false ? true : throw"
170+
171+
Notice: Object of class Exception could not be converted to number in %s on line %d
172+
string(22) "Can only throw objects"
173+
string(35) "throw $exception = new Exception();"
174+
string(37) "throw $exception ??= new Exception();"
175+
string(30) "throw null ?? new Exception();"

Zend/tests/throw/002.phpt

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
--TEST--
2+
Test throw with various expressions
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public function createNotFoundException() {
8+
return new Exception('Not found');
9+
}
10+
11+
public function throwException() {
12+
throw $this->createNotFoundException();
13+
}
14+
15+
public static function staticCreateNotFoundException() {
16+
return new Exception('Static not found');
17+
}
18+
19+
public static function staticThrowException() {
20+
throw static::staticCreateNotFoundException();
21+
}
22+
}
23+
24+
try {
25+
(new Foo())->throwException();
26+
} catch(Exception $e) {
27+
echo $e->getMessage() . "\n";
28+
}
29+
30+
try {
31+
Foo::staticThrowException();
32+
} catch(Exception $e) {
33+
echo $e->getMessage() . "\n";
34+
}
35+
36+
try {
37+
throw true ? new Exception('Ternary true 1') : new Exception('Ternary true 2');
38+
} catch(Exception $e) {
39+
echo $e->getMessage() . "\n";
40+
}
41+
42+
try {
43+
throw false ? new Exception('Ternary false 1') : new Exception('Ternary false 2');
44+
} catch(Exception $e) {
45+
echo $e->getMessage() . "\n";
46+
}
47+
48+
try {
49+
$exception1 = new Exception('Coalesce non-null 1');
50+
$exception2 = new Exception('Coalesce non-null 2');
51+
throw $exception1 ?? $exception2;
52+
} catch(Exception $e) {
53+
echo $e->getMessage() . "\n";
54+
}
55+
56+
try {
57+
$exception1 = null;
58+
$exception2 = new Exception('Coalesce null 2');
59+
throw $exception1 ?? $exception2;
60+
} catch(Exception $e) {
61+
echo $e->getMessage() . "\n";
62+
}
63+
64+
try {
65+
throw $exception = new Exception('Assignment');
66+
} catch(Exception $e) {
67+
echo $e->getMessage() . "\n";
68+
}
69+
70+
try {
71+
$exception = null;
72+
throw $exception ??= new Exception('Coalesce assignment null');
73+
} catch(Exception $e) {
74+
echo $e->getMessage() . "\n";
75+
}
76+
77+
try {
78+
$exception = new Exception('Coalesce assignment non-null 1');
79+
throw $exception ??= new Exception('Coalesce assignment non-null 2');
80+
} catch(Exception $e) {
81+
echo $e->getMessage() . "\n";
82+
}
83+
84+
$andConditionalTest = function ($condition1, $condition2) {
85+
throw $condition1 && $condition2
86+
? new Exception('And in conditional 1')
87+
: new Exception('And in conditional 2');
88+
};
89+
90+
try {
91+
$andConditionalTest(false, false);
92+
} catch(Exception $e) {
93+
echo $e->getMessage() . "\n";
94+
}
95+
96+
try {
97+
$andConditionalTest(false, true);
98+
} catch (Exception $e) {
99+
echo $e->getMessage() . "\n";
100+
}
101+
102+
try {
103+
$andConditionalTest(true, false);
104+
} catch (Exception $e) {
105+
echo $e->getMessage() . "\n";
106+
}
107+
108+
try {
109+
$andConditionalTest(true, true);
110+
} catch (Exception $e) {
111+
echo $e->getMessage() . "\n";
112+
}
113+
114+
--EXPECT--
115+
Not found
116+
Static not found
117+
Ternary true 1
118+
Ternary false 2
119+
Coalesce non-null 1
120+
Coalesce null 2
121+
Assignment
122+
Coalesce assignment null
123+
Coalesce assignment non-null 1
124+
And in conditional 2
125+
And in conditional 2
126+
And in conditional 2
127+
And in conditional 1

Zend/zend_compile.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4557,14 +4557,17 @@ void zend_compile_echo(zend_ast *ast) /* {{{ */
45574557
}
45584558
/* }}} */
45594559

4560-
void zend_compile_throw(zend_ast *ast) /* {{{ */
4560+
void zend_compile_throw(znode *result, zend_ast *ast) /* {{{ */
45614561
{
45624562
zend_ast *expr_ast = ast->child[0];
45634563

45644564
znode expr_node;
45654565
zend_compile_expr(&expr_node, expr_ast);
45664566

45674567
zend_emit_op(NULL, ZEND_THROW, &expr_node, NULL);
4568+
4569+
result->op_type = IS_CONST;
4570+
ZVAL_BOOL(&result->u.constant, 1);
45684571
}
45694572
/* }}} */
45704573

@@ -8741,9 +8744,6 @@ void zend_compile_stmt(zend_ast *ast) /* {{{ */
87418744
case ZEND_AST_ECHO:
87428745
zend_compile_echo(ast);
87438746
break;
8744-
case ZEND_AST_THROW:
8745-
zend_compile_throw(ast);
8746-
break;
87478747
case ZEND_AST_BREAK:
87488748
case ZEND_AST_CONTINUE:
87498749
zend_compile_break_continue(ast);
@@ -8953,6 +8953,9 @@ void zend_compile_expr(znode *result, zend_ast *ast) /* {{{ */
89538953
case ZEND_AST_ARROW_FUNC:
89548954
zend_compile_func_decl(result, ast, 0);
89558955
return;
8956+
case ZEND_AST_THROW:
8957+
zend_compile_throw(result, ast);
8958+
break;
89568959
default:
89578960
ZEND_ASSERT(0 /* not supported */);
89588961
}

Zend/zend_language_parser.y

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
5151
%destructor { zend_ast_destroy($$); } <ast>
5252
%destructor { if ($$) zend_string_release_ex($$, 0); } <str>
5353

54+
%precedence T_THROW
5455
%precedence PREC_ARROW_FUNCTION
5556
%precedence T_INCLUDE T_INCLUDE_ONCE T_REQUIRE T_REQUIRE_ONCE
5657
%left T_LOGICAL_OR
@@ -457,7 +458,6 @@ statement:
457458
| ';' /* empty statement */ { $$ = NULL; }
458459
| T_TRY '{' inner_statement_list '}' catch_list finally_statement
459460
{ $$ = zend_ast_create(ZEND_AST_TRY, $3, $5, $6); }
460-
| T_THROW expr ';' { $$ = zend_ast_create(ZEND_AST_THROW, $2); }
461461
| T_GOTO T_STRING ';' { $$ = zend_ast_create(ZEND_AST_GOTO, $2); }
462462
| T_STRING ':' { $$ = zend_ast_create(ZEND_AST_LABEL, $1); }
463463
;
@@ -1019,6 +1019,7 @@ expr:
10191019
| T_YIELD expr { $$ = zend_ast_create(ZEND_AST_YIELD, $2, NULL); CG(extra_fn_flags) |= ZEND_ACC_GENERATOR; }
10201020
| T_YIELD expr T_DOUBLE_ARROW expr { $$ = zend_ast_create(ZEND_AST_YIELD, $4, $2); CG(extra_fn_flags) |= ZEND_ACC_GENERATOR; }
10211021
| T_YIELD_FROM expr { $$ = zend_ast_create(ZEND_AST_YIELD_FROM, $2); CG(extra_fn_flags) |= ZEND_ACC_GENERATOR; }
1022+
| T_THROW expr { $$ = zend_ast_create(ZEND_AST_THROW, $2); }
10221023
| inline_function { $$ = $1; }
10231024
| T_STATIC inline_function { $$ = $2; ((zend_ast_decl *) $$)->flags |= ZEND_ACC_STATIC; }
10241025
;

0 commit comments

Comments
 (0)