Sauvegarde automatisée de vos bases de données

Soyez le premier à donner votre avis sur cette source.

Snippet vu 18 280 fois - Téléchargée 63 fois

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

Ajouter un commentaire

Commentaires

Messages postés
1
Date d'inscription
jeudi 28 décembre 2006
Statut
Membre
Dernière intervention
25 novembre 2010

Salut, je reçois le zip vide, et même sur le serveur ftp le zip est vide, quelqu'un aurait une idée ?
Messages postés
2
Date d'inscription
vendredi 29 janvier 2010
Statut
Membre
Dernière intervention
9 février 2010

Bonjour, je fais suite à mon post précédent : J'ai résolu le pb en renseignant les chemins complets dans le script
par exemple :

// test d'écriture (ne devrait en principe pas échouer)
if (!$handle = fopen('/media/portail/_Backups/sql/'.$filename, 'w'))

Car en fouinant sur le net j'ai vu qu'il est préférable de faire ça quand on lance un script par cron... Ca peut peut-être servir à quelqu'un... En tout cas pour moi ça a fonctionné (et ça fonctionne toujours d'ailleurs ;-) ).

Autre question : Est-ce que quelqu'un a testé une restauration d'une base sauvegardée par ce script? Avec quelle méthode?
Moi j'ai essayé un import via phpmyadmin, et ça plante : Ca crée bien la bdd et c'est tout. Le message d'erreur indique qu'aucune base n'est sélectionnée. J'ai donc rajouter une ligne dans la partie "en-tête du fichier d'export pour la base en cours" tel que suis :

// 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";
$sql_header .= 'USE '.$row->Database.";\n\n";

J'ai donc rajouté la dernière ligne afin de faire une sélection de base lors de la restauration. J'ai testé ensuite et ça fonctionne avec phpmyadmin... (Toujours au cas où quelqu'un rencontre ce même pb).

Voili voilou. Si quelqu'un a des commentaires, des conseils, ou une autre solution, je suis preneur.

Bonne continuation!
$teph
Messages postés
2
Date d'inscription
vendredi 29 janvier 2010
Statut
Membre
Dernière intervention
9 février 2010

Salut, et avant toutes choses : Un grand bravo pour ton précieux code!

Je précise d'entrée que je suis novice en PHP et en linux (ce qui a son importance pour la suite).
J'ai donc adapté ton code à mes besoins : Nickel
Quand j'appel le fichier php via un browser : Nickel
Quand j'execute le fichier ./monfichier.php que j'ai d'abord rendu executable : Nickel
Quand je veux le lancer à partir du crontab : il plante à l'étape de vérification de la disponibilité en écriture de /sql (je reçois donc un mail avec l'erreur indiquée).
Quand je regarde le mail reçu du cron (j'ai masqué quelques infos par des ##):
=======================================================================
Date: Fri, 29 Jan 2010 14:44:01 +0100
Message-Id: <201001291344.o0TDi18g001560@######.#######.####>
From: root@######.#######.#### (Cron Daemon)
To: root@######.#######.####
Subject: Cron <root@######> php /media/portail/_Backups/backupMYSQL.php
Content-Type: text/plain; charset=UTF-8
X-Cron-Env: <SHELL=/bin/sh>
X-Cron-Env: <HOME=/root>
X-Cron-Env:
X-Cron-Env: <LOGNAME=root>

Impossible d'ouvrir un ficher en écriture dans le répertoire "sql/"
Fin de la sauvegarde....
======================================================================
j'ai pourtant bien passé le dossier sql/ en chmod 1777 (avec le stickybit donc)
Je suis sous ubuntu 8.04 server. J'ai testé avec le crontab de l'utilisateur, et même du root et même pb.
J'ai créé un petit script qui ne fait que créer un repertoire test dans le dossier sql/ > Exactement pareil (ok via browser ou en ./test.php mais pas via crontab)

Voici quelques précisions :
---contenu du test.php :

#!/usr/bin/php
<?php
mkdir ("media/portail/_Backups/sql/test", 0777);
?>

---lancement du crontab :
$ sudo crontab -e

---ligne ajoutée au crontab (lancement toutes les minutes pour le test) :
*/1 * * * * php /media/portail/_Backups/backupMYSQL.php

Voila, où j'en suis, et là... Je bloque complétement... J'ai l'impression d'avoir tout essayé. C'est comme si le crontab n'avait pas le droit de créer des dossiers...
Est-ce que quelqu'un a une idée svp?
Merci d'avance

$teph
Messages postés
7
Date d'inscription
mardi 17 juin 2008
Statut
Membre
Dernière intervention
16 octobre 2009

Salut,

J'essaye en vain de faire marcher ta source chez OVH, j'ai du peut être louper quelques choses, car je mets le dossier / fichier en 777 (pour faire des essais), j'ai bien configurer les 3 petites lignes en bas de la source, puis quand je lance, bim Erreur 500 (d'entrée de jeux, donc c'est pas du à un time-out...

Aurais tu une idée de se qu'il faudrait que je modifie pour eviter cette erreur ?

A bientôt ^^
Messages postés
4
Date d'inscription
lundi 9 avril 2007
Statut
Membre
Dernière intervention
22 mars 2009

J'ai demandé sur le site du zero, mais il m'ont dis de voir ici.
Afficher les 46 commentaires

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.