Skip to content

Wszystko o WordPressie, tworzeniu stron — i nie tylko

💾 Kopia zapasowa i przywracanie bazy danych MySQL przez PHP

💾 Kopia zapasowa i przywracanie bazy danych MySQL przez PHP

Utrata bazy danych oznacza utratę zamówień, użytkowników i treści w ciągu sekund. Dla właściciela strony na WordPress kopia zapasowa nie jest „pożądana", lecz obowiązkowa. Jednak ręczne logowanie do phpMyAdmin każdego dnia zabiera czas, a w krytycznym momencie backupu po prostu nie ma.

Skrypt PHP rozwiązuje problem: dajesz mu dostęp do bazy, a on zrzuca strukturę i dane do pliku SQL. W pięć minut konfiguracji otrzymujesz powtarzalny, programowalny backup, o którym nie zapomnisz.

Dalej: gotowe skrypty do tworzenia kopii zapasowej i odtwarzania MySQL w PHP. Przeanalizujemy, jak działają, skąd wziąć dane dostępowe i jak uruchomić automatyczny backup według harmonogramu, aby baza danych nigdy nie pozostawała bez ochrony.

💡 Szybki przegląd:

  • Łączymy się z MySQL przez mysqli, pobieramy listę tabel i zrzucamy strukturę
  • Tworzymy zrzut SQL: CREATE TABLE plus INSERT dla każdej tabeli, zapisujemy do pliku
  • Odtwarzanie: czytamy zrzut i wykonujemy zapytania jednym pakietem przez mysqli_multi_query
  • Automatyzacja przez Cron (Linux) lub Harmonogram zadań (Windows) uruchamia skrypt bez Państwa udziału
  • Kompresja do .gz i rotacja starych kopii oszczędzają miejsce i przechowują historię backupów

Krok 1: Dane dostępowe i środowisko: co jest potrzebne przed uruchomieniem

Zanim zaczną Państwo pisać kod, proszę zebrać trzy rzeczy: dane logowania do MySQL, katalog do przechowywania backupów i wiedzę, gdzie skrypt będzie uruchamiany.

Do połączenia z bazą potrzebny jest host (zazwyczaj localhost lub 127.0.0.1), nazwa użytkownika, hasło i nazwa bazy danych. Na stronie WordPress dane te znajdują się w pliku wp-config.php: stałe DB_HOST (serwer), DB_USER (login), DB_PASSWORD (hasło) i DB_NAME (nazwa bazy).

Katalog na backupy proszę utworzyć poza folderem publicznym strony. Proszę umieścić folder backups/ poziom wyżej niż public_html, dzięki czemu pliki SQL nie będą dostępne z przeglądarki. Prawa do zapisu (chmod 755 lub chmod 775) są obowiązkowe: bez nich fopen() i fwrite() zakończą się błędem.

Skrypt PHP uruchamia się z wiersza poleceń: php backup.php. Uruchomienie przez przeglądarkę też jest możliwe, ale wiersz poleceń jest bezpieczniejszy, nikt nie odgadnie adresu URL skryptu i nie zrzuci Państwa bazy.

Krok 2: Skrypt PHP do tworzenia kopii zapasowej bazy danych

Skrypt wykonuje trzy przebiegi: pobiera listę tabel, dla każdej zrzuca strukturę (SHOW CREATE TABLE), a następnie dane (SELECT *). Wynik jest zbierany w jeden ciąg znaków i zapisywany do pliku .sql. Proszę zapisać kod jako backup.php i uruchomić z terminala.

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?>

Co tu się dzieje, linijka po linijce. mysqli_connect() otwiera połączenie, jeśli dostęp jest niemożliwy, skrypt przerywa działanie z czytelnym komunikatem, zamiast kontynuować w milczeniu. Pętla SHOW TABLES zbiera nazwy tabel do tablicy.

Dla każdej tabeli SHOW CREATE TABLE zwraca dokładnie jeden wiersz z drugim polem będącym pełnym zapytaniem DDL. Dodajemy DROP TABLE IF EXISTS przed nim, aby zrzut można było wgrać na czystą bazę bez konfliktów.

