<h2>But de ce document</h2>
<p class="spip">Ce document contient quelques programmes pour sauvegarder et dupliquer automatiquement une base de données MySQL d’un site internet sur un serveur local.</p>

<p class="spip">Le but final étant de dupliquer un site sous Spip ou un Wikini ou tout autre programme en PHP+MySQL d’un serveur distant sur un serveur local sans aucune intervention manuelle.</p>

<p class="spip">Ce premier article donne une solution pour dupliquer la base de données. L’article suivant donnera une solution pour dupliquer tous les fichiers (.html, .php, .jpg,...) du site distant sur le site local.</p>

<h2>Objectifs de cette sauvegarde </h2>
<p class="spip">La duplication de la base de données doit pouvoir se réaliser sans aucune intervention, car le programmer sera placé dans une Crontab d’un poste sous Linux et sera donc lancé automatiquement tous les jours à heure fixe.</p>

<p class="spip">De plus, cette duplication doit fonctionner avec un simple accès FTP au serveur (Pas d’accès total de type SSH).</p>

<h2>Sauvegarde de la base de données</h2>
<p class="spip">Pour sauvegarder la base de données de Spip, il est possible d’utiliser la procédure manuelle en passant par l’interface utilisateur, (Sauvegarde dans un fichier XML), mais il n’est pas possible d’automatiser cette procédure.</p>

<p class="spip">Pour le Wikini, il n’existe pas de procédure de sauvegarde et il faut obligatoirement passer par un autre programme comme phpMyAdmin pour arriver à faire une sauvegarde de la base de données. Mais une nouvelle fois, il n’est pas possible d’automatiser la procédure et celle-ci est relativement longue, compliquée et dangereuse.</p>

<p class="spip">De plus, l’installation de PhpMyAdmin sur un serveur ouvre des failles de sécurité qui pourraient compromettre les données.</p>

<h2>Programme pour faire un dump d’une table MySQL</h2>
<p class="spip">Sur un serveur sans accès SSH, la seule solution à ma connaissance pour sauvegarder la base de données est de passer par un programme PHP, qui va faire un dump de la table dans un fichier texte. Ensuite, il faudra récupérer par FTP ce fichier texte pour l’utiliser pour créer la table dans la base de données locale.</p>

<p class="spip">Ce premier programme va permettre de sauvegarder dans un fichier texte le contenu d’une table de MySQL.</p>

<p class="spip">Il serait possible de créer un script permettant de sauvegarder toutes les tables de la base de données, mais ce script serait probablement trop long à s’exécuter et le serveur Web abandonnerait et  enverrait une erreur avant d’avoir terminé. C’est pour cela que j’ai réalisé un script permettant de sauvegarder uniquement une table. Ce script sera ensuite appelé autant de fois que nécessaire pour sauvegarder toutes les tables.</p>

<p class="spip">Pour faire fonctionner ce programme, il faut que le serveur html puisse créer et accéder en écriture au fichier « sqldump.sql ». Si ce n’est pas le cas, il faut créer ce fichier manuellement et lui donner un accès complet.</p>

<p class="spip">Ensuite, il faut renseigner le nom du serveur, l’utilisateur, le mot de passe et la base de données à utiliser pour effectuer la sauvegarde.</p>

<p class="spip">Il est possible de préciser le nombre maximum de lignes à transférer (10000 par défaut)</p>

<p class="spip">Pour finir, il faut placer ce programme sur le serveur à sauvegarder.</p>

<p class="spip">Voici le programme PHP nommé « <strong class="spip">save_mysql_distant.php</strong> »</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code><br />
<br />
<?php<br />
<br />
$Serveur    ="www.monserveur.com";  //Serveur contenant la base de données<br />
<br />
$Utilisateur="MonUtilisateur";      //Utilisateur de connexion à la base de données<br />
<br />
$MotDePasse ="MonMotDePasse";       //Mot de passe<br />
<br />
$Base       ="MaBase";              //Nom de la base de données<br />
<br />
<br />
<br />
$NbLigMax=10000; //Nombre maximum d'enregistrements à sauvegarder <br />
<br />
$LeFichier="sqldump.sql"; //Nom du fichier contenant le dump de la table<br />
<br />
<br />
<br />
$LaTable =$HTTP_GET_VARS['LaTable']; //** Nom de la table passé en paramètre<br />
<br />
<br />
<br />
if ($LaTable=="") {<br />
<br />
        echo "Le paramètre LaTable est obligatoire !";<br />
<br />
        exit;<br />
<br />
}<br />
<br />
<br />
<br />
$Donnees=mysql_structure($Serveur,$Utilisateur,$MotDePasse,$Base,$LaTable,$NbLigMax);<br />
<br />
                                         <br />
