Files
MokoSuiteBackup/source/packages/com_mokosuitebackup/src/Engine/DatabaseImporter.php
T

128 lines
3.2 KiB
PHP
Raw Normal View History

<?php
/**
* @package MokoSuiteBackup
* @subpackage com_mokosuitebackup
* @author Moko Consulting <hello@mokoconsulting.tech>
* @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 the prefix from the dump with the current site prefix.
// The dump uses real table names (with the original prefix), but
// if restoring to a site with a different prefix we need to handle it.
// Our DatabaseDumper uses real names, so no replacement needed
// for same-site restores.
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)) {
try {
$db->setQuery($remaining);
$db->execute();
$statementsExecuted++;
} catch (\Exception $e) {
error_log('MokoSuiteBackup SQL import warning (final): ' . $e->getMessage());
}
}
} finally {
fclose($handle);
}
return $statementsExecuted;
}
}