SELECT * odczytuje wszystkie wiersze. Funkcja mysqli_real_escape_string() maskuje cudzysłowy i znaki specjalne wewnątrz wartości, w przeciwieństwie do addslashes() uwzględnia kodowanie połączenia i nie przepuszcza iniekcji przez sekwencje wielobajtowe. Wartości NULL są wstawiane jako NULL, a nie jako pusty ciąg.

Nazwa pliku zawiera datę i czas uruchomienia, co jest wygodne przy rotacji. Plik jest otwierany w trybie w+ (utwórz lub nadpisz) i otrzymuje cały zrzut w jednej operacji fwrite().

Krok 3: Skrypt do odtwarzania bazy danych ze zrzutu SQL

Skrypt odwrotny odczytuje plik .sql i wykonuje zapytania do bazy. W praktyce zrzut często waży dziesiątki megabajtów, dlatego file_get_contents() i explode po ; nie jest najlepszą opcją na produkcję. Jednak do celów edukacyjnych i dla małych baz to podejście działa.

Proszę zapisać jako restore.php. Przed uruchomieniem należy wykonać pełny backup bieżącej bazy, odtwarzanie nadpisuje tabele.

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?>

Kluczowa różnica w stosunku do sekwencyjnego mysqli_query(): mysqli_multi_query(). Przyjmuje ona ciąg z wieloma zapytaniami SQL i wykonuje je sekwencyjnie w jednym wywołaniu. Pętla do...while z mysqli_next_result() jest obowiązkowa: bez niej kolejne zapytania nie zostaną wykonane, a połączenie się zawiesi.

mysqli_store_result() pobiera wynik bieżącego zapytania, a mysqli_free_result() zwalnia pamięć. Jeśli zapytanie, INSERT lub CREATE TABLE, zwróci wynik false, jest to normalne: skrypt po prostu przechodzi do następnego.

Proszę zwrócić uwagę: skrypt nie wykonuje DROP DATABASE i nie odtwarza samej bazy. Oczekuje, że baza test już istnieje. Dla bezpieczeństwa można w pierwszym wierszu zrzutu umieścić CREATE DATABASE IF NOT EXISTS.

Krok 4: Automatyzacja backupu przez Cron i kompresja

Ręczne uruchamianie sprawdza się przy jednorazowych zadaniach. Jednak na stronie produkcyjnej backup powinien być wykonywany bez Państwa udziału, każdej nocy, kiedy Państwo śpią.

Na serwerze Linux proszę dodać zadanie w Cronie: crontab -e i wiersz 0 3 * * * php /path/to/backup.php uruchamia skrypt codziennie o 3 w nocy. W systemie Windows Harmonogram zadań z akcją „uruchom program" php.exe i argumentem będącym ścieżką do skryptu.

Dwa ulepszenia, które przekształcają skrypt edukacyjny w gotowy do użycia na produkcji:

Kompresja. Po zapisaniu pliku .sql proszę dodać exec("gzip " . $filename);, rozmiar zrzutu zmniejszy się 5-10 razy. Do odtworzenia gunzip rozpakuje plik z powrotem.

Rotacja. Backupy z ostatnich 7 dni proszę przechowywać, resztę usuwać. Prosty wariant: na początku skryptu proszę dodać czyszczenie plików starszych niż N dni:

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}

W rezultacie mają Państwo autonomiczny system: skrypt uruchamia się zgodnie z harmonogramem, tworzy skompresowany backup z datą w nazwie i czyści stare pliki. Własne umiejętności i 20 minut konfiguracji zastępują płatną wtyczkę.

Na filmie powyżej pełny cykl automatycznego backupu: od pliku konfiguracyjnego do sprawdzenia skompresowanego .sql.gz w folderze backups/. Podejście jest dokładnie takie samo, jak opisano w tym artykule, z dodatkowymi kontrolami błędów i logowaniem.

⁉️🤔 Często zadawane pytania

Czy przechowywanie hasła MySQL w skrypcie PHP jest bezpieczne?

