Skip to content

Tutto per WordPress, lo sviluppo web — e non solo

💾 Backup e ripristino di un database MySQL via PHP

💾 Backup e ripristino di un database MySQL via PHP

Perdere un database significa perdere ordini, utenti e contenuti in pochi secondi. Per chi gestisce un sito WordPress, il backup non è un optional, è obbligatorio. Ma accedere manualmente a phpMyAdmin ogni giorno fa perdere tempo e, quando serve, il backup semplicemente non c'è.

Uno script PHP risolve il problema: gli si danno le credenziali di accesso al database e lui esporta struttura e dati in un file SQL. Con cinque minuti di configurazione si ottiene un backup ripetibile e programmabile che non ci si dimenticherà di fare.

Di seguito: script pronti per backup e ripristino di MySQL in PHP. Vedremo come funzionano, dove recuperare le credenziali e come eseguire il backup automatico su pianificazione, così che il database non resti mai scoperto.

💡 Panoramica rapida:

  • Connessione a MySQL via mysqli, recupero dell'elenco tabelle ed esportazione della struttura
  • Creazione del dump SQL: CREATE TABLE più INSERT per ogni tabella, salvataggio su file
  • Ripristino: lettura del dump ed esecuzione delle query in un unico batch tramite mysqli_multi_query
  • Automazione via Cron (Linux) o Utilità di pianificazione (Windows) che esegue lo script senza intervento manuale
  • Compressione in .gz e rotazione delle copie vecchie per risparmiare spazio e mantenere una cronologia dei backup

Step 1: credenziali e ambiente, cosa serve prima di eseguire

Prima di scrivere codice, procurarsi tre cose: le credenziali MySQL, una directory dove salvare i backup e la consapevolezza di dove girerà lo script.

Per connettersi al database servono host (di solito localhost o 127.0.0.1), nome utente, password e nome del database. Su un sito WordPress questi dati si trovano in wp-config.php: le costanti DB_HOST (server), DB_USER (login), DB_PASSWORD (password) e DB_NAME (nome database).

Creare la directory di backup fuori dalla cartella pubblica del sito. Posizionare la cartella backups/ un livello sopra public_html, così i file SQL non saranno accessibili da browser. I permessi di scrittura (chmod 755 o chmod 775) sono obbligatori: senza, fopen() e fwrite() falliranno con un errore.

Lo script PHP si esegue da riga di comando: php backup.php. L'esecuzione via web (da browser) è comunque possibile, ma la riga di comando è più sicura: nessuno indovinerà l'URL dello script per scaricare il dump del database.

Step 2: script PHP per il backup del database

Lo script fa tre passaggi: recupera l'elenco tabelle, per ognuna esporta la struttura (SHOW CREATE TABLE), poi i dati (SELECT *). Il risultato viene assemblato in una stringa e scritto su un file .sql. Salvare il codice come backup.php ed eseguire da terminale.

1<?php
2
3$connection = mysqli_connect('localhost', 'user', 'pass', 'my_database');
4
5if (!$connection) {
6 die('Ошибка подключения: ' . mysqli_connect_error());
7}
8
9$tables = [];
10$result = mysqli_query($connection, "SHOW TABLES");
11while ($row = mysqli_fetch_row($result)) {
12 $tables[] = $row[0];
13}
14
15$dump = '';
16foreach ($tables as $table) {
17 $dump .= 'DROP TABLE IF EXISTS ' . $table . ";\n";
18
19 $create = mysqli_query($connection, "SHOW CREATE TABLE " . $table);
20 $row = mysqli_fetch_row($create);
21 $dump .= $row[1] . ";\n\n";
22
23 $data = mysqli_query($connection, "SELECT * FROM " . $table);
24 $num_fields = mysqli_num_fields($data);
25
26 while ($row = mysqli_fetch_row($data)) {
27 $dump .= "INSERT INTO " . $table . " VALUES(";
28 for ($j = 0; $j < $num_fields; $j++) {
29 if (isset($row[$j])) {
30 $row[$j] = mysqli_real_escape_string($connection, $row[$j]);
31 $dump .= '"' . $row[$j] . '"';
32 } else {
33 $dump .= 'NULL';
34 }
35 if ($j < $num_fields - 1) {
36 $dump .= ',';
37 }
38 }
39 $dump .= ");\n";
40 }
41 $dump .= "\n\n";
42}
43
44$filename = 'backups/dump_' . date('Y-m-d_H-i-s') . '.sql';
45$handle = fopen($filename, 'w+');
46fwrite($handle, $dump);
47fclose($handle);
48
49echo "Бэкап сохранён: " . $filename . "\n";
50
51?>

