Skip to content

Commit fc6205a

Browse files
Better bootstrap refactor (drush-ops#3820)
* Move state out of BootstrapManager and into boot object. Remove references to any external Drush classes from boot object; keep Drupal bootstrap state only. * Failed experiment to attempt to separate the bootstrap state from the bootstrap manager, so that we can save all state about the bootstrapped site in the bootstrap object. If this worked, then we would be able to run the Drush preflight multiple times in the integration tests, and re-inject the bootstrap object to recapture the 'state' of the Drupal bootstrap. There are two impediments to this: 1. we inject the Drupal-dependent commands as part of the bootstrap. 2. we inject a Drush logger object into Drupal. These problems might be overcome with further refactoring, but I am not going to continue with this experiment for the time being. * Describe continuation strategy * Re-inject logger and add Drupal commandfiles as needed in integration tests now that bootstrap object is cached, and preflight is done for every call to Drush. * [ci skip] Fix up comments
1 parent 9b11884 commit fc6205a

11 files changed

+233
-207
lines changed

src/Application.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ public function selectUri($cwd)
177177
if ($uri) {
178178
return $uri;
179179
}
180-
return $this->bootstrapManager()->selectUri($cwd);
180+
$uri = $this->bootstrapManager()->selectUri($cwd);
181+
return $uri;
181182
}
182183

183184
/**

src/Boot/BaseBoot.php

+24-39
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@
44

55
use Psr\Log\LoggerAwareInterface;
66
use Psr\Log\LoggerAwareTrait;
7-
use League\Container\ContainerAwareInterface;
8-
use League\Container\ContainerAwareTrait;
97

10-
abstract class BaseBoot implements Boot, LoggerAwareInterface, ContainerAwareInterface
8+
abstract class BaseBoot implements Boot, LoggerAwareInterface
119
{
1210
use LoggerAwareTrait;
13-
use ContainerAwareTrait;
1411

15-
protected $uri;
12+
protected $uri = false;
13+
protected $phase = false;
1614

1715
public function __construct()
1816
{
@@ -24,11 +22,32 @@ public function findUri($root, $uri)
2422
return 'default';
2523
}
2624

25+
public function getUri()
26+
{
27+
return $this->uri;
28+
}
29+
2730
public function setUri($uri)
2831
{
2932
$this->uri = $uri;
3033
}
3134

35+
/**
36+
* @return int
37+
*/
38+
public function getPhase()
39+
{
40+
return $this->phase;
41+
}
42+
43+
/**
44+
* @param int $phase
45+
*/
46+
public function setPhase($phase)
47+
{
48+
$this->phase = $phase;
49+
}
50+
3251
public function validRoot($path)
3352
{
3453
}
@@ -94,40 +113,6 @@ protected function hasRegisteredSymfonyCommand($application, $name)
94113
}
95114
}
96115

97-
protected function inflect($object)
98-
{
99-
// See \Drush\Runtime\DependencyInjection::addDrushServices and
100-
// \Robo\Robo\addInflectors
101-
$container = $this->getContainer();
102-
if ($object instanceof \Robo\Contract\ConfigAwareInterface) {
103-
$object->setConfig($container->get('config'));
104-
}
105-
if ($object instanceof \Psr\Log\LoggerAwareInterface) {
106-
$object->setLogger($container->get('logger'));
107-
}
108-
if ($object instanceof \League\Container\ContainerAwareInterface) {
109-
$object->setContainer($container->get('container'));
110-
}
111-
if ($object instanceof \Symfony\Component\Console\Input\InputAwareInterface) {
112-
$object->setInput($container->get('input'));
113-
}
114-
if ($object instanceof \Robo\Contract\OutputAwareInterface) {
115-
$object->setOutput($container->get('output'));
116-
}
117-
if ($object instanceof \Robo\Contract\ProgressIndicatorAwareInterface) {
118-
$object->setProgressIndicator($container->get('progressIndicator'));
119-
}
120-
if ($object instanceof \Consolidation\AnnotatedCommand\Events\CustomEventAwareInterface) {
121-
$object->setHookManager($container->get('hookManager'));
122-
}
123-
if ($object instanceof \Robo\Contract\VerbosityThresholdInterface) {
124-
$object->setOutputAdapter($container->get('outputAdapter'));
125-
}
126-
if ($object instanceof \Consolidation\SiteAlias\SiteAliasManagerAwareInterface) {
127-
$object->setSiteAliasManager($container->get('site.alias.manager'));
128-
}
129-
}
130-
131116
/**
132117
* {@inheritdoc}
133118
*/

src/Boot/BootstrapManager.php

