From 239a8a9e0abe649ae5dbfe2410fab249c69187cf Mon Sep 17 00:00:00 2001 From: Oliver Davies Date: Sun, 25 Nov 2018 19:33:41 +0000 Subject: [PATCH] Add registry_rebuild.php --- web/registry_rebuild.php | 247 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 web/registry_rebuild.php diff --git a/web/registry_rebuild.php b/web/registry_rebuild.php new file mode 100644 index 000000000..7f1fa1894 --- /dev/null +++ b/web/registry_rebuild.php @@ -0,0 +1,247 @@ +\n"; +define('MAINTENANCE_MODE', 'update'); + +global $_SERVER; +$_SERVER['REMOTE_ADDR'] = 'nothing'; + +global $include_dir; +$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'; +} + +require_once($include_dir . '/bootstrap.inc'); +drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); + +$includes = array( + $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 . '/locale.inc', +); + +if (function_exists('registry_rebuild')) { // == D7 + $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; + print "We will use relative variant of lock.inc.
\n"; + } + elseif (is_readable($cache_lock_path_absolute) && is_file($cache_lock_path_absolute)) { + $includes[] = $cache_lock_path_absolute; + print "We will use absolute variant of lock.inc.
\n"; + } + else { + print "We will use core implementation of lock.inc as fallback.
\n"; + $includes[] = DRUPAL_ROOT . '/includes/lock.inc'; + } + } + else { + $includes[] = DRUPAL_ROOT . '/includes/lock.inc'; + } +} +elseif (!function_exists('cache_clear_all')) { // D8+ + // 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); + } +} + +if (!function_exists('module_list')) { + print "ERROR! Registry Rebuild requires a working Drupal site to operate on.
\n"; + print "Please run this script from the correct Drupal site directory,
\n"; + print "or specify Drupal root path on command line, for example:
\n"; + print "php registry_rebuild.php --root=/path/to/drupal/root
\n"; + print "BYE!
\n"; + exit; +} + +print "Bootstrapping to DRUPAL_BOOTSTRAP_SESSION
\n"; +drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION); + +registry_rebuild_rebuild(); + +/** + * Find the drupal root directory by looking in parent directories. + * If unable to discover it, fail and exit. + */ +function define_drupal_root() { + $args = getopt(NULL, array('root::')); + if (!empty($args['root'])) { + return $args['root']; + } + + // This is the smallest number of directories to go up: from /sites/all/modules/registry_rebuild. + $parent_count = 4; + // 8 seems reasonably far to go looking up. + while ($parent_count < 8) { + $dir = realpath(getcwd() . str_repeat('/..', $parent_count)); + if (file_exists($dir . '/index.php')) { + return $dir; + } + $parent_count++; + } + print "Failure: Unable to discover DRUPAL_ROOT. You may want to explicitly define it near the top of registry_rebuild.php"; + exit(1); +} + +/** + * Before calling this we need to be bootstrapped to DRUPAL_BOOTSTRAP_SESSION. + */ +function registry_rebuild_rebuild() { + // 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 (function_exists('registry_rebuild')) { + $connection_info = Database::getConnectionInfo(); + $driver = $connection_info['default']['driver']; + global $include_dir; + 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 (function_exists('registry_rebuild')) { // D7 + cache_clear_all('lookup_cache', 'cache_bootstrap'); + cache_clear_all('variables', 'cache_bootstrap'); + cache_clear_all('module_implements', 'cache_bootstrap'); + print "Bootstrap caches have been cleared in DRUPAL_BOOTSTRAP_SESSION
\n"; + } + elseif (!function_exists('cache_clear_all')) { // D8+ + cache('bootstrap')->deleteAll(); + print "Bootstrap caches have been cleared in DRUPAL_BOOTSTRAP_SESSION
\n"; + } + + // 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. + if (function_exists('registry_rebuild')) { // D7 only + print "Doing registry_rebuild() in DRUPAL_BOOTSTRAP_SESSION
\n"; + registry_rebuild(); + } + elseif (!function_exists('registry_rebuild') && function_exists('system_rebuild_module_data')) { // D8+ + print "Doing system_rebuild_module_data() in DRUPAL_BOOTSTRAP_SESSION
\n"; + system_rebuild_module_data(); + } + else { // D5-D6 + print "Doing module_rebuild_cache() in DRUPAL_BOOTSTRAP_SESSION
\n"; + module_list(TRUE, FALSE); + module_rebuild_cache(); + } + + print "Bootstrapping to DRUPAL_BOOTSTRAP_FULL
\n"; + drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); + // We can run our wrapper now, since we are in a full bootstrap already. + print "Rebuilding registry via registry_rebuild_cc_all in DRUPAL_BOOTSTRAP_FULL
\n"; + registry_rebuild_cc_all(); + + // Extra cleanup available for D7 only. + if (function_exists('registry_rebuild')) { + $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(); + print("Deleted " . count($filenames) . ' stale files from registry manually.'); + } + $parsed_after = registry_get_parsed_files(); + print "There were " . count($parsed_before) . " files in the registry before and " . count($parsed_after) . " files now.
\n"; + registry_rebuild_cc_all(); + } + print "If you don't see any crazy fatal errors, your registry has been rebuilt.
\n"; +} + +/** + * 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 registry_rebuild_cc_all() { + module_invoke_all('pre_flush_all_caches'); + if (function_exists('cache_clear_all')) { + cache_clear_all('*', 'cache', TRUE); + cache_clear_all('*', 'cache_form', TRUE); + } + else { + cache('cache')->deleteAll(); + cache('cache_form')->deleteAll(); + } + + module_invoke_all('pre_flush_all_caches'); + + if (function_exists('module_rebuild_cache')) { // D5-D6 + module_list(TRUE, FALSE); + module_rebuild_cache(); + } + + if (function_exists('drupal_flush_all_caches')) { // D6+ + drupal_flush_all_caches(); + } + else { // D5 + cache_clear_all(); + system_theme_data(); + node_types_rebuild(); + menu_rebuild(); + } + print "All caches have been cleared with registry_rebuild_cc_all.
\n"; +}