Skip to content

Все для WordPress, веб-розробки — і не тільки

💾 Резервне копіювання та відновлення бази даних MySQL через PHP

💾 Резервне копіювання та відновлення бази даних MySQL через PHP

Втрата бази даних означає втрату замовлень, користувачів і контенту за секунди. Для власника сайту на WordPress резервне копіювання не «бажане», а обов’язкове. Але ручний вхід у phpMyAdmin щодня забирає час, і в потрібний момент бекапу просто немає.

PHP-скрипт вирішує проблему: ви даєте йому доступ до бази, він вивантажує структуру та дані в SQL-файл. За п’ять хвилин налаштування ви отримуєте повторюваний, програмований бекап, про який не забудете.

Далі: готові скрипти резервного копіювання та відновлення MySQL на PHP. Розберемо, як вони працюють, де взяти доступи та як запустити автобекап за розкладом, щоб база даних never залишалася без захисту.

💡 Швидкий огляд:

  • Підключаємося до MySQL через mysqli, отримуємо список таблиць і вивантажуємо структуру
  • Формуємо SQL-дамп: CREATE TABLE плюс INSERT для кожної таблиці, зберігаємо у файл
  • Відновлення: читаємо дамп і виконуємо запити одним пакетом через mysqli_multi_query
  • Автоматизація через Cron (Linux) або Task Scheduler (Windows) запускає скрипт без вашої участі
  • Стиснення в .gz і ротація старих копій економлять місце та зберігають історію бекапів

Шаг 1: Доступи та оточення: що потрібно перед запуском

Перед тим як писати код, зберіть три речі: облікові дані MySQL, директорію для зберігання бекапів і розуміння, де запускатиметься скрипт.

Для підключення до бази потрібні хост (зазвичай localhost або 127.0.0.1), ім’я користувача, пароль і назва бази. На WordPress-сайті ці дані лежать у wp-config.php: константи DB_HOST (сервер), DB_USER (логін), DB_PASSWORD (пароль) і DB_NAME (ім’я бази).

Директорію для бекапів створіть поза публічною папкою сайту. Покладіть папку backups/ на рівень вище за public_html, так до SQL-файлів не буде доступу з браузера. Права на запис (chmod 755 або chmod 775) обов’язкові: без них fopen() і fwrite() впадуть із помилкою.

PHP-скрипт запускається з командного рядка: php backup.php. Вебзапуск (через браузер) теж можливий, але командний рядок безпечніший, ніхто не підбере URL скрипта і не зіллє вашу базу.

Шаг 2: PHP-скрипт резервного копіювання бази даних

Скрипт робить три проходи: отримує список таблиць, для кожної вивантажує структуру (SHOW CREATE TABLE), потім дані (SELECT *). Результат збирається в один рядок і записується у .sql-файл. Збережіть код як backup.php і запустіть із терміналу.

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

Що тут відбувається порядково. mysqli_connect() відкриває з’єднання, якщо доступу немає, скрипт падає зі зрозумілим повідомленням, а не продовжує мовчки. Цикл SHOW TABLES збирає імена таблиць у масив.

Для кожної таблиці SHOW CREATE TABLE повертає рівно один рядок із другим полем, повним DDL-запитом. Ми додаємо DROP TABLE IF EXISTS перед ним, щоб дамп можна було накотити на чисту базу без конфліктів.

SELECT * читає всі рядки. Функція mysqli_real_escape_string() екранує лапки та спецсимволи всередині значень, на відміну від addslashes(), вона враховує кодування з’єднання і не пропускає ін’єкції через багатобайтові послідовності. NULL-значення вставляються як NULL, а не як порожній рядок.

Ім’я файлу містить дату та час запуску, зручно для ротації. Файл відкривається в режимі w+ (створити або перезаписати) і отримує весь дамп однією операцією fwrite().

Шаг 3: Скрипт відновлення бази даних із SQL-дампа

Зворотний скрипт читає .sql-файл і виконує запити до бази. На практиці дамп часто важить десятки мегабайт, тому file_get_contents() і explode по ;, не найкращий варіант для продакшену. Але для навчальних цілей і невеликих баз підхід робочий.

Збережіть як restore.php. Перед запуском зробіть повний бекап поточної бази, відновлення перезаписує таблиці.

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

Ключова відмінність від порядкового mysqli_query(), mysqli_multi_query(). Вона приймає рядок із кількома SQL-запитами і виконує їх послідовно за один виклик. Цикл do...while із mysqli_next_result() обов’язковий: без нього наступні запити не виконаються, а з’єднання зависне.

mysqli_store_result() забирає результат поточного запиту, а mysqli_free_result() звільняє пам’ять. Якщо запит, INSERT або CREATE TABLE, результат буде false, і це нормально: скрипт просто переходить до наступного.

