Introduzione classe Backup
Introduzione della classe Backup per la gestione dei backup del gestionale, con relativi miglioramenti nella gestione delle informazioni sui backup e la rispettiva generazione.
This commit is contained in:
parent
ab29b5b285
commit
f75a8a066b
2
bug.php
2
bug.php
|
@ -25,7 +25,7 @@ if (filter('op') == 'send') {
|
|||
// Aggiunta della copia del database (facoltativo)
|
||||
if (!empty($post['sql'])) {
|
||||
$backup_file = $docroot.'/Backup OSM '.date('Y-m-d').' '.date('H_i_s').'.sql';
|
||||
backup_tables($backup_file);
|
||||
Backup::database($backup_file);
|
||||
|
||||
$mail->AddAttachment($backup_file);
|
||||
|
||||
|
|
23
index.php
23
index.php
|
@ -10,35 +10,24 @@ switch ($op) {
|
|||
case 'login':
|
||||
$username = post('username');
|
||||
$password = post('password');
|
||||
|
||||
if ($dbo->isConnected() && $dbo->isInstalled() && Auth::getInstance()->attempt($username, $password)) {
|
||||
$_SESSION['keep_alive'] = (filter('keep_alive') != null);
|
||||
|
||||
// Auto backup del database giornaliero
|
||||
if (get_var('Backup automatico')) {
|
||||
$folders = glob($backup_dir.'*');
|
||||
$regexp = '/'.date('Y\-m\-d').'/';
|
||||
$result = Backup::daily();
|
||||
|
||||
// Controllo se esiste già un backup zip o folder creato per oggi
|
||||
if (!empty($folders)) {
|
||||
$found = false;
|
||||
foreach ($folders as $folder) {
|
||||
if (preg_match($regexp, $folder, $matches)) {
|
||||
$found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($found) {
|
||||
if (!isset($result)) {
|
||||
$_SESSION['infos'][] = tr('Backup saltato perché già esistente!');
|
||||
} elseif (empty($backup_dir)) {
|
||||
$_SESSION['errors'][] = tr('Non è possibile eseguire i backup poichè la cartella di backup non è stata impostata!!!');
|
||||
} elseif (directory($backup_dir) && do_backup()) {
|
||||
} elseif (!empty($result)) {
|
||||
$_SESSION['infos'][] = tr('Backup automatico eseguito correttamente!');
|
||||
} else {
|
||||
$_SESSION['errors'][] = tr('Errore durante la generazione del backup automatico!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'logout':
|
||||
|
@ -62,8 +51,10 @@ if (Auth::check() && isset($dbo) && $dbo->isConnected() && $dbo->isInstalled())
|
|||
exit();
|
||||
}
|
||||
|
||||
// Procedura di installazione
|
||||
include_once $docroot.'/include/configuration.php';
|
||||
|
||||
// Procedura di aggiornamento
|
||||
include_once $docroot.'/include/update.php';
|
||||
|
||||
$pageTitle = tr('Login');
|
||||
|
|
|
@ -209,106 +209,6 @@ function checkZip($zip_file)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Esegue il backup dell'intero progetto.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function do_backup($path = null)
|
||||
{
|
||||
global $backup_dir;
|
||||
|
||||
set_time_limit(0);
|
||||
|
||||
$path = !empty($path) ? $path : DOCROOT;
|
||||
|
||||
$backup_name = 'OSM backup '.date('Y-m-d').' '.date('H_i_s');
|
||||
if (
|
||||
(extension_loaded('zip') && directory($backup_dir.'tmp')) ||
|
||||
(!extension_loaded('zip') && directory($backup_dir.$backup_name))
|
||||
) {
|
||||
// Backup del database
|
||||
$database_file = $backup_dir.(extension_loaded('zip') ? 'tmp' : $backup_name).'/database.sql';
|
||||
backup_tables($database_file);
|
||||
|
||||
// Percorsi da ignorare di default
|
||||
$ignores = [
|
||||
'files' => [
|
||||
'config.inc.php',
|
||||
],
|
||||
'dirs' => [
|
||||
basename($backup_dir),
|
||||
'.couscous',
|
||||
'node_modules',
|
||||
'tests',
|
||||
],
|
||||
];
|
||||
|
||||
// Creazione dello zip
|
||||
if (extension_loaded('zip')) {
|
||||
$result = create_zip([$path, dirname($database_file)], $backup_dir.$backup_name.'.zip', $ignores);
|
||||
|
||||
// Rimozione cartella temporanea
|
||||
delete($database_file);
|
||||
}
|
||||
// Copia dei file di OSM
|
||||
else {
|
||||
$result = copyr($path, $backup_dir.$backup_name, $ignores);
|
||||
}
|
||||
|
||||
// Eliminazione vecchi backup se ce ne sono
|
||||
$max_backups = intval(get_var('Numero di backup da mantenere'));
|
||||
// Lettura file di backup
|
||||
if ($handle = opendir($backup_dir)) {
|
||||
$backups = [];
|
||||
while (false !== ($file = readdir($handle))) {
|
||||
// I nomi dei file di backup hanno questa forma:
|
||||
// OSM backup yyyy-mm-dd HH_ii_ss.zip (oppure solo cartella senza zip)
|
||||
if (preg_match('/^OSM backup ([0-9\-]{10}) ([0-9_]{8})/', $file, $m)) {
|
||||
$backups[] = $file;
|
||||
}
|
||||
}
|
||||
closedir($handle);
|
||||
|
||||
if (count($backups) > $max_backups) {
|
||||
// Fondo e ordino i backup dal più recente al più vecchio
|
||||
arsort($backups);
|
||||
$cont = 1;
|
||||
foreach ($backups as $backup) {
|
||||
if ($cont > $max_backups) {
|
||||
delete($backup_dir.'/'.$backup);
|
||||
}
|
||||
++$cont;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Funzione per fare il dump del database.
|
||||
*
|
||||
* @see http://davidwalsh.name/backup-mysql-database-php
|
||||
*
|
||||
* @param string $tables
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function backup_tables($file)
|
||||
{
|
||||
$config = App::getConfig();
|
||||
|
||||
$dump = new Ifsnop\Mysqldump\Mysqldump('mysql:host='.$config['db_host'].';dbname='.$config['db_name'], $config['db_username'], $config['db_password'], [
|
||||
'add-drop-table' => true,
|
||||
]);
|
||||
|
||||
$dump->start($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Individua la differenza tra le date indicate.
|
||||
* $interval può essere:
|
||||
|
@ -427,6 +327,7 @@ function getOS()
|
|||
'OS/2' => 'OS/2',
|
||||
'AIX' => 'AIX',
|
||||
];
|
||||
|
||||
foreach ($os as $key => $value) {
|
||||
if (strpos($_SERVER['HTTP_USER_AGENT'], $key)) {
|
||||
return $value;
|
||||
|
@ -859,18 +760,6 @@ function prepareToField($string)
|
|||
return str_replace('"', '"', $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restituisce la configurazione dell'installazione.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function getConfig()
|
||||
{
|
||||
include DOCROOT.'/config.inc.php';
|
||||
|
||||
return get_defined_vars();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restituisce se l'user-agent (browser web) è una versione mobile.
|
||||
*
|
||||
|
|
|
@ -28,7 +28,7 @@ switch (filter('op')) {
|
|||
break;
|
||||
|
||||
case 'backup':
|
||||
if (do_backup()) {
|
||||
if (Backup::create()) {
|
||||
$_SESSION['infos'][] = tr('Nuovo backup creato correttamente!');
|
||||
} else {
|
||||
$_SESSION['errors'][] = tr('Errore durante la creazione del backup!').' '.tr_replace('_DIR_', '"'.$backup_dir.'"', tr('Verifica che la cartella _DIR_ abbia i permessi di scrittura!'));
|
||||
|
|
|
@ -60,14 +60,12 @@ if (file_exists($backup_dir)) {
|
|||
$backups_zip = [];
|
||||
$backups_file = [];
|
||||
|
||||
$files = glob($backup_dir.'*');
|
||||
foreach ($files as $file) {
|
||||
//I nomi dei file di backup hanno questa forma:
|
||||
// OSM backup yyyy-mm-dd HH_ii_ss.zip (oppure solo cartella senza zip)
|
||||
if (preg_match('/^OSM backup ([0-9\-]{10}) ([0-9_]{8})\.zip$/', basename($file), $m)) {
|
||||
$backups_zip[] = $file;
|
||||
} elseif (preg_match('/^OSM backup ([0-9\-]{10}) ([0-9_]{8})$/', basename($file), $m)) {
|
||||
$backups_file[] = $file;
|
||||
$backups = Backup::getList();
|
||||
foreach ($backups as $backup) {
|
||||
if (ends_with($backup, '.zip')) {
|
||||
$backups_zip[] = $backup;
|
||||
} else {
|
||||
$backups_file[] = $backup;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,10 +76,6 @@ if (file_exists($backup_dir)) {
|
|||
'.tr('Se hai già inserito dei dati su OSM crealo il prima possibile...').'
|
||||
</div>';
|
||||
} else {
|
||||
// Ordino i backup dal più recente al più vecchio
|
||||
arsort($backups_zip);
|
||||
arsort($backups_file);
|
||||
|
||||
echo '
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-6">
|
||||
|
@ -90,13 +84,16 @@ if (file_exists($backup_dir)) {
|
|||
if (!empty($backups_zip)) {
|
||||
foreach ($backups_zip as $backup) {
|
||||
$name = basename($backup);
|
||||
preg_match('/^OSM backup ([0-9\-]{10}) ([0-9_]{8})\.zip$/', $name, $m);
|
||||
$info = Backup::readName($backup);
|
||||
|
||||
$data = $info['Y'].'-'.$info['m'].'-'.$info['d'];
|
||||
$ora = $info['H'].':'.$info['i'].':'.$info['s'];
|
||||
|
||||
echo '
|
||||
<div class="callout callout-info">
|
||||
<h4>'.tr('Backup del _DATE_ alle _TIME_', [
|
||||
'_DATE_' => Translator::dateToLocale($m[1]),
|
||||
'_TIME_' => Translator::timeToLocale(str_replace('_', ':', $m[2])),
|
||||
'_DATE_' => Translator::dateToLocale($data),
|
||||
'_TIME_' => Translator::timeToLocale($ora),
|
||||
]).'</h4>
|
||||
<p><small>
|
||||
'.tr('Nome del file').': '.$name.'<br>
|
||||
|
@ -127,13 +124,16 @@ if (file_exists($backup_dir)) {
|
|||
if (!empty($backups_file)) {
|
||||
foreach ($backups_file as $backup) {
|
||||
$name = basename($backup);
|
||||
preg_match('/^OSM backup ([0-9\-]{10}) ([0-9_]{8})$/', $name, $m);
|
||||
$info = Backup::readName($backup);
|
||||
|
||||
$data = $info['Y'].'-'.$info['m'].'-'.$info['d'];
|
||||
$ora = $info['H'].':'.$info['i'].':'.$info['s'];
|
||||
|
||||
echo '
|
||||
<div class="callout callout-warning">
|
||||
<h4>'.tr('Backup del _DATE_ alle _TIME_', [
|
||||
'_DATE_' => Translator::dateToLocale($m[1]),
|
||||
'_TIME_' => Translator::timeToLocale(str_replace('_', ':', $m[2])),
|
||||
'_DATE_' => Translator::dateToLocale($data),
|
||||
'_TIME_' => Translator::timeToLocale($ora),
|
||||
]).'</h4>
|
||||
<p><small>
|
||||
'.tr('Nome del file').': '.$name.'<br>
|
||||
|
|
|
@ -0,0 +1,339 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Classe per la gestione dei backup.
|
||||
*
|
||||
* @since 2.4
|
||||
*/
|
||||
class Backup
|
||||
{
|
||||
/** @var string Pattern per i nomi dei backup */
|
||||
const PATTERN = 'OSM backup Y-m-d H_i_s';
|
||||
|
||||
/** @var array Elenco delle varabili da sostituire nel pattern */
|
||||
protected static $replaces = [
|
||||
'Y' => [
|
||||
'regex' => '([0-9]{4})',
|
||||
],
|
||||
'm' => [],
|
||||
'd' => [],
|
||||
'H' => [],
|
||||
'i' => [],
|
||||
's' => [],
|
||||
];
|
||||
|
||||
/** @var array Elenco delle varabili che identificano i backup giornalieri */
|
||||
protected static $daily_replaces = [
|
||||
'Y', 'm', 'd',
|
||||
];
|
||||
|
||||
/**
|
||||
* Restituisce il percorso su previsto per il salvataggio dei backup.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getDirectory()
|
||||
{
|
||||
$result = App::getConfig()['backup_dir'];
|
||||
|
||||
$result = rtrim($result, '/');
|
||||
|
||||
if (!is_writable($result) || !directory($result)) {
|
||||
throw new UnexpectedValueException();
|
||||
}
|
||||
|
||||
return realpath($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restituisce il percorso su cui salvare temporeneamente il dump del database.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getDatabaseDirectory()
|
||||
{
|
||||
$result = self::getDirectory().'/database';
|
||||
|
||||
if (!directory($result)) {
|
||||
throw new UnexpectedValueException();
|
||||
}
|
||||
|
||||
return realpath($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restituisce l'elenco dei backup disponibili.
|
||||
*
|
||||
* @param string $pattern Eventuale pattern alternativo
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getList($pattern = null)
|
||||
{
|
||||
// Costruzione del pattern
|
||||
if (empty($pattern)) {
|
||||
$replaces = self::getReplaces();
|
||||
$regexs = array_column($replaces, 'regex');
|
||||
|
||||
$pattern = str_replace(array_keys($replaces), array_values($regexs), self::PATTERN);
|
||||
}
|
||||
|
||||
// Individuazione dei backup
|
||||
$backups = Symfony\Component\Finder\Finder::create()
|
||||
->name('/^'.$pattern.'/')
|
||||
->sortByName()
|
||||
->in(self::getDirectory());
|
||||
|
||||
$results = [];
|
||||
foreach ($backups as $backup) {
|
||||
$results[] = $backup->getRealPath();
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restituisce l'elenco delle variabili da sostituire normalizzato per l'utilizzo.
|
||||
*/
|
||||
protected static function getReplaces()
|
||||
{
|
||||
$replaces = self::$replaces;
|
||||
|
||||
foreach ($replaces as $key => $value) {
|
||||
if (!isset($replaces[$key]['value'])) {
|
||||
$replaces[$key]['value'] = date($key);
|
||||
}
|
||||
|
||||
if (!isset($replaces[$key]['regex'])) {
|
||||
$replaces[$key]['regex'] = '(.{'.strlen($replaces[$key]['value']).'})';
|
||||
}
|
||||
}
|
||||
|
||||
return $replaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restituisce il nome previsto per il backup successivo.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getNextName()
|
||||
{
|
||||
// Costruzione del pattern
|
||||
$replaces = self::getReplaces();
|
||||
$values = array_column($replaces, 'value');
|
||||
|
||||
$pattern = str_replace(array_keys($replaces), array_values($values), self::PATTERN);
|
||||
|
||||
return $pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restituisce i valori utilizzati sulle variabili sostituite.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function readName($string)
|
||||
{
|
||||
// Costruzione del pattern
|
||||
$replaces = self::getReplaces();
|
||||
$values = array_column($replaces, 'regex');
|
||||
|
||||
$pattern = str_replace(array_keys($replaces), array_values($values), self::PATTERN);
|
||||
|
||||
// Individuazione dei valori
|
||||
preg_match('/^'.$pattern.'/', basename($string), $m);
|
||||
|
||||
$keys = array_keys($replaces);
|
||||
|
||||
$results = [];
|
||||
for ($i = 1; $i < count($m); ++$i) {
|
||||
$results[$keys[$i - 1]] = $m[$i];
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Effettua il backup giornaliero.
|
||||
*
|
||||
* @return null|bool
|
||||
*/
|
||||
public static function daily()
|
||||
{
|
||||
// Costruzione del pattern
|
||||
$replaces = self::getReplaces();
|
||||
|
||||
foreach ($replaces as $key => $replace) {
|
||||
if (in_array($key, self::$daily_replaces)) {
|
||||
$replaces[$key] = $replace['value'];
|
||||
} else {
|
||||
$replaces[$key] = $replace['regex'];
|
||||
}
|
||||
}
|
||||
|
||||
$pattern = str_replace(array_keys($replaces), array_values($replaces), self::PATTERN);
|
||||
|
||||
// Individuazione dei backup
|
||||
$backups = self::getList($pattern);
|
||||
|
||||
// Creazione del backup eventuale
|
||||
if (empty($backups)) {
|
||||
return self::create();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Esegue il backup del progetto.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function create()
|
||||
{
|
||||
$backup_dir = self::getDirectory();
|
||||
$backup_name = self::getNextName();
|
||||
|
||||
set_time_limit(0);
|
||||
|
||||
// Backup del database
|
||||
$database_file = self::getDatabaseDirectory().'/database.sql';
|
||||
self::database($database_file);
|
||||
|
||||
// Percorsi da ignorare di default
|
||||
$ignores = [
|
||||
'files' => [
|
||||
'config.inc.php',
|
||||
],
|
||||
'dirs' => [
|
||||
basename($backup_dir),
|
||||
'.couscous',
|
||||
'node_modules',
|
||||
'tests',
|
||||
],
|
||||
];
|
||||
|
||||
// Lista dei file da inserire nel backup
|
||||
$files = Symfony\Component\Finder\Finder::create()
|
||||
->files()
|
||||
->exclude((array) $ignores['dirs'])
|
||||
->ignoreDotFiles(true)
|
||||
->ignoreVCS(true)
|
||||
->in(DOCROOT)
|
||||
->in(self::getDatabaseDirectory());
|
||||
|
||||
foreach ((array) $ignores['files'] as $value) {
|
||||
$files->notName($value);
|
||||
}
|
||||
|
||||
// Creazione backup in formato ZIP
|
||||
if (extension_loaded('zip')) {
|
||||
$result = self::zipBackup($files, $backup_dir.'/'.$backup_name.'.zip');
|
||||
}
|
||||
|
||||
// Creazione backup attraverso la copia dei file
|
||||
else {
|
||||
$result = self::folderBackup($files, $backup_dir.'/'.$backup_name);
|
||||
}
|
||||
|
||||
// Rimozione cartella temporanea
|
||||
delete($database_file);
|
||||
|
||||
self::cleanup();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Effettua il backup in formato ZIP.
|
||||
*
|
||||
* @param array $files Elenco dei file da includere
|
||||
* @param string $destination Nome del file ZIP
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected static function zipBackup($files, $destination)
|
||||
{
|
||||
if (!directory(dirname($destination))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$zip = new ZipArchive();
|
||||
|
||||
$result = $zip->open($destination, ZIPARCHIVE::CREATE);
|
||||
if ($result === true) {
|
||||
foreach ($files as $file) {
|
||||
$zip->addFile($file, $file->getRelativePathname());
|
||||
}
|
||||
|
||||
$zip->close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Effettua il backup attraverso la copia dei file.
|
||||
*
|
||||
* @param array $files Elenco dei file da includere
|
||||
* @param string $destination Nome della cartella
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected static function folderBackup($files, $destination)
|
||||
{
|
||||
if (!directory($destination)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = true;
|
||||
|
||||
// Filesystem Symfony
|
||||
$fs = new Symfony\Component\Filesystem\Filesystem();
|
||||
foreach ($files as $file) {
|
||||
$filename = $destination.DIRECTORY_SEPARATOR.$file->getRelativePathname();
|
||||
|
||||
// Copia
|
||||
try {
|
||||
$fs->copy($file, $filename);
|
||||
} catch (Symfony\Component\Filesystem\Exception\IOException $e) {
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Effettua il dump del database.
|
||||
*
|
||||
* @param string $file
|
||||
*/
|
||||
public static function database($file)
|
||||
{
|
||||
$config = App::getConfig();
|
||||
|
||||
$dump = new Ifsnop\Mysqldump\Mysqldump('mysql:host='.$config['db_host'].';dbname='.$config['db_name'], $config['db_username'], $config['db_password'], [
|
||||
'add-drop-table' => true,
|
||||
]);
|
||||
|
||||
$dump->start($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rimuove i backup più vecchi.
|
||||
*/
|
||||
public static function cleanup()
|
||||
{
|
||||
$max_backups = intval(Settings::get('Numero di backup da mantenere'));
|
||||
|
||||
$backups = self::getList();
|
||||
$count = count($backups);
|
||||
|
||||
// Rimozione dei backup più vecchi
|
||||
for ($i = 0; $i < $count - $max_backups; ++$i) {
|
||||
delete($backups[$i]);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue