Security update for Core, with self-updated composer
[yaffs-website] / vendor / drupal / console / src / Command / Update / ExecuteCommand.php
1 <?php
2
3 /**
4  * @file
5  * Contains \Drupal\Console\Command\Update\ExecuteCommand.
6  */
7
8 namespace Drupal\Console\Command\Update;
9
10 use Symfony\Component\Console\Input\InputInterface;
11 use Symfony\Component\Console\Input\InputArgument;
12 use Symfony\Component\Console\Output\OutputInterface;
13 use Drupal\Console\Core\Command\Command;
14 use Drupal\Core\State\StateInterface;
15 use Drupal\Core\Extension\ModuleHandler;
16 use Drupal\Core\Update\UpdateRegistry;
17 use Drupal\Console\Utils\Site;
18 use Drupal\Console\Extension\Manager;
19 use Drupal\Console\Core\Utils\ChainQueue;
20
21 class ExecuteCommand extends Command
22 {
23     /**
24      * @var Site
25      */
26     protected $site;
27
28     /**
29      * @var StateInterface
30      */
31     protected $state;
32
33     /**
34      * @var ModuleHandler
35      */
36     protected $moduleHandler;
37
38     /**
39      * @var UpdateRegistry
40      */
41     protected $postUpdateRegistry;
42
43
44     /**
45      * @var Manager
46      */
47     protected $extensionManager;
48
49     /**
50      * @var ChainQueue
51      */
52     protected $chainQueue;
53
54     /**
55      * @var String
56      */
57     private $module;
58
59     /**
60      * @var String
61      */
62     private $update_n;
63
64     /**
65      * EntitiesCommand constructor.
66      *
67      * @param Site           $site
68      * @param StateInterface $state
69      * @param ModuleHandler  $moduleHandler
70      * @param UpdateRegistry $postUpdateRegistry
71      * @param Manager        $extensionManager
72      * @param ChainQueue     $chainQueue
73      */
74     public function __construct(
75         Site $site,
76         StateInterface $state,
77         ModuleHandler $moduleHandler,
78         UpdateRegistry $postUpdateRegistry,
79         Manager $extensionManager,
80         ChainQueue $chainQueue
81     ) {
82         $this->site = $site;
83         $this->state = $state;
84         $this->moduleHandler = $moduleHandler;
85         $this->postUpdateRegistry = $postUpdateRegistry;
86         $this->extensionManager = $extensionManager;
87         $this->chainQueue = $chainQueue;
88         parent::__construct();
89     }
90
91     /**
92      * @inheritdoc
93      */
94     protected function configure()
95     {
96         $this
97             ->setName('update:execute')
98             ->setDescription($this->trans('commands.update.execute.description'))
99             ->addArgument(
100                 'module',
101                 InputArgument::OPTIONAL,
102                 $this->trans('commands.common.options.module'),
103                 'all'
104             )
105             ->addArgument(
106                 'update-n',
107                 InputArgument::OPTIONAL,
108                 $this->trans('commands.update.execute.options.update-n'),
109                 '9000'
110             )
111             ->setAliases(['upex']);
112     }
113
114     /**
115      * @inheritdoc
116      */
117     protected function execute(InputInterface $input, OutputInterface $output)
118     {
119         $this->module = $input->getArgument('module');
120         $this->update_n = (int)$input->getArgument('update-n');
121
122         $this->site->loadLegacyFile('/core/includes/install.inc');
123         $this->site->loadLegacyFile('/core/includes/update.inc');
124
125         drupal_load_updates();
126         update_fix_compatibility();
127
128         $start = $this->getUpdates($this->module!=='all'?$this->module:null);
129         $updates = update_resolve_dependencies($start);
130         $dependencyMap = [];
131         foreach ($updates as $function => $update) {
132             $dependencyMap[$function] = !empty($update['reverse_paths']) ? array_keys($update['reverse_paths']) : [];
133         }
134
135         if (!$this->checkUpdates($start, $updates)) {
136             if ($this->module === 'all') {
137                 $this->getIo()->warning(
138                     sprintf(
139                         $this->trans(
140                             'commands.update.execute.messages.no-pending-updates'
141                         )
142                     )
143                 );
144             } else {
145                 $this->getIo()->warning(
146                     sprintf(
147                         $this->trans(
148                             'commands.update.execute.messages.no-module-updates'
149                         ),
150                         $this->module
151                     )
152                 );
153             }
154
155             return 0;
156         }
157
158         $maintenanceMode = $this->state->get('system.maintenance_mode', false);
159
160         if (!$maintenanceMode) {
161             $this->getIo()->info($this->trans('commands.site.maintenance.description'));
162             $this->state->set('system.maintenance_mode', true);
163         }
164
165         try {
166             $this->runUpdates(
167                 $updates
168             );
169
170             // Post Updates are only safe to run after all schemas have been updated.
171             if (!$this->getUpdates()) {
172                 $this->runPostUpdates();
173             }
174         } catch (\Exception $e) {
175             watchdog_exception('update', $e);
176             $this->getIo()->error($e->getMessage());
177             return 1;
178         }
179
180         if (!$maintenanceMode) {
181             $this->state->set('system.maintenance_mode', false);
182             $this->getIo()->info($this->trans('commands.site.maintenance.messages.maintenance-off'));
183         }
184
185         if (!$this->getUpdates()) {
186             $this->chainQueue->addCommand('update:entities');
187         }
188
189         $this->chainQueue->addCommand('cache:rebuild', ['cache' => 'all']);
190
191         return 0;
192     }
193
194     /**
195      * @param array $start
196      * @param array $updates
197      *
198      * @return bool true if the selected module/update number exists.
199      */
200     private function checkUpdates(
201         array $start,
202         array $updates
203     ) {
204         if (!$start || !$updates) {
205             return false;
206         }
207
208         if ($this->module !== 'all') {
209             $module = $this->module;
210             $hooks = array_keys($updates);
211             $hooks = array_map(
212                 function ($v) use ($module) {
213                     return (int)str_replace(
214                         $module.'_update_',
215                         '',
216                         $v
217                     );
218                 },
219                 $hooks
220             );
221
222             if ((int)min($hooks) > (int)$this->update_n) {
223                 return false;
224             }
225         }
226
227         return true;
228     }
229
230     /**
231      * @param array       $updates
232      */
233     private function runUpdates(
234         array $updates
235     ) {
236         $this->getIo()->info(
237             $this->trans('commands.update.execute.messages.executing-required-previous-updates')
238         );
239
240         foreach ($updates as $function => $update) {
241             if (!$update['allowed']) {
242                 continue;
243             }
244
245             if ($this->module !== 'all' && $update['number'] > $this->update_n) {
246                 break;
247             }
248
249             $this->getIo()->comment(
250                 sprintf(
251                     $this->trans('commands.update.execute.messages.executing-update'),
252                     $update['number'],
253                     $update['module']
254                 )
255             );
256
257             $this->moduleHandler->loadInclude($update['module'], 'install');
258
259             $this->executeUpdate(
260                 $function,
261                 $context
262             );
263
264             drupal_set_installed_schema_version(
265                 $update['module'],
266                 $update['number']
267             );
268         }
269     }
270
271     /**
272      * @return bool
273      */
274     private function runPostUpdates()
275     {
276         $postUpdates = $this->postUpdateRegistry->getPendingUpdateInformation();
277         foreach ($postUpdates as $module => $updates) {
278             foreach ($updates['pending'] as $updateName => $update) {
279                 $this->getIo()->info(
280                     sprintf(
281                         $this->trans('commands.update.execute.messages.executing-update'),
282                         $updateName,
283                         $module
284                     )
285                 );
286
287                 $function = sprintf(
288                     '%s_post_update_%s',
289                     $module,
290                     $updateName
291                 );
292                 drupal_flush_all_caches();
293                 $this->executeUpdate(
294                     $function,
295                     $context
296                 );
297                 $this->postUpdateRegistry->registerInvokedUpdates([$function]);
298             }
299         }
300
301         return true;
302     }
303
304     protected function getUpdates($module = null)
305     {
306         $start = $this->getUpdateList();
307         if ($module) {
308             if (isset($start[$module])) {
309                 $start = [
310                     $module => $start[$module]
311                 ];
312             } else {
313                 $start = [];
314             }
315         }
316
317         return $start;
318     }
319
320     // Copy of protected \Drupal\system\Controller\DbUpdateController::getModuleUpdates.
321     protected function getUpdateList()
322     {
323         $start = [];
324         $updates = update_get_update_list();
325         foreach ($updates as $module => $update) {
326             $start[$module] = $update['start'];
327         }
328
329         return $start;
330     }
331
332     private function executeUpdate($function, &$context)
333     {
334         if (!$context || !array_key_exists('sandbox', $context)) {
335             $context['sandbox'] = [];
336         }
337
338         if (function_exists($function)) {
339             $function($context['sandbox']);
340         }
341
342         return true;
343     }
344 }