Przechowywanie danych logowania jawnym tekstem wewnątrz skryptu jest dopuszczalne tylko wtedy, gdy plik znajduje się poza publicznym katalogiem strony i jest niedostępny z przeglądarki. Lepiej wynieść dane dostępowe do osobnego pliku config.php poza public_html i dołączać go przez require. Jeszcze bezpieczniej jest przekazywać hasło przez zmienną środowiskową: getenv('DB_PASS').

Czy można zrobić backup tylko wybranych tabel?

Tak. Proszę zastąpić SHOW TABLES jawną tablicą nazw: $tables = ['wp_posts', 'wp_postmeta', 'wp_options'];. Lub wykluczyć nieistotne tabele przez if (!in_array($table, ['wp_litespeed_cache', 'wp_actionscheduler_logs'])). W praktyce tabele pamięci podręcznej i logów zajmują znaczną część rozmiaru bazy i nie są potrzebne do odtworzenia.

Ile miejsca zajmuje backup bazy przeciętnej strony na WordPress?

Nieskompresowany zrzut SQL bazy z 500 wpisami i 15 wtyczkami waży 15-40 MB. Po gzip 2-8 MB. Przez miesiąc codziennych backupów z kompresją zbiera się 100-300 MB, dla serwera z dyskiem 20 GB jest to niezauważalne. Rotacja za 7 dni utrzymuje zajęte miejsce w granicach 50-100 MB.

Co zrobić, jeśli plik backupu jest uszkodzony?

Przed odtworzeniem proszę sprawdzić integralność: gunzip -t dump.sql.gz dla skompresowanych, head -n 20 dump.sql dla nieskompresowanych, proszę upewnić się, że plik zaczyna się od komentarzy SQL, a nie od binarnych śmieci. Uszkodzonego zrzutu proszę nie odtwarzać, stracą Państwo dane. Właśnie dlatego proszę przechowywać co najmniej 2-3 ostatnie kopie.

Czym to podejście różni się od wtyczek do tworzenia kopii zapasowych WordPress?

Wtyczka taka jak UpdraftPlus daje interfejs webowy, przyrostowe backupy i wysyłanie do chmury „od ręki". Skrypt PHP to pełna kontrola nad procesem bez zbędnego kodu w panelu administracyjnym. Dla developera, który i tak pracuje z serwerem, skrypt jest szybszy i bardziej przejrzysty. Dla klienta bez dostępu do konsoli wtyczka jest wygodniejsza.

Ręczny skrypt, mysqldump czy wtyczka: co wybrać do swojego zadania

Skrypt PHP do tworzenia kopii zapasowych daje to, co najważniejsze: zrozumienie, co dokładnie dzieje się z Państwa bazą podczas backupu. Nie ufają Państwo czarnej skrzynce, lecz kontrolują każde zapytanie.

Jeśli strona jest na produkcji i ważna jest szybkość, proszę użyć mysqldump. Jedno polecenie mysqldump -u user -p db_name | gzip > dump.sql.gz robi to samo, ale wielokrotnie szybciej, ponieważ działa na poziomie silnika MySQL, a nie sekwencyjnym SELECT. Skrypt PHP jest do celów edukacyjnych, niestandardowych scenariuszy (backup nie całej bazy, lecz wyników konkretnego zapytania) i środowisk, gdzie mysqldump jest niedostępny.

Jeśli strona jest na WordPress i nie chcą Państwo wchodzić do konsoli, dowolna wtyczka do backupu załatwi sprawę bez kodu. UpdraftPlus, BackWPup, Duplicator pobierają zarówno bazę, jak i pliki strony.

Proszę zacząć od ręcznego skryptu, zrozumieć, jak tworzony jest zrzut SQL. Następnie proszę skonfigurować Cron i kompresję. Ostatni krok to sprawdzenie odtwarzania na serwerze testowym: backup bez sprawdzenia odtwarzania nie jest uznawany za wykonany. Jakiego narzędzia do backupu baz danych Państwo używają? Proszę napisać w komentarzach.