+88-58
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,21 @@
33
namespace Drush\Boot;
44

55
use Consolidation\AnnotatedCommand\AnnotationData;
6-
use Robo\Common\ConfigAwareTrait;
76
use DrupalFinder\DrupalFinder;
87
use Drush\Log\LogLevel;
8+
use League\Container\ContainerAwareInterface;
9+
use League\Container\ContainerAwareTrait;
910
use Psr\Log\LoggerAwareInterface;
1011
use Psr\Log\LoggerAwareTrait;
12+
use Robo\Common\ConfigAwareTrait;
1113
use Robo\Contract\ConfigAwareInterface;
1214

13-
class BootstrapManager implements LoggerAwareInterface, AutoloaderAwareInterface, ConfigAwareInterface
15+
class BootstrapManager implements LoggerAwareInterface, AutoloaderAwareInterface, ConfigAwareInterface, ContainerAwareInterface
1416
{
1517
use LoggerAwareTrait;
1618
use AutoloaderAwareTrait;
1719
use ConfigAwareTrait;
20+
use ContainerAwareTrait;
1821

1922
/**
2023
* @var DrupalFinder
@@ -26,63 +29,37 @@ class BootstrapManager implements LoggerAwareInterface, AutoloaderAwareInterface
2629
*/
2730
protected $bootstrapCandidates = [];
2831

29-
/**
30-
* @var \Drush\Boot\Boot
31-
*/
32-
protected $defaultBootstrapObject;
33-
3432
/**
3533
* @var \Drush\Boot\Boot
3634
*/
3735
protected $bootstrap;
3836

39-
/**
40-
* @var string
41-
*/
42-
protected $root;
43-
44-
/**
45-
* @var string
46-
*/
47-
protected $uri;
48-
4937
/**
5038
* @var int
5139
*/
5240
protected $phase;
5341

54-
/**
55-
* Constructor.
56-
*
57-
* @param \Drush\Boot\Boot
58-
* The default bootstrap object to use when there are
59-
* no viable candidates to use (e.g. no selected site)
60-
*/
61-
public function __construct(Boot $default)
62-
{
63-
$this->defaultBootstrapObject = $default;
64-
65-
// Reset our bootstrap phase to the beginning
66-
$this->setPhase(DRUSH_BOOTSTRAP_NONE);
67-
}
68-
6942
/**
7043
* @return int
7144
*/
7245
public function getPhase()
7346
{
74-
return $this->phase;
47+
if (!$this->hasBootstrap()) {
48+
return DRUSH_BOOTSTRAP_NONE;
49+
}
50+
return $this->bootstrap()->getPhase();
7551
}
7652

7753
/**
7854
* @param int $phase
7955
*/
80-
public function setPhase($phase)
56+
protected function setPhase($phase)
8157
{
82-
$this->phase = $phase;
58+
if ($this->bootstrap) {
59+
$this->bootstrap()->setPhase($phase);
60+
}
8361
}
8462

85-
8663
/**
8764
* Add a bootstrap object to the list of candidates.
8865
*
@@ -140,7 +117,10 @@ public function locateRoot($root, $start_path = null)
140117
*/
141118
public function getUri()
142119
{
143-
return $this->uri;
120+
if (!$this->hasBootstrap()) {
121+
return false;
122+
}
123+
return $this->bootstrap()->getUri();
144124
}
145125

146126
/**
@@ -158,10 +138,7 @@ public function setUri($uri)
158138
{
159139
// TODO: Throw if we already bootstrapped a framework?
160140
// n.b. site-install needs to set the uri.
161-
$this->uri = $uri;
162-
if ($this->bootstrap) {
163-
$this->bootstrap->setUri($this->getUri());
164-
}
141+
$this->bootstrap()->setUri($uri);
165142
}
166143

167144
/**
@@ -174,10 +151,23 @@ public function setUri($uri)
174151
*/
175152
public function bootstrap()
176153
{
177-
if ($this->bootstrap) {
178-
return $this->bootstrap;
154+
if (!$this->bootstrap) {
155+
$this->bootstrap = $this->selectBootstrapClass();
179156
}
180-
return $this->selectBootstrapClass();
157+
return $this->bootstrap;
158+
}
159+
160+
/**
161+
* For use in testing
162+
*/
163+
public function injectBootstrap($bootstrap)
164+
{
165+
$this->inflect($bootstrap);
166+
$this->bootstrap = $bootstrap;
167+
168+
// Our bootstrap object is always a DrupalBoot8.
169+
// TODO: make an API in the Boot interface to call.
170+
$bootstrap->addDrupalModuleDrushCommands($this);
181171
}
182172

183173
/**
@@ -195,7 +185,6 @@ public function bootstrapObjectForRoot($path)
195185
if ($candidate instanceof AutoloaderAwareInterface) {
196186
$candidate->setAutoloader($this->autoloader());
197187
}
198-
$candidate->setUri($this->getUri());
199188
return $candidate;
200189
}
201190
}
@@ -214,16 +203,7 @@ protected function selectBootstrapClass()
214203
{
215204
// Once we have selected a Drupal root, we will reduce our bootstrap
216205
// candidates down to just the one used to select this site root.
217-
$bootstrap = $this->bootstrapObjectForRoot($this->getRoot());
218-
// If we have not found a bootstrap class by this point,
219-
// then return our default bootstrap object. The default bootstrap object
220-
// should pass through all calls without doing anything that
221-
// changes state in a CMS-specific way.
222-
if ($bootstrap == null) {
223-
$bootstrap = $this->defaultBootstrapObject;
224-
}
225-
226-
return $bootstrap;
206+
return $this->bootstrapObjectForRoot($this->getRoot());
227207
}
228208

229209
/**
@@ -310,15 +290,23 @@ public function doBootstrap($phase, $phase_max = false, AnnotationData $annotati
310290
if ($result = $this->bootstrapValidate($phase_index)) {
311291
if (method_exists($bootstrap, $current_phase)) {
312292
$this->logger->log(LogLevel::BOOTSTRAP, 'Drush bootstrap phase: {function}()', ['function' => $current_phase]);
313-
$bootstrap->{$current_phase}($annotationData);
293+
$bootstrap->{$current_phase}($this, $annotationData);
314294
}
315-
$this->setPhase($phase_index);
295+
$bootstrap->setPhase($phase_index);
316296
}
317297
}
318298
}
319299
return true;
320300
}
321301

302+
/**
303+
* hasBootstrap determines whether the manager has a bootstrap object yet.
304+
*/
305+
public function hasBootstrap()
306+
{
307+
return $this->bootstrap != null;
308+
}
309+
322310
/**
323311
* Determine whether a given bootstrap phase has been completed.
324312
*
@@ -367,7 +355,7 @@ public function bootstrapValidate($phase)
367355
if ($phase_index > $validated_phase) {
368356
$current_phase .= 'Validate';
369357
if (method_exists($bootstrap, $current_phase)) {
370-
$result_cache[$phase_index] = $bootstrap->{$current_phase}();
358+
$result_cache[$phase_index] = $bootstrap->{$current_phase}($this);
371359
} else {
372360
$result_cache[$phase_index] = true;
373361
}
@@ -515,4 +503,46 @@ public function bootstrapMax($max_phase_index = false, AnnotationData $annotatio
515503

516504
return $this->getPhase();
517505
}
506+
507+
/**
508+
* Allow those with an instance to us to the BootstrapManager to use its logger
509+
*/
510+
public function logger()
511+
{
512+
return $this->logger;
513+
}
514+
515+
public function inflect($object)
516+
{
517+
// See \Drush\Runtime\DependencyInjection::addDrushServices and
518+
// \Robo\Robo\addInflectors
519+
$container = $this->getContainer();
520+
if ($object instanceof \Robo\Contract\ConfigAwareInterface) {
521+
$object->setConfig($container->get('config'));
522+
}
523+
if ($object instanceof \Psr\Log\LoggerAwareInterface) {
524+
$object->setLogger($container->get('logger'));
525+
}
526+
if ($object instanceof \League\Container\ContainerAwareInterface) {
527+
$object->setContainer($container->get('container'));
528+
}
529+
if ($object instanceof \Symfony\Component\Console\Input\InputAwareInterface) {
530+
$object->setInput($container->get('input'));
531+
}
532+
if ($object instanceof \Robo\Contract\OutputAwareInterface) {
533+
$object->setOutput($container->get('output'));
534+
}
535+
if ($object instanceof \Robo\Contract\ProgressIndicatorAwareInterface) {
536+
$object->setProgressIndicator($container->get('progressIndicator'));
537+
}
538+
if ($object instanceof \Consolidation\AnnotatedCommand\Events\CustomEventAwareInterface) {
539+
$object->setHookManager($container->get('hookManager'));
540+
}
541+
if ($object instanceof \Robo\Contract\VerbosityThresholdInterface) {
542+
$object->setOutputAdapter($container->get('outputAdapter'));
543+
}
544+
if ($object instanceof \Consolidation\SiteAlias\SiteAliasManagerAwareInterface) {
545+
$object->setSiteAliasManager($container->get('site.alias.manager'));
546+
}
547+
}
518548
}

0 commit comments

Comments
 (0)