4bfa437a5951484da30b24ea0eb625a8497b0bab
[yaffs-website] / vendor / drush / drush / src / Drupal / DrupalKernelTrait.php
1 <?php
2
3 namespace Drush\Drupal;
4
5 use Composer\Semver\Semver;
6 use Drupal\Core\DependencyInjection\ServiceModifierInterface;
7 use Drupal\Core\Site\Settings;
8 use Drush\Drush;
9 use Drush\Log\LogLevel;
10
11 /**
12  * Common functionality for overridden kernels.
13  */
14 trait DrupalKernelTrait
15 {
16     /** @var ServiceModifierInterface[] */
17     protected $serviceModifiers = [];
18
19     /**
20      * Add a service modifier to the container builder.
21      *
22      * The container is not compiled until $kernel->boot(), so there is a chance
23      * for clients to add compiler passes et. al. before then.
24      */
25     public function addServiceModifier(ServiceModifierInterface $serviceModifier)
26     {
27         drush_log(dt("Add service modifier"), LogLevel::DEBUG);
28         $this->serviceModifiers[] = $serviceModifier;
29     }
30
31     /**
32      * @inheritdoc
33      */
34     protected function getContainerBuilder()
35     {
36         drush_log(dt("Get container builder"), LogLevel::DEBUG);
37         $container = parent::getContainerBuilder();
38         foreach ($this->serviceModifiers as $serviceModifier) {
39             $serviceModifier->alter($container);
40         }
41         return $container;
42     }
43
44     /**
45      * Initializes the service container.
46      *
47      * @return \Symfony\Component\DependencyInjection\ContainerInterface
48      */
49     protected function initializeContainer()
50     {
51         $container_definition = $this->getCachedContainerDefinition();
52
53         if ($this->shouldDrushInvalidateContainer()) {
54             // Normally when the container is being rebuilt, the existing
55             // container is still available for use until the newly built one
56             // replaces it. Certain contrib modules rely on services (like State
57             // or the config factory) being available for things like defining
58             // event subscriptions.
59             // @see https://github.com/drush-ops/drush/issues/3123
60             if (isset($container_definition)) {
61                 $class = Settings::get('container_base_class', '\Drupal\Core\DependencyInjection\Container');
62                 $container = new $class($container_definition);
63                 $this->attachSynthetic($container);
64                 \Drupal::setContainer($container);
65             }
66
67             $this->invalidateContainer();
68         }
69         return parent::initializeContainer();
70     }
71
72     protected function shouldDrushInvalidateContainer()
73     {
74         if (empty($this->moduleList) && !$this->containerNeedsRebuild) {
75             $container_definition = $this->getCachedContainerDefinition();
76             foreach ($this->serviceModifiers as $serviceModifier) {
77                 if (!$serviceModifier->check($container_definition)) {
78                     return true;
79                 }
80             }
81         }
82         return false;
83     }
84
85     /**
86      * {@inheritdoc}
87      */
88     public function discoverServiceProviders()
89     {
90         // Let Drupal discover all of its service providers
91         parent::discoverServiceProviders();
92
93         // Add those Drush service providers from Drush core that
94         // need references to the Drupal DI container. This includes
95         // Drush commands, and those services needed by those Drush
96         // commands.
97         //
98         // Note that:
99         //  - We list all of the individual service files we use here.
100         //  - These commands are not available until Drupal is bootstrapped.
101         $this->addDrushServiceProvider("_drush__config", DRUSH_BASE_PATH . '/src/Drupal/Commands/config/drush.services.yml');
102         $this->addDrushServiceProvider("_drush__core", DRUSH_BASE_PATH . '/src/Drupal/Commands/core/drush.services.yml');
103         $this->addDrushServiceProvider("_drush__pm", DRUSH_BASE_PATH . '/src/Drupal/Commands/pm/drush.services.yml');
104         $this->addDrushServiceProvider("_drush__sql", DRUSH_BASE_PATH . '/src/Drupal/Commands/sql/drush.services.yml');
105
106         // TODO: We could potentially also add service providers from:
107         //  - DRUSH_BASE_PATH . '/drush/drush.services.yml');
108         //  - DRUSH_BASE_PATH . '/../drush/drush.services.yml');
109         // Or, perhaps better yet, from every Drush command directory
110         // (e.g. DRUSH_BASE_PATH/drush/mycmd/drush.services.yml) in
111         // any of these `drush` folders. In order to do this, it is
112         // necessary that the class files in these commands are available
113         // in the autoloader.
114
115         // Also add Drush services from all modules
116         $module_filenames = $this->getModuleFileNames();
117         // Load each module's serviceProvider class.
118         foreach ($module_filenames as $module => $filename) {
119             $this->addModuleDrushServiceProvider($module, $filename);
120         }
121     }
122
123     /**
124      * Determine whether or not the Drush services.yml file is applicable
125      * for this version of Drush.
126      */
127     protected function addModuleDrushServiceProvider($module, $filename)
128     {
129         $serviceYmlPath = $this->findModuleDrushServiceProvider($module, dirname($filename));
130         $this->addDrushServiceProvider("_drush.$module", $serviceYmlPath);
131     }
132
133     protected function findModuleDrushServiceProvider($module, $dir)
134     {
135         $services = $this->findModuleDrushServiceProviderFromComposer($dir);
136         if (!$services) {
137             return $this->findDefaultServicesFile($module, $dir);
138         }
139         return $this->findAppropriateServicesFile($module, $services, $dir);
140     }
141
142     protected function findDefaultServicesFile($module, $dir)
143     {
144         $result = $dir . "/drush.services.yml";
145         if (!file_exists($result)) {
146             return;
147         }
148         drush_log(dt("!module should have an extra.drush.services section in its composer.json. See http://docs.drush.org/en/master/commands/#specifying-the-services-file.", ['!module' => $module]), LogLevel::DEBUG);
149         return $result;
150     }
151
152     /**
153      * In composer.json, the Drush version constraints will appear
154      * in the 'extra' section like so:
155      *
156      *   "extra": {
157      *     "drush": {
158      *       "services": {
159      *         "drush.services.yml": "^9"
160      *       }
161      *     }
162      *   }
163      *
164      * There may be multiple drush service files listed; the first
165      * one that has a version constraint that matches the Drush version
166      * is used.
167      */
168     protected function findModuleDrushServiceProviderFromComposer($dir)
169     {
170         $composerJsonPath = "$dir/composer.json";
171         if (!file_exists($composerJsonPath)) {
172             return false;
173         }
174         $composerJsonContents = file_get_contents($composerJsonPath);
175         $info = json_decode($composerJsonContents, true);
176         if (!$info) {
177             drush_log(dt('Invalid json in {composer}', ['composer' => $composerJsonPath]), LogLevel::WARNING);
178             return false;
179         }
180         if (!isset($info['extra']['drush']['services'])) {
181             return false;
182         }
183         return $info['extra']['drush']['services'];
184     }
185
186     protected function findAppropriateServicesFile($module, $services, $dir)
187     {
188         $version = Drush::getVersion();
189         foreach ($services as $serviceYmlPath => $versionConstraint) {
190             $version = preg_replace('#-dev.*#', '', $version);
191             if (Semver::satisfies($version, $versionConstraint)) {
192                 drush_log(dt('Found {services} for {module} Drush commands', ['module' => $module, 'services' => $serviceYmlPath]), LogLevel::DEBUG);
193                 return $dir . '/' . $serviceYmlPath;
194             }
195         }
196         drush_log(dt('{module} has Drush commands, but none of {constraints} match the current Drush version "{version}"', ['module' => $module, 'constraints' => implode(',', $services), 'version' => $version]), LogLevel::DEBUG);
197         return false;
198     }
199
200     /**
201      * Add a services.yml file if it exists.
202      */
203     protected function addDrushServiceProvider($serviceProviderName, $serviceYmlPath)
204     {
205         if (file_exists($serviceYmlPath)) {
206             $this->serviceYamls['app'][$serviceProviderName] = $serviceYmlPath;
207         }
208     }
209 }