class RecipeCommand
Applies recipe.
@internal This API is experimental.
Hierarchy
- class \Symfony\Component\Console\Command\Command
- class \Drupal\Core\Recipe\RecipeCommand extends \Symfony\Component\Console\Command\Command uses \Drupal\Core\Command\BootableCommandTrait
Expanded class hierarchy of RecipeCommand
File
-
core/
lib/ Drupal/ Core/ Recipe/ RecipeCommand.php, line 28
Namespace
Drupal\Core\RecipeView source
final class RecipeCommand extends Command {
use BootableCommandTrait;
/**
* Constructs a new RecipeCommand command.
*
* @param object $class_loader
* The class loader.
*/
public function __construct($class_loader) {
parent::__construct('recipe');
$this->classLoader = $class_loader;
}
/**
* {@inheritdoc}
*/
protected function configure() : void {
$this->setDescription('Applies a recipe to a site.')
->addArgument('path', InputArgument::REQUIRED, 'The path to the recipe\'s folder to apply');
ConsoleInputCollector::configureCommand($this);
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output) : int {
$io = new SymfonyStyle($input, $output);
$recipe_path = $input->getArgument('path');
if (!is_string($recipe_path) || !is_dir($recipe_path)) {
$io->error(sprintf('The supplied path %s is not a directory', $recipe_path));
return 1;
}
// Recipes can only be applied to an already-installed site.
$container = $this->boot()
->getContainer();
/** @var \Drupal\Core\Config\Checkpoint\CheckpointStorageInterface $checkpoint_storage */
$checkpoint_storage = $container->get('config.storage.checkpoint');
$recipe = Recipe::createFromDirectory($recipe_path);
// Collect input for this recipe and all the recipes it directly and
// indirectly applies.
$recipe->input
->collectAll(new ConsoleInputCollector($input, $io));
if ($checkpoint_storage instanceof LoggerAwareInterface) {
$logger = new ConsoleLogger($output, [
// The checkpoint storage logs a notice if it decides to not create a
// checkpoint, and we want to be sure those notices are seen even
// without additional verbosity.
LogLevel::NOTICE => OutputInterface::VERBOSITY_NORMAL,
]);
$checkpoint_storage->setLogger($logger);
}
$backup_checkpoint = $checkpoint_storage->checkpoint("Backup before the '{$recipe->name}' recipe.");
try {
$steps = RecipeRunner::toBatchOperations($recipe);
$progress_bar = $io->createProgressBar();
$progress_bar->setFormat("%current%/%max% [%bar%]\n%message%\n");
$progress_bar->setMessage($this->toPlainString(t('Applying recipe')));
$progress_bar->start(count($steps));
/** @var array{message?: \Stringable|string, results: array{module?: string[], theme?: string[], content?: string[], recipe?: string[]}} $context */
$context = [
'results' => [],
];
foreach ($steps as $step) {
call_user_func_array($step[0], array_merge($step[1], [
&$context,
]));
if (isset($context['message'])) {
$progress_bar->setMessage($this->toPlainString($context['message']));
}
unset($context['message']);
$progress_bar->advance();
}
if ($io->isVerbose()) {
if (!empty($context['results']['module'])) {
$io->section($this->toPlainString(t('Modules installed')));
$modules = array_map(fn($module) => \Drupal::service('extension.list.module')->getName($module), $context['results']['module']);
sort($modules, SORT_NATURAL);
$io->listing($modules);
}
if (!empty($context['results']['theme'])) {
$io->section($this->toPlainString(t('Themes installed')));
$themes = array_map(fn($theme) => \Drupal::service('extension.list.theme')->getName($theme), $context['results']['theme']);
sort($themes, SORT_NATURAL);
$io->listing($themes);
}
if (!empty($context['results']['content'])) {
$io->section($this->toPlainString(t('Content created for recipes')));
$io->listing($context['results']['content']);
}
if (!empty($context['results']['recipe'])) {
$io->section($this->toPlainString(t('Recipes applied')));
$io->listing($context['results']['recipe']);
}
}
$io->success($this->toPlainString(t('%recipe applied successfully', [
'%recipe' => $recipe->name,
])));
return 0;
} catch (\Throwable $e) {
try {
$this->rollBackToCheckpoint($backup_checkpoint);
} catch (ConfigImporterException $importer_exception) {
$io->error($importer_exception->getMessage());
}
throw $e;
}
}
/**
* Converts a stringable like TranslatableMarkup to a plain text string.
*
* @param \Stringable|string $text
* The string to convert.
*
* @return string
* The plain text string.
*/
private function toPlainString(\Stringable|string $text) : string {
return PlainTextOutput::renderFromHtml((string) $text);
}
/**
* Rolls config back to a particular checkpoint.
*
* @param \Drupal\Core\Config\Checkpoint\Checkpoint $checkpoint
* The checkpoint to roll back to.
*/
private function rollBackToCheckpoint(Checkpoint $checkpoint) : void {
$container = \Drupal::getContainer();
/** @var \Drupal\Core\Config\Checkpoint\CheckpointStorageInterface $checkpoint_storage */
$checkpoint_storage = $container->get('config.storage.checkpoint');
$checkpoint_storage->setCheckpointToReadFrom($checkpoint);
$storage_comparer = new StorageComparer($checkpoint_storage, $container->get('config.storage'));
$storage_comparer->reset();
$config_importer = new ConfigImporter($storage_comparer, $container->get('event_dispatcher'), $container->get('config.manager'), $container->get('lock'), $container->get('config.typed'), $container->get('module_handler'), $container->get('module_installer'), $container->get('theme_handler'), $container->get('string_translation'), $container->get('extension.list.module'), $container->get('extension.list.theme'));
$config_importer->import();
}
}
Members
Title Sort descending | Modifiers | Object type | Summary | Overriden Title | Overrides |
---|---|---|---|---|---|
BootableCommandTrait::$classLoader | protected | property | The class loader. | ||
BootableCommandTrait::boot | protected | function | Boots up a Drupal environment. | ||
BootableCommandTrait::getSitePath | protected | function | Gets the site path. | ||
Command::$aliases | private | property | 1 | ||
Command::$application | private | property | |||
Command::$code | private | property | |||
Command::$definition | private | property | |||
Command::$description | private | property | 1 | ||
Command::$fullDefinition | private | property | |||
Command::$help | private | property | |||
Command::$helperSet | private | property | |||
Command::$hidden | private | property | |||
Command::$ignoreValidationErrors | private | property | 2 | ||
Command::$name | private | property | |||
Command::$processTitle | private | property | |||
Command::$synopsis | private | property | |||
Command::$usages | private | property | |||
Command::addArgument | public | function | Adds an argument. | 2 | |
Command::addOption | public | function | Adds an option. | 2 | |
Command::addUsage | public | function | Add a command usage example, it'll be prefixed with the command name. | 2 | |
Command::complete | public | function | Supplies suggestions when resolving possible completion options for input (e.g. option or argument). | 5 | |
Command::FAILURE | public | constant | |||
Command::getAliases | public | function | Returns the aliases for the command. | ||
Command::getApplication | public | function | Gets the application instance for this command. | 2 | |
Command::getDefaultDescription | public static | function | |||
Command::getDefaultName | public static | function | |||
Command::getDefinition | public | function | Gets the InputDefinition attached to this Command. | 2 | |
Command::getDescription | public | function | Returns the description for the command. | ||
Command::getHelp | public | function | Returns the help for the command. | 2 | |
Command::getHelper | public | function | Gets a helper instance by name. | 2 | |
Command::getHelperSet | public | function | Gets the helper set. | 1 | |
Command::getName | public | function | Returns the command name. | ||
Command::getNativeDefinition | public | function | Gets the InputDefinition to be used to create representations of this Command. | 2 | |
Command::getProcessedHelp | public | function | Returns the processed help for the command replacing the %command.name% and %command.full_name% patterns with the real values dynamically. |
2 | |
Command::getSynopsis | public | function | Returns the synopsis for the command. | 2 | |
Command::getUsages | public | function | Returns alternative usages of the command. | 2 | |
Command::ignoreValidationErrors | public | function | Ignores validation errors. | 2 | |
Command::initialize | protected | function | Initializes the command after the input has been bound and before the input is validated. |
4 | |
Command::interact | protected | function | Interacts with the user. | 5 | |
Command::INVALID | public | constant | |||
Command::isEnabled | public | function | Checks whether the command is enabled or not in the current environment. | 2 | |
Command::isHidden | public | function | |||
Command::mergeApplicationDefinition | public | function | Merges the application definition with the command definition. | 2 | |
Command::run | public | function | Runs the command. | 4 | |
Command::setAliases | public | function | Sets the aliases for the command. | ||
Command::setApplication | public | function | 2 | ||
Command::setCode | public | function | Sets the code to execute when running this command. | 2 | |
Command::setDefinition | public | function | Sets an array of argument and option instances. | 2 | |
Command::setDescription | public | function | Sets the description for the command. | ||
Command::setHelp | public | function | Sets the help for the command. | 2 | |
Command::setHelperSet | public | function | 2 | ||
Command::setHidden | public | function | |||
Command::setName | public | function | Sets the name of the command. | ||
Command::setProcessTitle | public | function | Sets the process title of the command. | 2 | |
Command::SUCCESS | public | constant | |||
Command::validateName | private | function | Validates a command name. | ||
RecipeCommand::configure | protected | function | Configures the current command. | Overrides Command::configure | |
RecipeCommand::execute | protected | function | Executes the current command. | Overrides Command::execute | |
RecipeCommand::rollBackToCheckpoint | private | function | Rolls config back to a particular checkpoint. | ||
RecipeCommand::toPlainString | private | function | Converts a stringable like TranslatableMarkup to a plain text string. | ||
RecipeCommand::__construct | public | function | Constructs a new RecipeCommand command. | Overrides Command::__construct |