
| Current Path : /var/www/html/sirius-pallets/vendor/drush/drush/src/Commands/sql/ |
Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64 |
| Current File : /var/www/html/sirius-pallets/vendor/drush/drush/src/Commands/sql/SqlSyncCommands.php |
<?php
declare(strict_types=1);
namespace Drush\Commands\sql;
use Consolidation\AnnotatedCommand\CommandData;
use Consolidation\AnnotatedCommand\Hooks\HookManager;
use Consolidation\SiteAlias\SiteAlias;
use Consolidation\SiteAlias\SiteAliasManagerInterface;
use Drush\Attributes as CLI;
use Drush\Boot\DrupalBootLevels;
use Drush\Commands\AutowireTrait;
use Drush\Commands\core\DocsCommands;
use Drush\Commands\core\RsyncCommands;
use Drush\Commands\core\StatusCommands;
use Drush\Commands\DrushCommands;
use Drush\Drush;
use Drush\Exceptions\UserAbortException;
use Symfony\Component\Filesystem\Path;
#[CLI\Bootstrap(DrupalBootLevels::NONE)]
final class SqlSyncCommands extends DrushCommands
{
use AutowireTrait;
const SYNC = 'sql:sync';
public function __construct(
private readonly SiteAliasManagerInterface $siteAliasManager
) {
parent::__construct();
}
/**
* Copy DB data from a source site to a target site. Transfers data via rsync.
*/
#[CLI\Command(name: self::SYNC, aliases: ['sql-sync'])]
#[CLI\Argument(name: 'source', description: 'A site-alias or site specification whose database you want to copy from.')]
#[CLI\Argument(name: 'target', description: 'A site-alias or site specification whose database you want to replace.')]
#[CLI\OptionsetTableSelection]
#[CLI\Option(name: 'no-dump', description: 'Do not dump the sql database; always use an existing dump file.')]
#[CLI\Option(name: 'no-sync', description: 'Do not rsync the database dump file from source to target.')]
#[CLI\Option(name: 'runner', description: 'Where to run the rsync command; defaults to the local site. Can also be <info>source</info> or <info>target</info>.')]
#[CLI\Option(name: 'create-db', description: 'Create a new database before importing the database dump on the target machine.')]
#[CLI\Option(name: 'db-su', description: 'Account to use when creating a new database (e.g. <info>root</info>).')]
#[CLI\Option(name: 'db-su-pw', description: 'Password for the db-su account.')]
#[CLI\Option(name: 'source-dump', description: 'The path for retrieving the sql-dump on source machine.')]
#[CLI\Option(name: 'target-dump', description: 'The path for storing the sql-dump on target machine.')]
#[CLI\Option(name: 'extra-dump', description: 'Add custom arguments/options to the dumping of the database (e.g. mysqldump command).')]
#[CLI\Usage(name: 'drush sql:sync @source @self', description: "Copy the database from the site with the alias 'source' to the local site.")]
#[CLI\Usage(name: 'drush sql:sync @self @target', description: "Copy the database from the local site to the site with the alias 'target'.")]
#[CLI\Usage(name: 'drush sql:sync #prod #dev', description: 'Copy the database from the site in /sites/prod to the site in /sites/dev (multisite installation).')]
#[CLI\Topics(topics: [DocsCommands::ALIASES, DocsCommands::POLICY, DocsCommands::CONFIGURATION, DocsCommands::EXAMPLE_SYNC_VIA_HTTP])]
public function sqlsync($source, $target, $options = ['no-dump' => false, 'no-sync' => false, 'runner' => self::REQ, 'create-db' => false, 'db-su' => self::REQ, 'db-su-pw' => self::REQ, 'target-dump' => self::REQ, 'source-dump' => self::OPT, 'extra-dump' => self::REQ]): void
{
$sourceRecord = $this->siteAliasManager->get($source);
$targetRecord = $this->siteAliasManager->get($target);
// Append --strict in case we are calling older versions of Drush.
$global_options = Drush::redispatchOptions() + ['strict' => 0];
// Create target DB if needed.
if ($options['create-db']) {
$this->logger()->notice(dt('Starting to create database on target.'));
$process = $this->processManager()->drush($targetRecord, SqlCommands::CREATE, [], $global_options);
$process->mustRun();
}
$source_dump_path = $this->dump($options, $global_options, $sourceRecord);
$target_dump_path = $this->rsync($options, $sourceRecord, $targetRecord, $source_dump_path);
$this->import($global_options, $target_dump_path, $targetRecord);
}
#[CLI\Hook(type: HookManager::ARGUMENT_VALIDATOR, target: self::SYNC)]
public function validate(CommandData $commandData): void
{
$source = $commandData->input()->getArgument('source');
$target = $commandData->input()->getArgument('target');
// Get target info for confirmation prompt.
if (!$sourceRecord = $this->siteAliasManager->get($source)) {
throw new \Exception(dt('Error: no alias record could be found for source !source', ['!source' => $source]));
}
if (!$targetRecord = $this->siteAliasManager->get($target)) {
throw new \Exception(dt('Error: no alias record could be found for target !target', ['!target' => $target]));
}
if (!$commandData->input()->getOption('no-dump') && !$source_db_name = $this->databaseName($sourceRecord)) {
throw new \Exception(dt('Error: no database record could be found for source !source', ['!source' => $source]));
}
if (!$target_db_name = $this->databaseName($targetRecord)) {
throw new \Exception(dt('Error: no database record could be found for target !target', ['!target' => $target]));
}
$txt_source = ($sourceRecord->remoteHost() ? $sourceRecord->remoteHost() . '/' : '') . $this->databaseName($sourceRecord);
$txt_target = ($targetRecord->remoteHost() ? $targetRecord->remoteHost() . '/' : '') . $target_db_name;
if ($commandData->input()->getOption('no-dump') && !$commandData->input()->getOption('source-dump')) {
throw new \Exception(dt('The --source-dump option must be supplied when --no-dump is specified.'));
}
if ($commandData->input()->getOption('no-sync') && !$commandData->input()->getOption('target-dump')) {
throw new \Exception(dt('The --target-dump option must be supplied when --no-sync is specified.'));
}
if (!$this->getConfig()->simulate()) {
$this->output()->writeln(dt("You will destroy data in !target and replace with data from !source.", [
'!source' => $txt_source,
'!target' => $txt_target
]));
if (!$this->io()->confirm(dt('Do you really want to continue?'))) {
throw new UserAbortException();
}
}
}
public function databaseName(SiteAlias $record): string
{
if ($this->processManager()->hasTransport($record) && $this->getConfig()->simulate()) {
return 'simulated_db';
}
$process = $this->processManager()->drush($record, StatusCommands::STATUS, [], ['fields' => 'db-name', 'format' => 'json'] + Drush::redispatchOptions() + ['strict' => 0]);
$process->setSimulated(false);
$process->mustRun();
$data = $process->getOutputAsJson();
if (!isset($data['db-name'])) {
throw new \Exception('Could not look up database name for ' . $record->name());
}
return trim($data['db-name']);
}
/**
* Perform sql-dump on source unless told otherwise. Returns the path to the dump file.
*/
public function dump(array $options, array $global_options, SiteAlias $sourceRecord): string
{
$dump_options = $global_options + [
'gzip' => true,
'result-file' => $options['source-dump'] ?: 'auto',
];
if (!$options['no-dump']) {
$this->logger()->notice(dt('Starting to dump database on source.'));
$process = $this->processManager()->drush($sourceRecord, SqlCommands::DUMP, [], $dump_options + ['format' => 'json'] + Drush::redispatchOptions() + ['strict' => 0]);
$process->mustRun();
if ($this->getConfig()->simulate()) {
$source_dump_path = '/simulated/path/to/dump.tgz';
} else {
$json = $process->getOutputAsJson();
$source_dump_path = $json['path'];
}
} else {
$source_dump_path = $options['source-dump'];
}
if (empty($source_dump_path)) {
throw new \Exception(dt('The Drush sql:dump command did not report the path to the dump file.'));
}
return $source_dump_path;
}
/**
* @param $source_dump_path
* Path to the target file.
* @throws \Exception
*/
public function rsync(array $options, SiteAlias $sourceRecord, SiteAlias $targetRecord, $source_dump_path): string
{
$do_rsync = !$options['no-sync'];
// Determine path/to/dump on target.
if ($options['target-dump']) {
$target_dump_path = $options['target-dump'];
} elseif (!$sourceRecord->isRemote() && !$targetRecord->isRemote()) {
$target_dump_path = $source_dump_path;
$do_rsync = false;
} else {
$tmp = '/tmp'; // Our fallback plan.
$this->logger()->notice(dt('Starting to discover temporary files directory on target.'));
$process = $this->processManager()->drush($targetRecord, StatusCommands::STATUS, [], ['format' => 'string', 'field' => 'drush-temp'] + Drush::redispatchOptions() + ['strict' => 0]);
$process->setSimulated(false);
$process->run();
if ($process->isSuccessful()) {
$tmp = trim($process->getOutput());
}
$target_dump_path = Path::join($tmp, basename($source_dump_path));
}
if ($do_rsync) {
$double_dash_options = [];
if (!$options['no-dump']) {
// Cleanup if this command created the dump file.
$double_dash_options['remove-source-files'] = true;
}
if (!$runner = $options['runner']) {
$runner = $sourceRecord->isRemote() && $targetRecord->isRemote() ? $targetRecord : $this->siteAliasManager->getSelf();
}
if ($runner == 'source') {
$runner = $sourceRecord;
}
if (($runner == 'target') || ($runner == 'destination')) {
$runner = $targetRecord;
}
$this->logger()->notice(dt('Copying dump file from source to target.'));
$process = $this->processManager()->drush($runner, RsyncCommands::RSYNC, [$sourceRecord->name() . ":$source_dump_path", $targetRecord->name() . ":$target_dump_path"], ['yes' => true] + Drush::redispatchOptions() + ['strict' => 0], $double_dash_options);
$process->mustRun($process->showRealtime());
}
return $target_dump_path;
}
/**
* Import file into target.
*
* @param $global_options
* @param $target_dump_path
* @param $targetRecord
*/
public function import($global_options, $target_dump_path, $targetRecord): void
{
$this->logger()->notice(dt('Starting to import dump file onto target database.'));
$query_options = $global_options + [
'file' => $target_dump_path,
'file-delete' => true,
];
$process = $this->processManager()->drush($targetRecord, SqlCommands::QUERY, [], $query_options + Drush::redispatchOptions() + ['strict' => 0]);
$process->mustRun();
}
}