diff options
author | Angie Byron <webchick@24967.no-reply.drupal.org> | 2010-02-17 22:44:52 +0000 |
---|---|---|
committer | Angie Byron <webchick@24967.no-reply.drupal.org> | 2010-02-17 22:44:52 +0000 |
commit | 8d01aeb4287be61195a43305e34f379086914f5d (patch) | |
tree | 88ef91e8532ed82085dfa5ce8a854116f891173b | |
parent | 8847f4c9a2edcd2f555d6a47b5037c88c671cc3c (diff) | |
download | brdo-8d01aeb4287be61195a43305e34f379086914f5d.tar.gz brdo-8d01aeb4287be61195a43305e34f379086914f5d.tar.bz2 |
#710142 by Berdir, moshe weitzman, chx: Handle exceptions in shutdown functions (with tests). Hopefully the last of these weird 'Stack frame in Unknown line 0' errors.
-rw-r--r-- | includes/batch.inc | 2 | ||||
-rw-r--r-- | includes/bootstrap.inc | 56 | ||||
-rw-r--r-- | includes/common.inc | 4 | ||||
-rw-r--r-- | includes/database/mysql/database.inc | 2 | ||||
-rw-r--r-- | includes/lock.inc | 2 | ||||
-rw-r--r-- | includes/menu.inc | 6 | ||||
-rw-r--r-- | includes/session.inc | 2 | ||||
-rw-r--r-- | modules/search/search.module | 2 | ||||
-rw-r--r-- | modules/simpletest/tests/system_test.module | 33 | ||||
-rw-r--r-- | modules/system/system.api.php | 2 | ||||
-rw-r--r-- | modules/system/system.test | 28 |
11 files changed, 128 insertions, 11 deletions
diff --git a/includes/batch.inc b/includes/batch.inc index 850eff710..7fcc91566 100644 --- a/includes/batch.inc +++ b/includes/batch.inc @@ -58,7 +58,7 @@ function _batch_page() { } // Register database update for the end of processing. - register_shutdown_function('_batch_shutdown'); + drupal_register_shutdown_function('_batch_shutdown'); // Add batch-specific CSS. foreach ($batch['sets'] as $batch_set) { diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 13c912cf4..05a654bf1 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -2644,3 +2644,59 @@ function drupal_is_cli() { function drupal_placeholder($variables) { return '<em class="placeholder">' . check_plain($variables['text']) . '</em>'; } + +/** + * + * Register a function for execution on shutdown. + * + * Wrapper for register_shutdown_function() which catches thrown exceptions + * to avoid "Exception thrown without a stack frame in Unknown". + * + * @param $callback + * The shutdown function to register. + * @param $parameters + * It is possible to pass parameters to the shutdown function by passing + * additional parameters. + * @return + * Array of shutdown functions to be executed. + * + * @see register_shutdown_function() + */ +function &drupal_register_shutdown_function($callback = NULL, $parameters = NULL) { + // We cannot use drupal_static() here because the static cache is reset + // during batch processing, which breaks batch handling. + static $callbacks = array(); + + if (isset($callback)) { + // Only register the internal shutdown function once. + if (empty($callbacks)) { + register_shutdown_function('_drupal_shutdown_function'); + } + $args = func_get_args(); + array_shift($args); + // Save callback and arguments + $callbacks[] = array('callback' => $callback, 'arguments' => $args); + } + return $callbacks; +} + +/** + * Internal function used to execute registered shutdown functions. + */ +function _drupal_shutdown_function() { + $callbacks = &drupal_register_shutdown_function(); + + try { + while (list($key, $callback) = each($callbacks)) { + call_user_func_array($callback['callback'], $callback['arguments']); + } + } + catch(Exception $exception) { + require_once DRUPAL_ROOT . '/includes/errors.inc'; + // The theme has already been printed so it doesn't make much sense to use + // drupal_exception_handler() because that would display the maintenaince + // page below the usual page. For now, just print the error for debugging. + // @todo: Improve this. + print t('%type: %message in %function (line %line of %file).', _drupal_decode_exception($exception)); + } +} diff --git a/includes/common.inc b/includes/common.inc index f41982da9..82180255d 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -4017,7 +4017,7 @@ function _drupal_bootstrap_full() { // Load all enabled modules module_load_all(); // Register automated cron run handler. - register_shutdown_function('system_run_automated_cron'); + drupal_register_shutdown_function('system_run_automated_cron'); // Make sure all stream wrappers are registered. file_get_stream_wrappers(); if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'simpletest') !== FALSE) { @@ -4130,7 +4130,7 @@ function drupal_cron_run() { DrupalQueue::get($queue_name)->createQueue(); } // Register shutdown callback - register_shutdown_function('drupal_cron_cleanup'); + drupal_register_shutdown_function('drupal_cron_cleanup'); // Lock cron semaphore variable_set('cron_semaphore', REQUEST_TIME); diff --git a/includes/database/mysql/database.inc b/includes/database/mysql/database.inc index 39ba59e4b..46acb4dda 100644 --- a/includes/database/mysql/database.inc +++ b/includes/database/mysql/database.inc @@ -85,7 +85,7 @@ class DatabaseConnection_mysql extends DatabaseConnection { $new_id = $this->query('INSERT INTO {sequences} () VALUES ()', array(), array('return' => Database::RETURN_INSERT_ID)); } if (!$shutdown_registered) { - register_shutdown_function(array(get_class($this), 'nextIdDelete')); + drupal_register_shutdown_function(array(get_class($this), 'nextIdDelete')); $shutdown_registered = TRUE; } return $new_id; diff --git a/includes/lock.inc b/includes/lock.inc index 5d03cdd99..3b649a050 100644 --- a/includes/lock.inc +++ b/includes/lock.inc @@ -84,7 +84,7 @@ function _lock_id() { // Assign a unique id. $lock_id = uniqid(mt_rand(), TRUE); // We only register a shutdown function if a lock is used. - register_shutdown_function('lock_release_all', $lock_id); + drupal_register_shutdown_function('lock_release_all', $lock_id); } return $lock_id; } diff --git a/includes/menu.inc b/includes/menu.inc index 83d36aa7b..dedb04a10 100644 --- a/includes/menu.inc +++ b/includes/menu.inc @@ -2216,7 +2216,7 @@ function menu_cache_clear($menu_name = 'navigation') { $cache_cleared[$menu_name] = 1; } elseif ($cache_cleared[$menu_name] == 1) { - register_shutdown_function('cache_clear_all', 'links:' . $menu_name . ':', 'cache_menu', TRUE); + drupal_register_shutdown_function('cache_clear_all', 'links:' . $menu_name . ':', 'cache_menu', TRUE); $cache_cleared[$menu_name] = 2; } @@ -2798,9 +2798,9 @@ function _menu_clear_page_cache() { $cache_cleared = 1; } elseif ($cache_cleared == 1) { - register_shutdown_function('cache_clear_all'); + drupal_register_shutdown_function('cache_clear_all'); // Keep track of which menus have expanded items. - register_shutdown_function('_menu_set_expanded_menus'); + drupal_register_shutdown_function('_menu_set_expanded_menus'); $cache_cleared = 2; } } diff --git a/includes/session.inc b/includes/session.inc index 5295173e5..6f1d5a8f4 100644 --- a/includes/session.inc +++ b/includes/session.inc @@ -72,7 +72,7 @@ function _drupal_session_read($sid) { // since PHP 5.0.5. // Thus destructors can use sessions but session handler can't use objects. // So we are moving session closure before destructing objects. - register_shutdown_function('session_write_close'); + drupal_register_shutdown_function('session_write_close'); // Handle the case of first time visitors and clients that don't store // cookies (eg. web crawlers). diff --git a/modules/search/search.module b/modules/search/search.module index 4fefecc60..36a0c1820 100644 --- a/modules/search/search.module +++ b/modules/search/search.module @@ -341,7 +341,7 @@ function search_dirty($word = NULL) { function search_cron() { // We register a shutdown function to ensure that search_total is always up // to date. - register_shutdown_function('search_update_totals'); + drupal_register_shutdown_function('search_update_totals'); foreach(variable_get('search_active_modules', array('node', 'user')) as $module) { // Update word index diff --git a/modules/simpletest/tests/system_test.module b/modules/simpletest/tests/system_test.module index b37019f8c..a638b70f5 100644 --- a/modules/simpletest/tests/system_test.module +++ b/modules/simpletest/tests/system_test.module @@ -94,6 +94,13 @@ function system_test_menu() { 'type' => MENU_CALLBACK, ); + $items['system-test/shutdown-functions'] = array( + 'title' => 'Test main content duplication', + 'page callback' => 'system_test_page_shutdown_functions', + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); + return $items; } @@ -280,3 +287,29 @@ function system_test_main_content_fallback() { return t('Content to test main content fallback'); } +/** + * A simple page callback which adds a register shutdown function. + */ +function system_test_page_shutdown_functions($arg1, $arg2) { + drupal_register_shutdown_function('_system_test_first_shutdown_function', $arg1, $arg2); +} + +/** + * Dummy shutdown function which registers another shutdown function. + */ +function _system_test_first_shutdown_function($arg1, $arg2) { + // Output something, page has already been printed and the session stored + // so we can't use drupal_set_message. + print t('First shutdown function, arg1 : @arg1, arg2: @arg2', array('@arg1' => $arg1, '@arg2' => $arg2)); + drupal_register_shutdown_function('_system_test_second_shutdown_function', $arg1, $arg2); +} + +/** + * Dummy shutdown function. + */ +function _system_test_second_shutdown_function($arg1, $arg2) { + // Output something, page has already been printed and the session stored + // so we can't use drupal_set_message. + print t('Second shutdown function, arg1 : @arg1, arg2: @arg2', array('@arg1' => $arg1, '@arg2' => $arg2)); +} + diff --git a/modules/system/system.api.php b/modules/system/system.api.php index 5162b69c7..153ddca83 100644 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -877,7 +877,7 @@ function hook_forms($form_id, $args) { function hook_boot() { // we need user_access() in the shutdown function. make sure it gets loaded drupal_load('module', 'user'); - register_shutdown_function('devel_shutdown'); + drupal_register_shutdown_function('devel_shutdown'); } /** diff --git a/modules/system/system.test b/modules/system/system.test index e33ad11a8..79e141225 100644 --- a/modules/system/system.test +++ b/modules/system/system.test @@ -1598,3 +1598,31 @@ class FloodFunctionalTest extends DrupalWebTestCase { $this->assertFalse(flood_is_allowed($name, $threshold)); } } + +/** + * Functional tests shutdown functions. + */ +class ShutdownFunctionsTest extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Shutdown functions', + 'description' => 'Functional tests for shutdown functions', + 'group' => 'System', + ); + } + + function setUp() { + parent::setUp('system_test'); + } + + /** + * Test flood control mechanism clean-up. + */ + function testShutdownFunctions() { + $arg1 = $this->randomName(); + $arg2 = $this->randomName(); + $this->drupalGet('system-test/shutdown-functions/' . $arg1 . '/' . $arg2); + $this->assertText(t('First shutdown function, arg1 : @arg1, arg2: @arg2', array('@arg1' => $arg1, '@arg2' => $arg2))); + $this->assertText(t('Second shutdown function, arg1 : @arg1, arg2: @arg2', array('@arg1' => $arg1, '@arg2' => $arg2))); + } +}
\ No newline at end of file |