Skip to content

Commit 2e28158

Browse files
committed
fix: mejorar compatibilidad y configuración de backups
1 parent dc28b35 commit 2e28158

6 files changed

Lines changed: 125 additions & 29 deletions

File tree

Controller/Backup.php

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -708,37 +708,28 @@ private function switchDbCharsetAction(): void
708708
return;
709709
}
710710

711-
$configCharset = Tools::config('mysql_charset');
712-
$configCollate = Tools::config('mysql_collate');
713-
if (empty($configCharset) || empty($configCollate)) {
714-
Tools::log()->error('config-mysql-charset-error', [
715-
'%config-charset%' => $configCharset,
716-
'%config-collate%' => $configCollate
717-
]);
718-
return;
719-
}
720-
721711
$selectedCharset = $this->request->query('charset');
722712
switch ($selectedCharset) {
723713
case 'utf8':
724-
$configFile = str_replace("'" . $configCharset . "'", "'utf8'", $configFile);
725-
$configFile = str_replace("'" . $configCollate . "'", "'utf8_bin'", $configFile);
714+
$selectedCollate = 'utf8_bin';
726715
break;
727716

728717
case 'utf8mb4':
729-
$configFile = str_replace("'" . $configCharset . "'", "'utf8mb4'", $configFile);
730-
$configFile = str_replace("'" . $configCollate . "'", "'utf8mb4_unicode_520_ci'", $configFile);
718+
$selectedCollate = 'utf8mb4_unicode_520_ci';
731719
break;
732720

733721
default:
734722
Tools::log()->error('config-mysql-charset-error', [
735-
'%config-charset%' => $configCharset,
736-
'%config-collate%' => $configCollate,
723+
'%config-charset%' => Tools::config('mysql_charset'),
724+
'%config-collate%' => Tools::config('mysql_collate'),
737725
'%selected-charset%' => $selectedCharset
738726
]);
739727
return;
740728
}
741729

730+
$configFile = $this->setConfigConstant($configFile, 'FS_MYSQL_CHARSET', $selectedCharset);
731+
$configFile = $this->setConfigConstant($configFile, 'FS_MYSQL_COLLATE', $selectedCollate);
732+
742733
// guardamos el archivo
743734
if (false === file_put_contents(Tools::folder('config.php'), $configFile)) {
744735
Tools::log()->error('record-save-error');
@@ -749,6 +740,28 @@ private function switchDbCharsetAction(): void
749740
Tools::log()->notice('record-updated-correctly');
750741
}
751742

