diff --git a/Zend/tests/match/006.phpt b/Zend/tests/match/006.phpt index fec28f82da631..cb731daea7baf 100644 --- a/Zend/tests/match/006.phpt +++ b/Zend/tests/match/006.phpt @@ -7,7 +7,7 @@ $x = match (true) {}; ?> --EXPECTF-- -Fatal error: Uncaught UnhandledMatchError: Unhandled match value of type bool in %s +Fatal error: Uncaught UnhandledMatchError: Unhandled match value: true in %s:%d Stack trace: #0 {main} thrown in %s on line %d diff --git a/Zend/tests/match/007.phpt b/Zend/tests/match/007.phpt index 7b442a66a03e5..ed79fe779609c 100644 --- a/Zend/tests/match/007.phpt +++ b/Zend/tests/match/007.phpt @@ -19,7 +19,7 @@ echo get_value(3) . "\n"; 1 2 -Fatal error: Uncaught UnhandledMatchError: Unhandled match value of type int in %s +Fatal error: Uncaught UnhandledMatchError: Unhandled match value: 3 in %s:%d Stack trace: #0 %s: get_value(3) #1 {main} diff --git a/Zend/tests/match/037.phpt b/Zend/tests/match/037.phpt index d3591fbfcba26..2d561da74a53c 100644 --- a/Zend/tests/match/037.phpt +++ b/Zend/tests/match/037.phpt @@ -53,13 +53,13 @@ var_dump(match(3) { ?> --EXPECTF-- -string(%d) "UnhandledMatchError: Unhandled match value of type bool in %s037.php:4 +string(%d) "UnhandledMatchError: Unhandled match value: true in %s:%d Stack trace: #0 {main}" -string(%d) "UnhandledMatchError: Unhandled match value of type int in %s037.php:12 +string(%d) "UnhandledMatchError: Unhandled match value: 6 in %s:%d Stack trace: #0 {main}" -string(%d) "UnhandledMatchError: Unhandled match value of type string in %s037.php:20 +string(%d) "UnhandledMatchError: Unhandled match value: "3" in %s:%d Stack trace: #0 {main}" string(3) "foo" diff --git a/Zend/tests/match/043.phpt b/Zend/tests/match/043.phpt new file mode 100644 index 0000000000000..0e50bae15bb47 --- /dev/null +++ b/Zend/tests/match/043.phpt @@ -0,0 +1,42 @@ +--TEST-- +Match expression error messages +--FILE-- +getMessage() . PHP_EOL; + } +} + +test(1); +test(5.5); +test("foo"); +test(true); +test(false); +test([1, 2, 3]); +test(new Beep()); +// Testing long strings. +test(str_repeat('e', 100)); +test(str_repeat("e\n", 100)); + +?> +--EXPECT-- +Unhandled match value: 1 +Unhandled match value: 5.5 +Unhandled match value: "foo" +Unhandled match value: true +Unhandled match value: false +Unhandled match value of type array +Unhandled match value of type Beep +Unhandled match value: "eeeeeeeeee..." +Unhandled match value: "e +e +e +e +e +..." diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index e820aef46cf17..00a8c617dc5c3 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -8935,7 +8935,42 @@ ZEND_VM_COLD_CONST_HANDLER(197, ZEND_MATCH_ERROR, CONST|TMPVARCV, UNUSED) SAVE_OPLINE(); op = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); - zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value of type %s", zend_zval_type_name(op)); + + // For simpler types where there is a convenient stringified version, include that + // in the error message. Otherwise just show its type. + switch (Z_TYPE_P(op)) { + case IS_FALSE: + zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value: false"); + break; + case IS_TRUE: + zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value: true"); + break; + case IS_LONG: + case IS_DOUBLE: { + zend_string* stringified = zval_get_string(op); + zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value: %s", ZSTR_VAL(stringified)); + zend_string_release_ex(stringified, false); + break; + } + case IS_STRING: { + zend_string* stringified = zval_get_string(op); + // This number was chosen mostly arbitrarily. But in context, + // a match() on a string should only be using short strings so something + // longer is unlikely to happen to begin with. + const int max_strlen = 10; + if (ZSTR_LEN(stringified) > max_strlen) { + stringified = zend_string_realloc(stringified, max_strlen + 3, 0); + memcpy(&ZSTR_VAL(stringified)[max_strlen], "...", sizeof("...")); + } + zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value: \"%s\"", ZSTR_VAL(stringified)); + zend_string_release_ex(stringified, false); + break; + } + default: + zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value of type %s", zend_zval_type_name(op)); + break; + } + HANDLE_EXCEPTION(); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 92acc5e9b32db..d513a8d720f0e 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -10507,7 +10507,42 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MATCH_ERROR_SPEC_ SAVE_OPLINE(); op = RT_CONSTANT(opline, opline->op1); - zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value of type %s", zend_zval_type_name(op)); + + // For simpler types where there is a convenient stringified version, include that + // in the error message. Otherwise just show its type. + switch (Z_TYPE_P(op)) { + case IS_FALSE: + zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value: false"); + break; + case IS_TRUE: + zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value: true"); + break; + case IS_LONG: + case IS_DOUBLE: { + zend_string* stringified = zval_get_string(op); + zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value: %s", ZSTR_VAL(stringified)); + zend_string_release_ex(stringified, false); + break; + } + case IS_STRING: { + zend_string* stringified = zval_get_string(op); + // This number was chosen mostly arbitrarily. But in context, + // a match() on a string should only be using short strings so something + // longer is unlikely to happen to begin with. + const int max_strlen = 10; + if (ZSTR_LEN(stringified) > max_strlen) { + stringified = zend_string_realloc(stringified, max_strlen + 3, 0); + memcpy(&ZSTR_VAL(stringified)[max_strlen], "...", sizeof("...")); + } + zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value: \"%s\"", ZSTR_VAL(stringified)); + zend_string_release_ex(stringified, false); + break; + } + default: + zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value of type %s", zend_zval_type_name(op)); + break; + } + HANDLE_EXCEPTION(); } @@ -14033,7 +14068,42 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MATCH_ERROR_SPEC_TMPVARCV_UNUS SAVE_OPLINE(); op = EX_VAR(opline->op1.var); - zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value of type %s", zend_zval_type_name(op)); + + // For simpler types where there is a convenient stringified version, include that + // in the error message. Otherwise just show its type. + switch (Z_TYPE_P(op)) { + case IS_FALSE: + zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value: false"); + break; + case IS_TRUE: + zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value: true"); + break; + case IS_LONG: + case IS_DOUBLE: { + zend_string* stringified = zval_get_string(op); + zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value: %s", ZSTR_VAL(stringified)); + zend_string_release_ex(stringified, false); + break; + } + case IS_STRING: { + zend_string* stringified = zval_get_string(op); + // This number was chosen mostly arbitrarily. But in context, + // a match() on a string should only be using short strings so something + // longer is unlikely to happen to begin with. + const int max_strlen = 10; + if (ZSTR_LEN(stringified) > max_strlen) { + stringified = zend_string_realloc(stringified, max_strlen + 3, 0); + memcpy(&ZSTR_VAL(stringified)[max_strlen], "...", sizeof("...")); + } + zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value: \"%s\"", ZSTR_VAL(stringified)); + zend_string_release_ex(stringified, false); + break; + } + default: + zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value of type %s", zend_zval_type_name(op)); + break; + } + HANDLE_EXCEPTION(); }