Cosa succede qui riga per riga. mysqli_connect() apre una connessione; se l'accesso viene negato, lo script termina con un messaggio chiaro invece di proseguire in silenzio. Il ciclo su SHOW TABLES raccoglie i nomi delle tabelle in un array.

Per ogni tabella, SHOW CREATE TABLE restituisce esattamente una riga in cui il secondo campo è la query DDL completa. Aggiungiamo DROP TABLE IF EXISTS prima di essa, così il dump può essere applicato a un database pulito senza conflitti.

SELECT * legge tutte le righe. La funzione mysqli_real_escape_string() esegue l'escape di apici e caratteri speciali all'interno dei valori; a differenza di addslashes(), rispetta la codifica di connessione e non si lascia sfuggire injection tramite sequenze multibyte. I valori NULL vengono inseriti come NULL, non come stringa vuota.

Il nome del file contiene data e ora di esecuzione, comodo per la rotazione. Il file viene aperto in modalità w+ (crea o sovrascrivi) e riceve l'intero dump in un'unica operazione fwrite().

Step 3: script per il ripristino del database da dump SQL

Lo script inverso legge il file .sql ed esegue le query sul database. In pratica i dump pesano spesso decine di megabyte, quindi file_get_contents() ed explode su ; non sono l'opzione migliore per la produzione. Ma per apprendimento e database piccoli l'approccio funziona.

Salvare come restore.php. Prima di eseguire, fare un backup completo del database attuale: il ripristino sovrascrive le tabelle.

1<?php
2
3$connection = mysqli_connect('localhost', 'root', '', 'test');
4
5if (!$connection) {
6 die('Ошибка подключения: ' . mysqli_connect_error());
7}
8
9$filename = 'backups/dump_2026-06-14_12-00-00.sql';
10
11if (!file_exists($filename)) {
12 die('Файл дампа не найден: ' . $filename);
13}
14
15$contents = file_get_contents($filename);
16
17if (mysqli_multi_query($connection, $contents)) {
18 do {
19 if ($result = mysqli_store_result($connection)) {
20 mysqli_free_result($result);
21 }
22 } while (mysqli_next_result($connection));
23 echo "База восстановлена из: " . $filename . "\n";
24} else {
25 echo "Ошибка восстановления: " . mysqli_error($connection) . "\n";
26}
27
28mysqli_close($connection);
29
30?>

La differenza chiave rispetto a mysqli_query() riga per riga è mysqli_multi_query(). Questa funzione accetta una stringa con più query SQL e le esegue in sequenza con una sola chiamata. Il ciclo do...while con mysqli_next_result() è obbligatorio: senza, le query successive non verranno eseguite e la connessione resterà in attesa.

mysqli_store_result() recupera il risultato della query corrente e mysqli_free_result() libera la memoria. Se la query è una INSERT o CREATE TABLE, il risultato sarà false, ed è normale: lo script passa semplicemente alla successiva.

Attenzione: lo script non esegue DROP DATABASE e non ricrea il database stesso. Si aspetta che il database test esista già. Per sicurezza, la prima riga del dump può contenere CREATE DATABASE IF NOT EXISTS.

Step 4: automazione del backup via Cron e compressione

L'esecuzione manuale va bene per attività una tantum. Ma su un sito attivo il backup deve avvenire da solo, ogni notte mentre si dorme.

Su un server Linux si aggiunge un'attività a Cron: crontab -e e la riga 0 3 * * * php /path/to/backup.php esegue lo script ogni giorno alle 3 del mattino. Su Windows, l'Utilità di pianificazione con azione "avvia programma" php.exe e come argomento il percorso dello script.

Due migliorie che trasformano uno script didattico in uno pronto per la produzione:

Compressione. Dopo aver scritto il file .sql, aggiungere exec("gzip " . $filename);: la dimensione del dump si ridurrà di 5-10 volte. Per il ripristino, gunzip decomprime il file.

