Skip to content

Commit d44af03

Browse files
committed
[RFC] Catch exceptions without capturing them to variables
The RFC is currently being drafted at https://wiki.php.net/rfc/non-capturing_catches
1 parent 3689807 commit d44af03

File tree

7 files changed

+83
-18
lines changed

7 files changed

+83
-18
lines changed

Zend/tests/try/catch_novar_1.phpt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
catch without capturing a variable
3+
--FILE--
4+
5+
6+
try {
7+
throw new Exception();
8+
} catch (Exception) {
9+
echo "Exception\n";
10+
}
11+
12+
try {
13+
throw new Exception();
14+
} catch (Exception) {
15+
echo "Exception\n";
16+
} catch (Error) {
17+
echo "FAIL\n";
18+
}
19+
20+
try {
21+
throw new Exception();
22+
} catch (Exception|Error) {
23+
echo "Exception\n";
24+
} catch (Throwable) {
25+
echo "FAIL\n";
26+
}
27+
28+
--EXPECT--
29+
Exception
30+
Exception
31+
Exception

Zend/tests/try/catch_novar_2.phpt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
catch without capturing a variable - exception in destructor
3+
--FILE--
4+
5+
class ThrowsOnDestruct extends Exception {
6+
public function __destruct() {
7+
echo "Throwing\n";
8+
throw new RuntimeException(__METHOD__);
9+
}
10+
}
11+
try {
12+
throw new ThrowsOnDestruct();
13+
} catch (Exception $ex) {
14+
echo "Unreachable catch\n";
15+
}
16+
echo "Unreachable fallthrough\n";
17+
18+
--EXPECT--
19+
Throwing

Zend/zend_ast.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1992,8 +1992,10 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
19921992
case ZEND_AST_CATCH:
19931993
smart_str_appends(str, "} catch (");
19941994
zend_ast_export_catch_name_list(str, zend_ast_get_list(ast->child[0]), indent);
1995-
smart_str_appends(str, " $");
1996-
zend_ast_export_var(str, ast->child[1], 0, indent);
1995+
if (ast->child[1]) {
1996+
smart_str_appends(str, " $");
1997+
zend_ast_export_var(str, ast->child[1], 0, indent);
1998+
}
19971999
smart_str_appends(str, ") {\n");
19982000
zend_ast_export_stmt(str, ast->child[2], indent + 1);
19992001
zend_ast_export_indent(str, indent);

Zend/zend_compile.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5202,7 +5202,7 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
52025202
zend_ast_list *classes = zend_ast_get_list(catch_ast->child[0]);
52035203
zend_ast *var_ast = catch_ast->child[1];
52045204
zend_ast *stmt_ast = catch_ast->child[2];
5205-
zend_string *var_name = zval_make_interned_string(zend_ast_get_zval(var_ast));
5205+
zend_string *var_name = var_ast ? zval_make_interned_string(zend_ast_get_zval(var_ast)) : NULL;
52065206
zend_bool is_last_catch = (i + 1 == catches->children);
52075207

52085208
uint32_t *jmp_multicatch = safe_emalloc(sizeof(uint32_t), classes->children - 1, 0);
@@ -5230,12 +5230,12 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
52305230
zend_resolve_class_name_ast(class_ast));
52315231
opline->extended_value = zend_alloc_cache_slot();
52325232

