1 | <?php |
||
2 | |||
3 | namespace Kaliop\eZMigrationBundle\Command; |
||
4 | |||
5 | use Kaliop\eZMigrationBundle\API\Exception\AfterMigrationExecutionException; |
||
6 | use Kaliop\eZMigrationBundle\API\Value\Migration; |
||
7 | use Kaliop\eZMigrationBundle\API\Value\MigrationDefinition; |
||
8 | use Kaliop\eZMigrationBundle\Core\Helper\ProcessManager; |
||
9 | use Kaliop\eZMigrationBundle\Core\Process\Process; |
||
10 | use Kaliop\eZMigrationBundle\Core\Process\ProcessBuilder; |
||
11 | use Symfony\Component\Console\Input\ArrayInput; |
||
12 | use Symfony\Component\Console\Input\InputOption; |
||
13 | use Symfony\Component\Console\Input\InputInterface; |
||
14 | use Symfony\Component\Console\Output\Output; |
||
15 | use Symfony\Component\Console\Output\OutputInterface; |
||
16 | use Symfony\Component\Process\PhpExecutableFinder; |
||
17 | |||
18 | class MassMigrateCommand extends MigrateCommand |
||
19 | { |
||
20 | const COMMAND_NAME = 'kaliop:migration:mass_migrate'; |
||
21 | |||
22 | // Note: in this array, we lump together in STATUS_DONE everything which is not failed or suspended |
||
23 | protected $migrationsDone = array(Migration::STATUS_DONE => 0, Migration::STATUS_FAILED => 0, Migration::STATUS_SKIPPED => 0); |
||
24 | protected $migrationsAlreadyDone = array(); |
||
25 | |||
26 | /** |
||
27 | * @todo (!important) can we rename the option --separate-process ? |
||
28 | */ |
||
29 | protected function configure() |
||
30 | 148 | { |
|
31 | parent::configure(); |
||
32 | 148 | ||
33 | $this |
||
34 | ->setName(self::COMMAND_NAME) |
||
35 | 148 | ->setAliases(array()) |
|
36 | 148 | ->setDescription('Executes available migration definitions, using parallelism.') |
|
37 | 148 | ->addOption('concurrency', 'r', InputOption::VALUE_REQUIRED, "The number of executors to run in parallel", 2) |
|
38 | 148 | ->setHelp(<<<EOT |
|
39 | 148 | This command is designed to scan recursively a directory for migration files and execute them all in parallel. |
|
40 | 148 | One child process will be spawned for each subdirectory found. |
|
41 | The maximum number of processes to run in parallel is specified via the 'concurrency' option. |
||
42 | <info>NB: this command does not guarantee that any given migration will be executed before another. Take care about dependencies.</info> |
||
43 | <info>NB: the rule that each migration filename has to be unique still applies, even if migrations are spread across different directories.</info> |
||
44 | Unlike for the 'normal' migration command, it is not recommended to use the <info>--separate-process</info> option, as it will make execution slower if you have many migrations |
||
45 | EOT |
||
46 | ) |
||
47 | ; |
||
48 | } |
||
49 | 148 | ||
50 | /** |
||
51 | * Execute the command. |
||
52 | * |
||
53 | * @param InputInterface $input |
||
54 | * @param OutputInterface $output |
||
55 | * @return null|int null or 0 if everything went fine, or an error code |
||
56 | */ |
||
57 | protected function execute(InputInterface $input, OutputInterface $output) |
||
58 | { |
||
59 | $start = microtime(true); |
||
60 | |||
61 | $this->setOutput($output); |
||
62 | $this->setVerbosity($output->getVerbosity()); |
||
63 | |||
64 | $isChild = $input->getOption('child'); |
||
65 | |||
66 | if ($isChild && $output->getVerbosity() <= OutputInterface::VERBOSITY_NORMAL) { |
||
67 | $this->setVerbosity(self::$VERBOSITY_CHILD); |
||
68 | } |
||
69 | |||
70 | $this->getContainer()->get('ez_migration_bundle.step_executed_listener.tracing')->setOutput($output); |
||
71 | |||
72 | // q: is it worth declaring a new, dedicated migration service ? |
||
73 | $migrationService = $this->getMigrationService(); |
||
74 | $migrationService->setLoader($this->getContainer()->get('ez_migration_bundle.loader.filesystem_recursive')); |
||
75 | |||
76 | $force = $input->getOption('force'); |
||
77 | |||
78 | $toExecute = $this->buildMigrationsList($this->normalizePaths($input->getOption('path')), $migrationService, $force, $isChild); |
||
79 | |||
80 | if (!count($toExecute)) { |
||
81 | $this->writeln('<info>No migrations to execute</info>'); |
||
82 | return 0; |
||
83 | } |
||
84 | |||
85 | if ($isChild) { |
||
86 | return $this->executeAsChild($input, $output, $toExecute, $force, $migrationService); |
||
87 | } else { |
||
88 | return $this->executeAsParent($input, $output, $toExecute, $start); |
||
89 | } |
||
90 | } |
||
91 | |||
92 | /** |
||
93 | * @param InputInterface $input |
||
94 | * @param OutputInterface $output |
||
95 | * @param MigrationDefinition[] $toExecute |
||
96 | * @param float $start |
||
97 | * @return int |
||
98 | */ |
||
99 | protected function executeAsParent($input, $output, $toExecute, $start) |
||
100 | { |
||
101 | $paths = $this->groupMigrationsByPath($toExecute); |
||
102 | $this->printMigrationsList($toExecute, $input, $output, $paths); |
||
103 | |||
104 | // ask user for confirmation to make changes |
||
105 | if (!$this->askForConfirmation($input, $output, null)) { |
||
106 | return 0; |
||
107 | } |
||
108 | |||
109 | // For cli scripts, this means: do not die if anyone yanks out our stdout. |
||
110 | // We presume that users who want to halt migrations do send us a KILL signal, and that a lost tty is |
||
111 | // generally a mistake, and that carrying on with executing migrations is the best outcome |
||
112 | if ($input->getOption('survive-disconnected-tty')) { |
||
113 | ignore_user_abort(true); |
||
114 | } |
||
115 | |||
116 | $concurrency = $input->getOption('concurrency'); |
||
117 | $this->writeln("Executing migrations using " . count($paths) . " processes with a concurrency of $concurrency"); |
||
118 | |||
119 | // Allow forcing handling of sigchild. Useful on eg. Debian and Ubuntu |
||
120 | if ($input->getOption('force-sigchild-enabled')) { |
||
121 | Process::forceSigchildEnabled(true); |
||
122 | } |
||
123 | |||
124 | $builder = new ProcessBuilder(); |
||
125 | $executableFinder = new PhpExecutableFinder(); |
||
126 | if (false !== ($php = $executableFinder->find())) { |
||
127 | $builder->setPrefix($php); |
||
128 | } |
||
129 | |||
130 | // mandatory args and options |
||
131 | $builderArgs = $this->createChildProcessArgs($input); |
||
132 | |||
133 | $processes = array(); |
||
134 | /** @var MigrationDefinition $migrationDefinition */ |
||
135 | foreach ($paths as $path => $count) { |
||
136 | $this->writeln("<info>Queueing processing of: $path ($count migrations)</info>", OutputInterface::VERBOSITY_VERBOSE); |
||
137 | |||
138 | $process = $builder |
||
139 | ->setArguments(array_merge($builderArgs, array('--path=' . $path))) |
||
140 | ->getProcess(); |
||
141 | |||
142 | $this->writeln('<info>Command: ' . $process->getCommandLine() . '</info>', OutputInterface::VERBOSITY_VERBOSE); |
||
143 | |||
144 | // allow long migrations processes by default |
||
145 | $process->setTimeout($this->subProcessTimeout); |
||
146 | $processes[] = $process; |
||
147 | } |
||
148 | |||
149 | $this->writeln("<info>Starting queued processes...</info>"); |
||
150 | |||
151 | $total = count($toExecute); |
||
152 | $this->migrationsDone = array(Migration::STATUS_DONE => 0, Migration::STATUS_FAILED => 0, Migration::STATUS_SKIPPED => 0); |
||
153 | |||
154 | $processManager = new ProcessManager(); |
||
155 | $processManager->runParallel($processes, $concurrency, 500, array($this, 'onChildProcessOutput')); |
||
156 | |||
157 | $subprocessesFailed = 0; |
||
158 | foreach ($processes as $i => $process) { |
||
159 | if (!$process->isSuccessful()) { |
||
160 | $errorOutput = $process->getErrorOutput(); |
||
161 | if ($errorOutput === '') { |
||
162 | $errorOutput = "(process used to execute migrations failed with no stderr output. Its exit code was: " . $process->getExitCode(); |
||
163 | // We go out of our way to help the user finding the cause of the error. |
||
164 | /// @todo another cause we might check for in case of an empty $errorOutput is when error_reporting |
||
165 | /// does not include fatal errors (E_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR?) |
||
166 | $errorLog = ini_get("error_log"); |
||
167 | if ($errorLog != '' && $errorLog != 'syslog') { |
||
168 | $errorOutput .= ". Error details might be in file $errorLog"; |
||
169 | } |
||
170 | if ($process->getExitCode() == -1) { |
||
171 | $errorOutput .= ". If you are using Debian or Ubuntu linux, please consider using the --force-sigchild-enabled option."; |
||
172 | } |
||
173 | $errorOutput .= ")"; |
||
174 | } |
||
175 | /// @todo should we always add the exit code, even when $errorOutput is not null ? |
||
176 | $this->writeErrorln("\n<error>Subprocess $i failed! Reason: " . $errorOutput . "</error>\n"); |
||
177 | $subprocessesFailed++; |
||
178 | } |
||
179 | } |
||
180 | |||
181 | if ($input->getOption('clear-cache')) { |
||
182 | /// @see the comment in the parent class about the problems tied to clearing Sf cache in-process |
||
183 | $command = $this->getApplication()->find('cache:clear'); |
||
184 | $inputArray = new ArrayInput(array('command' => 'cache:clear')); |
||
185 | $command->run($inputArray, $output); |
||
186 | } |
||
187 | |||
188 | $missed = $total - $this->migrationsDone[Migration::STATUS_DONE] - $this->migrationsDone[Migration::STATUS_FAILED] - $this->migrationsDone[Migration::STATUS_SKIPPED]; |
||
189 | $this->writeln("\nExecuted ".$this->migrationsDone[Migration::STATUS_DONE].' migrations'. |
||
190 | ', failed '.$this->migrationsDone[Migration::STATUS_FAILED]. |
||
191 | ', skipped '.$this->migrationsDone[Migration::STATUS_SKIPPED]. |
||
192 | ($missed ? ", missed $missed" : '')); |
||
193 | |||
194 | $time = microtime(true) - $start; |
||
195 | // since we use subprocesses, we can not measure max memory used |
||
196 | $this->writeln("<info>Time taken: ".sprintf('%.3f', $time)." secs</info>"); |
||
197 | |||
198 | return $subprocessesFailed + $this->migrationsDone[Migration::STATUS_FAILED] + $missed; |
||
199 | } |
||
200 | |||
201 | /** |
||
202 | * @param InputInterface $input |
||
203 | * @param OutputInterface $output |
||
204 | * @param MigrationDefinition[] $toExecute |
||
205 | * @param bool $force |
||
206 | * @param $migrationService |
||
207 | * @return int |
||
208 | * @todo does it make sense to honour the `survive-disconnected-tty` flag when executing as child? |
||
209 | */ |
||
210 | protected function executeAsChild($input, $output, $toExecute, $force, $migrationService) |
||
0 ignored issues
–
show
|
|||
211 | { |
||
212 | // @todo disable signal slots that are harmful during migrations, if any |
||
213 | |||
214 | if ($input->getOption('separate-process')) { |
||
215 | $builder = new ProcessBuilder(); |
||
216 | $executableFinder = new PhpExecutableFinder(); |
||
217 | if (false !== $php = $executableFinder->find()) { |
||
218 | $prefix = array($php); |
||
219 | |||
220 | if ($input->getOption('child-process-php-ini-config')) { |
||
221 | foreach ($input->getOption('child-process-php-ini-config') as $iniSpec) { |
||
222 | $ini = explode(':', $iniSpec, 2); |
||
223 | if (count($ini) < 2 || $ini[0] === '') { |
||
224 | throw new \InvalidArgumentException("Invalid php ini specification: '$iniSpec'"); |
||
225 | } |
||
226 | $prefix[] = '-d ' . $ini[0] . '=' . $ini[1]; |
||
227 | } |
||
228 | } |
||
229 | |||
230 | $builder->setPrefix($prefix); |
||
231 | } |
||
232 | |||
233 | $builderArgs = parent::createChildProcessArgs($input); |
||
234 | } else { |
||
235 | $forcedRefs = array(); |
||
236 | if ($input->getOption('set-reference')) { |
||
237 | foreach ($input->getOption('set-reference') as $refSpec) { |
||
238 | $ref = explode(':', $refSpec, 2); |
||
239 | if (count($ref) < 2 || $ref[0] === '') { |
||
240 | throw new \InvalidArgumentException("Invalid reference specification: '$refSpec'"); |
||
241 | } |
||
242 | $forcedRefs[$ref[0]] = $ref[1]; |
||
243 | } |
||
244 | } |
||
245 | $migrationContext = array( |
||
246 | 'useTransactions' => !$input->getOption('no-transactions'), |
||
247 | 'defaultLanguageCode' => $input->getOption('default-language'), |
||
248 | 'adminUserLogin' => $input->getOption('admin-login'), |
||
249 | 'forceExecution' => $force, |
||
250 | 'forcedReferences' => $forcedRefs |
||
251 | ); |
||
252 | } |
||
253 | |||
254 | // Allow forcing handling of sigchild. Useful on eg. Debian and Ubuntu |
||
255 | if ($input->getOption('force-sigchild-enabled')) { |
||
256 | Process::forceSigchildEnabled(true); |
||
257 | } |
||
258 | |||
259 | $aborted = false; |
||
260 | $executed = 0; |
||
261 | $failed = 0; |
||
262 | $skipped = 0; |
||
263 | $total = count($toExecute); |
||
264 | |||
265 | foreach ($toExecute as $name => $migrationDefinition) { |
||
266 | // let's skip migrations that we know are invalid - user was warned and he decided to proceed anyway |
||
267 | if ($migrationDefinition->status == MigrationDefinition::STATUS_INVALID) { |
||
268 | $this->writeln("<comment>Skipping migration (invalid definition?) Path: ".$migrationDefinition->path."</comment>", self::$VERBOSITY_CHILD); |
||
269 | $skipped++; |
||
270 | continue; |
||
271 | } |
||
272 | |||
273 | $this->writeln("<info>Processing $name</info>", self::$VERBOSITY_CHILD); |
||
274 | |||
275 | if ($input->getOption('separate-process')) { |
||
276 | |||
277 | try { |
||
278 | $this->executeMigrationInSeparateProcess($migrationDefinition, $migrationService, $builder, $builderArgs); |
||
279 | |||
280 | $executed++; |
||
281 | } catch (\Exception $e) { |
||
282 | $failed++; |
||
283 | |||
284 | $errorMessage = $e->getMessage(); |
||
285 | if ($errorMessage != $this->subProcessErrorString) { |
||
286 | /// @todo move these bits of strings to class constants |
||
287 | $errorMessage = preg_replace('/^\n*(\[[0-9]*\])?(Migration failed|Failure after migration end)! Reason: +/', '', $errorMessage); |
||
288 | /// @todo atm this is impossible case - executeMigrationInSeparateProcess does not know enough |
||
289 | /// to throw an AfterMigrationExecutionException |
||
290 | if ($e instanceof AfterMigrationExecutionException) { |
||
291 | $errorMessage = "Failure after migration end! Path: " . $migrationDefinition->path . ", Reason: " . $errorMessage; |
||
292 | } else { |
||
293 | $errorMessage = "Migration failed! Path: " . $migrationDefinition->path . ", Reason: " . $errorMessage; |
||
294 | } |
||
295 | |||
296 | $this->writeErrorln("\n<error>$errorMessage</error>"); |
||
297 | } |
||
298 | |||
299 | if (!$input->getOption('ignore-failures')) { |
||
300 | $aborted = true; |
||
301 | break; |
||
302 | } |
||
303 | } |
||
304 | |||
305 | } else { |
||
306 | |||
307 | try { |
||
308 | $this->executeMigrationInProcess($migrationDefinition, $migrationService, $migrationContext); |
||
309 | |||
310 | $executed++; |
||
311 | // in case the 1st mig changes values to the refs, we avoid injecting them in the 2nd mig and later |
||
312 | $migrationContext['forcedReferences'] = array(); |
||
313 | } catch (\Exception $e) { |
||
314 | $failed++; |
||
315 | |||
316 | $errorMessage = $e->getMessage(); |
||
317 | if ($e instanceof AfterMigrationExecutionException) { |
||
318 | $errorMessage = "Failure after migration end! Path: " . $migrationDefinition->path . ", Reason: " . $errorMessage; |
||
319 | } else { |
||
320 | $errorMessage = "Migration failed! Path: " . $migrationDefinition->path . ", Reason: " . $errorMessage; |
||
321 | } |
||
322 | $this->writeErrorln("\n<error>$errorMessage</error>"); |
||
323 | |||
324 | if (!$input->getOption('ignore-failures')) { |
||
325 | $aborted = true; |
||
326 | break; |
||
327 | } |
||
328 | } |
||
329 | |||
330 | } |
||
331 | } |
||
332 | |||
333 | $missed = $total - $executed - $failed - $skipped; |
||
334 | |||
335 | if ($aborted && $missed > 0) { |
||
336 | $this->writeErrorln("\n<error>Migration execution aborted</error>"); |
||
337 | } |
||
338 | |||
339 | $this->writeln("Migrations executed: $executed, failed: $failed, skipped: $skipped, missed: $missed", self::$VERBOSITY_CHILD); |
||
340 | |||
341 | // We do not return an error code > 0 if migrations fail but , but only on proper fatals. |
||
342 | // The parent will analyze the output of the child process to gather the number of executed/failed migrations anyway |
||
343 | return 0; |
||
344 | } |
||
345 | |||
346 | /** |
||
347 | * @param string $type |
||
348 | * @param string $buffer |
||
349 | * @param null|\Symfony\Component\Process\Process $process |
||
350 | */ |
||
351 | public function onChildProcessOutput($type, $buffer, $process=null) |
||
352 | { |
||
353 | $lines = explode("\n", trim($buffer)); |
||
354 | |||
355 | foreach ($lines as $line) { |
||
356 | if (preg_match('/Migrations executed: ([0-9]+), failed: ([0-9]+), skipped: ([0-9]+)/', $line, $matches)) { |
||
357 | $this->migrationsDone[Migration::STATUS_DONE] += $matches[1]; |
||
358 | $this->migrationsDone[Migration::STATUS_FAILED] += $matches[2]; |
||
359 | $this->migrationsDone[Migration::STATUS_SKIPPED] += $matches[3]; |
||
360 | |||
361 | // swallow the recap lines unless we are in verbose mode |
||
362 | if ($this->verbosity <= Output::VERBOSITY_NORMAL) { |
||
363 | return; |
||
364 | } |
||
365 | } |
||
366 | |||
367 | // we tag the output with the id of the child process |
||
368 | if (trim($line) !== '') { |
||
369 | $msg = '[' . ($process ? $process->getPid() : ''). '] ' . trim($line); |
||
370 | if ($type == 'err') { |
||
371 | $this->writeErrorln($msg, OutputInterface::VERBOSITY_QUIET, OutputInterface::OUTPUT_RAW); |
||
372 | } else { |
||
373 | // swallow output of child processes in quiet mode |
||
374 | $this->writeLn($msg, self::$VERBOSITY_CHILD, OutputInterface::OUTPUT_RAW); |
||
375 | } |
||
376 | } |
||
377 | } |
||
378 | } |
||
379 | |||
380 | /** |
||
381 | * @param string[] $paths |
||
382 | * @param $migrationService |
||
383 | * @param bool $force |
||
384 | * @param bool $isChild when not in child mode, do not waste time parsing migrations |
||
385 | * @return MigrationDefinition[] parsed or unparsed, depending on |
||
386 | * |
||
387 | * @todo this does not scale well with many definitions or migrations |
||
388 | */ |
||
389 | protected function buildMigrationsList($paths, $migrationService, $force = false, $isChild = false) |
||
390 | { |
||
391 | $migrationDefinitions = $migrationService->getMigrationsDefinitions($paths); |
||
392 | $migrations = $migrationService->getMigrations(); |
||
393 | |||
394 | $this->migrationsAlreadyDone = array(Migration::STATUS_DONE => 0, Migration::STATUS_FAILED => 0, Migration::STATUS_SKIPPED => 0, Migration::STATUS_STARTED => 0); |
||
395 | |||
396 | $allowedStatuses = array(Migration::STATUS_TODO); |
||
397 | if ($force) { |
||
398 | $allowedStatuses = array_merge($allowedStatuses, array(Migration::STATUS_DONE, Migration::STATUS_FAILED, Migration::STATUS_SKIPPED)); |
||
399 | } |
||
400 | |||
401 | // filter away all migrations except 'to do' ones |
||
402 | $toExecute = array(); |
||
403 | foreach ($migrationDefinitions as $name => $migrationDefinition) { |
||
404 | if (!isset($migrations[$name]) || (($migration = $migrations[$name]) && in_array($migration->status, $allowedStatuses))) { |
||
405 | $toExecute[$name] = $isChild ? $migrationService->parseMigrationDefinition($migrationDefinition) : $migrationDefinition; |
||
406 | } |
||
407 | // save the list of non-executable migrations as well (even when using 'force') |
||
408 | if (!$isChild && isset($migrations[$name]) && (($migration = $migrations[$name]) && $migration->status != Migration::STATUS_TODO)) { |
||
409 | $this->migrationsAlreadyDone[$migration->status]++; |
||
410 | } |
||
411 | } |
||
412 | |||
413 | // if user wants to execute 'all' migrations: look for some which are registered in the database even if not |
||
414 | // found by the loader |
||
415 | if (empty($paths)) { |
||
416 | foreach ($migrations as $migration) { |
||
417 | if (in_array($migration->status, $allowedStatuses) && !isset($toExecute[$migration->name])) { |
||
418 | $migrationDefinitions = $migrationService->getMigrationsDefinitions(array($migration->path)); |
||
419 | if (count($migrationDefinitions)) { |
||
420 | $migrationDefinition = $migrationDefinitions->reset(); |
||
421 | $toExecute[$migration->name] = $isChild ? $migrationService->parseMigrationDefinition($migrationDefinition) : $migrationDefinition; |
||
422 | } else { |
||
423 | // q: shall we raise a warning here ? |
||
424 | } |
||
425 | } |
||
426 | } |
||
427 | } |
||
428 | |||
429 | ksort($toExecute); |
||
430 | |||
431 | return $toExecute; |
||
432 | } |
||
433 | |||
434 | /** |
||
435 | * We use a more compact output when there are *many* migrations |
||
436 | * @param MigrationDefinition[] $toExecute |
||
437 | * @param array $paths |
||
438 | * @param InputInterface $input |
||
439 | * @param OutputInterface $output |
||
440 | */ |
||
441 | protected function printMigrationsList($toExecute, InputInterface $input, OutputInterface $output, $paths = array()) |
||
442 | { |
||
443 | $output->writeln('Found ' . count($toExecute) . ' migrations in ' . count($paths) . ' directories'); |
||
444 | $output->writeln('In the same directories, migrations previously executed: ' . $this->migrationsAlreadyDone[Migration::STATUS_DONE] . |
||
445 | ', failed: ' . $this->migrationsAlreadyDone[Migration::STATUS_FAILED] . ', skipped: '. $this->migrationsAlreadyDone[Migration::STATUS_SKIPPED]); |
||
446 | if ($this->migrationsAlreadyDone[Migration::STATUS_STARTED]) { |
||
447 | $output->writeln('<info>In the same directories, migrations currently executing: ' . $this->migrationsAlreadyDone[Migration::STATUS_STARTED] . '</info>'); |
||
448 | } |
||
449 | } |
||
450 | |||
451 | /** |
||
452 | * @param MigrationDefinition[] $toExecute |
||
453 | * @return array key: folder name, value: number of migrations found |
||
454 | */ |
||
455 | protected function groupMigrationsByPath($toExecute) |
||
456 | { |
||
457 | $paths = array(); |
||
458 | foreach ($toExecute as $name => $migrationDefinition) { |
||
459 | $path = dirname($migrationDefinition->path); |
||
460 | if (!isset($paths[$path])) { |
||
461 | $paths[$path] = 1; |
||
462 | } else { |
||
463 | $paths[$path]++; |
||
464 | } |
||
465 | } |
||
466 | |||
467 | ksort($paths); |
||
468 | |||
469 | return $paths; |
||
470 | } |
||
471 | |||
472 | /** |
||
473 | * Returns the command-line arguments needed to execute a separate subprocess that will run a set of migrations |
||
474 | * (except path, which should be added after this call) |
||
475 | * @param InputInterface $input |
||
476 | * @return array |
||
477 | * @todo check if it is a good idea to pass on the current verbosity |
||
478 | * @todo shall we pass to child processes the `survive-disconnected-tty` flag? |
||
479 | */ |
||
480 | protected function createChildProcessArgs(InputInterface $input) |
||
481 | { |
||
482 | $kernel = $this->getContainer()->get('kernel'); |
||
483 | |||
484 | // mandatory args and options |
||
485 | $builderArgs = array( |
||
486 | $this->getConsoleFile(), // sf console |
||
487 | self::COMMAND_NAME, // name of sf command. Can we get it from the Application instead of hardcoding? |
||
488 | '--env=' . $kernel->getEnvironment(), // sf env |
||
489 | '--child' |
||
490 | ); |
||
491 | // sf/ez env options |
||
492 | if (!$kernel->isDebug()) { |
||
493 | $builderArgs[] = '--no-debug'; |
||
494 | } |
||
495 | if ($input->getOption('siteaccess')) { |
||
496 | $builderArgs[] = '--siteaccess=' . $input->getOption('siteaccess'); |
||
497 | } |
||
498 | switch ($this->verbosity) { |
||
499 | // no propagation of 'quiet' mode, as we always need to have at least the child output with executed migs |
||
500 | case OutputInterface::VERBOSITY_VERBOSE: |
||
501 | $builderArgs[] = '-v'; |
||
502 | break; |
||
503 | case OutputInterface::VERBOSITY_VERY_VERBOSE: |
||
504 | $builderArgs[] = '-vv'; |
||
505 | break; |
||
506 | case OutputInterface::VERBOSITY_DEBUG: |
||
507 | $builderArgs[] = '-vvv'; |
||
508 | break; |
||
509 | } |
||
510 | // 'optional' options |
||
511 | // note: options 'clear-cache', 'no-interaction', 'path' and 'survive-disconnected-tty' we never propagate |
||
512 | if ($input->getOption('admin-login')) { |
||
513 | $builderArgs[] = '--admin-login=' . $input->getOption('admin-login'); |
||
514 | } |
||
515 | if ($input->getOption('default-language')) { |
||
516 | $builderArgs[] = '--default-language=' . $input->getOption('default-language'); |
||
517 | } |
||
518 | if ($input->getOption('force')) { |
||
519 | $builderArgs[] = '--force'; |
||
520 | } |
||
521 | // useful in case the subprocess has a migration step of type process/run |
||
522 | if ($input->getOption('force-sigchild-enabled')) { |
||
523 | $builderArgs[] = '--force-sigchild-enabled'; |
||
524 | } |
||
525 | if ($input->getOption('ignore-failures')) { |
||
526 | $builderArgs[] = '--ignore-failures'; |
||
527 | } |
||
528 | if ($input->getOption('no-transactions')) { |
||
529 | $builderArgs[] = '--no-transactions'; |
||
530 | } |
||
531 | if ($input->getOption('separate-process')) { |
||
532 | $builderArgs[] = '--separate-process'; |
||
533 | } |
||
534 | if ($input->getOption('set-reference')) { |
||
535 | foreach ($input->getOption('set-reference') as $refSpec) { |
||
536 | $builderArgs[] = '--set-reference=' . $refSpec; |
||
537 | } |
||
538 | } |
||
539 | if ($input->getOption('child-process-php-ini-config')) { |
||
540 | foreach ($input->getOption('child-process-php-ini-config') as $iniSpec) { |
||
541 | $builderArgs[] = '--child-process-php-ini-config=' . $iniSpec; |
||
542 | } |
||
543 | } |
||
544 | return $builderArgs; |
||
545 | } |
||
546 | } |
||
547 |
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.