Skip to content

Commit e3f5c7f

Browse files
committed
Added replacement Drush cache:rebuild command to correctly handle app root so contrib modules don’t vanish.
1 parent 251353f commit e3f5c7f

File tree

2 files changed

+120
-6
lines changed

2 files changed

+120
-6
lines changed

README.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,6 @@ Once the installation is complete, you can install Drupal as normal, either with
2727

2828
## Limitations
2929

30-
Most Drush commands work, but `drush cr` does *not*. To rebuild the Drupal
31-
container and clear caches, install Devel module and use its menu items instead.
32-
33-
A fix is being worked on: see
34-
https://www.drupal.org/project/drupal/issues/1792310.
35-
3630
During some Composer commands you may see multiple copies of this error message:
3731

3832
> Could not scan for classes inside [Drupal class filename].
@@ -147,6 +141,9 @@ Several workarounds are necessary to make Drupal core work correctly when
147141
symlinked into the project. These are all taken care of by Composer scripts
148142
during installation. Details are below.
149143

144+
Most if not all of these will no longer be needed once
145+
https://www.drupal.org/project/drupal/issues/1792310 is fixed.
146+
150147
#### Vendor folder
151148

152149
The vendor folder has to be symlinked into the Drupal core repository, because
@@ -176,6 +173,15 @@ cd web && patch -p1 <../scaffold/scaffold-patch-update-php.patch
176173

177174
See https://www.drupal.org/project/drupal/issues/3188703 for more detail.
178175

176+
#### Drush rebuild command
177+
178+
The Drush cache:rebuild command does not work correctly if contrib modules are
179+
present, because it calls drupal_rebuild() which lets DrupalKernel guess the
180+
app root incorrectly.
181+
182+
This project template contains a /drush folder which has a command class which
183+
replaces that command with custom code to correctly handle the app root.
184+
179185
#### Simpletest folder
180186

181187
When running browser tests, the initial setup of Drupal in
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<?php
2+
3+
namespace Drush\Commands\core_development;
4+
5+
use Consolidation\AnnotatedCommand\AnnotationData;
6+
use Drupal\Core\DrupalKernel;
7+
use Drupal\Core\Site\Settings;
8+
use Drush\Commands\core\CacheCommands;
9+
use Drush\Drush;
10+
use Symfony\Component\Console\Command\Command;
11+
use Symfony\Component\Console\Input\ArgvInput;
12+
use Symfony\Component\Console\Input\InputInterface;
13+
use Symfony\Component\HttpFoundation\Request;
14+
15+
/**
16+
* Replaces the cache:rebuild command to correctly work with symlinked Drupal.
17+
*
18+
* This prevents contrib modules from vanishing when the cache:rebuild command
19+
* is run.
20+
*
21+
* For maintainability, changes from the original are marked 'CHANGE'.
22+
*/
23+
class DevelopmentProjectCommands extends CacheCommands {
24+
25+
/**
26+
* @hook replace-command cache:rebuild
27+
*/
28+
public function rebuild($options = ['cache-clear' => true]) {
29+
if (!$options['cache-clear']) {
30+
$this->logger()->info(dt("Skipping cache-clear operation due to --no-cache-clear option."));
31+
return true;
32+
}
33+
34+
// CHANGE: Get the app root ourselves instead of using DRUPAL_ROOT.
35+
$app_root = $this->getAppRoot();
36+
chdir($this->getAppRoot());
37+
38+
// We no longer clear APC and similar caches as they are useless on CLI.
39+
// See https://github.com/drush-ops/drush/pull/2450
40+
41+
$autoloader = $this->loadDrupalAutoloader(DRUPAL_ROOT);
42+
require_once DRUSH_DRUPAL_CORE . '/includes/utility.inc';
43+
44+
$request = Drush::bootstrap()->getRequest();
45+
DrupalKernel::bootEnvironment();
46+
47+
// Avoid 'Only variables should be passed by reference'
48+
// CHANGE: Don't use DRUPAL_ROOT.
49+
$root = $app_root;
50+
$site_path = DrupalKernel::findSitePath($request);
51+
Settings::initialize($root, $site_path, $autoloader);
52+
53+
// drupal_rebuild() calls drupal_flush_all_caches() itself, so we don't do it manually.
54+
// CHANGE: call our own version of drupal_rebuild().
55+
$this->drupal_rebuild($autoloader, $request);
56+
$this->logger()->success(dt('Cache rebuild complete.'));
57+
}
58+
59+
/**
60+
* Replacement for drupal_rebuild().
61+
*
62+
* This passes the app root to DrupalKernel.
63+
*/
64+
function drupal_rebuild($class_loader, Request $request) {
65+
// Remove Drupal's error and exception handlers; they rely on a working
66+
// service container and other subsystems and will only cause a fatal error
67+
// that hides the actual error.
68+
restore_error_handler();
69+
restore_exception_handler();
70+
71+
// Invalidate the container.
72+
// Bootstrap up to where caches exist and clear them.
73+
// CHANGE: Pass the correct app root to DrupalKernel.
74+
$kernel = new DrupalKernel('prod', $class_loader, TRUE, $this->getAppRoot());
75+
$kernel->setSitePath(DrupalKernel::findSitePath($request));
76+
$kernel->invalidateContainer();
77+
$kernel->boot();
78+
$kernel->preHandle($request);
79+
// Ensure our request includes the session if appropriate.
80+
if (PHP_SAPI !== 'cli') {
81+
$request->setSession($kernel->getContainer()->get('session'));
82+
}
83+
84+
drupal_flush_all_caches($kernel);
85+
86+
// Disable recording of cached pages.
87+
\Drupal::service('page_cache_kill_switch')->trigger();
88+
89+
// Restore Drupal's error and exception handlers.
90+
// @see \Drupal\Core\DrupalKernel::boot()
91+
set_error_handler('_drupal_error_handler');
92+
set_exception_handler('_drupal_exception_handler');
93+
}
94+
95+
/**
96+
* Gets the app root.
97+
*
98+
* @return string
99+
* The app root.
100+
*/
101+
protected function getAppRoot(): string {
102+
// This core belongs to the project template, so we can hardcode the
103+
// location of this file relative to the project root, and the scaffold
104+
// location defined in the project's composer.json.
105+
return dirname(__DIR__, 3) . '/web';
106+
}
107+
108+
}

0 commit comments

Comments
 (0)