Зверніть увагу: скрипт не робить DROP DATABASE і не перестворює саму базу. Він очікує, що база test уже існує. Для перестраховки можна першим рядком дампа тримати CREATE DATABASE IF NOT EXISTS.

Шаг 4: Автоматизація бекапу через Cron і стиснення

Ручний запуск працює для разових задач. Але на бойовому сайті бекап має робитися без вас, щоночі, поки ви спите.

На Linux-сервері додайте задачу в Cron: crontab -e і рядок 0 3 * * * php /path/to/backup.php запускає скрипт щодня о 3 годині ночі. На Windows, Task Scheduler із дією «запустити програму» php.exe та аргументом-шляхом до скрипта.

Два доопрацювання, які перетворюють навчальний скрипт на production-ready:

Стиснення. Після запису .sql-файлу додайте exec("gzip " . $filename);, розмір дампа зменшиться в 5-10 разів. Для відновлення gunzip розпакує файл назад.

Ротація. Бекапи за останні 7 днів зберігайте, решту видаляйте. Простий варіант: на початок скрипта додайте очищення файлів, старіших за N днів:

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}

У результаті ви маєте автономну систему: скрипт запускається за розкладом, створює стиснений бекап із датою в імені та чистить старе. Прямі руки і 20 хвилин налаштування замінюють платний плагін.

На відео вище, повний цикл автобекапу: від конфігураційного файлу до перевірки стисненого .sql.gz у папці backups/. Підхід точно такий самий, як описано в цій статті, з додатковими перевірками помилок і логуванням.

⁉️🤔 Часті питання

Чи безпечно зберігати пароль MySQL у PHP-скрипті?

Зберігати облікові дані у відкритому вигляді всередині скрипта прийнятно, тільки якщо файл лежить поза публічною директорією сайту і недоступний із браузера. Краще винести доступи в окремий config.php за межами public_html і підключати через require. Ще надійніше, передавати пароль через змінну оточення: getenv('DB_PASS').

Чи можна робити бекап лише певних таблиць?

Так. Замініть SHOW TABLES на явний масив імен: $tables = ['wp_posts', 'wp_postmeta', 'wp_options'];. Або виключіть неважливі таблиці через if (!in_array($table, ['wp_litespeed_cache', 'wp_actionscheduler_logs'])). На практиці таблиці кешів і логів займають значну частину розміру бази і не потрібні для відновлення.

Скільки місця займає бекап бази середнього WordPress-сайту?

Нестиснений SQL-дамп бази на 500 записів і 15 плагінів важить 15-40 МБ. Після gzip, 2-8 МБ. За місяць щоденних бекапів зі стисненням накопичується 100-300 МБ, для сервера з 20 ГБ диска це непомітно. Ротація за 7 днів тримає зайняте місце в межах 50-100 МБ.

Що робити, якщо бекап-файл пошкоджений?

Перед відновленням перевірте цілісність: gunzip -t dump.sql.gz для стиснених, head -n 20 dump.sql для нестиснених, переконайтеся, що файл починається з SQL-коментарів, а не з бінарного сміття. Пошкоджений дамп не відновлюйте, ви втратите дані. Саме тому тримайте щонайменше 2-3 останні копії.

Чим цей підхід відрізняється від плагінів резервного копіювання WordPress?

Плагін на кшталт UpdraftPlus дає вебінтерфейс, інкрементальні бекапи та завантаження в хмару «з коробки». PHP-скрипт — це повний контроль над процесом без зайвого коду в адмінці. Для розробника, який і так працює з сервером, скрипт швидший і прозоріший. Для клієнта без доступу до консолі плагін зручніший.

Ручний скрипт, mysqldump чи плагін: що обрати під вашу задачу

PHP-скрипт резервного копіювання дає головне, розуміння того, що саме відбувається з вашою базою під час бекапу. Ви не довіряєте чорному ящику, а контролюєте кожен запит.

Якщо сайт на продакшені і важлива швидкість, беріть mysqldump. Одна команда mysqldump -u user -p db_name | gzip > dump.sql.gz робить те саме, але в рази швидше, тому що працює на рівні рушія MySQL, а не порядковим SELECT. Скрипт PHP, для навчальних цілей, кастомних сценаріїв (бекап не всієї бази, а результатів конкретного запиту) і середовищ, де mysqldump недоступний.

Якщо сайт на WordPress і ви не хочете лізти в консоль, будь-який плагін резервного копіювання закриє задачу без коду. UpdraftPlus, BackWPup, Duplicator забирають і базу, і файли сайту.

Почніть із ручного скрипта, зрозумійте, як формується SQL-дамп. Потім налаштуйте Cron і стиснення. Останній крок, перевірка відновлення на тестовому сервері: бекап без перевірки відновлення не вважається зробленим. Який інструмент для бекапу баз даних використовуєте ви? Напишіть у коментарях.