Backup Database MySQL in PHP

In questo tutorial costruiremo un semplice script PHP che esegue il backup di un database MySQL.

Lo script PHP consiste in un unico file molto corto (107 righe) facilmente configurabile impostando alcuni parametri da GET, in modo da poter rapidamente impostare se si vuole solo la struttura del database, struttura e dati eccetera.

Una volta eseguito lo script, esso restituirà al browser un file.
Il browser in questo modo avvierà il download del dump del database che andrà avanti durante le iterazioni e le varie query, cercando quindi di non fare andare in timeout la richiesta.

Questo vi consentirà di fare il backup di database MySQL consistenti.
Vediamo per prima cosa la testa dello script che contiene tutte le costanti che vi consentiranno di comandare l'applicazione.

set_time_limit(0); // Rimuove limiti di tempo per l'esecuzione dello script corrente
error_reporting(E_ALL); // Dice a PHP di segnalare tutti gli errori in cui incorre

define('DB_SERVER', 'localhost'); // Host dove risiede il database MySQL
define('DB_USER', 'root'); // Nome utente db
define('DB_PASS', 'mypass'); // Password db
define('DB_NAME', 'test'); // Database di cui effettuare il backup
define('DB_FILE', 'dump_' . date("d_m_Y__\hH_\mi", time()) . '.sql'); // Nome del dump 

define('DB_DROP', $_GET['drop'] == '1');
define('DB_IGNORE', $_GET['ignore'] == '1');
define('DB_DATA', $_GET['data'] == '1');
define('DB_STRUCTURE', $_GET['struc'] == '1');
define('INSERT_QUERIES', 100);

Diamo un'occhiata alle cinque costanti soprastanti che non ho commentato:

  • DB_DROP - Aggiunge il comando MySQL "DROP TABLE IF EXISTS" che elimina la tabella specificata se esistente. Utile prima di un "CREATE TABLE" della medesima tabella.
    Per attivarlo impostate drop a 1 via GET come nell'esempio che segue:
    http://localhost/dumpetor.php?drop=1
  • DB_IGNORE - Aggiunge il comando MySQL "IGNORE" al comando "INSERT INTO", consentendo di continuare l'esecuzione del codice SQL anche nel caso in cui si violasse l'univocità di una chiave.
    Quindi, se ad esempio provate ad inserire due volte un record con lo stesso ID, che poniamo sia la chiave primaria della tabella, il secondo inserimento verrà semplicemente ignorato in quanto il valore risulta già presente.
    http://localhost/dumpetor.php?ignore=1
  • DB_DATA - Dice allo script che desideriamo eseguire il backup dei dati (INSERT ...)
    http://localhost/dumpetor.php?data=1
  • DB_STRUCTURE - Dice allo script che desideriamo eseguire il backup della struttura del database (CREATE TABLE ...)
    http://localhost/dumpetor.php?struc=1
  • INSERT_QUERIES - Imposta il numero di record massimo che ogni query INSERT può contenere (default = 100)

In base a quanto riportato pocanzi, l'url per eseguire il backup completo del database sarà il seguente:
http://localhost/dumpetor.php?data=1&struc=1&drop=1&ignore=1
... in cui ovviamente drop e ignore saranno opzionali.

Lo script prosegue con una condizione che termina lo script nel caso in cui non venga impostato alcun parametro e continua collegandosi al database mediante la classica mysql_connect().

if (!DB_DROP && !DB_IGNORE && !DB_DATA && !DB_STRUCTURE) exit;

$db = mysql_connect(DB_SERVER, DB_USER, DB_PASS) or die('Errore MySQL: ' . mysql_error() . ' (' . mysql_errno() . ')');
mysql_select_db(DB_NAME);

Continuiamo il tutorial introducendo la funzione datadump($table) che fornirà le query INSERT con tutti i dati della tabella specificata.

Questa funzione si servirà a sua volta di due query: una SELECT * per il prelievo di tutti i dati ed una SHOW COLUMNS usata per leggere il nome dei campi ma soprattutto se accettano come valore predefinito il valore speciale NULL.