<br />
$fp = fopen($LeFichier, "w+");<br />
<br />
if(!$fp) {<br />
<br />
        echo "Impossible d'écrire dans le fichier $LeFichier !";<br />
<br />
        exit;<br />
<br />
}<br />
<br />
$NbCar=fputs($fp, $Donnees);<br />
<br />
fclose($fp);<br />
<br />
                                                                                                 <br />
<br />
function mysql_structure($Serveur, $Utilisateur, $MotDePasse, $Base, $LaTable, $NbLigMax) {<br />
<br />
        $ct=0;<br />
<br />
        mysql_connect($Serveur, $Utilisateur, $MotDePasse)<br />
<br />
                or die ("Impossible de se connecter à $Serveur sous $Utilisateur \n");<br />
<br />
        mysql_select_db($Base);<br />
<br />
        $Resultat = mysql_query("SHOW CREATE TABLE $LaTable");<br />
<br />
        if ($Resultat) {<br />
<br />
                $insertions = "";<br />
<br />
                $tableau = mysql_fetch_array($Resultat);<br />
<br />
                $tableau[1] .= ";";<br />
<br />
                $dumpsql[] = str_replace("\n", "", $tableau[1]);<br />
<br />
                $req_table = mysql_query("SELECT * FROM $LaTable");<br />
<br />
                $nbr_champs = mysql_num_fields($req_table);<br />
<br />
                $nb_lig = mysql_num_rows($req_table);<br />
<br />
                while ($ligne = mysql_fetch_array($req_table) and $ct<$NbLigMax) {<br />
<br />
                        $insertions .= "INSERT INTO $LaTable VALUES(";<br />
<br />
                        for ($i=0; $i<=$nbr_champs-1; $i++) {<br />
<br />
                                $insertions .= "'" . mysql_real_escape_string($ligne[$i]) . "', ";<br />
<br />
                        }<br />
<br />
                        $insertions = substr($insertions, 0, -2);<br />
<br />
                        $insertions .= ");\n";<br />
<br />
                        $ct++;<br />
<br />
                }<br />
<br />
                if ($insertions != "") $dumpsql[] = $insertions;<br />
<br />
        }<br />
<br />
        if ($NbLigMax<$nb_lig) <br />
<br />
                echo "ATTENTION : $NbLigMax / $nb_lig lignes transférées pour ".trim($LaTable); <br />
<br />
        else  echo "$LaTable -> ".($nb_lig/1)." lignes transférées.";<br />
<br />
        $dumpsql[] = "\n ";<br />
<br />
        return implode("\r", $dumpsql);<br />
<br />
}<br />
<br />
?><br />
<br />
</code></div>
<h2>Sécuriser l’accès au programme.</h2>
<p class="spip">Ce programme ouvre une faille de sécurité sur le serveur, car il permet de sauvegarder n’importe quelle table et ensuite de télécharger le fichier « sqldump.sql ».</p>

<p class="spip">Il est donc fortement conseillé de placer ce programme dans un dossier sécurisé par un fichier « .htaccess ».</p>

<p class="spip">La sécurisation d’un dossier sur le serveur dépasse le cadre de ce mémo.</p>

<h2>Exécuter ce programme</h2>
<p class="spip">Pour exécuter ce programme, il suffit de saisir son adresse dans un navigateur sans oublier de mettre en paramètre le nom de la table à sauvegarder :</p>

<p class="spip"><code class='spip_code' dir='ltr'>http://www.monserveur.com/save/save_mysql_distant.php?LaTable=MaTable</code></p>

<p class="spip">Pour exécuter ce programme en ligne de commande, il faut utiliser un navigateur qui fonctionne en ligne de commande comme « lynx ».</p>

<p class="spip">Si l’accès au programme n’est pas sécurisé, la commande est la suivante :</p>

<p class="spip"><code class='spip_code' dir='ltr'>lynx -dump http://www.monserveur.com/save/save_mysql_distant.php?LaTable=MaTable</code></p>

<p class="spip">Si l’accès au programme est sécurisé, la commande est la suivante :</p>

<p class="spip"><code class='spip_code' dir='ltr'>lynx -dump http://www.monserveur.com/save/save_mysql_distant.php?LaTable=MaTable -auth=LeLogin:LeMotDePasse        </code></p>

