root/Zend/zend_generators.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. zend_generator_cleanup_unfinished_execution
  2. zend_generator_close
  3. zend_generator_dtor_storage
  4. zend_generator_free_storage
  5. zend_generator_create
  6. copy_closure_static_var
  7. zend_generator_create_zval
  8. zend_generator_get_constructor
  9. zend_generator_resume
  10. zend_generator_ensure_initialized
  11. zend_generator_rewind
  12. ZEND_METHOD
  13. ZEND_METHOD
  14. ZEND_METHOD
  15. ZEND_METHOD
  16. ZEND_METHOD
  17. ZEND_METHOD
  18. ZEND_METHOD
  19. ZEND_METHOD
  20. zend_generator_iterator_dtor
  21. zend_generator_iterator_valid
  22. zend_generator_iterator_get_data
  23. zend_generator_iterator_get_key
  24. zend_generator_iterator_move_forward
  25. zend_generator_iterator_rewind
  26. zend_generator_get_iterator
  27. zend_register_generator_ce

   1 /*
   2    +----------------------------------------------------------------------+
   3    | Zend Engine                                                          |
   4    +----------------------------------------------------------------------+
   5    | Copyright (c) 1998-2016 Zend Technologies Ltd. (http://www.zend.com) |
   6    +----------------------------------------------------------------------+
   7    | This source file is subject to version 2.00 of the Zend license,     |
   8    | that is bundled with this package in the file LICENSE, and is        |
   9    | available through the world-wide-web at the following url:           |
  10    | http://www.zend.com/license/2_00.txt.                                |
  11    | If you did not receive a copy of the Zend license and are unable to  |
  12    | obtain it through the world-wide-web, please send a note to          |
  13    | license@zend.com so we can mail you a copy immediately.              |
  14    +----------------------------------------------------------------------+
  15    | Authors: Nikita Popov <nikic@php.net>                                |
  16    +----------------------------------------------------------------------+
  17 */
  18 
  19 /* $Id$ */
  20 
  21 #include "zend.h"
  22 #include "zend_API.h"
  23 #include "zend_interfaces.h"
  24 #include "zend_exceptions.h"
  25 #include "zend_generators.h"
  26 
  27 ZEND_API zend_class_entry *zend_ce_generator;
  28 static zend_object_handlers zend_generator_handlers;
  29 
  30 static zend_object_value zend_generator_create(zend_class_entry *class_type TSRMLS_DC);
  31 
  32 static void zend_generator_cleanup_unfinished_execution(zend_generator *generator TSRMLS_DC) /* {{{ */
  33 {
  34         zend_execute_data *execute_data = generator->execute_data;
  35         zend_op_array *op_array = execute_data->op_array;
  36 
  37         if (generator->send_target) {
  38                 Z_DELREF_PP(generator->send_target);
  39                 generator->send_target = NULL;
  40         }
  41 
  42         /* Manually free loop variables, as execution couldn't reach their
  43          * SWITCH_FREE / FREE opcodes. */
  44         {
  45                 /* -1 required because we want the last run opcode, not the
  46                  * next to-be-run one. */
  47                 zend_uint op_num = execute_data->opline - op_array->opcodes - 1;
  48 
  49                 int i;
  50                 for (i = 0; i < op_array->last_brk_cont; ++i) {
  51                         zend_brk_cont_element *brk_cont = op_array->brk_cont_array + i;
  52 
  53                         if (brk_cont->start < 0) {
  54                                 continue;
  55                         } else if (brk_cont->start > op_num) {
  56                                 break;
  57                         } else if (brk_cont->brk > op_num) {
  58                                 zend_op *brk_opline = op_array->opcodes + brk_cont->brk;
  59 
  60                                 switch (brk_opline->opcode) {
  61                                         case ZEND_SWITCH_FREE:
  62                                                 {
  63                                                         temp_variable *var = EX_TMP_VAR(execute_data, brk_opline->op1.var);
  64                                                         zval_ptr_dtor(&var->var.ptr);
  65                                                 }
  66                                                 break;
  67                                         case ZEND_FREE:
  68                                                 {
  69                                                         temp_variable *var = EX_TMP_VAR(execute_data, brk_opline->op1.var);
  70                                                         zval_dtor(&var->tmp_var);
  71                                                 }
  72                                                 break;
  73                                 }
  74                         }
  75                 }
  76         }
  77 
  78         /* Clear any backed up stack arguments */
  79         {
  80                 void **ptr = generator->stack->top - 1;
  81                 void **end = zend_vm_stack_frame_base(execute_data);
  82 
  83                 for (; ptr >= end; --ptr) {
  84                         zval_ptr_dtor((zval **) ptr);
  85                 }
  86         }
  87 
  88         /* If yield was used as a function argument there may be active
  89          * method calls those objects need to be freed */
  90         while (execute_data->call >= execute_data->call_slots) {
  91                 if (execute_data->call->object) {
  92                         zval_ptr_dtor(&execute_data->call->object);
  93                 }
  94                 execute_data->call--;
  95         }
  96 }
  97 /* }}} */
  98 
  99 ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished_execution TSRMLS_DC) /* {{{ */
 100 {
 101         if (generator->value) {
 102                 zval_ptr_dtor(&generator->value);
 103                 generator->value = NULL;
 104         }
 105 
 106         if (generator->key) {
 107                 zval_ptr_dtor(&generator->key);
 108                 generator->key = NULL;
 109         }
 110 
 111         if (generator->execute_data) {
 112                 zend_execute_data *execute_data = generator->execute_data;
 113                 zend_op_array *op_array = execute_data->op_array;
 114 
 115                 if (!execute_data->symbol_table) {
 116                         zend_free_compiled_variables(execute_data TSRMLS_CC);
 117                 } else {
 118                         zend_clean_and_cache_symbol_table(execute_data->symbol_table TSRMLS_CC);
 119                 }
 120 
 121                 if (execute_data->current_this) {
 122                         zval_ptr_dtor(&execute_data->current_this);
 123                 }
 124 
 125                 /* A fatal error / die occurred during the generator execution. Trying to clean
 126                  * up the stack may not be safe in this case. */
 127                 if (CG(unclean_shutdown)) {
 128                         generator->execute_data = NULL;
 129                         return;
 130                 }
 131 
 132                 /* We have added an additional stack frame in prev_execute_data, so we
 133                  * have to free it. It also contains the arguments passed to the
 134                  * generator (for func_get_args) so those have to be freed too. */
 135                 {
 136                         zend_execute_data *prev_execute_data = execute_data->prev_execute_data;
 137                         void **arguments = prev_execute_data->function_state.arguments;
 138 
 139                         if (arguments) {
 140                                 int arguments_count = (int) (zend_uintptr_t) *arguments;
 141                                 zval **arguments_start = (zval **) (arguments - arguments_count);
 142                                 int i;
 143 
 144                                 for (i = 0; i < arguments_count; ++i) {
 145                                         zval_ptr_dtor(arguments_start + i);
 146                                 }
 147                         }
 148                 }
 149 
 150                 /* Some cleanups are only necessary if the generator was closued
 151                  * before it could finish execution (reach a return statement). */
 152                 if (!finished_execution) {
 153                         zend_generator_cleanup_unfinished_execution(generator TSRMLS_CC);
 154                 }
 155 
 156                 /* Free a clone of closure */
 157                 if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
 158                         destroy_op_array(op_array TSRMLS_CC);
 159                         efree(op_array);
 160                 }
 161 
 162                 efree(generator->stack);
 163                 generator->execute_data = NULL;
 164         }
 165 }
 166 /* }}} */
 167 
 168 static void zend_generator_dtor_storage(zend_generator *generator, zend_object_handle handle TSRMLS_DC) /* {{{ */
 169 {
 170         zend_execute_data *ex = generator->execute_data;
 171         zend_uint op_num, finally_op_num;
 172         int i;
 173 
 174         if (!ex || !ex->op_array->has_finally_block) {
 175                 return;
 176         }
 177 
 178         /* -1 required because we want the last run opcode, not the
 179          * next to-be-run one. */
 180         op_num = ex->opline - ex->op_array->opcodes - 1;
 181 
 182         /* Find next finally block */
 183         finally_op_num = 0;
 184         for (i = 0; i < ex->op_array->last_try_catch; i++) {
 185                 zend_try_catch_element *try_catch = &ex->op_array->try_catch_array[i];
 186 
 187                 if (op_num < try_catch->try_op) {
 188                         break;
 189                 }
 190 
 191                 if (op_num < try_catch->finally_op) {
 192                         finally_op_num = try_catch->finally_op;
 193                 }
 194         }
 195 
 196         /* If a finally block was found we jump directly to it and
 197          * resume the generator. */
 198         if (finally_op_num) {
 199                 ex->opline = &ex->op_array->opcodes[finally_op_num];
 200                 ex->fast_ret = NULL;
 201                 ex->delayed_exception = EG(exception);
 202                 EG(exception) = NULL;
 203 
 204                 generator->flags |= ZEND_GENERATOR_FORCED_CLOSE;
 205                 zend_generator_resume(generator TSRMLS_CC);
 206         }
 207 }
 208 /* }}} */
 209 
 210 static void zend_generator_free_storage(zend_generator *generator TSRMLS_DC) /* {{{ */
 211 {
 212         zend_generator_close(generator, 0 TSRMLS_CC);
 213 
 214         zend_object_std_dtor(&generator->std TSRMLS_CC);
 215         efree(generator);
 216 }
 217 /* }}} */
 218 
 219 static zend_object_value zend_generator_create(zend_class_entry *class_type TSRMLS_DC) /* {{{ */
 220 {
 221         zend_generator *generator;
 222         zend_object_value object;
 223 
 224         generator = emalloc(sizeof(zend_generator));
 225         memset(generator, 0, sizeof(zend_generator));
 226 
 227         /* The key will be incremented on first use, so it'll start at 0 */
 228         generator->largest_used_integer_key = -1;
 229 
 230         zend_object_std_init(&generator->std, class_type TSRMLS_CC);
 231 
 232         object.handle = zend_objects_store_put(generator,
 233                 (zend_objects_store_dtor_t)          zend_generator_dtor_storage,
 234                 (zend_objects_free_object_storage_t) zend_generator_free_storage,
 235                 NULL TSRMLS_CC
 236         );
 237         object.handlers = &zend_generator_handlers;
 238 
 239         return object;
 240 }
 241 /* }}} */
 242 
 243 static void copy_closure_static_var(zval **var TSRMLS_DC, int num_args, va_list args, zend_hash_key *key) /* {{{ */
 244 {
 245         HashTable *target = va_arg(args, HashTable *);
 246 
 247         SEPARATE_ZVAL_TO_MAKE_IS_REF(var);
 248         Z_ADDREF_PP(var);
 249         zend_hash_quick_update(target, key->arKey, key->nKeyLength, key->h, var, sizeof(zval *), NULL);
 250 }
 251 /* }}} */
 252 
 253 /* Requires globals EG(scope), EG(current_scope), EG(This),
 254  * EG(active_symbol_table) and EG(current_execute_data). */
 255 ZEND_API zval *zend_generator_create_zval(zend_op_array *op_array TSRMLS_DC) /* {{{ */
 256 {
 257         zval *return_value;
 258         zend_generator *generator;
 259         zend_execute_data *current_execute_data;
 260         zend_op **opline_ptr;
 261         HashTable *current_symbol_table;
 262         zend_execute_data *execute_data;
 263         zend_vm_stack current_stack = EG(argument_stack);
 264 
 265         /* Create a clone of closure, because it may be destroyed */
 266         if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
 267                 zend_op_array *op_array_copy = (zend_op_array*)emalloc(sizeof(zend_op_array));
 268                 *op_array_copy = *op_array;
 269 
 270                 (*op_array->refcount)++;
 271                 op_array->run_time_cache = NULL;
 272                 if (op_array->static_variables) {
 273                         ALLOC_HASHTABLE(op_array_copy->static_variables);
 274                         zend_hash_init(
 275                                 op_array_copy->static_variables, 
 276                                 zend_hash_num_elements(op_array->static_variables),
 277                                 NULL, ZVAL_PTR_DTOR, 0
 278                         );
 279                         zend_hash_apply_with_arguments(
 280                                 op_array->static_variables TSRMLS_CC,
 281                                 (apply_func_args_t) copy_closure_static_var,
 282                                 1, op_array_copy->static_variables
 283                         );
 284                 }
 285 
 286                 op_array = op_array_copy;
 287         }
 288         
 289         /* Create new execution context. We have to back up and restore
 290          * EG(current_execute_data), EG(opline_ptr) and EG(active_symbol_table)
 291          * here because the function modifies or uses them  */
 292         current_execute_data = EG(current_execute_data);
 293         opline_ptr = EG(opline_ptr);
 294         current_symbol_table = EG(active_symbol_table);
 295         EG(active_symbol_table) = NULL;
 296         execute_data = zend_create_execute_data_from_op_array(op_array, 0 TSRMLS_CC);
 297         EG(active_symbol_table) = current_symbol_table;
 298         EG(current_execute_data) = current_execute_data;
 299         EG(opline_ptr) = opline_ptr;
 300 
 301         ALLOC_INIT_ZVAL(return_value);
 302         object_init_ex(return_value, zend_ce_generator);
 303 
 304         if (EG(This)) {
 305                 Z_ADDREF_P(EG(This));
 306         }
 307 
 308         /* Back up executor globals. */
 309         execute_data->current_scope = EG(scope);
 310         execute_data->current_called_scope = EG(called_scope);
 311         execute_data->symbol_table = EG(active_symbol_table);
 312         execute_data->current_this = EG(This);
 313 
 314         /* Save execution context in generator object. */
 315         generator = (zend_generator *) zend_object_store_get_object(return_value TSRMLS_CC);
 316         generator->execute_data = execute_data;
 317         generator->stack = EG(argument_stack);
 318         EG(argument_stack) = current_stack;
 319 
 320         return return_value;
 321 }
 322 /* }}} */
 323 
 324 static zend_function *zend_generator_get_constructor(zval *object TSRMLS_DC) /* {{{ */
 325 {
 326         zend_error(E_RECOVERABLE_ERROR, "The \"Generator\" class is reserved for internal use and cannot be manually instantiated");
 327 
 328         return NULL;
 329 }
 330 /* }}} */
 331 
 332 ZEND_API void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */
 333 {
 334         /* The generator is already closed, thus can't resume */
 335         if (!generator->execute_data) {
 336                 return;
 337         }
 338 
 339         if (generator->flags & ZEND_GENERATOR_CURRENTLY_RUNNING) {
 340                 zend_error(E_ERROR, "Cannot resume an already running generator");
 341         }
 342 
 343         /* Drop the AT_FIRST_YIELD flag */
 344         generator->flags &= ~ZEND_GENERATOR_AT_FIRST_YIELD;
 345 
 346         {
 347                 /* Backup executor globals */
 348                 zval **original_return_value_ptr_ptr = EG(return_value_ptr_ptr);
 349                 zend_execute_data *original_execute_data = EG(current_execute_data);
 350                 zend_op **original_opline_ptr = EG(opline_ptr);
 351                 zend_op_array *original_active_op_array = EG(active_op_array);
 352                 HashTable *original_active_symbol_table = EG(active_symbol_table);
 353                 zval *original_This = EG(This);
 354                 zend_class_entry *original_scope = EG(scope);
 355                 zend_class_entry *original_called_scope = EG(called_scope);
 356                 zend_vm_stack original_stack = EG(argument_stack);
 357 
 358                 /* We (mis)use the return_value_ptr_ptr to provide the generator object
 359                  * to the executor, so YIELD will be able to set the yielded value */
 360                 EG(return_value_ptr_ptr) = (zval **) generator;
 361 
 362                 /* Set executor globals */
 363                 EG(current_execute_data) = generator->execute_data;
 364                 EG(opline_ptr) = &generator->execute_data->opline;
 365                 EG(active_op_array) = generator->execute_data->op_array;
 366                 EG(active_symbol_table) = generator->execute_data->symbol_table;
 367                 EG(This) = generator->execute_data->current_this;
 368                 EG(scope) = generator->execute_data->current_scope;
 369                 EG(called_scope) = generator->execute_data->current_called_scope;
 370                 EG(argument_stack) = generator->stack;
 371 
 372                 /* We want the backtrace to look as if the generator function was
 373                  * called from whatever method we are current running (e.g. next()).
 374                  * The first prev_execute_data contains an additional stack frame,
 375                  * which makes the generator function show up in the backtrace and
 376                  * makes the arguments available to func_get_args(). So we have to
 377                  * set the prev_execute_data of that prev_execute_data :) */
 378                 generator->execute_data->prev_execute_data->prev_execute_data = original_execute_data;
 379 
 380                 /* Resume execution */
 381                 generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING;
 382                 zend_execute_ex(generator->execute_data TSRMLS_CC);
 383                 generator->flags &= ~ZEND_GENERATOR_CURRENTLY_RUNNING;
 384 
 385                 /* Restore executor globals */
 386                 EG(return_value_ptr_ptr) = original_return_value_ptr_ptr;
 387                 EG(current_execute_data) = original_execute_data;
 388                 EG(opline_ptr) = original_opline_ptr;
 389                 EG(active_op_array) = original_active_op_array;
 390                 EG(active_symbol_table) = original_active_symbol_table;
 391                 EG(This) = original_This;
 392                 EG(scope) = original_scope;
 393                 EG(called_scope) = original_called_scope;
 394                 EG(argument_stack) = original_stack;
 395 
 396                 /* If an exception was thrown in the generator we have to internally
 397                  * rethrow it in the parent scope. */
 398                 if (UNEXPECTED(EG(exception) != NULL)) {
 399                         zend_throw_exception_internal(NULL TSRMLS_CC);
 400                 }
 401         }
 402 }
 403 /* }}} */
 404 
 405 static void zend_generator_ensure_initialized(zend_generator *generator TSRMLS_DC) /* {{{ */
 406 {
 407         if (generator->execute_data && !generator->value) {
 408                 zend_generator_resume(generator TSRMLS_CC);
 409                 generator->flags |= ZEND_GENERATOR_AT_FIRST_YIELD;
 410         }
 411 }
 412 /* }}} */
 413 
 414 static void zend_generator_rewind(zend_generator *generator TSRMLS_DC) /* {{{ */
 415 {
 416         zend_generator_ensure_initialized(generator TSRMLS_CC);
 417 
 418         if (!(generator->flags & ZEND_GENERATOR_AT_FIRST_YIELD)) {
 419                 zend_throw_exception(NULL, "Cannot rewind a generator that was already run", 0 TSRMLS_CC);
 420         }
 421 }
 422 /* }}} */
 423 
 424 /* {{{ proto void Generator::rewind()
 425  * Rewind the generator */
 426 ZEND_METHOD(Generator, rewind)
 427 {
 428         zend_generator *generator;
 429 
 430         if (zend_parse_parameters_none() == FAILURE) {
 431                 return;
 432         }
 433 
 434         generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
 435 
 436         zend_generator_rewind(generator TSRMLS_CC);
 437 }
 438 /* }}} */
 439 
 440 /* {{{ proto bool Generator::valid()
 441  * Check whether the generator is valid */
 442 ZEND_METHOD(Generator, valid)
 443 {
 444         zend_generator *generator;
 445 
 446         if (zend_parse_parameters_none() == FAILURE) {
 447                 return;
 448         }
 449 
 450         generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
 451 
 452         zend_generator_ensure_initialized(generator TSRMLS_CC);
 453 
 454         RETURN_BOOL(generator->value != NULL);
 455 }
 456 /* }}} */
 457 
 458 /* {{{ proto mixed Generator::current()
 459  * Get the current value */
 460 ZEND_METHOD(Generator, current)
 461 {
 462         zend_generator *generator;
 463 
 464         if (zend_parse_parameters_none() == FAILURE) {
 465                 return;
 466         }
 467 
 468         generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
 469 
 470         zend_generator_ensure_initialized(generator TSRMLS_CC);
 471 
 472         if (generator->value) {
 473                 RETURN_ZVAL_FAST(generator->value);
 474         }
 475 }
 476 /* }}} */
 477 
 478 /* {{{ proto mixed Generator::key()
 479  * Get the current key */
 480 ZEND_METHOD(Generator, key)
 481 {
 482         zend_generator *generator;
 483 
 484         if (zend_parse_parameters_none() == FAILURE) {
 485                 return;
 486         }
 487 
 488         generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
 489 
 490         zend_generator_ensure_initialized(generator TSRMLS_CC);
 491 
 492         if (generator->key) {
 493                 RETURN_ZVAL_FAST(generator->key);
 494         }
 495 }
 496 /* }}} */
 497 
 498 /* {{{ proto void Generator::next()
 499  * Advances the generator */
 500 ZEND_METHOD(Generator, next)
 501 {
 502         zend_generator *generator;
 503 
 504         if (zend_parse_parameters_none() == FAILURE) {
 505                 return;
 506         }
 507 
 508         generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
 509 
 510         zend_generator_ensure_initialized(generator TSRMLS_CC);
 511 
 512         zend_generator_resume(generator TSRMLS_CC);
 513 }
 514 /* }}} */
 515 
 516 /* {{{ proto mixed Generator::send(mixed $value)
 517  * Sends a value to the generator */
 518 ZEND_METHOD(Generator, send)
 519 {
 520         zval *value;
 521         zend_generator *generator;
 522 
 523         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
 524                 return;
 525         }
 526 
 527         generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
 528 
 529         zend_generator_ensure_initialized(generator TSRMLS_CC); 
 530 
 531         /* The generator is already closed, thus can't send anything */
 532         if (!generator->execute_data) {
 533                 return;
 534         }
 535 
 536         /* Put sent value in the target VAR slot, if it is used */
 537         if (generator->send_target) {
 538                 Z_DELREF_PP(generator->send_target);
 539                 Z_ADDREF_P(value);
 540                 *generator->send_target = value;
 541         }
 542 
 543         zend_generator_resume(generator TSRMLS_CC);
 544 
 545         if (generator->value) {
 546                 RETURN_ZVAL_FAST(generator->value);
 547         }
 548 }
 549 /* }}} */
 550 
 551 /* {{{ proto mixed Generator::throw(Exception $exception)
 552  * Throws an exception into the generator */
 553 ZEND_METHOD(Generator, throw)
 554 {
 555         zval *exception, *exception_copy;
 556         zend_generator *generator;
 557 
 558         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &exception) == FAILURE) {
 559                 return;
 560         }
 561 
 562         ALLOC_ZVAL(exception_copy);
 563         MAKE_COPY_ZVAL(&exception, exception_copy);
 564 
 565         generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
 566 
 567         zend_generator_ensure_initialized(generator TSRMLS_CC); 
 568 
 569         if (generator->execute_data) {
 570                 /* Throw the exception in the context of the generator */
 571                 zend_execute_data *current_execute_data = EG(current_execute_data);
 572                 EG(current_execute_data) = generator->execute_data;
 573 
 574                 zend_throw_exception_object(exception_copy TSRMLS_CC);
 575 
 576                 EG(current_execute_data) = current_execute_data;
 577 
 578                 zend_generator_resume(generator TSRMLS_CC);
 579 
 580                 if (generator->value) {
 581                         RETURN_ZVAL_FAST(generator->value);
 582                 }
 583         } else {
 584                 /* If the generator is already closed throw the exception in the
 585                  * current context */
 586                 zend_throw_exception_object(exception_copy TSRMLS_CC);
 587         }
 588 }
 589 /* }}} */
 590 
 591 /* {{{ proto void Generator::__wakeup()
 592  * Throws an Exception as generators can't be serialized */
 593 ZEND_METHOD(Generator, __wakeup)
 594 {
 595         /* Just specifying the zend_class_unserialize_deny handler is not enough,
 596          * because it is only invoked for C unserialization. For O the error has
 597          * to be thrown in __wakeup. */
 598 
 599         if (zend_parse_parameters_none() == FAILURE) {
 600                 return;
 601         }
 602 
 603         zend_throw_exception(NULL, "Unserialization of 'Generator' is not allowed", 0 TSRMLS_CC);
 604 }
 605 /* }}} */
 606 
 607 /* get_iterator implementation */
 608 
 609 static void zend_generator_iterator_dtor(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */
 610 {
 611         zend_generator_iterator *iter = (zend_generator_iterator *) iterator;
 612 
 613         zend_objects_store_del_ref_by_handle(iter->handle TSRMLS_CC);
 614 }
 615 /* }}} */
 616 
 617 static int zend_generator_iterator_valid(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */
 618 {
 619         zend_generator *generator = (zend_generator *) iterator->data;
 620 
 621         zend_generator_ensure_initialized(generator TSRMLS_CC);
 622 
 623         return generator->value != NULL ? SUCCESS : FAILURE;
 624 }
 625 /* }}} */
 626 
 627 static void zend_generator_iterator_get_data(zend_object_iterator *iterator, zval ***data TSRMLS_DC) /* {{{ */
 628 {
 629         zend_generator *generator = (zend_generator *) iterator->data;
 630 
 631         zend_generator_ensure_initialized(generator TSRMLS_CC);
 632 
 633         if (generator->value) {
 634                 *data = &generator->value;
 635         } else {
 636                 *data = NULL;
 637         }
 638 }
 639 /* }}} */
 640 
 641 static void zend_generator_iterator_get_key(zend_object_iterator *iterator, zval *key TSRMLS_DC) /* {{{ */
 642 {
 643         zend_generator *generator = (zend_generator *) iterator->data;
 644 
 645         zend_generator_ensure_initialized(generator TSRMLS_CC);
 646 
 647         if (generator->key) {
 648                 ZVAL_ZVAL(key, generator->key, 1, 0);
 649         } else {
 650                 ZVAL_NULL(key);
 651         }
 652 }
 653 /* }}} */
 654 
 655 static void zend_generator_iterator_move_forward(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */
 656 {
 657         zend_generator *generator = (zend_generator *) iterator->data;
 658 
 659         zend_generator_ensure_initialized(generator TSRMLS_CC);
 660 
 661         zend_generator_resume(generator TSRMLS_CC);
 662 }
 663 /* }}} */
 664 
 665 static void zend_generator_iterator_rewind(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */
 666 {
 667         zend_generator *generator = (zend_generator *) iterator->data;
 668 
 669         zend_generator_rewind(generator TSRMLS_CC);
 670 }
 671 /* }}} */
 672 
 673 static zend_object_iterator_funcs zend_generator_iterator_functions = {
 674         zend_generator_iterator_dtor,
 675         zend_generator_iterator_valid,
 676         zend_generator_iterator_get_data,
 677         zend_generator_iterator_get_key,
 678         zend_generator_iterator_move_forward,
 679         zend_generator_iterator_rewind
 680 };
 681 
 682 zend_object_iterator *zend_generator_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC) /* {{{ */
 683 {
 684         zend_generator_iterator *iterator;
 685         zend_generator *generator;
 686 
 687         generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC);
 688 
 689         if (!generator->execute_data) {
 690                 zend_throw_exception(NULL, "Cannot traverse an already closed generator", 0 TSRMLS_CC);
 691                 return NULL;
 692         }
 693 
 694         if (by_ref && !(generator->execute_data->op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
 695                 zend_throw_exception(NULL, "You can only iterate a generator by-reference if it declared that it yields by-reference", 0 TSRMLS_CC);
 696                 return NULL;
 697         }
 698 
 699         iterator = &generator->iterator;
 700         iterator->intern.funcs = &zend_generator_iterator_functions;
 701         iterator->intern.data = (void *) generator;
 702 
 703         /* We have to keep a reference to the generator object zval around,
 704          * otherwise the generator may be destroyed during iteration. */
 705         iterator->handle = Z_OBJ_HANDLE_P(object);
 706         zend_objects_store_add_ref_by_handle(iterator->handle TSRMLS_CC);
 707 
 708         return (zend_object_iterator *) iterator;
 709 }
 710 /* }}} */
 711 
 712 ZEND_BEGIN_ARG_INFO(arginfo_generator_void, 0)
 713 ZEND_END_ARG_INFO()
 714 
 715 ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_send, 0, 0, 1)
 716         ZEND_ARG_INFO(0, value)
 717 ZEND_END_ARG_INFO()
 718 
 719 ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_throw, 0, 0, 1)
 720         ZEND_ARG_INFO(0, exception)
 721 ZEND_END_ARG_INFO()
 722 
 723 static const zend_function_entry generator_functions[] = {
 724         ZEND_ME(Generator, rewind,   arginfo_generator_void, ZEND_ACC_PUBLIC)
 725         ZEND_ME(Generator, valid,    arginfo_generator_void, ZEND_ACC_PUBLIC)
 726         ZEND_ME(Generator, current,  arginfo_generator_void, ZEND_ACC_PUBLIC)
 727         ZEND_ME(Generator, key,      arginfo_generator_void, ZEND_ACC_PUBLIC)
 728         ZEND_ME(Generator, next,     arginfo_generator_void, ZEND_ACC_PUBLIC)
 729         ZEND_ME(Generator, send,     arginfo_generator_send, ZEND_ACC_PUBLIC)
 730         ZEND_ME(Generator, throw,    arginfo_generator_throw, ZEND_ACC_PUBLIC)
 731         ZEND_ME(Generator, __wakeup, arginfo_generator_void, ZEND_ACC_PUBLIC)
 732         ZEND_FE_END
 733 };
 734 
 735 void zend_register_generator_ce(TSRMLS_D) /* {{{ */
 736 {
 737         zend_class_entry ce;
 738 
 739         INIT_CLASS_ENTRY(ce, "Generator", generator_functions);
 740         zend_ce_generator = zend_register_internal_class(&ce TSRMLS_CC);
 741         zend_ce_generator->ce_flags |= ZEND_ACC_FINAL_CLASS;
 742         zend_ce_generator->create_object = zend_generator_create;
 743         zend_ce_generator->serialize = zend_class_serialize_deny;
 744         zend_ce_generator->unserialize = zend_class_unserialize_deny;
 745 
 746         /* get_iterator has to be assigned *after* implementing the inferface */
 747         zend_class_implements(zend_ce_generator TSRMLS_CC, 1, zend_ce_iterator);
 748         zend_ce_generator->get_iterator = zend_generator_get_iterator;
 749         zend_ce_generator->iterator_funcs.funcs = &zend_generator_iterator_functions;
 750 
 751         memcpy(&zend_generator_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
 752         zend_generator_handlers.get_constructor = zend_generator_get_constructor;
 753         zend_generator_handlers.clone_obj = NULL;
 754 }
 755 /* }}} */
 756 
 757 /*
 758  * Local variables:
 759  * tab-width: 4
 760  * c-basic-offset: 4
 761  * indent-tabs-mode: t
 762  * End:
 763  */

/* [<][>][^][v][top][bottom][index][help] */