mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
Support catching exceptions without capturing them to variables
RFC: https://wiki.php.net/rfc/non-capturing_catches Closes GH-5345.
This commit is contained in:
parent
1203bbf07e
commit
23ee4d4b57
@ -509,6 +509,9 @@ PHP 8.0 UPGRADE NOTES
|
||||
RFC: https://wiki.php.net/rfc/throw_expression
|
||||
. An optional trailing comma is now allowed in parameter lists.
|
||||
RFC: https://wiki.php.net/rfc/trailing_comma_in_parameter_list
|
||||
. It is now possible to write `catch (Exception)` to catch an exception
|
||||
without storing it in a variable.
|
||||
RFC: https://wiki.php.net/rfc/non-capturing_catches
|
||||
|
||||
- Date:
|
||||
. Added DateTime::createFromInterface() and
|
||||
|
31
Zend/tests/try/catch_novar_1.phpt
Normal file
31
Zend/tests/try/catch_novar_1.phpt
Normal file
@ -0,0 +1,31 @@
|
||||
--TEST--
|
||||
catch without capturing a variable
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
try {
|
||||
throw new Exception();
|
||||
} catch (Exception) {
|
||||
echo "Exception\n";
|
||||
}
|
||||
|
||||
try {
|
||||
throw new Exception();
|
||||
} catch (Exception) {
|
||||
echo "Exception\n";
|
||||
} catch (Error) {
|
||||
echo "FAIL\n";
|
||||
}
|
||||
|
||||
try {
|
||||
throw new Exception();
|
||||
} catch (Exception|Error) {
|
||||
echo "Exception\n";
|
||||
} catch (Throwable) {
|
||||
echo "FAIL\n";
|
||||
}
|
||||
|
||||
--EXPECT--
|
||||
Exception
|
||||
Exception
|
||||
Exception
|
26
Zend/tests/try/catch_novar_2.phpt
Normal file
26
Zend/tests/try/catch_novar_2.phpt
Normal file
@ -0,0 +1,26 @@
|
||||
--TEST--
|
||||
catch without capturing a variable - exception in destructor
|
||||
--FILE--
|
||||
<?php
|
||||
class ThrowsOnDestruct extends Exception {
|
||||
public function __destruct() {
|
||||
echo "Throwing\n";
|
||||
throw new RuntimeException(__METHOD__);
|
||||
}
|
||||
}
|
||||
try {
|
||||
throw new ThrowsOnDestruct();
|
||||
} catch (Exception) {
|
||||
echo "Unreachable catch\n";
|
||||
}
|
||||
echo "Unreachable fallthrough\n";
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Throwing
|
||||
|
||||
Fatal error: Uncaught RuntimeException: ThrowsOnDestruct::__destruct in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): ThrowsOnDestruct->__destruct()
|
||||
#1 {main}
|
||||
thrown in %s on line %d
|
@ -1993,8 +1993,10 @@ simple_list:
|
||||
case ZEND_AST_CATCH:
|
||||
smart_str_appends(str, "} catch (");
|
||||
zend_ast_export_catch_name_list(str, zend_ast_get_list(ast->child[0]), indent);
|
||||
smart_str_appends(str, " $");
|
||||
zend_ast_export_var(str, ast->child[1], 0, indent);
|
||||
if (ast->child[1]) {
|
||||
smart_str_appends(str, " $");
|
||||
zend_ast_export_var(str, ast->child[1], 0, indent);
|
||||
}
|
||||
smart_str_appends(str, ") {\n");
|
||||
zend_ast_export_stmt(str, ast->child[2], indent + 1);
|
||||
zend_ast_export_indent(str, indent);
|
||||
|
@ -5213,7 +5213,7 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
|
||||
zend_ast_list *classes = zend_ast_get_list(catch_ast->child[0]);
|
||||
zend_ast *var_ast = catch_ast->child[1];
|
||||
zend_ast *stmt_ast = catch_ast->child[2];
|
||||
zend_string *var_name = zval_make_interned_string(zend_ast_get_zval(var_ast));
|
||||
zend_string *var_name = var_ast ? zval_make_interned_string(zend_ast_get_zval(var_ast)) : NULL;
|
||||
zend_bool is_last_catch = (i + 1 == catches->children);
|
||||
|
||||
uint32_t *jmp_multicatch = safe_emalloc(sizeof(uint32_t), classes->children - 1, 0);
|
||||
@ -5241,12 +5241,12 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
|
||||
zend_resolve_class_name_ast(class_ast));
|
||||
opline->extended_value = zend_alloc_cache_slot();
|
||||
|
||||
if (zend_string_equals_literal(var_name, "this")) {
|
||||
if (var_name && zend_string_equals_literal(var_name, "this")) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this");
|
||||
}
|
||||
|
||||
opline->result_type = IS_CV;
|
||||
opline->result.var = lookup_cv(var_name);
|
||||
opline->result_type = var_name ? IS_CV : IS_UNUSED;
|
||||
opline->result.var = var_name ? lookup_cv(var_name) : -1;
|
||||
|
||||
if (is_last_catch && is_last_class) {
|
||||
opline->extended_value |= ZEND_LAST_CATCH;
|
||||
|
@ -248,7 +248,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
|
||||
%type <ast> encaps_var encaps_var_offset isset_variables
|
||||
%type <ast> top_statement_list use_declarations const_list inner_statement_list if_stmt
|
||||
%type <ast> alt_if_stmt for_exprs switch_case_list global_var_list static_var_list
|
||||
%type <ast> echo_expr_list unset_variables catch_name_list catch_list parameter_list class_statement_list
|
||||
%type <ast> echo_expr_list unset_variables catch_name_list catch_list optional_variable parameter_list class_statement_list
|
||||
%type <ast> implements_list case_list if_stmt_without_else
|
||||
%type <ast> non_empty_parameter_list argument_list non_empty_argument_list property_list
|
||||
%type <ast> class_const_list class_const_decl class_name_list trait_adaptations method_body non_empty_for_exprs
|
||||
@ -465,7 +465,7 @@ statement:
|
||||
catch_list:
|
||||
%empty
|
||||
{ $$ = zend_ast_create_list(0, ZEND_AST_CATCH_LIST); }
|
||||
| catch_list T_CATCH '(' catch_name_list T_VARIABLE ')' '{' inner_statement_list '}'
|
||||
| catch_list T_CATCH '(' catch_name_list optional_variable ')' '{' inner_statement_list '}'
|
||||
{ $$ = zend_ast_list_add($1, zend_ast_create(ZEND_AST_CATCH, $4, $5, $8)); }
|
||||
;
|
||||
|
||||
@ -474,6 +474,11 @@ catch_name_list:
|
||||
| catch_name_list '|' class_name { $$ = zend_ast_list_add($1, $3); }
|
||||
;
|
||||
|
||||
optional_variable:
|
||||
%empty { $$ = NULL; }
|
||||
| T_VARIABLE { $$ = $1; }
|
||||
;
|
||||
|
||||
finally_statement:
|
||||
%empty { $$ = NULL; }
|
||||
| T_FINALLY '{' inner_statement_list '}' { $$ = $3; }
|
||||
|
@ -4453,7 +4453,6 @@ ZEND_VM_HANDLER(107, ZEND_CATCH, CONST, JMP_ADDR, LAST_CATCH|CACHE_SLOT)
|
||||
USE_OPLINE
|
||||
zend_class_entry *ce, *catch_ce;
|
||||
zend_object *exception;
|
||||
zval *ex;
|
||||
|
||||
SAVE_OPLINE();
|
||||
/* 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)
|
||||
}
|
||||
|
||||
exception = EG(exception);
|
||||
ex = EX_VAR(opline->result.var);
|
||||
{
|
||||
EG(exception) = NULL;
|
||||
if (RETURN_VALUE_USED(opline)) {
|
||||
/* Always perform a strict assignment. There is a reasonable expectation that if you
|
||||
* write "catch (Exception $e)" then $e will actually be instanceof Exception. As such,
|
||||
* we should not permit coercion to string here. */
|
||||
zval tmp;
|
||||
ZVAL_OBJ(&tmp, exception);
|
||||
EG(exception) = NULL;
|
||||
zend_assign_to_variable(ex, &tmp, IS_TMP_VAR, /* strict */ 1);
|
||||
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
|
||||
zend_assign_to_variable(EX_VAR(opline->result.var), &tmp, IS_TMP_VAR, /* strict */ 1);
|
||||
} else {
|
||||
OBJ_RELEASE(exception);
|
||||
}
|
||||
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
|
||||
}
|
||||
|
||||
ZEND_VM_HOT_HANDLER(65, ZEND_SEND_VAL, CONST|TMPVAR, NUM)
|
||||
|
@ -3669,7 +3669,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CATCH_SPEC_CONST_HANDLER(ZEND_
|
||||
USE_OPLINE
|
||||
zend_class_entry *ce, *catch_ce;
|
||||
zend_object *exception;
|
||||
zval *ex;
|
||||
|
||||
SAVE_OPLINE();
|
||||
/* 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_
|
||||
}
|
||||
|
||||
exception = EG(exception);
|
||||
ex = EX_VAR(opline->result.var);
|
||||
{
|
||||
EG(exception) = NULL;
|
||||
if (RETURN_VALUE_USED(opline)) {
|
||||
/* Always perform a strict assignment. There is a reasonable expectation that if you
|
||||
* write "catch (Exception $e)" then $e will actually be instanceof Exception. As such,
|
||||
* we should not permit coercion to string here. */
|
||||
zval tmp;
|
||||
ZVAL_OBJ(&tmp, exception);
|
||||
EG(exception) = NULL;
|
||||
zend_assign_to_variable(ex, &tmp, IS_TMP_VAR, /* strict */ 1);
|
||||
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
|
||||
zend_assign_to_variable(EX_VAR(opline->result.var), &tmp, IS_TMP_VAR, /* strict */ 1);
|
||||
} else {
|
||||
OBJ_RELEASE(exception);
|
||||
}
|
||||
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
|
||||
}
|
||||
|
||||
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
|
||||
|
Loading…
Reference in New Issue
Block a user