Skip to content

Commit 23ee4d4

Browse files
MaxSemnikic
authored andcommitted
1 parent 1203bbf commit 23ee4d4

File tree

8 files changed

+87
-20
lines changed

8 files changed

+87
-20
lines changed

UPGRADING

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,9 @@ PHP 8.0 UPGRADE NOTES
509509
RFC: https://wiki.php.net/rfc/throw_expression
510510
. An optional trailing comma is now allowed in parameter lists.
511511
RFC: https://wiki.php.net/rfc/trailing_comma_in_parameter_list
512+
. It is now possible to write `catch (Exception)` to catch an exception
513+
without storing it in a variable.
514+
RFC: https://wiki.php.net/rfc/non-capturing_catches
512515

513516
- Date:
514517
. Added DateTime::createFromInterface() and

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+
<?php
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: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
catch without capturing a variable - exception in destructor
3+
--FILE--
4+
<?php
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) {
14+
echo "Unreachable catch\n";
15+
}
16+
echo "Unreachable fallthrough\n";
17+
18+
?>
19+
--EXPECTF--
20+
Throwing
21+
22+
Fatal error: Uncaught RuntimeException: ThrowsOnDestruct::__destruct in %s:%d
23+
Stack trace:
24+
#0 %s(%d): ThrowsOnDestruct->__destruct()
25+
#1 {main}
26+
thrown in %s on line %d

Zend/zend_ast.c

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

Zend/zend_compile.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5213,7 +5213,7 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
52135213
zend_ast_list *classes = zend_ast_get_list(catch_ast->child[0]);
52145214
zend_ast *var_ast = catch_ast->child[1];
52155215
zend_ast *stmt_ast = catch_ast->child[2];
5216-
zend_string *var_name = zval_make_interned_string(zend_ast_get_zval(var_ast));
5216+
zend_string *var_name = var_ast ? zval_make_interned_string(zend_ast_get_zval(var_ast)) : NULL;
52175217
zend_bool is_last_catch = (i + 1 == catches->children);
52185218

52195219
uint32_t *jmp_multicatch = safe_emalloc(sizeof(uint32_t), classes->children - 1, 0);
@@ -5241,12 +5241,12 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
52415241
zend_resolve_class_name_ast(class_ast));
52425242
opline->extended_value = zend_alloc_cache_slot();
52435243

5244-
if (zend_string_equals_literal(var_name, "this")) {
5244+
if (var_name && zend_string_equals_literal(var_name, "this")) {
52455245
zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this");
52465246
}
52475247

5248-
opline->result_type = IS_CV;
5249-
opline->result.var = lookup_cv(var_name);
5248+
opline->result_type = var_name ? IS_CV : IS_UNUSED;
5249+
opline->result.var = var_name ? lookup_cv(var_name) : -1;
52505250

52515251
if (is_last_catch && is_last_class) {
52525252
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 <ast> encaps_var encaps_var_offset isset_variables
249249
%type <ast> top_statement_list use_declarations const_list inner_statement_list if_stmt
250250
%type <ast> alt_if_stmt for_exprs switch_case_list global_var_list static_var_list
251-
%type <ast> echo_expr_list unset_variables catch_name_list catch_list parameter_list class_statement_list
251+
%type <ast> echo_expr_list unset_variables catch_name_list catch_list optional_variable parameter_list class_statement_list
252252
%type <ast> implements_list case_list if_stmt_without_else
253253
%type <ast> non_empty_parameter_list argument_list non_empty_argument_list property_list
254254
%type <ast> 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: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4453,7 +4453,6 @@ ZEND_VM_HANDLER(107, ZEND_CATCH, CONST, JMP_ADDR, LAST_CATCH|CACHE_SLOT)
44534453
USE_OPLINE
44544454
zend_class_entry *ce, *catch_ce;
44554455
zend_object *exception;
4456-
zval *ex;
44574456

44584457
SAVE_OPLINE();
44594458
/* Check whether an exception has been thrown, if not, jump over code */
@@ -4486,17 +4485,18 @@ ZEND_VM_HANDLER(107, ZEND_CATCH, CONST, JMP_ADDR, LAST_CATCH|CACHE_SLOT)
44864485
}
44874486

44884487
exception = EG(exception);
4489-
ex = EX_VAR(opline->result.var);
4490-
{
4488+
EG(exception) = NULL;
4489+
if (RETURN_VALUE_USED(opline)) {
44914490
/* Always perform a strict assignment. There is a reasonable expectation that if you
44924491
* write "catch (Exception $e)" then $e will actually be instanceof Exception. As such,
44934492
* we should not permit coercion to string here. */
44944493
zval tmp;
44954494
ZVAL_OBJ(&tmp, exception);
4496-
EG(exception) = NULL;
4497-
zend_assign_to_variable(ex, &tmp, IS_TMP_VAR, /* strict */ 1);
4498-
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
4495+
zend_assign_to_variable(EX_VAR(opline->result.var), &tmp, IS_TMP_VAR, /* strict */ 1);
4496+
} else {
4497+
OBJ_RELEASE(exception);
44994498
}
4499+
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
45004500
}
45014501

45024502
ZEND_VM_HOT_HANDLER(65, ZEND_SEND_VAL, CONST|TMPVAR, NUM)

Zend/zend_vm_execute.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3669,7 +3669,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CATCH_SPEC_CONST_HANDLER(ZEND_
36693669
USE_OPLINE
36703670
zend_class_entry *ce, *catch_ce;
36713671
zend_object *exception;
3672-
zval *ex;
36733672

36743673
SAVE_OPLINE();
36753674
/* Check whether an exception has been thrown, if not, jump over code */
@@ -3702,17 +3701,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CATCH_SPEC_CONST_HANDLER(ZEND_
37023701
}
37033702

37043703
exception = EG(exception);
3705-
ex = EX_VAR(opline->result.var);
3706-
{
3704+
EG(exception) = NULL;
3705+
if (RETURN_VALUE_USED(opline)) {
37073706
/* Always perform a strict assignment. There is a reasonable expectation that if you
37083707
* write "catch (Exception $e)" then $e will actually be instanceof Exception. As such,
37093708
* we should not permit coercion to string here. */
37103709
zval tmp;
37113710
ZVAL_OBJ(&tmp, exception);
3712-
EG(exception) = NULL;
3713-
zend_assign_to_variable(ex, &tmp, IS_TMP_VAR, /* strict */ 1);
3714-
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
3711+
zend_assign_to_variable(EX_VAR(opline->result.var), &tmp, IS_TMP_VAR, /* strict */ 1);
3712+
} else {
3713+
OBJ_RELEASE(exception);
37153714
}
3715+
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
37163716
}
37173717

37183718
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)

0 commit comments

Comments
 (0)