void zend_shutdown(void) /* {{{ */ { zend_destroy_rsrc_list(&EG(persistent_list)); if (EG(active)) { /* * The order of destruction is important here. * See bugs #65463 and 66036. */ zend_function *func; zend_class_entry *ce; ZEND_HASH_REVERSE_FOREACH_PTR(GLOBAL_FUNCTION_TABLE, func) { if (func->type == ZEND_USER_FUNCTION) { zend_cleanup_op_array_data((zend_op_array *) func); } } ZEND_HASH_FOREACH_END(); ZEND_HASH_REVERSE_FOREACH_PTR(GLOBAL_CLASS_TABLE, ce) { if (ce->type == ZEND_USER_CLASS) { zend_cleanup_user_class_data(ce); } else { break; } } ZEND_HASH_FOREACH_END(); zend_cleanup_internal_classes(); zend_hash_reverse_apply(GLOBAL_FUNCTION_TABLE, (apply_func_t) clean_non_persistent_function_full); zend_hash_reverse_apply(GLOBAL_CLASS_TABLE, (apply_func_t) clean_non_persistent_class_full); } zend_destroy_modules(); virtual_cwd_deactivate(); virtual_cwd_shutdown(); zend_hash_destroy(GLOBAL_FUNCTION_TABLE); zend_hash_destroy(GLOBAL_CLASS_TABLE); zend_hash_destroy(GLOBAL_AUTO_GLOBALS_TABLE); free(GLOBAL_AUTO_GLOBALS_TABLE); zend_shutdown_extensions(); free(zend_version_info); free(GLOBAL_FUNCTION_TABLE); free(GLOBAL_CLASS_TABLE); zend_hash_destroy(GLOBAL_CONSTANTS_TABLE); free(GLOBAL_CONSTANTS_TABLE); zend_shutdown_strtod(); #ifdef ZTS GLOBAL_FUNCTION_TABLE = NULL; GLOBAL_CLASS_TABLE = NULL; GLOBAL_AUTO_GLOBALS_TABLE = NULL; GLOBAL_CONSTANTS_TABLE = NULL; #endif zend_destroy_rsrc_list_dtors(); zend_interned_strings_dtor(); }
void shutdown_executor(void) /* {{{ */ { zend_function *func; zend_class_entry *ce; zend_try { /* Removed because this can not be safely done, e.g. in this situation: Object 1 creates object 2 Object 3 holds reference to object 2. Now when 1 and 2 are destroyed, 3 can still access 2 in its destructor, with very problematic results */ /* zend_objects_store_call_destructors(&EG(objects_store)); */ /* Moved after symbol table cleaners, because some of the cleaners can call destructors, which would use EG(symtable_cache_ptr) and thus leave leaks */ /* while (EG(symtable_cache_ptr)>=EG(symtable_cache)) { zend_hash_destroy(*EG(symtable_cache_ptr)); efree(*EG(symtable_cache_ptr)); EG(symtable_cache_ptr)--; } */ zend_llist_apply(&zend_extensions, (llist_apply_func_t) zend_extension_deactivator); if (CG(unclean_shutdown)) { EG(symbol_table).ht.pDestructor = zend_unclean_zval_ptr_dtor; } zend_hash_graceful_reverse_destroy(&EG(symbol_table).ht); } zend_end_try(); EG(valid_symbol_table) = 0; zend_try { zval *zeh; /* remove error handlers before destroying classes and functions, * so that if handler used some class, crash would not happen */ if (Z_TYPE(EG(user_error_handler)) != IS_UNDEF) { zeh = &EG(user_error_handler); zval_ptr_dtor(zeh); ZVAL_UNDEF(&EG(user_error_handler)); } if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) { zeh = &EG(user_exception_handler); zval_ptr_dtor(zeh); ZVAL_UNDEF(&EG(user_exception_handler)); } zend_stack_clean(&EG(user_error_handlers_error_reporting), NULL, 1); zend_stack_clean(&EG(user_error_handlers), (void (*)(void *))ZVAL_DESTRUCTOR, 1); zend_stack_clean(&EG(user_exception_handlers), (void (*)(void *))ZVAL_DESTRUCTOR, 1); } zend_end_try(); zend_try { /* Cleanup static data for functions and arrays. * We need a separate cleanup stage because of the following problem: * Suppose we destroy class X, which destroys the class's function table, * and in the function table we have function foo() that has static $bar. * Now if an object of class X is assigned to $bar, its destructor will be * called and will fail since X's function table is in mid-destruction. * So we want first of all to clean up all data and then move to tables destruction. * Note that only run-time accessed data need to be cleaned up, pre-defined data can * not contain objects and thus are not probelmatic */ if (EG(full_tables_cleanup)) { ZEND_HASH_FOREACH_PTR(EG(function_table), func) { if (func->type == ZEND_USER_FUNCTION) { zend_cleanup_op_array_data((zend_op_array *) func); } } ZEND_HASH_FOREACH_END(); ZEND_HASH_REVERSE_FOREACH_PTR(EG(class_table), ce) { if (ce->type == ZEND_USER_CLASS) { zend_cleanup_user_class_data(ce); } else { zend_cleanup_internal_class_data(ce); } } ZEND_HASH_FOREACH_END(); } else { ZEND_HASH_REVERSE_FOREACH_PTR(EG(function_table), func) { if (func->type != ZEND_USER_FUNCTION) { break; } zend_cleanup_op_array_data((zend_op_array *) func); } ZEND_HASH_FOREACH_END(); ZEND_HASH_REVERSE_FOREACH_PTR(EG(class_table), ce) { if (ce->type != ZEND_USER_CLASS) { break; } zend_cleanup_user_class_data(ce); } ZEND_HASH_FOREACH_END(); zend_cleanup_internal_classes(); } } zend_end_try();