resolveExecutable(['mysqldump', 'mariadb-dump']); if ($dumpBinary === null) { $this->error('Не найден mysqldump/mariadb-dump. Установите mysql-client в окружение, где запускается команда.'); return self::FAILURE; } $connection = $this->option('connection'); $config = config("database.connections.{$connection}"); if (!$config || !in_array($config['driver'] ?? null, ['mysql', 'mariadb'], true)) { $this->error("Соединение {$connection} не найдено или не MySQL/MariaDB."); return self::FAILURE; } $database = $config['database']; $host = $config['host'] ?? '127.0.0.1'; $port = (string) ($config['port'] ?? '3306'); $user = $config['username'] ?? 'root'; $password = (string) ($config['password'] ?? ''); $sslOptions = $this->resolveCliSslOptions($config); $gzip = !$this->option('no-gzip'); $timestamp = date('Y-m-d_His'); $filename = "{$database}_{$timestamp}.sql" . ($gzip ? '.gz' : ''); $dir = $this->resolveBackupDirectory(); if (!is_dir($dir) && !mkdir($dir, 0775, true) && !is_dir($dir)) { $this->error("Не удалось создать каталог: {$dir}"); return self::FAILURE; } $filepath = rtrim($dir, '/') . '/' . $filename; $cmd = sprintf( '%s --host=%s --port=%s --user=%s %s %s --single-transaction --quick --routines --triggers --events --hex-blob --default-character-set=utf8mb4 %s', escapeshellarg($dumpBinary), escapeshellarg($host), escapeshellarg($port), escapeshellarg($user), $password !== '' ? '--password=' . escapeshellarg($password) : '', $sslOptions, escapeshellarg($database), ); if ($gzip) { $cmd .= ' | gzip'; } $cmd .= ' > ' . escapeshellarg($filepath); $this->info("Создаю дамп БД {$database} → {$filepath}"); $process = new Process(['bash', '-o', 'pipefail', '-c', $cmd]); $process->setTimeout(null); $process->run(function ($type, $buffer) { if ($type === Process::ERR) { $this->getOutput()->write($buffer); } }); if (!$process->isSuccessful()) { @unlink($filepath); $this->error('Ошибка mysqldump: ' . $process->getErrorOutput()); return self::FAILURE; } $size = is_file($filepath) ? filesize($filepath) : 0; $this->info(sprintf('Готово. Размер: %.2f MB', $size / 1024 / 1024)); $this->line($filepath); return self::SUCCESS; } private function resolveBackupDirectory(): string { $path = $this->option('path'); if (is_string($path) && $path !== '') { return $path; } return storage_path('db'); } /** * @param array $config */ private function resolveCliSslOptions(array $config): string { $sslCa = $config['options'][\PDO::MYSQL_ATTR_SSL_CA] ?? null; if (is_string($sslCa) && $sslCa !== '') { return '--ssl-ca=' . escapeshellarg($sslCa); } return '--skip-ssl'; } /** * @param list $candidates */ private function resolveExecutable(array $candidates): ?string { $finder = new ExecutableFinder(); foreach ($candidates as $candidate) { $binary = $finder->find($candidate); if ($binary !== null) { return $binary; } } return null; } }