* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE * * Imports a SQL dump file created by DatabaseDumper. * Handles #__ prefix replacement, multi-statement execution, * and DROP TABLE before CREATE TABLE for clean restores. */ namespace Joomla\Component\MokoSuiteBackup\Administrator\Engine; defined('_JEXEC') or die; use Joomla\CMS\Factory; class DatabaseImporter { /** * Import a SQL dump file into the database. * * @param string $sqlFile Absolute path to the SQL dump file * * @return int Number of statements executed * * @throws \RuntimeException On import failure */ public function import(string $sqlFile): int { if (!is_file($sqlFile) || !is_readable($sqlFile)) { throw new \RuntimeException('SQL file not readable: ' . $sqlFile); } $db = Factory::getDbo(); $prefix = $db->getPrefix(); $handle = fopen($sqlFile, 'r'); if ($handle === false) { throw new \RuntimeException('Cannot open SQL file: ' . $sqlFile); } $statementsExecuted = 0; $currentStatement = ''; $inMultiLineComment = false; try { while (($line = fgets($handle)) !== false) { $trimmed = trim($line); // Skip empty lines if ($trimmed === '') { continue; } // Skip single-line comments if (str_starts_with($trimmed, '--') || str_starts_with($trimmed, '#')) { continue; } // Handle multi-line comments if (str_starts_with($trimmed, '/*')) { $inMultiLineComment = true; } if ($inMultiLineComment) { if (str_contains($trimmed, '*/')) { $inMultiLineComment = false; } continue; } // Accumulate the statement $currentStatement .= $line; // Check if statement is complete (ends with semicolon) if (str_ends_with($trimmed, ';')) { $statement = trim($currentStatement); $currentStatement = ''; if (empty($statement)) { continue; } // Replace abstract #__ prefix with the current site's prefix $statement = str_replace('#__', $prefix, $statement); try { $db->setQuery($statement); $db->execute(); $statementsExecuted++; } catch (\Exception $e) { // Log but don't abort — some statements may fail on // different MySQL versions (e.g. charset differences) // but the overall restore should continue. error_log('MokoSuiteBackup SQL import warning: ' . $e->getMessage()); } } } // Execute any remaining statement without trailing semicolon $remaining = trim($currentStatement); if (!empty($remaining)) { $remaining = str_replace('#__', $prefix, $remaining); try { $db->setQuery($remaining); $db->execute(); $statementsExecuted++; } catch (\Exception $e) { error_log('MokoSuiteBackup SQL import warning (final): ' . $e->getMessage()); } } } finally { fclose($handle); } return $statementsExecuted; } }