<h2>Obtenir la liste des tables de la base de données</h2>
<p class="spip">Le programme précédent, permet uniquement de sauvegarder une table. Donc, il est nécessaire de connaître la liste des tables pour pouvoir sauvegarder toute la base de données.</p>

<p class="spip">Le programme suivant qu’il faut placer également sur le serveur et de préférence dans un dossier sécurisé permet d’obtenir la liste des tables.</p>

<p class="spip">Il faut renseigner le nom du serveur, l’utilisateur, le mot de passe et la base de données à interroger.</p>

<p class="spip">Il est possible de préciser une liste de tables à exclure de la liste.</p>

<p class="spip">Voici le programme PHP nommé « <strong class="spip">save_mysql_liste_tables.php</strong> »</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code><br />
<br />
<?php<br />
<br />
<br />
<br />
$Serveur    ="www.monserveur.com";  //Serveur contenant la base de données<br />
<br />
$Utilisateur="MonUtilisateur";      //Utilisateur de connexion à la base de données<br />
<br />
$MotDePasse ="MonMotDePasse";       //Mot de passe<br />
<br />
$Base       ="MaBase";              //Nom de la base de données<br />
<br />
<br />
<br />
//** Liste des tables à exclure de la sauvegarde ***************<br />
<br />
$exclure_table=array(<br />
<br />
        "spip_visites_articles",<br />
<br />
        "spip_index_articles");<br />
<br />
//*************************************************************<br />
<br />
<br />
<br />
mysql_connect($Serveur, $Utilisateur, $MotDePasse)<br />
<br />
        or die ("Impossible de se connecter à $Serveur sous $Utilisateur \n");<br />
<br />
mysql_select_db($Base);<br />
<br />
$tables = mysql_list_tables($Base);<br />
<br />
while ($donnees = mysql_fetch_array($tables))  {<br />
<br />
        $table = $donnees[0];<br />
<br />
        if (in_array($table,$exclure_table)==FALSE) echo "$table<br>";<br />
<br />
}<br />
<br />
?><br />
<br />
</code></div>
<p class="spip">Ce programme peut également se lancer en ligne de commande avec Lynx :</p>

<p class="spip"><code class='spip_code' dir='ltr'>lynx -dump http://www.monserveur.com/save/save_mysql_liste_tables.php </code></p>

<h2>Dupliquer la base de données distante sur un serveur locale.</h2>
<p class="spip">Ce programme va donc utiliser les deux programmes précédents pour obtenir la liste des tables et sauvegarder chaque table individuellement.</p>

<p class="spip">Chaque table sauvegardée sera ensuite téléchargée en local via un accès FTP et sera intégrée dans la base de données du serveur local.</p>

<p class="spip">Ce programme contrairement aux deux précédents, sera lancé en local et non pas sur un serveur distant, ce qui permet de le lancer directement en tant que script et non pas à travers un navigateur.</p>

<p class="spip"><strong class="spip">ATTENTION</strong> : Ce programme commence par supprimer la table locale avant de la recréer à partir des données du serveur distant.</p>