function datadump($table)
{
	global $db;
	$dump = @mysql_query("SELECT * FROM `$table`;", $db);

	if (@mysql_num_rows($dump) <= 0) // Se la tabella è vuota restituisce una stringa vuota
		return '';

	$result = "";
	$insert_fields = "";
	$stack = array();
	$colonne = @mysql_query("SHOW COLUMNS FROM `$table`;");

	$cols = array();
	while ($col = @mysql_fetch_object($colonne))
	{
		$cols[$col->Field] = $col->Null; // Carica in un'array associativo il nome dei campi con il NULL si/no
		$insert_fields .= "`" . $col->Field . "`, ";
	}

	$insert_fields = substr($insert_fields, 0, -2);

	while ($d = @mysql_fetch_assoc($dump)) // Carica la tabella in un'array
		$stack[] = $d;

	foreach ($stack as $i => $val)
	{
		if (!($i % INSERT_QUERIES)) // Controlla se è stato raggiunto il limite di record per creare una nuova INSERT
		{
			if (strlen($result))
				$result = substr($result, 0, -2) . ";\n";
			$result .= "INSERT " . ((DB_IGNORE) ? 'IGNORE ' : '') . "INTO `$table` ($insert_fields) VALUES ";
		}

		$result .= "(";

		foreach ($val as $key => $value)
		{
			if ($value == NULL) // Se il campo è vuoto controlla l'array $cols per assegnare il giusto valore predefinito
			{
				if ($cols[$key] == "YES")
					$result .= 'NULL, ';
				else $result .= "'', ";
			}
			else $result .= "'" . mysql_real_escape_string($value, $db) . "', ";
		}

		$result = substr($result, 0, -2) . "), ";
	}

	return substr($result, 0, -2) . ";\n";
}

Ora vediamo la parte finale dello script.
Con due chiamate a header() impostiamo il contenuto dell'output introducendo al browser che si tratta di un file che prenderà il nome dalla costante DB_FILE:

header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename = " . DB_FILE);

$query = "SHOW TABLE STATUS FROM `" . DB_NAME . "`;"; // Legge la lista completa delle tabelle dal DB specificato
$tabelle = @mysql_query($query, $db);

echo 'SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";' . "\n\n"; // Imposta il comportamento per la direttiva AUTO_INCREMENT

while ($tab = @mysql_fetch_object($tabelle))
{
	if (DB_STRUCTURE) // Se viene richiesta la struttura delle tabelle ...
	{
		if (DB_DROP) // Se viene impostato il drop via GET
			echo "DROP TABLE IF EXISTS " . $tab->Name . ";\n";

		$query = "SHOW CREATE TABLE `" . $tab->Name . "`;";
		$create = @mysql_query($query, $db);
		$create = @mysql_fetch_assoc($create);

		echo $create["Create Table"] . ";\n\n"; // Stampa il CREATE TABLE generato da MySQL
	}

	if (DB_DATA) // Se vengono richiesti i dati ...
		echo datadump($tab->Name) . "\n\n";
}

E con questo pezzo di codice abbiamo finito il tutorial.
Potete scaricare lo script allegato pronto a funzionare con un colpo di FTP.
Se riscontrate anomalie o avete suggerimenti per migliorare lo script scrivete un commento.

Buon lavoro.

AllegatoDimensione
dumpetor.zip1.16 KB

Commenti

Problemi con header

Salve volevo dirti che lo script è fantastico, ma se io volessi salvare il file sempre in una cartella senza selezionarla come posso farlo?

header

togli gli header e metti tutto in una variabile invece di fare sempre echo e a fine script scrivi il file, se vuoi usa file_put_contents()

ripristino

Ciao, grazie per l'articolo.
Ti volevo chiedere una volta ottenuta la copia di backup come faccio poi, in caso di problemi, a ripristinare la medesima copia?

Grazie.

backup

il backup che ritrovi non è altro che codice SQL, quindi puoi eseguirlo da phpMyAdmin o da console mysql.

forum

Se necessitate di aiuto usate il forum apposito:
http://www.realizzazione-sito.info/forum/

ciao ho il parametro safe

ciao ho il parametro safe mode di apache attivato e mi da errore in questo senso, come posso modificare il codice senza togliere il safe mode?
grazie.

dimenticavo ... se commenti

dimenticavo ... se commenti le due header() ti dovrebbe uscire il dump direttamente senza farti scaricare il .sql, quindi copia il sorgente html e salvatelo in un .sql tuo.

prova a commentare le due

prova a commentare le due header() in testa.
il safe mode cmq è deprecato, ti consiglio di aggiornare il tuo ambiente.

Opzioni visualizzazione commenti

Seleziona il tuo modo preferito per visualizzare i commenti e premi "Salva impostazioni" per attivare i cambiamenti.