diff --git a/Zend/tests/generators/raise_deep.phpt b/Zend/tests/generators/raise_deep.phpt new file mode 100644 index 0000000000000..131e499617442 --- /dev/null +++ b/Zend/tests/generators/raise_deep.phpt @@ -0,0 +1,35 @@ +--TEST-- +Raise exception in the generator +--FILE-- +getMessage()); + throw $e; + } + var_dump(yield "yield bar"); +} + +function gen2() { + var_dump("start sub generator"); + + var_dump(yield "yield foo"); +} + +$gen = gen(); +var_dump($gen->current()); +$gen->raise(new Exception("raise exception")); +var_dump($gen->current()); +$gen->send("send foo"); + +?> +--EXPECT-- +string(15) "start generator" +string(9) "yield foo" +string(14) "raise exception" +string(14) "exception dtor" +string(9) "yield bar" +string(8) "send foo" diff --git a/Zend/tests/generators/raise_exception.phpt b/Zend/tests/generators/raise_exception.phpt new file mode 100644 index 0000000000000..420d75505a4d4 --- /dev/null +++ b/Zend/tests/generators/raise_exception.phpt @@ -0,0 +1,35 @@ +--TEST-- +Raise exception in the generator +--FILE-- +getMessage()); + unset($e); + } + var_dump(yield "yield bar"); +} + +$gen = gen(); +var_dump($gen->current()); +$gen->raise(new ExceptionEx("raise exception")); +var_dump($gen->current()); +$gen->send("send foo"); + +?> +--EXPECT-- +string(15) "start generator" +string(9) "yield foo" +string(15) "raise exception" +string(14) "exception dtor" +string(9) "yield bar" +string(8) "send foo" diff --git a/Zend/tests/generators/raise_unexpected_exception.phpt b/Zend/tests/generators/raise_unexpected_exception.phpt new file mode 100644 index 0000000000000..7a1316c2a1ab1 --- /dev/null +++ b/Zend/tests/generators/raise_unexpected_exception.phpt @@ -0,0 +1,32 @@ +--TEST-- +Raise exception in the generator +--FILE-- +current()); +$gen->raise(new Exception("raise exception")); +var_dump($gen->current()); +$gen->send("send foo"); + +?> +--EXPECT-- +string(15) "start generator" +string(9) "yield foo" +string(14) "raise exception" +string(14) "exception dtor" +string(9) "yield bar" +string(8) "send foo" diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index dc7ae1479b41e..76e9009574fe2 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -451,6 +451,12 @@ void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ * set the prev_execute_data of that prev_execute_data :) */ generator->execute_data->prev_execute_data->prev_execute_data = original_execute_data; + if(generator->exception) { + EG(exception) = generator->exception; + generator->exception = NULL; + zend_throw_exception_internal(NULL TSRMLS_CC); + } + /* Resume execution */ generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING; zend_execute_ex(generator->execute_data TSRMLS_CC); @@ -623,6 +629,43 @@ ZEND_METHOD(Generator, send) } /* }}} */ +/* {{{ proto mixed Generator::raise() + * Throw exception into the generator */ +ZEND_METHOD(Generator, raise) +{ + zval *exc; + zend_generator *generator; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &exc) == FAILURE) { + return; + } + + generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); + + if(generator->exception) { + zval_ptr_dtor(&(generator->exception)); + } + + ALLOC_INIT_ZVAL(generator->exception); + ZVAL_ZVAL(generator->exception, exc, 1, 0); + //generator->exception = exc; + zend_generator_ensure_initialized(generator TSRMLS_CC); + + /* The generator is already closed, thus can't send anything */ + if (!generator->execute_data) { + return; + } + + /* The sent value was initialized to NULL, so dtor that */ + zval_ptr_dtor(&generator->send_target->var.ptr); + + zend_generator_resume(generator TSRMLS_CC); + + if (generator->value) { + RETURN_ZVAL(generator->value, 1, 0); + } +} +/* }}} */ + /* {{{ proto void Generator::__wakeup() * Throws an Exception as generators can't be serialized */ ZEND_METHOD(Generator, __wakeup) @@ -764,6 +807,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_send, 0, 0, 1) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_raise, 0, 0, 1) + ZEND_ARG_INFO(0, exception) +ZEND_END_ARG_INFO() + static const zend_function_entry generator_functions[] = { ZEND_ME(Generator, rewind, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_ME(Generator, valid, arginfo_generator_void, ZEND_ACC_PUBLIC) @@ -771,6 +818,7 @@ static const zend_function_entry generator_functions[] = { ZEND_ME(Generator, key, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_ME(Generator, next, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_ME(Generator, send, arginfo_generator_send, ZEND_ACC_PUBLIC) + ZEND_ME(Generator, raise, arginfo_generator_raise, ZEND_ACC_PUBLIC) ZEND_ME(Generator, __wakeup, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_FE_END }; diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index 90f8160f710b1..61f33d64e5e91 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -43,11 +43,13 @@ typedef struct _zend_generator { /* The separate stack used by generator */ zend_vm_stack stack; - + /* Current value */ zval *value; /* Current key */ zval *key; + + zval *exception; /* Variable to put sent value into */ temp_variable *send_target; /* Largest used integer key for auto-incrementing keys */ @@ -60,6 +62,7 @@ typedef struct _zend_generator { static const zend_uchar ZEND_GENERATOR_CURRENTLY_RUNNING = 0x1; static const zend_uchar ZEND_GENERATOR_FORCED_CLOSE = 0x2; static const zend_uchar ZEND_GENERATOR_AT_FIRST_YIELD = 0x4; +static const zend_uchar ZEND_GENERATOR_RISE_EXCEPTION = 0x8; void zend_register_generator_ce(TSRMLS_D); zval *zend_generator_create_zval(zend_op_array *op_array TSRMLS_DC);