diff --git a/.htaccess b/.htaccess index ce89e172cec41adec8450186d53ebffc0b61a80a..526c60be3ef6dd9dfe6ea3d8b26033d72d24fa2f 100644 --- a/.htaccess +++ b/.htaccess @@ -111,7 +111,8 @@ DirectoryIndex index.php index.html index.htm # Redirect common PHP files to their new locations. RewriteCond %{REQUEST_URI} ^(.*)?/(update.php) [OR] - RewriteCond %{REQUEST_URI} ^(.*)?/(install.php) + RewriteCond %{REQUEST_URI} ^(.*)?/(install.php) [OR] + RewriteCond %{REQUEST_URI} ^(.*)?/(rebuild.php) RewriteCond %{REQUEST_URI} !core RewriteRule ^ %1/core/%2 [L,QSA,R=301] diff --git a/core/includes/utility.inc b/core/includes/utility.inc index 5b2719d34d86b4090375c2b3339ce20ba41df475..a9002620db15a9c60e6f6ea1e7c3821576495230 100644 --- a/core/includes/utility.inc +++ b/core/includes/utility.inc @@ -6,6 +6,8 @@ */ use Drupal\Component\Utility\Variable; +use Drupal\Component\PhpStorage\PhpStorageFactory; +use Drupal\Core\Cache\Cache; /** * Drupal-friendly var_export(). @@ -23,3 +25,29 @@ function drupal_var_export($var, $prefix = '') { return Variable::export($var, $prefix); } + +/** + * Rebuilds all caches even when Drupal itself does not work. + * + * Requires DRUPAL_BOOTSTRAP_CONFIGURATION. + * + * @see rebuild.php + */ +function drupal_rebuild() { + // drupal_bootstrap(DRUPAL_BOOTSTRAP_KERNEL) will build a new kernel. This + // comes before DRUPAL_BOOTSTRAP_PAGE_CACHE. + PhpStorageFactory::get('service_container')->deleteAll(); + PhpStorageFactory::get('twig')->deleteAll(); + + // Disable the page cache. + drupal_page_is_cacheable(FALSE); + + // Bootstrap up to where caches exist and clear them. + drupal_bootstrap(DRUPAL_BOOTSTRAP_PAGE_CACHE); + foreach (Cache::getBins() as $bin) { + $bin->deleteAll(); + } + + drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); + drupal_flush_all_caches(); +} diff --git a/core/modules/system/system.install b/core/modules/system/system.install index af38deefc325e87927575b41f99d1fe94527856e..56b547e0f471e57a4a099e133a57a90a99e06555 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -518,6 +518,15 @@ function system_requirements($phase) { ); } $requirements['update status']['title'] = t('Update notifications'); + + if (settings()->get('rebuild_access')) { + $requirements['rebuild access'] = array( + 'title' => t('Rebuild access'), + 'value' => t('Enabled'), + 'severity' => REQUIREMENT_ERROR, + 'description' => t('The rebuild_access setting is enabled in settings.php. It is recommended to have this setting disabled unless you are performing a rebuild.'), + ); + } } // Ensure that if upgrading from 7 to 8 we have no disabled modules. diff --git a/core/rebuild.php b/core/rebuild.php new file mode 100644 index 0000000000000000000000000000000000000000..1b946018256820cc35760d4b0497e7a6dee651d8 --- /dev/null +++ b/core/rebuild.php @@ -0,0 +1,33 @@ +<?php + +/** + * @file + * Rebuilds all Drupal caches even when Drupal itself does not work. + * + * Needs a token query argument which can be calculated using the + * scripts/rebuild_token_calculator.sh script. + * + * @see drupal_rebuild() + */ + +use Drupal\Component\Utility\Crypt; + +// Change the directory to the Drupal root. +chdir('..'); + +require_once dirname(__DIR__) . '/core/includes/bootstrap.inc'; +require_once dirname(__DIR__) . '/core/includes/utility.inc'; + +drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); + +if (settings()->get('rebuild_access', FALSE) || + (isset($_GET['token'], $_GET['timestamp']) && + ((REQUEST_TIME - $_GET['timestamp']) < 300) && + ($_GET['token'] === Crypt::hmacBase64($_GET['timestamp'], $GLOBALS['drupal_hash_salt'])) + )) { + + drupal_rebuild(); + drupal_set_message('Cache rebuild complete.'); +} + +header('Location: ' . $GLOBALS['base_url']); diff --git a/core/scripts/rebuild_token_calculator.sh b/core/scripts/rebuild_token_calculator.sh new file mode 100644 index 0000000000000000000000000000000000000000..9b948b48ed12568c12e086d6ab31fe4e2473c828 --- /dev/null +++ b/core/scripts/rebuild_token_calculator.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env php + +<?php + +/** + * @file + * Command line token calculator for rebuild.php. + */ + +require_once __DIR__ . '/../vendor/autoload.php'; +require_once dirname(__DIR__) . '/includes/bootstrap.inc'; + +use Drupal\Component\Utility\Crypt; + +drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); + +if (!drupal_is_cli()) { + exit; +} + +$timestamp = time(); +$token = Crypt::hmacBase64($timestamp, $drupal_hash_salt); + +print "timestamp=$timestamp&token=$token\n"; diff --git a/index.php b/index.php index 426aa225bbdb3291810e6f3f49345e2aec419b76..2181fb3d8d04d2f57daee25b46ba4ec9036b24af 100644 --- a/index.php +++ b/index.php @@ -15,6 +15,11 @@ drupal_handle_request(); } catch (Exception $e) { - print 'If you have just changed code (for example deployed a new module or moved an existing one) read http://drupal.org/documentation/rebuild'; + $message = 'If you have just changed code (for example deployed a new module or moved an existing one) read <a href="http://drupal.org/documentation/rebuild">http://drupal.org/documentation/rebuild</a>'; + if (settings()->get('rebuild_access', FALSE)) { + $rebuild_path = $GLOBALS['base_url'] . '/rebuild.php'; + $message .= " or run the <a href=\"$rebuild_path\">rebuild script</a>"; + } + print $message; throw $e; } diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index 0574b23bea01a9fb4caa3da8cdc1512ecdd6fe0a..b3c54b329b8f55196379c25ecc07bc0fb6f34000 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -501,6 +501,16 @@ */ # $settings['maintenance_theme'] = 'bartik'; +/** + * Enable access to rebuild.php. + * + * This setting can be enabled to allow Drupal's php and database cached + * storage to be cleared via the rebuild.php page. Access to this page can also + * be gained by generating a query string from rebuild_token_calculator.sh and + * using these parameters in a request to rebuild.php. + */ +# $settings['rebuild_access'] = TRUE; + /** * Base URL (optional). *