Rotazione. Mantenere i backup degli ultimi 7 giorni, eliminare il resto. Opzione semplice: aggiungere all'inizio dello script la pulizia dei file più vecchi di N giorni:

1$retention_days = 7;
2$backup_dir = 'backups/';
3foreach (glob($backup_dir . '*.sql.gz') as $file) {
4 if (filemtime($file) < time() - $retention_days * 86400) {
5 unlink($file);
6 }
7}

Il risultato è un sistema autonomo: lo script gira su pianificazione, crea un backup compresso con la data nel nome del file e pulisce i vecchi dati. Manualità e 20 minuti di configurazione sostituiscono un plugin a pagamento.

Il video qui sopra mostra il ciclo completo di backup automatico: dal file di configurazione alla verifica del .sql.gz compresso nella cartella backups/. L'approccio è esattamente lo stesso descritto in questo articolo, con controlli di errore e logging aggiuntivi.

⁉️🤔 FAQ

È sicuro conservare la password di MySQL in uno script PHP?

Conservare le credenziali in chiaro dentro uno script è accettabile solo se il file risiede fuori dalla directory pubblica del sito ed è inaccessibile da browser. Meglio spostare le credenziali in un config.php separato fuori da public_html e includerlo via require. Ancora più affidabile: passare la password tramite variabile d'ambiente: getenv('DB_PASS').

Posso fare il backup solo di tabelle specifiche?

Sì. Sostituire SHOW TABLES con un array esplicito di nomi: $tables = ['wp_posts', 'wp_postmeta', 'wp_options'];. Oppure escludere le tabelle non importanti con if (!in_array($table, ['wp_litespeed_cache', 'wp_actionscheduler_logs'])). In pratica, le tabelle di cache e log occupano una parte significativa della dimensione del database e non servono per il ripristino.

Quanto spazio occupa il backup del database di un sito WordPress medio?

Un dump SQL non compresso di un database con 500 articoli e 15 plugin pesa 15-40 MB. Dopo gzip, 2-8 MB. In un mese di backup giornalieri con compressione si accumulano 100-300 MB: per un server con 20 GB di disco è trascurabile. La rotazione a 7 giorni mantiene lo spazio occupato entro 50-100 MB.

Cosa fare se il file di backup è corrotto?

Prima del ripristino verificare l'integrità: gunzip -t dump.sql.gz per i compressi, head -n 20 dump.sql per i non compressi, assicurandosi che il file inizi con commenti SQL, non con spazzatura binaria. Non ripristinare un dump corrotto: si perderebbero dati. È proprio per questo che si tengono almeno 2-3 copie recenti.

In cosa si differenzia questo approccio dai plugin di backup per WordPress?

Un plugin come UpdraftPlus offre un'interfaccia web, backup incrementali e caricamento su cloud già pronti. Uno script PHP dà il pieno controllo sul processo senza codice aggiuntivo nell'admin. Per uno sviluppatore che lavora già sul server, lo script è più veloce e trasparente. Per un cliente senza accesso alla console, un plugin è più comodo.

Script manuale, mysqldump o plugin: cosa scegliere per il proprio lavoro

Uno script PHP di backup dà la cosa principale: la comprensione esatta di cosa succede al database durante il backup. Non ci si affida a una scatola nera, si controlla ogni query.

Se il sito è in produzione e la velocità conta, usare mysqldump. Un solo comando mysqldump -u user -p db_name | gzip > dump.sql.gz fa la stessa cosa ma molto più velocemente, perché lavora a livello di motore MySQL, non con SELECT riga per riga. Uno script PHP serve per apprendimento, scenari personalizzati (fare il backup non dell'intero database ma dei risultati di una query specifica) e ambienti dove mysqldump non è disponibile.

Se il sito è su WordPress e non si vuole usare la console, qualsiasi plugin di backup risolve il problema senza scrivere codice. UpdraftPlus, BackWPup, Duplicator coprono sia database che file del sito.

Iniziare con uno script manuale, capire come si forma un dump SQL. Poi impostare Cron e la compressione. Ultimo passo: verificare il ripristino su un server di test: un backup senza verifica del ripristino non si considera completato. Che strumento usi per il backup del database? Scrivilo nei commenti.