Sauvegarde automatisée de vos bases de données

Contenu du snippet

N'ayant pas trouvé mon bonheur auparavant pour sauvegarder mes bases de données, je m'y suis mis.

Ce code a été fait pour créer automatiquement un fichier de sauvegarde avec une copie de vos bases de données, au format zip. Ce zip est ensuite stocké dans un répertoire protégé sur un site distant. Un mail peut confirmer l'opération et le zip peut également être envoyé en fichier joint. Le tout, idéalement, doit être utiliser via CRON (ou webcron.org).

Vous devez bien sûr avoir accès à un site distant! Ceci dit, même si vous n'avez pas un accès, vous pouvez retirer la partie FTP du code et utiliser l'envoi par email du zip.

Pour ma part, tous les jeudis à 2h du matin, le fichier est exécuté et je reçois un email confirmant si tout s'est bien passé ou non.

En espérant que ce code vous soit utile... à moi il l'est :-)

Source / Exemple :


<?php

function saveDB($mysql_array,$ftp_array,$mail_array = array()){

	// données de connexion au serveur où se trouvent les bases de données
	$mysql_host = $mysql_array[0];
	$mysql_user = $mysql_array[1];
	$mysql_password = $mysql_array[2];
	// données de connexion au serveur où sera mis la sauvegarde des bases de données
	$ftp_server = $ftp_array[0];
	$ftp_user_name = $ftp_array[1];
	$ftp_user_pass = $ftp_array[2];
	$ftp_dest_folder = $ftp_array[3];
	// désactiver le rapport d'erreurs afin de ne pas les afficher
	error_reporting(0);
	$err_msg = '';
	// nombre maximum d'INSERT écrit par fichier avant de libérer la variable sql_export
	$maxinsert = 500;
	
	// si on a demandé un mail à la fin...	
	if (!empty($mail_array)){
		$mail = $mail_array[0];
		// $attachement == 1 => la sauvegarde sera également envoyée par mail. ATTENTION au poids du fichier!
		$attachement = $mail_array[1];
	}
	
	/* 
		PARTIE MYSQL - on dump toutes les bases et on les met dans un zip 

  • /
// librairie pour zipper include_once('pclzip.lib.php'); // test de connection if(!$link = mysql_connect($mysql_host,$mysql_user,$mysql_password)) $err_msg .= 'Impossible de se connecter à '.$mysql_host."\n"; else{ // Vérification de la disponibilité en écriture du répertoire "sql/" // is_writable() retourne TRUE seulement si le fichier testé existe et est accessible en écriture, // ce qui ne convient pas ici puisque le répertoire est vide $testfile = 'sql/'.uniqid(mt_rand()).'.tmp'; if (!$handle = fopen($testfile,'w')) $err_msg .= 'Impossible d\'ouvrir un fichier en écriture dans le répertoire "sql/"'."\n"; else fclose($handle); if (file_exists($testfile)) unlink($testfile); if ($err_msg == ''){ // liste des base de données $db_list = mysql_list_dbs($link); // tableau pour les noms des bases de données $names = array(); // dump des bases de données while ($row = mysql_fetch_object($db_list)) { if ($row->Database != 'information_schema'){ $q = 'SHOW CREATE DATABASE `'.($row->Database).'`'; $r = mysql_query($q); while ($s=mysql_fetch_object($r)){ // ouverture du fichier pour stocker la base en cours (une base de données par fichier) $filename = ($row->Database).'_'.date('Ymd').'.sql'; $names[] = $filename; // test d'écriture (ne devrait en principe pas échouer) if (!$handle = fopen('sql/'.$filename, 'w')) $err_msg .= 'Impossible d\'ouvrir en écriture le fichier sql de '.($row->Database)."\n"; else{ // en-tête du fichier d'export pour la base en cours $sql_header = '###############################################'."\n"; $sql_header .= '## BASE DE '.($row->Database)."\n"; $sql_header .= '###############################################'."\n\n"; $sql_header .= $s->{'Create Database'}.";\n\n"; if (fwrite($handle, $sql_header) === FALSE) $err_msg .= 'Impossible d\'écrire dans le fichier sql de '.($row->Database)."\n"; else{ mysql_select_db($row->Database, $link); // listing des tables de la base de données courante $q3 = 'SHOW TABLES'; $r3 = mysql_query($q3); while ($s3 = mysql_fetch_object($r3)){ // structure de chaque table $table = $s3->{'Tables_in_'.($row->Database)}; $q4 = 'SHOW CREATE TABLE `'.$table.'`;'; $r4 = mysql_query($q4); while ($s4 = mysql_fetch_object($r4)){ $sql_export = ''; $sql_export .= '## TABLE `'.$table.'`'."\n\n"; $sql_export .= $s4->{'Create Table'}.";\n\n"; // construction des INSERT $q5 = 'SELECT * FROM `'.$table.'`;'; $r5 = mysql_query($q5); // slashes en fonction du type de colonne $slashes = array(); $nbfields = mysql_num_fields($r5); for($i=0; $i < $nbfields; $i++) if (in_array(mysql_field_type($r5, $i), array('string','blob','date','timestamp'))) $slashes[$i] = '\''; $count = 0; while($s5 = mysql_fetch_array($r5)){ $count++; $sql_export .= 'INSERT INTO `'.$table.'` VALUES('; $sql_export .= $slashes[0].mysql_real_escape_string($s5[0]).$slashes[0]; for($i=1; $i < $nbfields; $i++) $sql_export .= ', '.$slashes[$i].mysql_real_escape_string($s5[$i]).$slashes[$i]; $sql_export .= ");\n"; if($count == $maxinsert){ if (fwrite($handle, $sql_export) === FALSE){ $err_msg .= 'Erreur d\'exportation avec la '.$table.' de la base '.($row->Database)."\n"; // mettre un break 3 ici pour stopper le tout. Je ne préfère pas personnellement } else $sql_export = ''; } } $sql_export .= "\n"; // Pour chaque table, on écrit le résultat dans le fichier if (fwrite($handle, $sql_export) === FALSE) $err_msg .= 'Erreur d\'exportation avec la '.$table.' de la base '.($row->Database)."\n"; } } } fclose($handle); } } } } } } // si on a pu dumper les bases if ($err_msg == ''){ // création d'un zip avec les bases de données chdir('sql/'); $archs = 'dbsave_'.date('Ymd').'.zip'; $archive = new PclZip($archs); $cnames = implode(',', $names); $v_list = $archive->create($cnames); if ($v_list == 0) $err_msg .= 'Impossible de créer le fichier ZIP "'.$archs.'"'."\n"; else for ($i=0;$i<count($names);$i++) // on efface les fichiers temporaires unlink($names[$i]); } /* PARTIE FTP - on transfert le zip sur notre serveur de sauvegarde (on ne sauvegarde pas sur le même serveur, logique!)
  • /
// si on a pu zipper if ($err_msg == ''){ // connection ftp au site distant $conn_id = ftp_connect($ftp_server); $login_result = ftp_login($conn_id, $ftp_user_name, $ftp_user_pass); if ((!$conn_id) || (!$login_result)) $err_msg .= 'Impossible de se connecter au serveur '.$ftp_server."\n"; else{ $source_file = $archs; $destination_file = $archs; // copie du zip sur le serveur distant if (!$upload = ftp_put($conn_id, $ftp_dest_folder.$destination_file, $source_file, FTP_BINARY)) $err_msg .= 'Impossible de transférer le fichier '.$archs.' sur '.$ftp_server."\n"; } // Récupère la liste des fichiers sur le serveur distant $contents = ftp_nlist($conn_id, $ftp_dest_folder); // Vérifie l'espace disque utilisé par les sauvegardes $size = ''; for ($i=0;$i<count($contents);$i++) $size += ftp_size($conn_id, $contents[$i]); if (1024 > $size && $size >=1)// si c'est compris entre 1B et 1KB $sz = round($size,2).' octets'; if (1024 > ($size/1024) && ($size/1024) >=1)// si c'est compris entre 1KB et 1MB $sz = round(($size/1024),2).' KB'; if (1024 > ($size/(1024*1024)) && ($size/(1024*1024)) >=1)// si c'est compris entre 1MB et 1GB $sz = round(($size/(1024*1024)),2).' MB'; ftp_close($conn_id); } /* PARTIE ENVOIE DE MAIL
  • /
if (!empty($mail_array)){ if (file_exists($archs)){ $fp = fopen($archs, 'rb'); $content = fread($fp, filesize($archs)); $checksum = md5($content); fclose($fp); } $sujet = 'Sauvegarde journalière du '.date("d/m/Y"); if ($err_msg == ''){ $msg = $sujet.' terminée avec succès.'."\n\n".'Serveur : http://'.$_SERVER['SERVER_NAME']."\n".'Fichier : '.$archs."\n".'Checksum : '.$checksum."\n".'URL : http://'.$ftp_server.'/sql/'.$archs."\n"; $sujet = '[SUCCES] '.$sujet; } else{ $msg = 'La sauvegarde n\'a pas fonctionné sur le serveur http://'.$_SERVER['SERVER_NAME'].".\n\n".'Les erreurs suivantes ont été identifiées:'."\n\n".$err_msg; $sujet = '[ERREURS] '.$sujet; } $headers = "From: DBSave<dbsave@pouet.com>\n"; $headers .= "MIME-Version: 1.0\n"; $stat = ''; if ($err_msg == ''){ $stat .= "\n\n".'####### STATISTIQUES'."\n"; $stat .= 'Le répertoire de sauvegarde contient '.count($contents).' fichiers (taille totale: '.$sz.')'."\n"; for ($i=0;$i<count($contents);$i++) $stat .= '- '.substr($contents[$i],strlen($ftp_dest_folder))."\n"; } $msg .= $stat; if ($attachement == 1){ $boundary = '-----='.md5(uniqid(rand())); // création du fichier joint if (file_exists($archs)){ $content_encode = chunk_split(base64_encode($content)); $headers .= "Content-Type: multipart/mixed; boundary=\"$boundary\""; $message = "\n\n"; $message .= "--" . $boundary . "\n"; $message .= "Content-Type: text/plain; charset=\"iso-8859-1\"\n"; $message .= "Content-Transfer-Encoding: 8bit\n\n"; $message .= $msg; $message .= "--" . $boundary . "\n"; $message .= "Content-Type: application/zip; name=\"$archs\"\n"; $message .= "Content-Transfer-Encoding: base64\n"; $message .= "Content-Disposition: attachment; filename=\"$archs\"\n\n"; $message .= $content_encode . "\n"; $message .= "\n\n"; $message .= "--" . $boundary . "--\n"; } else $message = $msg; } } // on purge le répertoire sql/ unlink($source_file); // on envoit le mail if (!empty($mail_array)) mail($mail,$sujet,$message,$headers); } // site destination $ftp_data = array('www.abcdefg.com','abc','123','/abcdefg.com/sql/'); // site source $mysql_data = array('localhost','yxcvbn','456'); // 0: mail de confirmation sans fichier joint, 1: mail de confirmation avec fichier joint (le zip) $mail_data = array('mon@email.com',1); // saveDB($mysql_data,$ftp_data) sauvegarde et n'envoit pas de mail saveDB($mysql_data,$ftp_data,$mail_data); ?>

Conclusion :


Pour utiliser ce code de manière optimale:

1. créer un fichier saveDB.php contenant le code.

2. créer un répertoire sur le site distant ftp avec les droits en écriture et créer un .htaccess et un .htpasswd pour le répertoire de sauvegarde ftp
Vous devriez avoir la structure suivante:
[path]/repertoire_de_sauvegarde_ftp/.htaccess et.htpasswd

3. adapter saveDB.php à vos besoins et le mettre sur le serveur contenant les bd à sauvegarder dans un répertoire ("sql/" ici):
$ftp_data = array('www.votresiteftp.com','votre_user_ftp','votre_mot_de_passe_ftp','/repertoire_de_sauvegarde_ftp/');
$mysql_data = array('localhost','votre_user','votre_mot_de_passe');
$mail_data = array('votre@email.com',0); // 0: juste envoyer un mail de confirmation, 1: mettre en fichier joint le zip

--> Ne pas oublier de remplacer dans le code "sql/" par le bon répertoire

4. protéger le répertoire "sql/" par un .htaccess et un .htpasswd
Vous devriez avoir la structure suivante:
[path]/repertoire_avec_saveDB.php/.htaccess et .htpasswd et saveDB.php et pclzip.lib.php et le répertoire sql/ (cf fichier joint)

5. si vous avez un accès à cron, ajouter la ligne "0 2 * * 4 php -f /chemin_a_saveDB/saveDB.php" par exemple pour exécuter ce script tous les jeudis à 2h du matin, sinon vous pouvez utiliser les services de webcron.org

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.