diff --git a/Zend/tests/assert/expect_001.phpt b/Zend/tests/assert/expect_001.phpt new file mode 100644 index 00000000000..6c78a0bd755 --- /dev/null +++ b/Zend/tests/assert/expect_001.phpt @@ -0,0 +1,12 @@ +--TEST-- +test passing assertion +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- + +--EXPECTF-- +bool(true) diff --git a/Zend/tests/assert/expect_002.phpt b/Zend/tests/assert/expect_002.phpt new file mode 100644 index 00000000000..bdb5e1969ae --- /dev/null +++ b/Zend/tests/assert/expect_002.phpt @@ -0,0 +1,16 @@ +--TEST-- +test failing assertion +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught exception 'AssertionException' with message 'assert(false)' in %sexpect_002.php:%d +Stack trace: +#0 %sexpect_002.php(%d): assert(false, 'assert(false)') +#1 {main} + thrown in %sexpect_002.php on line %d diff --git a/Zend/tests/assert/expect_003.phpt b/Zend/tests/assert/expect_003.phpt new file mode 100644 index 00000000000..e2c84edb117 --- /dev/null +++ b/Zend/tests/assert/expect_003.phpt @@ -0,0 +1,15 @@ +--TEST-- +test catching failed assertion +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- +getMessage()); +} +?> +--EXPECT-- +string(13) "assert(false)" diff --git a/Zend/tests/assert/expect_004.phpt b/Zend/tests/assert/expect_004.phpt new file mode 100644 index 00000000000..1f16adb3c68 --- /dev/null +++ b/Zend/tests/assert/expect_004.phpt @@ -0,0 +1,15 @@ +--TEST-- +test providing reason (fail) +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- +getMessage()); +} +?> +--EXPECT-- +string(25) "I require this to succeed" diff --git a/Zend/tests/assert/expect_005.phpt b/Zend/tests/assert/expect_005.phpt new file mode 100644 index 00000000000..fab983b0e6d --- /dev/null +++ b/Zend/tests/assert/expect_005.phpt @@ -0,0 +1,17 @@ +--TEST-- +test providing reason (pass) +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- +getMessage()); +} +var_dump(true); +?> +--EXPECT-- +bool(true) diff --git a/Zend/tests/assert/expect_006.phpt b/Zend/tests/assert/expect_006.phpt new file mode 100644 index 00000000000..8c88aeb923e --- /dev/null +++ b/Zend/tests/assert/expect_006.phpt @@ -0,0 +1,14 @@ +--TEST-- +test looping assert (pass) +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- + +--EXPECT-- +bool(true) diff --git a/Zend/tests/assert/expect_007.phpt b/Zend/tests/assert/expect_007.phpt new file mode 100644 index 00000000000..c9c7b5fc98c --- /dev/null +++ b/Zend/tests/assert/expect_007.phpt @@ -0,0 +1,22 @@ +--TEST-- +test compiled reason +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- + "X-HTTP ", + "value" => "testing" +); + +class HeaderMalfunctionException extends AssertionException {} + +assert (preg_match("~^([a-zA-Z0-9-]+)$~", $data["key"]), new HeaderMalfunctionException("malformed key found at {$next} \"{$data["key"]}\"")); +?> +--EXPECTF-- +Fatal error: Uncaught exception 'HeaderMalfunctionException' with message 'malformed key found at 1 "X-HTTP "' in %sexpect_007.php:10 +Stack trace: +#0 {main} + thrown in %sexpect_007.php on line 10 diff --git a/Zend/tests/assert/expect_008.phpt b/Zend/tests/assert/expect_008.phpt new file mode 100644 index 00000000000..67779bec7f3 --- /dev/null +++ b/Zend/tests/assert/expect_008.phpt @@ -0,0 +1,34 @@ +--TEST-- +test disabled expectations have no ill side effects +--INI-- +zend.assertions=0 +assert.exception=1 +--FILE-- + +--EXPECT-- +bool(true) diff --git a/Zend/tests/assert/expect_009.phpt b/Zend/tests/assert/expect_009.phpt new file mode 100644 index 00000000000..3d9f679e6f9 --- /dev/null +++ b/Zend/tests/assert/expect_009.phpt @@ -0,0 +1,26 @@ +--TEST-- +test stack trace is correct from failed exception in extended class +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught exception 'AssertionException' with message 'assert(false)' in %sexpect_009.php:%d +Stack trace: +#0 %sexpect_009.php(%d): assert(false, 'assert(false)') +#1 %sexpect_009.php(%d): Two->__construct() +#2 {main} + thrown in %sexpect_009.php on line %d diff --git a/Zend/tests/assert/expect_010.phpt b/Zend/tests/assert/expect_010.phpt new file mode 100644 index 00000000000..f942f6d6f01 --- /dev/null +++ b/Zend/tests/assert/expect_010.phpt @@ -0,0 +1,23 @@ +--TEST-- +test stack trace is correct from failed exception in extended class (parent implementing constructor) +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught exception 'AssertionException' with message 'assert(false)' in %sexpect_010.php:%d +Stack trace: +#0 %sexpect_010.php(%d): assert(false, 'assert(false)') +#1 %sexpect_010.php(%d): One->__construct() +#2 {main} + thrown in %sexpect_010.php on line %d diff --git a/Zend/tests/assert/expect_011.phpt b/Zend/tests/assert/expect_011.phpt new file mode 100644 index 00000000000..23c21aaa686 --- /dev/null +++ b/Zend/tests/assert/expect_011.phpt @@ -0,0 +1,30 @@ +--TEST-- +test overloaded __toString on custom exception +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught exception 'AssertionException' with message '[Message]: MyExpectations' in %sexpect_011.php:%d +Stack trace: +#0 %sexpect_011.php(%d): assert(false, '[Message]: MyEx...') +#1 %sexpect_011.php(%d): One->__construct() +#2 {main} + thrown in %sexpect_011.php on line %d diff --git a/Zend/tests/assert/expect_012.phpt b/Zend/tests/assert/expect_012.phpt new file mode 100644 index 00000000000..c1ae817f62d --- /dev/null +++ b/Zend/tests/assert/expect_012.phpt @@ -0,0 +1,21 @@ +--TEST-- +test enable/disable assertions at runtime +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- + +--EXPECT-- +int(1) +int(0) +int(1) +bool(true) diff --git a/Zend/tests/assert/expect_013.phpt b/Zend/tests/assert/expect_013.phpt new file mode 100644 index 00000000000..cf8fa1cd02b --- /dev/null +++ b/Zend/tests/assert/expect_013.phpt @@ -0,0 +1,11 @@ +--TEST-- +test failing assertion when disabled (with return value) +--INI-- +zend.assertions=0 +assert.exception=1 +--FILE-- + +--EXPECT-- +bool(true) \ No newline at end of file diff --git a/Zend/tests/assert/expect_014.phpt b/Zend/tests/assert/expect_014.phpt new file mode 100644 index 00000000000..7cf4fb19311 --- /dev/null +++ b/Zend/tests/assert/expect_014.phpt @@ -0,0 +1,12 @@ +--TEST-- +test failing assertion when disabled +--INI-- +zend.assertions=0 +assert.exception=1 +--FILE-- + +--EXPECT-- +bool(true) \ No newline at end of file diff --git a/Zend/zend.c b/Zend/zend.c index acbbcbebc7c..e5f43243363 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -101,6 +101,7 @@ static ZEND_INI_MH(OnUpdateScriptEncoding) /* {{{ */ ZEND_INI_BEGIN() ZEND_INI_ENTRY("error_reporting", NULL, ZEND_INI_ALL, OnUpdateErrorReporting) + STD_ZEND_INI_BOOLEAN("zend.assertions", "1", ZEND_INI_SYSTEM, OnUpdateBool, assertions, zend_executor_globals, executor_globals) STD_ZEND_INI_BOOLEAN("zend.enable_gc", "1", ZEND_INI_ALL, OnUpdateGCEnabled, gc_enabled, zend_gc_globals, gc_globals) STD_ZEND_INI_BOOLEAN("zend.multibyte", "0", ZEND_INI_PERDIR, OnUpdateBool, multibyte, zend_compiler_globals, compiler_globals) ZEND_INI_ENTRY("zend.script_encoding", NULL, ZEND_INI_ALL, OnUpdateScriptEncoding) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index f789e3397f2..01579f637d3 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1969,6 +1969,22 @@ int zend_do_begin_function_call(znode *function_name, zend_bool check_namespace } lcname = zend_str_tolower_dup(Z_STRVAL(function_name->u.constant), Z_STRLEN(function_name->u.constant)); + + if (Z_STRLEN(function_name->u.constant) == sizeof("assert")-1 && + memcmp(lcname, "assert", sizeof("assert")-1) == 0) { + + int op_number = get_next_op_number(CG(active_op_array)); + zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); + + opline->opcode = ZEND_ASSERT_CHECK; + opline->extended_value = 0; + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); + opline->op2.ptr = (void*)LANG_SCNG(yy_text); + + function_name->EA = op_number; + } + if ((zend_hash_find(CG(function_table), lcname, Z_STRLEN(function_name->u.constant)+1, (void **) &function)==FAILURE) || ((CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS) && (function->type == ZEND_INTERNAL_FUNCTION))) { @@ -2538,19 +2554,85 @@ int zend_do_begin_class_member_function_call(znode *class_name, znode *method_na } /* }}} */ +static inline void zend_copy_assertion_text(zval *target, const char *start_statement, const char *end_statement TSRMLS_DC) /* {{{ */ +{ + char *str; + const char *statement = start_statement; + size_t statement_length = end_statement - start_statement; + + while (statement && isspace(*statement)) { + statement_length--; + statement++; + } + + while (end_statement && isspace(*end_statement)) { + statement_length--; + end_statement--; + } + + str = emalloc(sizeof("assert") + statement_length + 1); + memcpy(str, "assert", sizeof("assert") - 1); + memcpy(str + sizeof("assert") - 1, statement, statement_length + 1); + str[sizeof("assert") + statement_length] = 0; + ZVAL_STRINGL(target, str, sizeof("assert") + statement_length, 0); +} +/* }}} */ + void zend_do_end_function_call(znode *function_name, znode *result, const znode *argument_list, int is_method, int is_dynamic_fcall TSRMLS_DC) /* {{{ */ { zend_op *opline; + long arg_num = Z_LVAL(argument_list->u.constant); if (is_method && function_name && function_name->op_type == IS_UNUSED) { /* clone */ - if (Z_LVAL(argument_list->u.constant) != 0) { + if (arg_num != 0) { zend_error(E_WARNING, "Clone method does not require arguments"); } opline = &CG(active_op_array)->opcodes[Z_LVAL(function_name->u.constant)]; } else { zend_function **function_ptr_ptr; zend_stack_top(&CG(function_call_stack), (void **) &function_ptr_ptr); + + if (!is_method && + function_name->op_type==IS_CONST && + Z_STRLEN(function_name->u.constant) == sizeof("assert")-1 && + memcmp(Z_STRVAL(function_name->u.constant), "assert", sizeof("assert")-1) == 0) { + + int assert_op_number = function_name->EA; + + if (arg_num == 1) { + int last_op_number = get_next_op_number(CG(active_op_array)); + zend_op *last_op = &CG(active_op_array)->opcodes[last_op_number-1]; + + if (last_op->opcode != ZEND_SEND_VAL || + last_op->op1_type != IS_CONST || + Z_TYPE(CONSTANT(last_op->op1.constant)) != IS_STRING) { + + zval message; + + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + opline->opcode = ZEND_SEND_VAL; + + zend_copy_assertion_text( + &message, + (const char*)CG(active_op_array)->opcodes[assert_op_number].op2.ptr, + (const char*)LANG_SCNG(yy_text) TSRMLS_CC); + opline->op1_type = IS_CONST; + opline->op1.constant = zend_add_literal(CG(active_op_array), &message TSRMLS_CC); + CALCULATE_LITERAL_HASH(opline->op1.constant); + opline->op2.opline_num = 2; + opline->extended_value = !is_dynamic_fcall ? ZEND_DO_FCALL : ZEND_DO_FCALL_BY_NAME; + SET_UNUSED(opline->op2); + arg_num = 2; + CG(context).used_stack++; + } + } + + CG(active_op_array)->opcodes[assert_op_number].op1.opline_num = + get_next_op_number(CG(active_op_array)) + 1; + CG(active_op_array)->opcodes[assert_op_number].op2.ptr = + NULL; + } opline = get_next_op(CG(active_op_array) TSRMLS_CC); if (*function_ptr_ptr) { @@ -2580,12 +2662,12 @@ void zend_do_end_function_call(znode *function_name, znode *result, const znode GET_NODE(result, opline->result); zend_stack_del_top(&CG(function_call_stack)); - opline->extended_value = Z_LVAL(argument_list->u.constant); + opline->extended_value = arg_num; if (CG(context).used_stack + 1 > CG(active_op_array)->used_stack) { CG(active_op_array)->used_stack = CG(context).used_stack + 1; } - CG(context).used_stack -= Z_LVAL(argument_list->u.constant); + CG(context).used_stack -= arg_num; } /* }}} */ diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 3988074779b..3c7a30b9356 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1299,6 +1299,7 @@ void execute_new_code(TSRMLS_D) /* {{{ */ } /* break omitted intentionally */ case ZEND_JMP: + case ZEND_ASSERT_CHECK: opline->op1.jmp_addr = &CG(active_op_array)->opcodes[opline->op1.opline_num]; break; case ZEND_JMPZ: diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 31dd9af6197..aa2bf4f413d 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -256,6 +256,7 @@ struct _zend_executor_globals { zend_property_info std_property_info; zend_bool active; + zend_bool assertions; zend_op *start_op; diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index af64e5a7f98..61db501ab1e 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -699,6 +699,7 @@ ZEND_API int pass_two(zend_op_array *op_array TSRMLS_DC) } /* break omitted intentionally */ case ZEND_JMP: + case ZEND_ASSERT_CHECK: case ZEND_FAST_CALL: opline->op1.jmp_addr = &op_array->opcodes[opline->op1.opline_num]; break; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index b2cecf229e5..9bd34722cd7 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5659,4 +5659,24 @@ ZEND_VM_HANDLER(163, ZEND_FAST_RET, ANY, ANY) } } +ZEND_VM_HANDLER(166, ZEND_ASSERT_CHECK, ANY, ANY) +{ + USE_OPLINE + + if (!EG(assertions)) { + if (RETURN_VALUE_USED((opline->op1.jmp_addr-1))) { + zval *ret; + + MAKE_STD_ZVAL(ret); + Z_SET_REFCOUNT_P(ret, 0); + ZVAL_BOOL(ret, 1); + PZVAL_LOCK(ret); + AI_SET_PTR(&EX_T((opline->op1.jmp_addr-1)->result.var), ret); + } + ZEND_VM_JMP(opline->op1.jmp_addr); + } else { + ZEND_VM_NEXT_OPCODE(); + } +} + ZEND_VM_EXPORT_HELPER(zend_do_fcall, zend_do_fcall_common_helper) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 49d04a2bae7..eb2dd21a32b 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1383,6 +1383,26 @@ static int ZEND_FASTCALL ZEND_FAST_RET_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) } } +static int ZEND_FASTCALL ZEND_ASSERT_CHECK_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + if (!EG(assertions)) { + if (RETURN_VALUE_USED((opline->op1.jmp_addr-1))) { + zval *ret; + + MAKE_STD_ZVAL(ret); + Z_SET_REFCOUNT_P(ret, 0); + ZVAL_BOOL(ret, 1); + PZVAL_LOCK(ret); + AI_SET_PTR(&EX_T((opline->op1.jmp_addr-1)->result.var), ret); + } + ZEND_VM_JMP(opline->op1.jmp_addr); + } else { + ZEND_VM_NEXT_OPCODE(); + } +} + static int ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -45078,6 +45098,31 @@ void zend_init_opcodes_handlers(void) ZEND_SEND_UNPACK_SPEC_HANDLER, ZEND_SEND_UNPACK_SPEC_HANDLER, ZEND_SEND_UNPACK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, ZEND_NULL_HANDLER }; zend_opcode_handlers = (opcode_handler_t*)labels; diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index d6f51cce5c3..89bb0f77a09 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -21,7 +21,7 @@ #include #include -const char *zend_vm_opcodes_map[166] = { +const char *zend_vm_opcodes_map[167] = { "ZEND_NOP", "ZEND_ADD", "ZEND_SUB", @@ -188,6 +188,7 @@ const char *zend_vm_opcodes_map[166] = { "ZEND_FAST_RET", "ZEND_RECV_VARIADIC", "ZEND_SEND_UNPACK", + "ZEND_ASSERT_CHECK", }; ZEND_API const char* zend_get_opcode_name(zend_uchar opcode) { diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 0f75196ffb2..fb27cbc4c1a 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -171,5 +171,6 @@ ZEND_API const char *zend_get_opcode_name(zend_uchar opcode); #define ZEND_FAST_RET 163 #define ZEND_RECV_VARIADIC 164 #define ZEND_SEND_UNPACK 165 +#define ZEND_ASSERT_CHECK 166 #endif diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index a6114733e3b..4ab921b60f6 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -118,6 +118,12 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg) } START_BLOCK_OP(opno + 1); break; +#endif +#ifdef ZEND_ASSERT_CHECK + case ZEND_ASSERT_CHECK: + START_BLOCK_OP(ZEND_OP1(opline).opline_num); + START_BLOCK_OP(opno + 1); + break; #endif case ZEND_JMP: START_BLOCK_OP(ZEND_OP1(opline).opline_num); @@ -268,6 +274,12 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg) cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num]; } break; +#endif +#ifdef ZEND_ASSERT_CHECK + case ZEND_ASSERT_CHECK: + cur_block->op1_to = &blocks[ZEND_OP1(opline).opline_num]; + cur_block->follow_to = &blocks[opno]; + break; #endif case ZEND_JMP: cur_block->op1_to = &blocks[ZEND_OP1(opline).opline_num]; diff --git a/ext/opcache/Optimizer/nop_removal.c b/ext/opcache/Optimizer/nop_removal.c index b2fb667ed55..026b583ad55 100644 --- a/ext/opcache/Optimizer/nop_removal.c +++ b/ext/opcache/Optimizer/nop_removal.c @@ -57,6 +57,9 @@ static void nop_removal(zend_op_array *op_array) /* update JMPs */ for (opline = op_array->opcodes; oplineopcode) { +#ifdef ZEND_ASSERT_CHECK + case ZEND_ASSERT_CHECK: +#endif case ZEND_JMP: #if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO case ZEND_GOTO: diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c index 577d1b66098..40f272941f1 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1_5.c @@ -485,6 +485,9 @@ if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) { #if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO case ZEND_FAST_CALL: case ZEND_FAST_RET: +#endif +#ifdef ZEND_ASSERT_CHECK + case ZEND_ASSERT_CHECK: #endif case ZEND_JMP: case ZEND_JMPZNZ: diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 973ba3aaacb..5f5d9b00ac3 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -474,6 +474,9 @@ static void zend_accel_optimize(zend_op_array *op_array, } #endif switch (opline->opcode) { +#ifdef ZEND_ASSERT_CHECK + case ZEND_ASSERT_CHECK: +#endif case ZEND_JMP: #if ZEND_EXTENSION_API_NO > PHP_5_2_X_API_NO case ZEND_GOTO: @@ -515,6 +518,9 @@ static void zend_accel_optimize(zend_op_array *op_array, } #endif switch (opline->opcode) { +#ifdef ZEND_ASSERT_CHECK + case ZEND_ASSERT_CHECK: +#endif case ZEND_JMP: #if ZEND_EXTENSION_API_NO > PHP_5_2_X_API_NO case ZEND_GOTO: diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 47f8f883127..728ecd888b7 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -345,6 +345,9 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc if (ZEND_DONE_PASS_TWO(op_array)) { /* fix jumps to point to new array */ switch (opline->opcode) { +#ifdef ZEND_ASSERT_CHECK + case ZEND_ASSERT_CHECK: +#endif case ZEND_JMP: case ZEND_GOTO: #if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO diff --git a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt index dfec951f799..0728d505c31 100644 --- a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt @@ -9,12 +9,14 @@ var_dump($standard->getClassNames()); ?> ==DONE== --EXPECTF-- -array(3) { +array(4) { [0]=> %s(22) "__PHP_Incomplete_Class" [1]=> %s(15) "php_user_filter" [2]=> %s(9) "Directory" + [3]=> + %s(18) "AssertionException" } ==DONE== diff --git a/ext/standard/assert.c b/ext/standard/assert.c index a9567f3b0a7..a09ff3e1368 100644 --- a/ext/standard/assert.c +++ b/ext/standard/assert.c @@ -22,6 +22,7 @@ #include "php.h" #include "php_assert.h" #include "php_ini.h" +#include "zend_exceptions.h" /* }}} */ ZEND_BEGIN_MODULE_GLOBALS(assert) @@ -29,12 +30,15 @@ ZEND_BEGIN_MODULE_GLOBALS(assert) long bail; long warning; long quiet_eval; + long exception; zval *callback; char *cb; ZEND_END_MODULE_GLOBALS(assert) ZEND_DECLARE_MODULE_GLOBALS(assert) +static zend_class_entry *assertion_exception_ce; + #ifdef ZTS #define ASSERTG(v) TSRMG(assert_globals_id, zend_assert_globals *, v) #else @@ -48,7 +52,8 @@ enum { ASSERT_CALLBACK, ASSERT_BAIL, ASSERT_WARNING, - ASSERT_QUIET_EVAL + ASSERT_QUIET_EVAL, + ASSERT_EXCEPTION }; static PHP_INI_MH(OnChangeCallback) /* {{{ */ @@ -84,6 +89,7 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("assert.warning", "1", PHP_INI_ALL, OnUpdateLong, warning, zend_assert_globals, assert_globals) PHP_INI_ENTRY("assert.callback", NULL, PHP_INI_ALL, OnChangeCallback) STD_PHP_INI_ENTRY("assert.quiet_eval", "0", PHP_INI_ALL, OnUpdateLong, quiet_eval, zend_assert_globals, assert_globals) + STD_PHP_INI_ENTRY("assert.exception", "0", PHP_INI_ALL, OnUpdateLong, exception, zend_assert_globals, assert_globals) PHP_INI_END() static void php_assert_init_globals(zend_assert_globals *assert_globals_p TSRMLS_DC) /* {{{ */ @@ -95,6 +101,8 @@ static void php_assert_init_globals(zend_assert_globals *assert_globals_p TSRMLS PHP_MINIT_FUNCTION(assert) /* {{{ */ { + zend_class_entry ce; + ZEND_INIT_MODULE_GLOBALS(assert, php_assert_init_globals, NULL); REGISTER_INI_ENTRIES(); @@ -104,6 +112,10 @@ PHP_MINIT_FUNCTION(assert) /* {{{ */ REGISTER_LONG_CONSTANT("ASSERT_BAIL", ASSERT_BAIL, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("ASSERT_WARNING", ASSERT_WARNING, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("ASSERT_QUIET_EVAL", ASSERT_QUIET_EVAL, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("ASSERT_EXCEPTION", ASSERT_EXCEPTION, CONST_CS|CONST_PERSISTENT); + + INIT_CLASS_ENTRY(ce, "AssertionException", NULL); + assertion_exception_ce = zend_register_internal_class_ex(&ce, zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC); return SUCCESS; } @@ -140,24 +152,25 @@ PHP_MINFO_FUNCTION(assert) /* {{{ */ Checks if assertion is false */ PHP_FUNCTION(assert) { - zval **assertion; - int val, description_len = 0; + zval *assertion; + zval *description = NULL; + int val; char *myeval = NULL; - char *compiled_string_description, *description = NULL; + char *compiled_string_description; if (! ASSERTG(active)) { RETURN_TRUE; } - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|s", &assertion, &description, &description_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|z", &assertion, &description) == FAILURE) { return; } - if (Z_TYPE_PP(assertion) == IS_STRING) { + if (Z_TYPE_P(assertion) == IS_STRING) { zval retval; int old_error_reporting = 0; /* shut up gcc! */ - myeval = Z_STRVAL_PP(assertion); + myeval = Z_STRVAL_P(assertion); if (ASSERTG(quiet_eval)) { old_error_reporting = EG(error_reporting); @@ -165,12 +178,13 @@ PHP_FUNCTION(assert) } compiled_string_description = zend_make_compiled_string_description("assert code" TSRMLS_CC); - if (zend_eval_stringl(myeval, Z_STRLEN_PP(assertion), &retval, compiled_string_description TSRMLS_CC) == FAILURE) { + if (zend_eval_stringl(myeval, Z_STRLEN_P(assertion), &retval, compiled_string_description TSRMLS_CC) == FAILURE) { efree(compiled_string_description); - if (description_len == 0) { + if (!description) { php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Failure evaluating code: %s%s", PHP_EOL, myeval); } else { - php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Failure evaluating code: %s%s:\"%s\"", PHP_EOL, description, myeval); + convert_to_string(description); + php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Failure evaluating code: %s%s:\"%s\"", PHP_EOL, Z_STRVAL_P(description), myeval); } if (ASSERTG(bail)) { zend_bailout(); @@ -186,8 +200,11 @@ PHP_FUNCTION(assert) convert_to_boolean(&retval); val = Z_LVAL(retval); } else { - convert_to_boolean_ex(assertion); - val = Z_LVAL_PP(assertion); +#if PHP_VERSION_ID >= 50700 + val = zend_is_true(assertion TSRMLS_CC); +#else + val = zend_is_true(assertion); +#endif } if (val) { @@ -200,7 +217,7 @@ PHP_FUNCTION(assert) } if (ASSERTG(callback)) { - zval **args = safe_emalloc(description_len == 0 ? 3 : 4, sizeof(zval *), 0); + zval **args = safe_emalloc(!description ? 3 : 4, sizeof(zval *), 0); zval *retval; int i; uint lineno = zend_get_executed_lineno(TSRMLS_C); @@ -218,14 +235,15 @@ PHP_FUNCTION(assert) ZVAL_FALSE(retval); /* XXX do we want to check for error here? */ - if (description_len == 0) { + if (!description) { call_user_function(CG(function_table), NULL, ASSERTG(callback), retval, 3, args TSRMLS_CC); for (i = 0; i <= 2; i++) { zval_ptr_dtor(&(args[i])); } } else { - MAKE_STD_ZVAL(args[3]); - ZVAL_STRINGL(args[3], SAFE_STRING(description), description_len, 1); + convert_to_string(description); + Z_ADDREF_P(description); + args[3] = description; call_user_function(CG(function_table), NULL, ASSERTG(callback), retval, 4, args TSRMLS_CC); for (i = 0; i <= 3; i++) { @@ -237,18 +255,30 @@ PHP_FUNCTION(assert) zval_ptr_dtor(&retval); } - if (ASSERTG(warning)) { - if (description_len == 0) { + if (ASSERTG(exception)) { + if (!description) { + zend_throw_exception(assertion_exception_ce, NULL, E_ERROR TSRMLS_CC); + } else if (Z_TYPE_P(description) == IS_OBJECT && + instanceof_function(Z_OBJCE_P(description), assertion_exception_ce TSRMLS_CC)) { + Z_ADDREF_P(description); + zend_throw_exception_object(description TSRMLS_CC); + } else { + convert_to_string(description); + zend_throw_exception(assertion_exception_ce, Z_STRVAL_P(description), E_ERROR TSRMLS_CC); + } + } else if (ASSERTG(warning)) { + if (!description) { if (myeval) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Assertion \"%s\" failed", myeval); } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Assertion failed"); } } else { + convert_to_string(description); if (myeval) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s: \"%s\" failed", description, myeval); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s: \"%s\" failed", Z_STRVAL_P(description), myeval); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s failed", description); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s failed", Z_STRVAL_P(description)); } } } @@ -327,6 +357,15 @@ PHP_FUNCTION(assert_options) return; break; + case ASSERT_EXCEPTION: + oldint = ASSERTG(exception); + if (ac == 2) { + convert_to_string_ex(value); + zend_alter_ini_entry_ex("assert.exception", sizeof("assert.exception"), Z_STRVAL_PP(value), Z_STRLEN_PP(value), PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC); + } + RETURN_LONG(oldint); + break; + default: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown value %ld", what); break; diff --git a/ext/standard/tests/assert/assert04.phpt b/ext/standard/tests/assert/assert04.phpt index bffadcb97c6..dce2c805e36 100644 --- a/ext/standard/tests/assert/assert04.phpt +++ b/ext/standard/tests/assert/assert04.phpt @@ -42,7 +42,7 @@ Warning: assert_options() expects at most 2 parameters, 3 given in %s on line %d Warning: assert_options() expects parameter 1 to be long, %unicode_string_optional% given in %s on line %d -Warning: assert(): Assertion failed in %s on line %d +Warning: assert(): assert(0) failed in %s on line %d -Warning: assert(): Assertion failed in %s on line %d +Warning: assert(): assert(0) failed in %s on line %d diff --git a/ext/standard/tests/assert/assert_basic2.phpt b/ext/standard/tests/assert/assert_basic2.phpt index 277e5cad187..42c59b1b9a6 100644 --- a/ext/standard/tests/assert/assert_basic2.phpt +++ b/ext/standard/tests/assert/assert_basic2.phpt @@ -29,9 +29,9 @@ assert(0); string(2) "f1" f1 called -Warning: assert(): Assertion failed in %s on line 13 +Warning: assert(): assert(0) failed in %s on line 13 string(2) "f1" string(2) "f2" f2 called -Warning: assert(): Assertion failed in %s on line 17 +Warning: assert(): assert(0) failed in %s on line 17 diff --git a/ext/standard/tests/assert/assert_basic3.phpt b/ext/standard/tests/assert/assert_basic3.phpt index 0ce326606fb..9fcbbd5c3c2 100644 --- a/ext/standard/tests/assert/assert_basic3.phpt +++ b/ext/standard/tests/assert/assert_basic3.phpt @@ -22,4 +22,4 @@ echo "If this is printed BAIL hasn't worked"; int(0) f1 called -Warning: assert(): Assertion "0 != 0" failed in %s on line 10 +Warning: assert(): assert($sa): "0 != 0" failed in %s on line 10 diff --git a/ext/standard/tests/assert/assert_basic5.phpt b/ext/standard/tests/assert/assert_basic5.phpt index 737f902d640..d67d8792623 100644 --- a/ext/standard/tests/assert/assert_basic5.phpt +++ b/ext/standard/tests/assert/assert_basic5.phpt @@ -26,7 +26,7 @@ var_dump($rao=assert_options(ASSERT_WARNING, 0)); int(0) f1 called -Warning: assert(): Assertion "0 != 0" failed in %s on line 10 +Warning: assert(): assert($sa): "0 != 0" failed in %s on line 10 NULL bool(true) int(1) diff --git a/ext/standard/tests/assert/assert_closures.phpt b/ext/standard/tests/assert/assert_closures.phpt index e01c11ace98..4a013283a8d 100644 --- a/ext/standard/tests/assert/assert_closures.phpt +++ b/ext/standard/tests/assert/assert_closures.phpt @@ -13,4 +13,4 @@ assert(0); --EXPECTF-- Hello World! -Warning: assert(): Assertion failed in %s on line %d +Warning: assert(): assert(0) failed in %s on line %d diff --git a/ext/standard/tests/assert/assert_error2.phpt b/ext/standard/tests/assert/assert_error2.phpt index da7c3d9e0bb..41a7197535e 100644 --- a/ext/standard/tests/assert/assert_error2.phpt +++ b/ext/standard/tests/assert/assert_error2.phpt @@ -17,8 +17,7 @@ function f1($script, $line, $message, $user_message) //bail out on error var_dump($rao = assert_options(ASSERT_BAIL, 1)); -$sa = "0 != 0"; -var_dump($r2 = assert($sa)); +var_dump($r2 = assert("0 != 0")); echo "If this is printed BAIL hasn't worked"; --EXPECTF-- int(0) @@ -26,5 +25,5 @@ int(0) Warning: Missing argument 4 for f1() in %s on line 2 f1 called -Warning: assert(): Assertion "0 != 0" failed in %s on line 10 +Warning: assert(): Assertion "0 != 0" failed in %s on line 9 diff --git a/ext/standard/tests/assert/assert_error3.phpt b/ext/standard/tests/assert/assert_error3.phpt index 54b91edd3d9..201a4544fa0 100644 --- a/ext/standard/tests/assert/assert_error3.phpt +++ b/ext/standard/tests/assert/assert_error3.phpt @@ -10,12 +10,11 @@ error_reporting = -1 display_errors = 1 --FILE--