743+
private function setConfigConstant(string $configFile, string $name, string $value): string
744+
{
745+
$definition = "define('" . $name . "', '" . $value . "');";
746+
$pattern = "/define\\s*\\(\\s*(['\"])" . preg_quote($name, '/')
747+
. "\\1\\s*,\\s*(['\"])[^'\"]*\\2\\s*\\)\\s*;/";
748+
749+
if (preg_match($pattern, $configFile)) {
750+
return preg_replace($pattern, $definition, $configFile, 1);
751+
}
752+
753+
$closingTagPosition = strrpos($configFile, '?>');
754+
if ($closingTagPosition === false) {
755+
$separator = str_ends_with($configFile, "\n") ? '' : PHP_EOL;
756+
return $configFile . $separator . $definition . PHP_EOL;
757+
}
758+
759+
$beforeClosingTag = substr($configFile, 0, $closingTagPosition);
760+
$separator = str_ends_with($beforeClosingTag, "\n") ? '' : PHP_EOL;
761+
return $beforeClosingTag . $separator . $definition . PHP_EOL
762+
. substr($configFile, $closingTagPosition);
763+
}
764+
752765
private function unzipDatabase(string $gzFilePath): string
753766
{
754767
// abrimos el archivo .sql.gz

Lib/BackupFile.php

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919

2020
namespace FacturaScripts\Plugins\Backup\Lib;
2121

22-
use Exception;
2322
use FacturaScripts\Core\Tools;
2423
use RecursiveDirectoryIterator;
2524
use RecursiveIteratorIterator;
25+
use Throwable;
2626
use ZipStream\Option\Archive;
2727
use ZipStream\ZipStream;
2828

@@ -64,11 +64,20 @@ protected static function zipFolder(string $fileName): bool
6464

6565
try {
6666
// configuramos ZipStream para escribir directamente al stream del archivo
67-
$options = new Archive();
68-
$options->setSendHttpHeaders(false);
69-
$options->setOutputStream($outputStream);
70-
71-
$zip = new ZipStream(basename($fileName), $options);
67+
if (class_exists(Archive::class)) {
68+
// ZipStream 2.x
69+
$options = new Archive();
70+
$options->setSendHttpHeaders(false);
71+
$options->setOutputStream($outputStream);
72+
$zip = new ZipStream(basename($fileName), $options);
73+
} else {
74+
// ZipStream 3.x
75+
$zip = new ZipStream(
76+
outputName: basename($fileName),
77+
sendHttpHeaders: false,
78+
outputStream: $outputStream
79+
);
80+
}
7281

7382
$files = new RecursiveIteratorIterator(
7483
new RecursiveDirectoryIterator(FS_FOLDER),
@@ -95,7 +104,7 @@ protected static function zipFolder(string $fileName): bool
95104
}
96105

97106
$zip->finish();
98-
} catch (Exception $e) {
107+
} catch (Throwable $e) {
99108
fclose($outputStream);
100109
return false;
101110
}

Test/main/BackupControllerTest.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
/**
3+
* This file is part of Backup plugin for FacturaScripts
4+
* Copyright (C) 2026 Carlos Garcia Gomez <carlos@facturascripts.com>
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU Lesser General Public License as
8+
* published by the Free Software Foundation, either version 3 of the
9+
* License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
namespace FacturaScripts\Test\Plugins;
21+
22+
use FacturaScripts\Plugins\Backup\Controller\Backup;
23+
use PHPUnit\Framework\TestCase;
24+
use ReflectionClass;
25+
26+
final class BackupControllerTest extends TestCase
27+
{
28+
public function testSetConfigConstantUpdatesExistingDefinition(): void
29+
{
30+
$config = "<?php\ndefine(\"FS_MYSQL_CHARSET\", \"utf8\");\n";
31+
32+
$result = $this->setConfigConstant($config, 'FS_MYSQL_CHARSET', 'utf8mb4');
33+
34+
$this->assertStringContainsString("define('FS_MYSQL_CHARSET', 'utf8mb4');", $result);
35+
$this->assertStringNotContainsString('utf8\");', $result);
36+
}
37+
38+
public function testSetConfigConstantAddsMissingDefinition(): void
39+
{
40+
$config = "<?php\ndefine('FS_DB_TYPE', 'mysql');\n";
41+
42+
$result = $this->setConfigConstant($config, 'FS_MYSQL_CHARSET', 'utf8mb4');
43+
44+
$this->assertSame(
45+
$config . "define('FS_MYSQL_CHARSET', 'utf8mb4');\n",
46+
$result
47+
);
48+
}
49+
50+
public function testSetConfigConstantAddsDefinitionBeforeClosingTag(): void
51+
{
52+
$config = "<?php\ndefine('FS_DB_TYPE', 'mysql');\n?>";
53+
54+
$result = $this->setConfigConstant($config, 'FS_MYSQL_COLLATE', 'utf8mb4_unicode_520_ci');
55+
56+
$this->assertSame(
57+
"<?php\ndefine('FS_DB_TYPE', 'mysql');\ndefine('FS_MYSQL_COLLATE', 'utf8mb4_unicode_520_ci');\n?>",
58+
$result
59+
);
60+
}
61+
62+
private function setConfigConstant(string $config, string $name, string $value): string
63+
{
64+
$reflection = new ReflectionClass(Backup::class);
65+
$controller = $reflection->newInstanceWithoutConstructor();
66+
$method = $reflection->getMethod('setConfigConstant');
67+
$method->setAccessible(true);
68+
69+
return $method->invoke($controller, $config, $name, $value);
70+
}
71+
}

Test/main/CronApplyBackupLimitTest.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,11 @@ public function testLimiteSeAplicaASqlYZipPorSeparado(): void
107107
$restantesSql = array_filter($archivosSql, 'file_exists');
108108
$restantesZip = array_filter($archivosZip, 'file_exists');
109109

110-
$this->assertLessThanOrEqual(3, count($restantesSql), 'No deben quedar mas de 3 sql de test');
111-
$this->assertLessThanOrEqual(3, count($restantesZip), 'No deben quedar mas de 3 zip de test');
110+
$esperadosSql = min(count($archivosSql), max(0, $limite - $this->sqlPrevios));
111+
$esperadosZip = min(count($archivosZip), max(0, $limite - $this->zipPrevios));
112+
113+
$this->assertCount($esperadosSql, $restantesSql, 'El limite de archivos sql no se aplico correctamente');
114+
$this->assertCount($esperadosZip, $restantesZip, 'El limite de archivos zip no se aplico correctamente');
112115
}
113116

114117
/** @return array<string> */
@@ -133,4 +136,4 @@ private function ejecutarLimite(): void
133136
$metodo->setAccessible(true);
134137
$metodo->invoke($cron);
135138
}
136-
}
139+
}

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@
1515
"require": {
1616
"ext-mbstring": "*",
1717
"ext-zip": "*",
18-
"maennchen/zipstream-php": "^2.4"
18+
"maennchen/zipstream-php": "^2.4 || ^3.0"
1919
}
2020
}

facturascripts.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name = "Backup"
22
description = "Permite realizar y restaurar copias de seguridad."
3-
version = 3.51
3+
version = 3.6
44
min_version = 2025.6
5-
require_php = 'zip'
5+
require_php = 'zip'

0 commit comments

Comments
 (0)