<p class="spip">Voici le programme PHP nommé « <strong class="spip">save_mysql_local.php</strong> »</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code><br />
<br />
#!/usr/bin/php<br />
<br />
<?php<br />
<br />
<br />
<br />
$Serveur    ="www.monserveur.com";  //Serveur contenant la base de données et le serveur ftp<br />
<br />
$Utilisateur="MonUtilisateur";      //Utilisateur de connexion à la base de données<br />
<br />
$MotDePasse ="MonMotDePasse";       //Mot de passe<br />
<br />
$Base       ="MaBase";              //Nom de la base de données<br />
<br />
<br />
<br />
$ServeurDistant    ="www.monserveur.com"; //Serveur distant contenant la base de données<br />
<br />
$LoginDistant      ="MonUtilisateur1";    //Utilisateur de connexion à la base de données<br />
<br />
$MotDePasseDistant ="MonMotDePasse1";     //Mot de passe<br />
<br />
<br />
<br />
$ServeurLocal    = 'localhost';           //Serveur local contenant la base de données<br />
<br />
$LoginLocal      = 'MonUtilisateur2';     //Utilisateur de connexion à la base de données<br />
<br />
$MotDePasseLocal = 'MonMotDePasse2';      //Mot de passe<br />
<br />
$BaseLocal       = 'MaBase';              //Nom de la base de données<br />
<br />
<br />
<br />
$MotDePasseSecurite="MonMotDePasse3";     //Mot de passe pour l'accès au site sécurisé<br />
<br />
<br />
<br />
chdir("/tmp"); //Dossier de téléchargement du fichier "sqldump.sql"<br />
<br />
<br />
<br />
//** Récupération de la liste des tables en lancant un script distant *******************<br />
<br />
$LesTables=`lynx -dump http://www.coagul.org/save-site/save_mysql_liste_tables.php -auth=$LoginDistant:$MotDePasseSecurite`;<br />
<br />
$TabTables=explode("\n",$LesTables);<br />
<br />
<br />
<br />
echo count($TabTables)." tables à transférer... \n";<br />
<br />
$TabTables[]="Vide"; //** Permet de vider le fichier sqldump.sql<br />
<br />
<br />
<br />
for ($i=0;$i<count($TabTables);$i++) {<br />
<br />
        //** Lancement du script distant pour sauvegarder la table indiquée *************<br />
<br />
        $table=trim($TabTables[$i]);<br />
<br />
        if ($table!="") {<br />
<br />
                //echo "Sauvegarde de la table $table en cours... \n";<br />
<br />
                $resultat=`lynx -dump http://www.coagul.org/save-site/save_mysql_distant.php?LaTable=$table -auth=$LoginDistant:$MotDePasseSecurite`;<br />
<br />
                echo trim($resultat)."\n";<br />
<br />
        <br />
<br />
                //** Téléchargement via ftp du fichier sauvegardé *******************************<br />
<br />
                $NomFichier="sqldump.sql";<br />
<br />
                $Connexion=ftp_connect($ServeurDistant)<br />
<br />
                        or die ("Serveur $ServeurDistant non disponible ! \n");<br />
<br />
                ftp_login($Connexion, $LoginDistant, $MotDePasseDistant)<br />
<br />
                        or die ("Utilisateur $LoginDistant inconnu ou mot de passe non valide ! \n");<br />
<br />
                ftp_chdir($Connexion, "html/save-site");<br />
<br />
                ftp_get ($Connexion, $NomFichier, $NomFichier, FTP_BINARY);<br />
<br />
                $ftpclose=ftp_close ($Connexion);<br />
<br />
                <br />
<br />
                //** Connexion à la base de données locale **************************************<br />
<br />
                mysql_connect($ServeurLocal, $LoginLocal, $MotDePasseLocal)<br />
<br />
                        or dir ("Connexion impossible à $ServeurLocal ! \n");<br />
<br />
                mysql_select_db($BaseLocal)<br />
<br />
                        or die ("Impossible de sélectionner la base $BaseLocal ! \n");<br />
<br />
                <br />
<br />
                //Suprresion de la table local et chargement des nouvelles données **************        <br />
<br />
                mysql_query ("DROP TABLE $table");<br />
<br />
                `cat "$NomFichier" | mysql -u $LoginLocal -p$MotDePasseLocal $BaseLocal`;<br />
<br />
        }<br />
<br />
}<br />
<br />
?><br />
<br />
</code></div>
<h2>Lancement automatique du programme </h2>
<p class="spip">Pour exécuter le programme précédent, il suffit de le lancer en ligne de commande :</p>

<p class="spip"><code class='spip_code' dir='ltr'>$ ./save_mysql_local.php</code></p>

<p class="spip">Pour exécuter ce programme automatiquement à heure fixe, il faut le placer dans un fichier crontab.</p>

<p class="spip">Pour modifier le fichier crontab d’un utilisateur, il faut saisir la commande suivante :</p>

<p class="spip"><code class='spip_code' dir='ltr'>$ crontab -e</code></p>

<p class="spip">Par exemple pour lancer le programme tous les jours à 22:30, il faut écrire la ligne suivante dans le crontab :</p>

<p class="spip">30 22 * * * /LeCheminDuProgramme/save_mysql_local.php</p>

<h2>Historique des modifications</h2>
<table class="spip">
<thead><tr class='row_first'><th scope='col'><strong class="spip">Version</strong></th><th scope='col'><strong class="spip">Date</strong></th><th scope='col'><strong class="spip">Commentaire</strong></th></tr></thead>
<tbody>
<tr class="row_even"><td>0.1</td><td>04/05/05</td><td>Création par Tony GALMICHE</td></tr>
<tr class="row_odd"><td> </td><td> </td><td> </td></tr>
</tbody>
</table>