'Rebuild the registry table (for classes) and the system table (for module locations) in a Drupal install.', 'callback' => 'drush_registry_rebuild', 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH, // No bootstrap. 'options' => array( 'no-cache-clear' => 'Rebuild the registry only, do not clear caches, unless --fire-bazooka is also used.', 'fire-bazooka' => 'Truncate registry and registry_file tables and build them from scratch. Forces all caches clear.', ), 'examples' => array( 'drush rr --no-cache-clear' => 'Rebuild the registry only, do not clear caches, unless --fire-bazooka is also used.', 'drush rr --fire-bazooka' => 'Truncate registry and registry_file tables and build them from scratch. Forces all caches clear.', ), 'aliases' => array('rr'), ); return $items; } /** * Rebuild the registry. * * Before calling this we need to be bootstrapped to DRUPAL_BOOTSTRAP_DATABASE. */ function drush_registry_rebuild() { define('MAINTENANCE_MODE', 'update'); ini_set('memory_limit', -1); if (!drush_bootstrap_to_phase(DRUSH_BOOTSTRAP_DRUPAL_DATABASE)) { return drush_set_error('DRUPAL_SITE_NOT_FOUND', dt('You need to specify an alias or run this command within a drupal site.')); } $include_dir = DRUPAL_ROOT . '/includes'; $module_dir = DRUPAL_ROOT . '/modules'; // Use core directory if it exists. if (file_exists(DRUPAL_ROOT . '/core/includes/bootstrap.inc')) { $include_dir = DRUPAL_ROOT . '/core/includes'; $module_dir = DRUPAL_ROOT . '/core/modules'; } $includes = array( $include_dir . '/bootstrap.inc', $include_dir . '/common.inc', $include_dir . '/database.inc', $include_dir . '/schema.inc', $include_dir . '/actions.inc', $include_dir . '/entity.inc', $module_dir . '/system/system.module', $include_dir . '/database/database.inc', $include_dir . '/database/query.inc', $include_dir . '/database/select.inc', $include_dir . '/registry.inc', $include_dir . '/module.inc', $include_dir . '/menu.inc', $include_dir . '/file.inc', $include_dir . '/theme.inc', $include_dir . '/unicode.inc', $include_dir . '/locale.inc', ); if (drush_drupal_major_version() == 7) { $cache_lock_path_absolute = variable_get('lock_inc'); if (!empty($cache_lock_path_absolute)) { $cache_lock_path_relative = DRUPAL_ROOT . '/'. variable_get('lock_inc'); // Ensure that the configured lock.inc really exists at that location and // is accessible. Otherwise we use the core lock.inc as fallback. if (is_readable($cache_lock_path_relative) && is_file($cache_lock_path_relative)) { $includes[] = $cache_lock_path_relative; drush_log(dt("We will use relative variant of lock.inc: @lock", array('@lock' => $cache_lock_path_relative))); } elseif (is_readable($cache_lock_path_absolute) && is_file($cache_lock_path_absolute)) { $includes[] = $cache_lock_path_absolute; drush_log(dt("We will use absolute variant of lock.inc: @lock", array('@lock' => $cache_lock_path_absolute))); } else { drush_log(dt('We will use core implementation of lock.inc as fallback.')); $includes[] = DRUPAL_ROOT . '/includes/lock.inc'; } } else { $includes[] = DRUPAL_ROOT . '/includes/lock.inc'; } } elseif (drush_drupal_major_version() > 7) { // TODO // http://api.drupal.org/api/drupal/namespace/Drupal!Core!Lock/8 $includes[] = $module_dir . '/entity/entity.module'; $includes[] = $module_dir . '/entity/entity.controller.inc'; } // In Drupal 6 the configured lock.inc is already loaded during // DRUSH_BOOTSTRAP_DRUPAL_DATABASE foreach ($includes as $include) { if (file_exists($include)) { require_once($include); } } // We may need to clear also Drush 5+ internal cache first. drush_log(dt("This DRUSH_MAJOR_VERSION is: @ver", array('@ver' => DRUSH_MAJOR_VERSION))); if (DRUSH_MAJOR_VERSION > 4) { drush_cache_clear_drush(); drush_log(dt('Internal Drush cache cleared with drush_cache_clear_drush (1).')); } if (!function_exists('module_list')) { drush_log(dt('ERROR! Registry Rebuild requires a working Drupal site to operate on.'), 'warning'); drush_log(dt('Please either cd to a directory containing a Drupal settings.php file,'), 'warning'); drush_log(dt('or use a working site @alias outside of Drupal root directory tree.'), 'warning'); drush_log(dt('Example for forced mode: drush @sitename rr --fire-bazooka'), 'warning'); drush_log(dt('BYE!'), 'warning'); exit; } // This section is not functionally important. It's just using the // registry_get_parsed_files() so that it can report the change. Drupal 7 only. if (drush_drupal_major_version() == 7) { $connection_info = Database::getConnectionInfo(); $driver = $connection_info['default']['driver']; require_once $include_dir . '/database/' . $driver . '/query.inc'; $parsed_before = registry_get_parsed_files(); } // Separate bootstrap cache exists only in Drupal 7 or newer. // They are cleared later again via drupal_flush_all_caches(). if (drush_drupal_major_version() == 7) { cache_clear_all('lookup_cache', 'cache_bootstrap'); cache_clear_all('variables', 'cache_bootstrap'); cache_clear_all('module_implements', 'cache_bootstrap'); drush_log(dt('Bootstrap caches have been cleared in DRUSH_BOOTSTRAP_DRUPAL_DATABASE.')); } elseif (drush_drupal_major_version() >= 8) { cache('bootstrap')->deleteAll(); drush_log(dt('Bootstrap caches have been cleared in DRUSH_BOOTSTRAP_DRUPAL_DATABASE.')); } // We later run system_rebuild_module_data() and registry_update() on Drupal 7 via // D7-only registry_rebuild() wrapper, which is run inside drupal_flush_all_caches(). // It is an equivalent of module_rebuild_cache() in D5-D6 and is normally run via // our universal wrapper registry_rebuild_cc_all() -- see further below. // However, we are still on the DRUPAL_BOOTSTRAP_SESSION level here, // and we want to make the initial rebuild as atomic as possible, so we can't // run everything from registry_rebuild_cc_all() yet, so we run an absolute // minimum we can at this stage, core specific. drush_log(dt('We are on the DRUSH_BOOTSTRAP_DRUPAL_DATABASE level still.')); if (drush_drupal_major_version() == 7) { registry_rebuild(); // D7 only drush_log(dt('The registry has been rebuilt via registry_rebuild (A).'), 'success'); bootstrap_invoke_all('registry_rebuild'); registry_rebuild(); drush_log(dt('The registry has been rebuilt via registry_rebuild (A2).'), 'success'); } elseif (drush_drupal_major_version() > 7) { system_rebuild_module_data(); // D8+ drush_log(dt('The registry has been rebuilt via system_rebuild_module_data (A).'), 'success'); } else { // D5-D6 module_list(TRUE, FALSE); module_rebuild_cache(); drush_log(dt('The registry has been rebuilt via module_rebuild_cache (A).'), 'success'); } // We may need to clear also Drush 5+ internal cache at this point again. if (DRUSH_MAJOR_VERSION > 4) { drush_cache_clear_drush(); drush_log(dt('Internal Drush cache cleared with drush_cache_clear_drush (2).')); } drush_log(dt('Bootstrapping to DRUPAL_BOOTSTRAP_FULL.')); drush_bootstrap_to_phase(DRUSH_BOOTSTRAP_DRUPAL_FULL); // We can run our wrapper now, since we are in a full bootstrap already. // Note that the wrapper normally honors the --no-cache-clear option if used. drush_registry_rebuild_cc_all(); drush_log(dt('The registry has been rebuilt via drush_registry_rebuild_cc_all (B).'), 'success'); if (drush_get_option('fire-bazooka') && drush_drupal_major_version() == 7) { $force_all_cache_clear = TRUE; if (drush_get_option('no-cache-clear')) { // Force all caches clear before rebuilding tables from scratch, // but only if --no-cache-clear is used, since otherwise // it already has been done above. drush_registry_rebuild_cc_all(); drush_log(dt('Forced pre-fire-bazooka extra all caches clear.')); } db_truncate('registry')->execute(); db_truncate('registry_file')->execute(); drush_log(dt('The registry_file and registry tables truncated with --fire-bazooka.')); // We have to force API-based rebuild integrated with cache clears // directly after truncating registry tables. drush_registry_rebuild_cc_all(); drush_log(dt('The registry has been rebuilt via drush_registry_rebuild_cc_all (C).'), 'success'); } // Extra cleanup available for D7 only. if (drush_drupal_major_version() == 7) { $parsed_after = registry_get_parsed_files(); // Remove files which don't exist anymore. $filenames = array(); foreach ($parsed_after as $filename => $file) { if (!file_exists($filename)) { $filenames[] = $filename; } } if (!empty($filenames)) { db_delete('registry_file') ->condition('filename', $filenames) ->execute(); db_delete('registry') ->condition('filename', $filenames) ->execute(); $dt_args = array( '@files' => implode(', ', $filenames), ); $singular = 'Manually deleted 1 stale file from the registry.'; $plural = 'Manually deleted @count stale files from the registry.'; drush_log(format_plural(count($filenames), $singular, $plural), 'success'); $singular = "A file has been declared in a module's .info, but could not be found. This is probably indicative of a bug. The missing file is @files."; $plural = "@count files were declared in a module's .info, but could not be found. This is probably indicative of a bug. The missing files are @files."; drush_log(format_plural(count($filenames), $singular, $plural, $dt_args), 'warning'); } $parsed_after = registry_get_parsed_files(); $message = 'There were @parsed_before files in the registry before and @parsed_after files now.'; $dt_args = array( '@parsed_before' => count($parsed_before), '@parsed_after' => count($parsed_after), ); drush_log(dt($message, $dt_args)); drush_registry_rebuild_cc_all(); } // Everything done. drush_log(dt('All registry rebuilds have been completed.'), 'success'); } /** * Registry Rebuild needs to aggressively clear all caches, * not just some bins (at least to attempt it) also *before* * attempting to rebuild registry, or it may not be able * to fix the problem at all, if it relies on some cached * and no longer valid data/paths etc. This problem has been * confirmed and reproduced many times with option --fire-bazooka * which is available only in the Drush variant, but it confirms * the importance of starting with real, raw and not cached * in any way site state. While the --no-cache-clear option * still disables this procedure, --fire-bazooka takes precedence * and forces all caches clear action. All caches are cleared * by default in the PHP script variant. */ function drush_registry_rebuild_cc_all() { module_invoke_all('pre_flush_all_caches'); if (!drush_get_option('no-cache-clear') || isset($force_all_cache_clear)) { if (function_exists('cache_clear_all') || drush_drupal_major_version() < 8) { cache_clear_all('*', 'cache', TRUE); cache_clear_all('*', 'cache_form', TRUE); } else { cache('cache')->deleteAll(); cache('cache_form')->deleteAll(); } if (function_exists('module_rebuild_cache') || (drush_drupal_major_version() >= 5 && drush_drupal_major_version() < 7)) { // D5-D6 module_list(TRUE, FALSE); module_rebuild_cache(); } if (function_exists('drupal_flush_all_caches') || drush_drupal_major_version() >= 6) { // D6+ drupal_flush_all_caches(); } else { // D5 cache_clear_all(); system_theme_data(); node_types_rebuild(); menu_rebuild(); } drush_log(dt('All caches have been cleared with drush_registry_rebuild_cc_all.'), 'success'); } else { if (drush_drupal_major_version() == 7) { registry_rebuild(); // D7 only drush_log(dt('The registry has been rebuilt via registry_rebuild (no-cache-clear).'), 'success'); } elseif (drush_drupal_major_version() > 7) { system_rebuild_module_data(); // D8+ drush_log(dt('The registry has been rebuilt via system_rebuild_module_data (no-cache-clear).'), 'success'); } else { // D5-D6 module_list(TRUE, FALSE); module_rebuild_cache(); drush_log(dt('The registry has been rebuilt via module_rebuild_cache (no-cache-clear).'), 'success'); } drush_log(dt('The Drupal caches have NOT been cleared after all registry rebuilds.'), 'info'); drush_log(dt('It is highly recommended you clear the Drupal caches as soon as possible.'), 'info'); } }