5233-
if (zend_string_equals_literal(var_name, "this")) {
5233+
if (var_name && zend_string_equals_literal(var_name, "this")) {
52345234
zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this");
52355235
}
52365236

5237-
opline->result_type = IS_CV;
5238-
opline->result.var = lookup_cv(var_name);
5237+
opline->result_type = var_name ? IS_CV : IS_UNUSED;
5238+
opline->result.var = var_name ? lookup_cv(var_name) : -1;
52395239

52405240
if (is_last_catch && is_last_class) {
52415241
opline->extended_value |= ZEND_LAST_CATCH;

Zend/zend_language_parser.y

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
248248
%type encaps_var encaps_var_offset isset_variables
249249
%type top_statement_list use_declarations const_list inner_statement_list if_stmt
250250
%type alt_if_stmt for_exprs switch_case_list global_var_list static_var_list
251-
%type echo_expr_list unset_variables catch_name_list catch_list parameter_list class_statement_list
251+
%type echo_expr_list unset_variables catch_name_list catch_list optional_variable parameter_list class_statement_list
252252
%type implements_list case_list if_stmt_without_else
253253
%type non_empty_parameter_list argument_list non_empty_argument_list property_list
254254
%type class_const_list class_const_decl class_name_list trait_adaptations method_body non_empty_for_exprs
@@ -465,7 +465,7 @@ statement:
465465
catch_list:
466466
%empty
467467
{ $$ = zend_ast_create_list(0, ZEND_AST_CATCH_LIST); }
468-
| catch_list T_CATCH '(' catch_name_list T_VARIABLE ')' '{' inner_statement_list '}'
468+
| catch_list T_CATCH '(' catch_name_list optional_variable ')' '{' inner_statement_list '}'
469469
{ $$ = zend_ast_list_add($1, zend_ast_create(ZEND_AST_CATCH, $4, $5, $8)); }
470470
;
471471

@@ -474,6 +474,11 @@ catch_name_list:
474474
| catch_name_list '|' class_name { $$ = zend_ast_list_add($1, $3); }
475475
;
476476

477+
optional_variable:
478+
%empty { $$ = NULL; }
479+
| T_VARIABLE { $$ = $1; }
480+
;
481+
477482
finally_statement:
478483
%empty { $$ = NULL; }
479484
| T_FINALLY '{' inner_statement_list '}' { $$ = $3; }

Zend/zend_vm_def.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4486,12 +4486,16 @@ ZEND_VM_HANDLER(107, ZEND_CATCH, CONST, JMP_ADDR, LAST_CATCH|CACHE_SLOT)
44864486
}
44874487

44884488
exception = EG(exception);
4489-
ex = EX_VAR(opline->result.var);
4490-
if (UNEXPECTED(Z_ISREF_P(ex))) {
4491-
ex = Z_REFVAL_P(ex);
4489+
if (RETURN_VALUE_USED(opline)) {
4490+
ex = EX_VAR(opline->result.var);
4491+
if (UNEXPECTED(Z_ISREF_P(ex))) {
4492+
ex = Z_REFVAL_P(ex);
4493+
}
4494+
zval_ptr_dtor(ex);
4495+
ZVAL_OBJ(ex, EG(exception));
4496+
} else {
4497+
OBJ_RELEASE(EG(exception));
44924498
}
4493-
zval_ptr_dtor(ex);
4494-
ZVAL_OBJ(ex, EG(exception));
44954499
if (UNEXPECTED(EG(exception) != exception)) {
44964500
GC_ADDREF(EG(exception));
44974501
HANDLE_EXCEPTION();

Zend/zend_vm_execute.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3702,12 +3702,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CATCH_SPEC_CONST_HANDLER(ZEND_
37023702
}
37033703

37043704
exception = EG(exception);
3705-
ex = EX_VAR(opline->result.var);
3706-
if (UNEXPECTED(Z_ISREF_P(ex))) {
3707-
ex = Z_REFVAL_P(ex);
3705+
if (RETURN_VALUE_USED(opline)) {
3706+
ex = EX_VAR(opline->result.var);
3707+
if (UNEXPECTED(Z_ISREF_P(ex))) {
3708+
ex = Z_REFVAL_P(ex);
3709+
}
3710+
zval_ptr_dtor(ex);
3711+
ZVAL_OBJ(ex, EG(exception));
3712+
} else {
3713+
OBJ_RELEASE(EG(exception));
37083714
}
3709-
zval_ptr_dtor(ex);
3710-
ZVAL_OBJ(ex, EG(exception));
37113715
if (UNEXPECTED(EG(exception) != exception)) {
37123716
GC_ADDREF(EG(exception));
37133717
HANDLE_EXCEPTION();

0 commit comments

Comments
 (0)