diff --git a/Zend/tests/throw/001.phpt b/Zend/tests/throw/001.phpt new file mode 100644 index 0000000000000..072d9f45b54e1 --- /dev/null +++ b/Zend/tests/throw/001.phpt @@ -0,0 +1,175 @@ +--TEST-- +throw expression +--FILE-- +getMessage()); +} + +try { + $result = false && throw new Exception("false && throw"); + var_dump($result); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +try { + $result = true and throw new Exception("true and throw"); + var_dump($result); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +try { + $result = false and throw new Exception("false and throw"); + var_dump($result); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +try { + $result = true || throw new Exception("true || throw"); + var_dump($result); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +try { + $result = false || throw new Exception("false || throw"); + var_dump($result); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +try { + $result = true or throw new Exception("true or throw"); + var_dump($result); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +try { + $result = false or throw new Exception("false or throw"); + var_dump($result); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +try { + $result = null ?? throw new Exception("null ?? throw"); + var_dump($result); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +try { + $result = "foo" ?? throw new Exception('"foo" ?? throw'); + var_dump($result); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +try { + $result = null ?: throw new Exception("null ?: throw"); + var_dump($result); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +try { + $result = "foo" ?: throw new Exception('"foo" ?: throw'); + var_dump($result); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +try { + $callable = fn() => throw new Exception("fn() => throw"); + var_dump("not yet"); + $callable(); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +$result = "bar"; +try { + $result = throw new Exception(); +} catch (Exception $e) {} +var_dump($result); + +try { + var_dump( + throw new Exception("exception 1"), + throw new Exception("exception 2") + ); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +try { + $result = true ? true : throw new Exception("true ? true : throw"); + var_dump($result); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +try { + $result = false ? true : throw new Exception("false ? true : throw"); + var_dump($result); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +try { + throw new Exception() + 1; +} catch (Throwable $e) { + var_dump($e->getMessage()); +} + +try { + throw $exception = new Exception('throw $exception = new Exception();'); +} catch (Exception $e) {} +var_dump($exception->getMessage()); + +try { + $exception = null; + throw $exception ??= new Exception('throw $exception ??= new Exception();'); +} catch (Exception $e) {} +var_dump($exception->getMessage()); + +try { + throw null ?? new Exception('throw null ?? new Exception();'); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +?> +--EXPECTF-- +string(13) "true && throw" +bool(false) +string(14) "true and throw" +bool(false) +bool(true) +string(14) "false || throw" +bool(true) +string(14) "false or throw" +string(13) "null ?? throw" +string(3) "foo" +string(13) "null ?: throw" +string(3) "foo" +string(7) "not yet" +string(13) "fn() => throw" +string(3) "bar" +string(11) "exception 1" +bool(true) +string(20) "false ? true : throw" + +Notice: Object of class Exception could not be converted to number in %s on line %d +string(22) "Can only throw objects" +string(35) "throw $exception = new Exception();" +string(37) "throw $exception ??= new Exception();" +string(30) "throw null ?? new Exception();" diff --git a/Zend/tests/throw/002.phpt b/Zend/tests/throw/002.phpt new file mode 100644 index 0000000000000..8736c27fc3142 --- /dev/null +++ b/Zend/tests/throw/002.phpt @@ -0,0 +1,127 @@ +--TEST-- +Test throw with various expressions +--FILE-- +createNotFoundException(); + } + + public static function staticCreateNotFoundException() { + return new Exception('Static not found'); + } + + public static function staticThrowException() { + throw static::staticCreateNotFoundException(); + } +} + +try { + (new Foo())->throwException(); +} catch(Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + Foo::staticThrowException(); +} catch(Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + throw true ? new Exception('Ternary true 1') : new Exception('Ternary true 2'); +} catch(Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + throw false ? new Exception('Ternary false 1') : new Exception('Ternary false 2'); +} catch(Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + $exception1 = new Exception('Coalesce non-null 1'); + $exception2 = new Exception('Coalesce non-null 2'); + throw $exception1 ?? $exception2; +} catch(Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + $exception1 = null; + $exception2 = new Exception('Coalesce null 2'); + throw $exception1 ?? $exception2; +} catch(Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + throw $exception = new Exception('Assignment'); +} catch(Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + $exception = null; + throw $exception ??= new Exception('Coalesce assignment null'); +} catch(Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + $exception = new Exception('Coalesce assignment non-null 1'); + throw $exception ??= new Exception('Coalesce assignment non-null 2'); +} catch(Exception $e) { + echo $e->getMessage() . "\n"; +} + +$andConditionalTest = function ($condition1, $condition2) { + throw $condition1 && $condition2 + ? new Exception('And in conditional 1') + : new Exception('And in conditional 2'); +}; + +try { + $andConditionalTest(false, false); +} catch(Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + $andConditionalTest(false, true); +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + $andConditionalTest(true, false); +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + $andConditionalTest(true, true); +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} + +--EXPECT-- +Not found +Static not found +Ternary true 1 +Ternary false 2 +Coalesce non-null 1 +Coalesce null 2 +Assignment +Coalesce assignment null +Coalesce assignment non-null 1 +And in conditional 2 +And in conditional 2 +And in conditional 2 +And in conditional 1 diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 2e30295980c4f..aa108c4d7f91c 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -4557,7 +4557,7 @@ void zend_compile_echo(zend_ast *ast) /* {{{ */ } /* }}} */ -void zend_compile_throw(zend_ast *ast) /* {{{ */ +void zend_compile_throw(znode *result, zend_ast *ast) /* {{{ */ { zend_ast *expr_ast = ast->child[0]; @@ -4565,6 +4565,9 @@ void zend_compile_throw(zend_ast *ast) /* {{{ */ zend_compile_expr(&expr_node, expr_ast); zend_emit_op(NULL, ZEND_THROW, &expr_node, NULL); + + result->op_type = IS_CONST; + ZVAL_BOOL(&result->u.constant, 1); } /* }}} */ @@ -8741,9 +8744,6 @@ void zend_compile_stmt(zend_ast *ast) /* {{{ */ case ZEND_AST_ECHO: zend_compile_echo(ast); break; - case ZEND_AST_THROW: - zend_compile_throw(ast); - break; case ZEND_AST_BREAK: case ZEND_AST_CONTINUE: zend_compile_break_continue(ast); @@ -8953,6 +8953,9 @@ void zend_compile_expr(znode *result, zend_ast *ast) /* {{{ */ case ZEND_AST_ARROW_FUNC: zend_compile_func_decl(result, ast, 0); return; + case ZEND_AST_THROW: + zend_compile_throw(result, ast); + break; default: ZEND_ASSERT(0 /* not supported */); } diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index c1ced9a35aa21..c75c1e6993865 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -51,6 +51,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %destructor { zend_ast_destroy($$); } %destructor { if ($$) zend_string_release_ex($$, 0); } +%precedence T_THROW %precedence PREC_ARROW_FUNCTION %precedence T_INCLUDE T_INCLUDE_ONCE T_REQUIRE T_REQUIRE_ONCE %left T_LOGICAL_OR @@ -457,7 +458,6 @@ statement: | ';' /* empty statement */ { $$ = NULL; } | T_TRY '{' inner_statement_list '}' catch_list finally_statement { $$ = zend_ast_create(ZEND_AST_TRY, $3, $5, $6); } - | T_THROW expr ';' { $$ = zend_ast_create(ZEND_AST_THROW, $2); } | T_GOTO T_STRING ';' { $$ = zend_ast_create(ZEND_AST_GOTO, $2); } | T_STRING ':' { $$ = zend_ast_create(ZEND_AST_LABEL, $1); } ; @@ -1019,6 +1019,7 @@ expr: | T_YIELD expr { $$ = zend_ast_create(ZEND_AST_YIELD, $2, NULL); CG(extra_fn_flags) |= ZEND_ACC_GENERATOR; } | T_YIELD expr T_DOUBLE_ARROW expr { $$ = zend_ast_create(ZEND_AST_YIELD, $4, $2); CG(extra_fn_flags) |= ZEND_ACC_GENERATOR; } | T_YIELD_FROM expr { $$ = zend_ast_create(ZEND_AST_YIELD_FROM, $2); CG(extra_fn_flags) |= ZEND_ACC_GENERATOR; } + | T_THROW expr { $$ = zend_ast_create(ZEND_AST_THROW, $2); } | inline_function { $$ = $1; } | T_STATIC inline_function { $$ = $2; ((zend_ast_decl *) $$)->flags |= ZEND_ACC_STATIC; } ; diff --git a/ext/tokenizer/tests/PhpToken_getAll.phpt b/ext/tokenizer/tests/PhpToken_getAll.phpt index 604a979023ed7..baab0a5658e24 100644 --- a/ext/tokenizer/tests/PhpToken_getAll.phpt +++ b/ext/tokenizer/tests/PhpToken_getAll.phpt @@ -32,7 +32,7 @@ array(15) { [1]=> object(PhpToken)#2 (4) { ["id"]=> - int(342) + int(343) ["text"]=> string(8) "function" ["line"]=> @@ -54,7 +54,7 @@ array(15) { [3]=> object(PhpToken)#4 (4) { ["id"]=> - int(310) + int(311) ["text"]=> string(3) "foo" ["line"]=> @@ -121,7 +121,7 @@ array(15) { [9]=> object(PhpToken)#10 (4) { ["id"]=> - int(324) + int(325) ["text"]=> string(4) "echo" ["line"]=> @@ -143,7 +143,7 @@ array(15) { [11]=> object(PhpToken)#12 (4) { ["id"]=> - int(314) + int(315) ["text"]=> string(5) ""bar"" ["line"]=> @@ -202,7 +202,7 @@ array(15) { [1]=> object(PhpToken)#14 (4) { ["id"]=> - int(342) + int(343) ["text"]=> string(8) "function" ["line"]=> @@ -224,7 +224,7 @@ array(15) { [3]=> object(PhpToken)#12 (4) { ["id"]=> - int(310) + int(311) ["text"]=> string(3) "foo" ["line"]=> @@ -291,7 +291,7 @@ array(15) { [9]=> object(PhpToken)#6 (4) { ["id"]=> - int(324) + int(325) ["text"]=> string(4) "echo" ["line"]=> @@ -313,7 +313,7 @@ array(15) { [11]=> object(PhpToken)#4 (4) { ["id"]=> - int(314) + int(315) ["text"]=> string(5) ""bar"" ["line"]=> diff --git a/ext/tokenizer/tests/token_get_all_variation4.phpt b/ext/tokenizer/tests/token_get_all_variation4.phpt index 66e3cb413d2c4..be18ddf0b35c1 100644 --- a/ext/tokenizer/tests/token_get_all_variation4.phpt +++ b/ext/tokenizer/tests/token_get_all_variation4.phpt @@ -80,7 +80,7 @@ array(88) { [5]=> array(3) { [0]=> - int(286) + int(%d) [1]=> string(2) "==" [2]=> diff --git a/ext/tokenizer/tokenizer_data.c b/ext/tokenizer/tokenizer_data.c index 3ddf89521a8cd..9c7df932076ce 100644 --- a/ext/tokenizer/tokenizer_data.c +++ b/ext/tokenizer/tokenizer_data.c @@ -25,6 +25,7 @@ void tokenizer_register_constants(INIT_FUNC_ARGS) { + REGISTER_LONG_CONSTANT("T_THROW", T_THROW, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_INCLUDE", T_INCLUDE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_INCLUDE_ONCE", T_INCLUDE_ONCE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_REQUIRE", T_REQUIRE, CONST_CS | CONST_PERSISTENT); @@ -114,7 +115,6 @@ void tokenizer_register_constants(INIT_FUNC_ARGS) { REGISTER_LONG_CONSTANT("T_TRY", T_TRY, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_CATCH", T_CATCH, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_FINALLY", T_FINALLY, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("T_THROW", T_THROW, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_USE", T_USE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_INSTEADOF", T_INSTEADOF, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_GLOBAL", T_GLOBAL, CONST_CS | CONST_PERSISTENT); @@ -168,6 +168,7 @@ char *get_token_type_name(int token_type) { switch (token_type) { + case T_THROW: return "T_THROW"; case T_INCLUDE: return "T_INCLUDE"; case T_INCLUDE_ONCE: return "T_INCLUDE_ONCE"; case T_REQUIRE: return "T_REQUIRE"; @@ -257,7 +258,6 @@ char *get_token_type_name(int token_type) case T_TRY: return "T_TRY"; case T_CATCH: return "T_CATCH"; case T_FINALLY: return "T_FINALLY"; - case T_THROW: return "T_THROW"; case T_USE: return "T_USE"; case T_INSTEADOF: return "T_INSTEADOF"; case T_GLOBAL: return "T_GLOBAL";