The PrestaShopBackup Class. How to use it in your solutions?

Table of contents

PrestaShop is a robust e-commerce platform designed not only for merchants but also — and perhaps above all — for developers who want to build custom solutions. It offers plenty of built-in features that can be extended and adapted to specific needs, minimizing the necessity of developing functions from scratch. One of the less-known but useful tools is the PrestaShopBackup
class, which simplifies the implementation of backup mechanisms.
classes/PrestaShopBackup.php
The PrestaShopBackup
class exists to make it easier to create database backups. It contains all the necessary logic for gathering database tables, generating a backup file name, and saving the backup data into a file. By default, it is used in the Advanced → Database → DB Backup section, but to illustrate a practical use of the PrestaShopBackup
class in your own solution, let’s consider a scenario where you need a backup mechanism for specific tables related to product and category languages.
We’ll assume we need these tables because we created a mechanism that generates, for example, SEO-friendly URLs and other product SEO data based on given criteria, but we want to ensure the user always has a backup.
Don’t reinvent the wheel
Instead of building this from scratch, you can extend the PrestaShopBackup
class by creating a new class, for example ProductLangTablesBackup
.
The method responsible for creating and saving a new backup is PrestaShopBackupCore::add()
. By default, it creates a backup of all the data in your store (except for some tables, such as statistics, if backups were configured that way). Obviously, we are not interested in a full backup, so in our code we need to add a condition that simply skips all the tables we don’t care about.
Example
Below you’ll find code I used in one of my projects. It’s based on the class from PrestaShop 1.7.8. If you want to follow this article and build something for your own solution, make sure to use the PrestaShop 8.2 codebase.
In the code, I’ve highlighted the lines that were modified compared to the original file. We changed:
- the file name
- reversed the backup condition — instead of ignoring selected tables, we allow only selected tables
The most important changes are highlighted in the code below.
<?php
class ProductLangTablesBackup extends PrestaShopBackupCore
{
/**
* Creates a new backup file.
*
* @return bool true on successful backup
*/
public function add()
{
$tablesToBackup = [_DB_PREFIX_ . 'product_lang'];
// Generate some random number, to make it extra hard to guess backup file names
$rand = dechex(mt_rand(0, min(0xffffffff, mt_getrandmax())));
$date = time();
$backupfile = $this->getRealBackupPath() . $date . '-' . $rand . '-ProductLanguageData.sql';
// Figure out what compression is available and open the file
if (function_exists('bzopen')) {
$backupfile .= '.bz2';
$fp = @bzopen($backupfile, 'w');
} elseif (function_exists('gzopen')) {
$backupfile .= '.gz';
$fp = @gzopen($backupfile, 'w');
} else {
$fp = @fopen($backupfile, 'wb');
}
if ($fp === false) {
echo Context::getContext()->getTranslator()->trans('Unable to create backup file', array(), 'Admin.Advparameters.Notification') . ' "' . addslashes($backupfile) . '"';
return false;
}
$this->id = realpath($backupfile);
fwrite($fp, '/* Backup for ' . Tools::getHttpHost(false, false) . __PS_BASE_URI__ . "\n * at " . date($date) . "\n */\n");
fwrite($fp, "\n" . 'SET NAMES \'utf8\';');
fwrite($fp, "\n" . 'SET FOREIGN_KEY_CHECKS = 0;');
fwrite($fp, "\n" . 'SET SESSION sql_mode = \'\';' . "\n\n");
// Find all tables
$tables = Db::getInstance()->executeS('SHOW TABLES');
$found = 0;
foreach ($tables as $table) {
$table = current($table);
// Skip tables which do not start with _DB_PREFIX_
if (strlen($table) < strlen(_DB_PREFIX_) || strncmp($table, _DB_PREFIX_, strlen(_DB_PREFIX_)) != 0) {
continue;
}
if (!in_array($table, $tablesToBackup)) {
continue;
}
// Export the table schema
$schema = Db::getInstance()->executeS('SHOW CREATE TABLE `' . $table . '`');
if (count($schema) != 1 || !isset($schema[0]['Table']) || !isset($schema[0]['Create Table'])) {
fclose($fp);
$this->delete();
echo Context::getContext()->getTranslator()->trans('An error occurred while backing up. Unable to obtain the schema of %s', array($table), 'Admin.Advparameters.Notification');
return false;
}
fwrite($fp, '/* Scheme for table ' . $schema[0]['Table'] . " */\n");
if ($this->psBackupDropTable) {
fwrite($fp, 'DROP TABLE IF EXISTS `' . $schema[0]['Table'] . '`;' . "\n");
}
fwrite($fp, $schema[0]['Create Table'] . ";\n\n");
$data = Db::getInstance()->query('SELECT * FROM `' . $schema[0]['Table'] . '`', false);
$sizeof = Db::getInstance()->numRows();
$lines = explode("\n", $schema[0]['Create Table']);
if ($data && $sizeof > 0) {
// Export the table data
fwrite($fp, 'INSERT INTO `' . $schema[0]['Table'] . "` VALUES\n");
$i = 1;
while ($row = Db::getInstance()->nextRow($data)) {
$s = '(';
foreach ($row as $field => $value) {
$tmp = "'" . pSQL($value, true) . "',";
if ($tmp != "'',") {
$s .= $tmp;
} else {
foreach ($lines as $line) {
if (strpos($line, '`' . $field . '`') !== false) {
if (preg_match('/(.*NOT NULL.*)/Ui', $line)) {
$s .= "'',";
} else {
$s .= 'NULL,';
}
break;
}
}
}
}
$s = rtrim($s, ',');
if ($i % 200 == 0 && $i < $sizeof) {
$s .= ");\nINSERT INTO `" . $schema[0]['Table'] . "` VALUES\n";
} elseif ($i < $sizeof) {
$s .= "),\n";
} else {
$s .= ");\n";
}
fwrite($fp, $s);
++$i;
}
}
++$found;
}
fclose($fp);
if ($found == 0) {
$this->delete();
echo Context::getContext()->getTranslator()->trans('No valid tables were found to backup.', array(), 'Admin.Advparameters.Notification');
return false;
}
return true;
}
}
Executing a backup of only the tables we care about is as simple as running:
// The following code will create a new backup based on your file
$backup = new ProductLangTablesBackup();
$backup->add();
This is, of course, a very simple example of how you can make your work easier. If you need something more advanced, there are ready-made libraries you can use in your modules. And finally, I’d like to recommend that module developers consider including such solutions in their products. More than once, it can help your users — and as you can see, the implementation is not complicated. 😉
Subscribe to my newsletter
Read articles from Krystian Podemski directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Krystian Podemski
Krystian Podemski
🛠️ Tech Evangelist at @PrestaShop • Core Maintainer • Communication • Public speaking • Technical Marketing 🚀 15+ years in e-commerce • Led dozens of successful PrestaShop projects 🧭 impSolutions agency founder • Experts in PrestaShop development🎤 Public speaker • Passionate about community building and OSS 💬 Ask me anything PrestaShop – code, strategy, or project direction