Create your own PHP Backup Solution | Complete Tutorial with Source Files

I’ve recently finished and launched my website. Since it is built on WordPress, it took me a while to set it up as I wanted it. Now, I want to back everything up, just in case.

What if you need to change web host or switch your database server? This is probably the time when backups are really the easiest way to do the change.

There are a few excellent and quite cheap solutions you can find on the web, but if you are anything like me you wouldn’t want to rely on 3rd party solutions to do this job and on the plus side – it’s free!

So let’s start.

What to back-up?

Basically there are two things you need to backup: all your server files and MySQL databases.

You can do this manually. Zip and download all the files from your server and dump your MySQL database(s) and save them to your local disk. But the tricky part (well, not really) is to restore the backup you created manually. You need to copy back all of the files and import MySQL dump files through phpMyAdmin or some other app.

Well, why not have an automatic script to do this for you?

Backup MySQL

When working with MySQL, I always start by setting MySQL DB configurations to a config file. What we need to set are DB host, DB UserName, DB Password, name of MySQL DB and tables we want to backup within selected MySQL DB.

Save these settings to config.php file.

<?php
    // Settings
    $DBhost = 'localhost';
    $DBuser = 'mysql_username';
    $DBpass = 'mysql_password';
    $DBName = 'name_of_mysql_db';
    $table = '*';
?>

Ok, so now we need to create a function that will back-up MySQL DB.

function backup_tables()
{

}

Since this function will do almost everything, we need to pass MySQL config to the function. So we will pass everything that we previously saved to config file.

function backup_tables($host,$user,$pass,$name,$tables)
{

}

When working with MySQL the first thing needed is to connect to MySQL DB.

function backup_tables($host,$user,$pass,$name,$tables)
{
    $link = mysql_connect($host,$user,$pass);
    mysql_select_db($name,$link);
}

Once we have established connection to MySQL we can start working with it.
Everything we want to save will be held in a single variable. So, define the var $return and set it’s value to an empty string.

function backup_tables($host,$user,$pass,$name,$tables)
{
    $link = mysql_connect($host,$user,$pass);
    mysql_select_db($name,$link);
    $return = "";
}

Because we want to provide an option to back-up all the tables within MySQL database or some specific table(s), first we have to check what the user wants to backup.
So if the script detects that all the tables should be backed-up, it will then get the names of tables from MySQL DB. Otherwise, it will just take the provided table names and work with them. Either way, array $tables will contain all the table names with which the script should work. In case that only a single table is to be backed-up, $tables will still hold the name of that table.

function backup_tables($host,$user,$pass,$name,$tables)
{
    $link = mysql_connect($host,$user,$pass);
    mysql_select_db($name,$link);
    $return = "";

    // Get all of the tables
    if($tables == '*') {
        $tables = array();
        $result = mysql_query('SHOW TABLES');
        while($row = mysql_fetch_row($result)) {
            $tables[] = $row[0];
        }
    } else {
        if (is_array($tables)) {
            $tables = explode(',', $tables);
        }
    }
}

We now need to cycle through each table and save the contents of that table. For each table we will create three-part output. This output is what will be saved into a backup file.
First part is to tell the script, which will be restoring this backup, to remove existing table, which we want to restore. Second is to create new table with the same name as the deleted one, and the final part is to insert backed-up values into newly created table.

Another thing I should mention, you can see that after each command we are saving, I’m adding ;<|||||||> set of characters. This is only as a separator, so that the restore script (which we are going to write, in a bit) will now where the commands end. While testing the script on a real-world database, I used semicolon as a separator, but then restoring didn’t go quite well (because inputs already had semicolons in the content). After changing it to current, everything works fine. You can use whatever you like, just as long as it can’t be found in any of the tables you are backing-up.

function backup_tables($host,$user,$pass,$name,$tables)
{
    $link = mysql_connect($host,$user,$pass);
    mysql_select_db($name,$link);
    $return = "";

    // Get all of the tables
    if($tables == '*') {
        $tables = array();
        $result = mysql_query('SHOW TABLES');
        while($row = mysql_fetch_row($result)) {
            $tables[] = $row[0];
        }
    } else {
        if (is_array($tables)) {
            $tables = explode(',', $tables);
        }
    }

    // Cycle through each provided table
    foreach($tables as $table) {
        $result = mysql_query('SELECT * FROM '.$table);
        $num_fields = mysql_num_fields($result);

        // First part of the output – remove the table
        $return .= 'DROP TABLE ' . $table . ';<|||||||>';

        // Second part of the output – create table
        $row2 = mysql_fetch_row(mysql_query('SHOW CREATE TABLE '.$table));
        $return .= "\n\n" . $row2[1] . ";<|||||||>\n\n";

        // Third part of the output – insert values into new table
        for ($i = 0; $i < $num_fields; $i++) {
            while($row = mysql_fetch_row($result)) {
                $return.= 'INSERT INTO '.$table.' VALUES(';
                for($j=0; $j<$num_fields; $j++) {
                    $row[$j] = addslashes($row[$j]);
                    $row[$j] = ereg_replace("\n","\\n",$row[$j]);
                    if (isset($row[$j])) {
                        $return .= '"' . $row[$j] . '"';
                    } else {
                        $return .= '""';
                    }
                    if ($j<($num_fields-1)) {
                        $return.= ',';
                    }
                }
                $return.= ");<|||||||>\n";
            }
        }
    $return.="\n\n\n";
    }
}

We are almost done, we now just need to save the output to a file. I will save the backup file to backup subdirectory. Output is held by $return variable.
We will set the filename to which the output should be saved and then save the backup. The filename is formatted as follows:  dbbackup_date_time.sql
I also want to print that the backup is finished and to provide a link to the backup file. I’m also closing MySQL connection at the end of this function.

function backup_tables($host,$user,$pass,$name,$tables)
{
    $link = mysql_connect($host,$user,$pass);
    mysql_select_db($name,$link);
    $return = "";

    // Get all of the tables
    if($tables == '*') {
        $tables = array();
        $result = mysql_query('SHOW TABLES');
        while($row = mysql_fetch_row($result)) {
            $tables[] = $row[0];
        }
    } else {
        if (is_array($tables)) {
            $tables = explode(',', $tables);
        }
    }

    // Cycle through each provided table
    foreach($tables as $table) {
        $result = mysql_query('SELECT * FROM '.$table);
        $num_fields = mysql_num_fields($result);

        // First part of the output – remove the table
        $return .= 'DROP TABLE ' . $table . ';<|||||||>';

        // Second part of the output – create table
        $row2 = mysql_fetch_row(mysql_query('SHOW CREATE TABLE '.$table));
        $return .= "\n\n" . $row2[1] . ";<|||||||>\n\n";

        // Third part of the output – insert values into new table
        for ($i = 0; $i < $num_fields; $i++) {
            while($row = mysql_fetch_row($result)) {
                $return.= 'INSERT INTO '.$table.' VALUES(';
                for($j=0; $j<$num_fields; $j++) {
                    $row[$j] = addslashes($row[$j]);
                    $row[$j] = ereg_replace("\n","\\n",$row[$j]);
                    if (isset($row[$j])) {
                        $return .= '"' . $row[$j] . '"';
                    } else {
                        $return .= '""';
                    }
                    if ($j<($num_fields-1)) {
                        $return.= ',';
                    }
                }
                $return.= ");<|||||||>\n";
            }
        }
        $return.="\n\n\n";
    }

    // Generate the filename for the sql file
    $filess = 'backup/dbbackup_' . date("d.m.Y_H:i:s") . '.sql';

    // Save the sql file
    $handle = fopen($filess,'w+');
    fwrite($handle,$return);
    fclose($handle);

    // Print the message
    print("The backup has been created successfully. You can get the file <a href='$filess'>here</a>.<br>\n");

    // Close MySQL Connection
    mysql_close();
}

We have our backup function ready. We can now call it. To do this we need to include the settings from the config file and call the function.

// Include settings
include("config.php");

// Call the function
backup_tables($DBhost,$DBuser,$DBpass,$DBName,$table);

Before going and testing this function I wanted to change the filename of the backup a bit. Since our function is checking if the user entered all the tables to be backed-up or just some specific ones I want to add small changes to the filename, depending on the input. So I’m adding the following code between inclusion of the config file and function call. But this also requires that we pass another arg to our function. I named it bckextname.

// Call the backup function for all tables in a DB
if ($table == '*') {
    $extname = 'alltbls';
}else{
    $extname = str_replace(",", "_", $table);
    $extname = str_replace(" ", "_", $extname);
}

What this does is that it checks if the input is ‘*’ (which, if you remember from the code above, tells the function to back-up all the tables), and sets the extended name var $extname to alltbls. Otherwise it sets this var to all the table names that will be backed-up. This means that we will be able to see which tables are in which backup, just by looking at filename of the backup.

So now, after adding another argument, we need to change the $filess variable, near the end of the code. So the whole page now looks like this

<?php
    // Include settings
    include("config.php");

    // Set the suffix of the backup filename
    if ($table == '*') {
        $extname = 'all';
    }else{
        $extname = str_replace(",", "_", $table);
        $extname = str_replace(" ", "_", $extname);
    }

    // Call the backup function for all tables in a DB
    backup_tables($DBhost,$DBuser,$DBpass,$DBName,$table,$extname);

    // Backup the table and save it to a sql file
    function backup_tables($host,$user,$pass,$name,$tables,$bckextname)
    {
        $link = mysql_connect($host,$user,$pass);
        mysql_select_db($name,$link);
        $return = "";

        // Get all of the tables
        if($tables == '*') {
            $tables = array();
            $result = mysql_query('SHOW TABLES');
            while($row = mysql_fetch_row($result)) {
                $tables[] = $row[0];
            }
        } else {
            if (is_array($tables)) {
            $tables = explode(',', $tables);
        }
    }

    // Cycle through each provided table
    foreach($tables as $table) {
        $result = mysql_query('SELECT * FROM '.$table);
        $num_fields = mysql_num_fields($result);

        // First part of the output – remove the table
        $return .= 'DROP TABLE ' . $table . ';<|||||||>';

        // Second part of the output – create table
        $row2 = mysql_fetch_row(mysql_query('SHOW CREATE TABLE '.$table));
        $return .= "\n\n" . $row2[1] . ";<|||||||>\n\n";

        // Third part of the output – insert values into new table
        for ($i = 0; $i < $num_fields; $i++) {
            while($row = mysql_fetch_row($result)) {
                $return.= 'INSERT INTO '.$table.' VALUES(';
                for($j=0; $j<$num_fields; $j++) {
                    $row[$j] = addslashes($row[$j]);
                    $row[$j] = ereg_replace("\n","\\n",$row[$j]);
                    if (isset($row[$j])) {
                        $return .= '"' . $row[$j] . '"';
                    } else {
                        $return .= '""';
                    }
                    if ($j<($num_fields-1)) {
                        $return.= ',';
                    }
                }
                $return.= ");<|||||||>\n";
            }
        }
        $return.="\n\n\n";
    }

    // Generate the filename for the sql file
    $filess = 'backup/dbbackup_' . date("d.m.Y_H:i:s") . '_' . $extname . '.sql';

    // Save the sql file
    $handle = fopen($filess,'w+');
    fwrite($handle,$return);
    fclose($handle);

    // Print the message
    print("The backup has been created successfully. You can get the file <a href='$filess'>here</a>.<br>\n");

    // Close MySQL Connection
    mysql_close();
}
?>

I saved this code to backup_perform.php file.

Restore MySQL Backup

Since we have already saved the backup in the form of MySQL commands, we now just need to import these commands and run them.

Since the backup filenames are generated by time and date and table names, we will need to provide the filename of the backup to the restore script, so the script will know which backup to use.

We will establish MySQL connection, read-in the file and run the commands from the file.

First we will get the provided backup filename and include config file since we will need MySQL settings.

// Get the provided arg
$id=$_GET['id'];

// Check if the file has needed args
if ($id==NULL){
    print("<script type='text/javascript'>window.alert('You have not provided a backup to restore.')</script>");
    print("<script type='text/javascript'>window.location='backup_overview.php'</script>");
    print("You have not provided a backup to restore.<br>Click <a href='backup_overview.php'>here</a> if your browser doesn't automatically redirect you.");
}

// Include settings
include("config.php");

We need to generate the filename of the backup. Remember: backup file is located in the backup dir.

$filename = 'backup/' . $id;

So now, we will restore the backup (basically just run MySQL commands from the file). Remember to change the separator characters if you changed them in previous code (backup_perform.php file). Otherwise, the script won’t work correctly.

// Restore the backup
$con = mysql_connect($DBhost,$DBuser,$DBpass);
if ($con !== false){

    // Load and explode the sql file
    mysql_select_db("$DBName");
    $f = fopen($filename,"r+");
    $sqlFile = fread($f,filesize($filename));
    $sqlArray = explode(';<|||||||>',$sqlFile);

    // Process the sql file by statements
    foreach ($sqlArray as $stmt) {
        if (strlen($stmt)>3){
            $result = mysql_query($stmt);
        }
    }
}

// Close the connection
mysql_close();

I will also add error reporting, so that I will know if anything goes wrong (but it shouldn’t). So the script looks like this. Save it as backup_restore.php

<?php
    // Get the provided arg
    $id=$_GET['id'];

    // Check if the file has needed args
    if ($id==NULL){
        print("<script type='text/javascript'>window.alert('You have not provided a backup to restore.')</script>");
        print("<script type='text/javascript'>window.location='backup_overview.php'</script>");
        print("You have not provided a backup to restore.<br>Click <a href='backup_overview.php'>here</a> if your browser doesn't automatically redirect you.");
    }

    // Include settings
    include("config.php");

    // Generate filename and set error variables
    $filename = 'backup/' . $id;
    $sqlErrorText = '';
    $sqlErrorCode = 0;
    $sqlStmt      = '';

    // Restore the backup
    $con = mysql_connect($DBhost,$DBuser,$DBpass);
    if ($con !== false){

        // Load and explode the sql file
        mysql_select_db("$DBName");
        $f = fopen($filename,"r+");
        $sqlFile = fread($f,filesize($filename));
        $sqlArray = explode(';<|||||||>',$sqlFile);

        // Process the sql file by statements
        foreach ($sqlArray as $stmt) {
            if (strlen($stmt)>3){
                $result = mysql_query($stmt);
            }
        }
    }

    // Print message (error or success)
    if ($sqlErrorCode == 0){
        print("Database restored successfully!<br>\n");
        print("Backup used: " . $filename);
    } else {
        print("An error occurred while restoring backup!<br><br>\n");
        print("Error code: $sqlErrorCode<br>\n");
        print("Error text: $sqlErrorText<br>\n");
        print("Statement:<br/> $sqlStmt<br>");
    }

    // Close the connection
    mysql_close();
?>

Maintenance part of the script

Now, we have written backup script and restore script. I just want to add a management script, so that we wouldn’t need to call these files manually each time.

I will print a simple table that will have the list of all backups, and that will have Restore, Download and Delete links.

This isn’t hard at all. All I need to do is to list the files in the backup dir.

<table>
<tr><th>Filename</th></tr>
<?php
    // List the files
    $dir = opendir ("./backup");
    while (false !== ($file = readdir($dir))) {

        // Print the filenames that have .sql extension
        if (strpos($file,'.sql',1)) {

            // Print the cells
            print("<tr>\n");
            print("  <td>" . $file . "</td>\n");
            print("</tr>\n");
        }
    }
?>
</table>

Now, just add the links to other functions. I have also added the part where the script gets the date and time of backup creation and puts it in the table.

<p>Create new backup by clicking <a href="backup_perform.php">here</a> or use the links below to manage existing backups.</p>
<table>
<tr><th>Filename</th><th>Date and time</th><th>Restore</th><th>Get the file</th><th>Delete</th></tr>
<?php
    // List the files
    $dir = opendir ("./backup");
    while (false !== ($file = readdir($dir))) {

        // Print the filenames that have .sql extension
        if (strpos($file,'.sql',1)) {

            // Get time and date from filename
            $date = substr($file, 9, 10);
            $time = substr($file, 20, 8);

            // Print the cells
            print("<tr>\n");
            print("  <td>" . $file . "</td>\n");
            print("  <td>" . $date . " " . $time . "</td>\n");
            print("  <td><a href='backup_restore.php?id=" . $file . "'>Restore</a></td>\n");
            print("  <td><a href='backup/" . $file . "'>Download</a></td>\n");
            print("  <td><a href='delete_file.php?file=" . $file . "'>Delete</a></td>\n");
            print("</tr>\n");
        }
    }
?>
</table>

I have saved this code as backup_restore.php. We just need to create one more file: delete_file.php

It will allow us to delete existing backup files. We will be using unlink() PHP function. First we are checking if the file to delete has been provided, and then we are checking that it actually is a file. Using unlink() we are removing the selected file and returning to backup_restore.php.

<?php
    // Get the filename to be deleted
    $file=$_GET['file'];

    // Check if the file has needed args
    if ($file==NULL){
        print("<script type='text/javascript'>window.alert('You have not provided a file to delete.')</script>");
        print("<script type='text/javascript'>window.location='backup_overview.php'</script>");
        print("You have not provided a file to delete.<br>Click <a href='backup_overview.php'>here</a> if your browser doesn't automatically redirect you.");
        die();
    }

    // Delete the file
    if (!is_dir("backup/" . $file)) {
        unlink("backup/" . $file);
    }

    // Redirect
    header("Location: backup_overview.php");
?>

That’s it! We have created the first part of our Backup & Restore Script: MySQL Backup & Restore. There is one job left, create Backup & Restore script for the server files and merge these two parts.

Server Files Backup

Now we are going to make a script that will be creating a zip archive. It will be zipping all files and directories from the root dir.

For this I will use PclZip library. It is licensed under GNU/LGPL, so there is no license problem for using it. You can download this library from here (http://www.phpconcept.net/pclzip). I have included it in the download package.

PclZip does not nativly support the addition of directories, so we will have to do the following

<?php
    // Include the PclZip library
    require_once('pclzip.lib.php');

    // Set the arhive filename
    $archive = new PclZip('backup/archive.zip');

    // Set the dir to archive
    $v_dir = dirname(getcwd()); // or dirname(__FILE__);
    $v_remove = $v_dir;

    // Create the archive
    $v_list = $archive->create($v_dir, PCLZIP_OPT_REMOVE_PATH, $v_remove);
    if ($v_list == 0) {
        die("Error : ".$archive->errorInfo(true));
    }
?>

Since this script is not in the root dir on my server but in a subdir, I’m using this dirname(getcwd()) to tell the script where my root dir is compared to current dir (one level back). getcwd() gets my current dir, while dirname() returns dir one level up from the current dir (return value of getcwd()).

Save this as a files_backup.php and upload pclzip.lib.php that you have downloaded from PclZip website. When you run files_backup.php, you will see that it has created new file in the backup dir, named archive.php. If you download this archive you will see that all your files and dirs are in there.

Server Files Restore

To restore a zip archive, we will need first to delete all files and dirs, and then to unpack our zipped backup. So, we will start by recursively deleting everything in the target dir, and then extracting to the target dir.

I have placed pczlib library to be included at the very beginning, so that we could easily delete it afterwards. After including this library, we are calling the rrmdir() function (recursively remove directory) with provided path which is one level up from the working dir. In the previous script we have backed-up the root dir, so now we want to extract to the root dir.

We are limiting the rrmdir() function not to delete current working file (files_restore.php) and our backup archive so that we could finish the extraction, after which we are deleting these two files ourselves.

<?php
    // Include this library so we could delete the file
    include('pclzip.lib.php');

    // Remove the current dir
    rrmdir(dirname(getcwd()));

    // Recursively remove dir
    function rrmdir($dir) {
        if (is_dir($dir)) {
            $objects = scandir($dir);
            foreach ($objects as $object) {
                if ($object != "." &amp;&amp; $object != ".." &amp;&amp; $object != "files_restore.php" &amp;&amp; $object != "archive.zip") {
                    if (filetype($dir."/".$object) == "dir") {
                        rrmdir($dir."/".$object);
                    } else {
                        unlink($dir."/".$object);
                    }
                }
            }
            reset($objects);
        }
    }

    // Extract archive
    $archive = new PclZip('archive.zip');
    if ($archive->extract(PCLZIP_OPT_PATH, "../") == 0) {
        die("Error : ".$archive->errorInfo(true));
    }

    // Remove two left files
    unlink("backup/archive.zip");
    rmdir("backup");
    unlink("files_restore.php");
    rmdir(getcwd());
?>

Our File Restoring script is now working. The last thing is to merge MySQL Backup script with Files Backup script and the Restore scripts, too. We will also change the Maintenance script a bit, so it will reflect both MySQL and Files backups.

Merging and Rewriting Maintenance Script

Copy the content of files_backup.php into backup_perform.php, at the very end, right after the function backup_tables is closed. Move the line where we generate the filename before the backup_tables function and after setting the suffix of the backup filename.

Add a new argument to the function, named $filess, and also add it to the function call, two lines above.

The beginning of the script should look like this

<?php
    // Include settings
    include("config.php");

    // Set the suffix of the backup filename
    if ($table == '*') {
        $extname = 'all';
    }else{
        $extname = str_replace(",", "_", $table);
        $extname = str_replace(" ", "_", $extname);
    }

    // Generate the filename for the backup file
    $filess = 'backup/dbbackup_' . date("d.m.Y_H:i:s") . '_' . $bckextname . '.sql';

    // Call the backup function for all tables in a DB
    backup_tables($DBhost,$DBuser,$DBpass,$DBName,$table,$extname,$filess);

    // Backup the table and save it to a sql file
    function backup_tables($host,$user,$pass,$name,$tables,$bckextname,$filess)
    {
        $link = mysql_connect($host,$user,$pass);
        mysql_select_db($name,$link);
        $return = "";

Remove the  . ‘.sql’ part of the $filess variable.

In the Save the sql file part of the backup_tables function, add . ’.sql’ so that the script will write the MySQL dump into .sql file. So this part should look like this, now.

// Save the sql file
$handle = fopen($filess.'.sql','w+');
fwrite($handle,$return);
fclose($handle);

In the part that we copied from the files_backup.php file, replace “backup/archive.zip” with $filess.’.zip’, so this part should look like this.

require_once('pclzip.lib.php');
$archive = new PclZip($filess.'.zip');
$v_dir = dirname(getcwd()); // or dirname(__FILE__);
$v_remove = $v_dir;
$v_list = $archive->create($v_dir, PCLZIP_OPT_REMOVE_PATH, $v_remove);
if ($v_list == 0) {
    die("Error : ".$archive->errorInfo(true));
}

Move the success message at the end of the file, and change it a bit, so it will display both MySQL dump file and zip archive (both of these files have the same name, just different extensions).

// Print the message
print('The backup has been created successfully. <br />You can get <b>MySQL dump file</b> <a href="' . $filess . '.sql">here</a>.<br>' . "\n");
print('You can get <b>Backed-up files archive</b> <a href="' . $filess . '.zip">here</a>.<br>' . "\n");

Now, the whole script looks like this. Save it as backup.php.

<?php
    // Include settings
    include("config.php");

    // Set the suffix of the backup filename
    if ($table == '*') {
        $extname = 'all';
    }else{
        $extname = str_replace(",", "_", $table);
        $extname = str_replace(" ", "_", $extname);
    }

    // Generate the filename for the backup file
    $filess = 'backup/dbbackup_' . date("d.m.Y_H:i:s") . '_' . $bckextname;

    // Call the backup function for all tables in a DB
    backup_tables($DBhost,$DBuser,$DBpass,$DBName,$table,$extname,$filess);

    // Backup the table and save it to a sql file
    function backup_tables($host,$user,$pass,$name,$tables,$bckextname,$filess)
    {
        $link = mysql_connect($host,$user,$pass);
        mysql_select_db($name,$link);
        $return = "";

        // Get all of the tables
        if($tables == '*') {
            $tables = array();
            $result = mysql_query('SHOW TABLES');
            while($row = mysql_fetch_row($result)) {
                $tables[] = $row[0];
            }
        } else {
            if (is_array($tables)) {
                $tables = explode(',', $tables);
            }
        }

        // Cycle through each provided table
        foreach($tables as $table) {
            $result = mysql_query('SELECT * FROM '.$table);
            $num_fields = mysql_num_fields($result);

            // First part of the output – remove the table
            $return .= 'DROP TABLE ' . $table . ';<|||||||>';

            // Second part of the output – create table
            $row2 = mysql_fetch_row(mysql_query('SHOW CREATE TABLE '.$table));
            $return .= "\n\n" . $row2[1] . ";<|||||||>\n\n";

            // Third part of the output – insert values into new table
            for ($i = 0; $i < $num_fields; $i++) {
                while($row = mysql_fetch_row($result)) {
                    $return.= 'INSERT INTO '.$table.' VALUES(';
                    for($j=0; $j<$num_fields; $j++) {
                        $row[$j] = addslashes($row[$j]);
                        $row[$j] = ereg_replace("\n","\\n",$row[$j]);
                        if (isset($row[$j])) {
                            $return .= '"' . $row[$j] . '"';
                        } else {
                            $return .= '""';
                        }
                        if ($j<($num_fields-1)) {
                            $return.= ',';
                        }
                    }
                    $return.= ");<|||||||>\n";
                }
            }
            $return.="\n\n\n";
        }

        // Save the sql file
        $handle = fopen($filess.'.sql','w+');
        fwrite($handle,$return);
        fclose($handle);

        // Close MySQL Connection
        mysql_close();
    }

    require_once('pclzip.lib.php');
    $archive = new PclZip($filess.'.zip');
    $v_dir = dirname(getcwd()); // or dirname(__FILE__);
    $v_remove = $v_dir;
    $v_list = $archive->create($v_dir, PCLZIP_OPT_REMOVE_PATH, $v_remove);
    if ($v_list == 0) {
        die("Error : ".$archive->errorInfo(true));
    }

    // Print the message
    print('The backup has been created successfully. <br />You can get <b>MySQL dump file</b> <a href="' . $filess . '.sql">here</a>.<br>' . "\n");
    print('You can get <b>Backed-up files archive</b> <a href="' . $filess . '.zip">here</a>.<br>' . "\n");
?>

Now we will just merge two restore scripts.

Copy the content of the files_restore.php into backup_restore.php file, just below mysql_close()., replace ‘archive.zip’ with $filename (you should do this in the rrmdir() function, extract archive part and in the last part where we remove two last files),  replace ‘files_restore.php’ with ‘restore.php’ and ‘backup_overview.php’ with ‘manage.php’.

When setting $filename var add . ‘.sql’ after $id.

Between mysql_close() and include(‘pclzip.lib.php’) insert the following code

// Change the filename from sql to zip
$filename = str_replace('.sql', '.zip', $filename);

This code will replace SQL extension with ZIP extension.

At the end of the file add a notice that files have been restored successfully. Your code should look something like this.

<?php
    // Get the provided arg
    $id=$_GET['id'];

    // Check if the file has needed args
    if ($id==NULL){
        print("<script type='text/javascript'>window.alert('You have not provided a backup to restore.')</script>");
        print("<script type='text/javascript'>window.location='manage.php'</script>");
        print("You have not provided a backup to restore.<br>Click <a href='manage.php'>here</a> if your browser doesn't automatically redirect you.");
    }

    // Include settings
    include("config.php");

    // Generate filename and set error variables
    $filename = 'backup/' . $id;
    $sqlErrorText = '';
    $sqlErrorCode = 0;
    $sqlStmt      = '';

    // Restore the backup
    $con = mysql_connect($DBhost,$DBuser,$DBpass);
    if ($con !== false){

        // Load and explode the sql file
        mysql_select_db("$DBName");
        $f = fopen($filename,"r+");
        $sqlFile = fread($f,filesize($filename));
        $sqlArray = explode(';<|||||||>',$sqlFile);

        // Process the sql file by statements
        foreach ($sqlArray as $stmt) {
            if (strlen($stmt)>3){
                $result = mysql_query($stmt);
            }
        }
    }

    // Print message (error or success)
    if ($sqlErrorCode == 0){
        print("Database restored successfully!<br>\n");
        print("Backup used: " . $filename . "<br>\n");
    } else {
        print("An error occurred while restoring backup!<br><br>\n");
        print("Error code: $sqlErrorCode<br>\n");
        print("Error text: $sqlErrorText<br>\n");
        print("Statement:<br/> $sqlStmt<br>");
    }

    // Close the connection
    mysql_close();

    // Change the filename from sql to zip
    $filename = str_replace('.sql', '.zip', $filename);

    // Include this library so we could delete the file
    include('pclzip.lib.php');

    // Remove the current dir
    rrmdir(dirname(getcwd()));

    // Recursively remove dir
    function rrmdir($dir) {
        if (is_dir($dir)) {
            $objects = scandir($dir);
            foreach ($objects as $object) {
                if ($object != "." &amp;&amp; $object != ".." &amp;&amp; $object != "restore.php" &amp;&amp; $object != $filename) {
                    if (filetype($dir."/".$object) == "dir") {
                        rrmdir($dir."/".$object);
                    } else {
                        unlink($dir."/".$object);
                }
            }
        }
        reset($objects);
    }

    // Extract archive
    $archive = new PclZip($filename);
    if ($archive->extract(PCLZIP_OPT_PATH, "../") == 0) {
        die("Error : ".$archive->errorInfo(true));
    }

    // Remove two left files
    unlink($filename);
    rmdir("backup");
    unlink("restore.php");
    rmdir(getcwd());

    // Files restored successfully
    print("Files restored successfully!<br>\n");
    print("Backup used: " . $filename . "<br>\n");
?>

Save this file as restore.php.

Next file to edit is backup_overview.php

In the first line, change from backup_overview.php to manage.php. Add following lines below the line where we set the $date variable. It removes the sql extension from the filename.

// Remove the sql extension part in the filename
$filenameboth = str_replace('.sql', '', $file);

In the Print the cells part, replace $file with $filenameboth. There are 4 occurrences. In the same section, replace ‘backup_restore.php?id=’ with ‘restore.php?id=’ and replace ‘delete_file.php?file=’ with ‘delete.php?file=’.

Replace the line

print("  <td><a href='backup/" . $filenameboth . ".sql'>Download SQL</a></td>\n");

with these two lines

print("  <td><a href='backup/" . $filenameboth . ".sql'>Download SQL</a></td>\n");
print("  <td><a href='backup/" . $filenameboth . ".zip'>Download ZIP</a></td>\n");

Save the file as manage.php. The complete code of this file is

<p>Create new backup by clicking <a href="backup.php">here</a> or use the links below to manage existing backups.</p>
<table>
<tr><th>Filename</th><th>Date and time</th><th>Restore</th><th>Get SQL file</th><th>Get ZIP file</th><th>Delete</th></tr>
<?php
    // List the files
    $dir = opendir ("./backup");
    while (false !== ($file = readdir($dir))) {

        // Print the filenames that have .sql extension
        if (strpos($file,'.sql',1)) {

            // Get time and date from filename
            $date = substr($file, 9, 10);
            $time = substr($file, 20, 8);

            // Remove the sql extension part in the filename
            $filenameboth = str_replace('.sql', '', $file);

            // Print the cells
            print("<tr>\n");
            print("  <td>" . $filenameboth . "</td>\n");
            print("  <td>" . $date . " " . $time . "</td>\n");
            print("  <td><a href='restore.php?id=" . $filenameboth . "'>Restore</a></td>\n");
            print("  <td><a href='backup/" . $filenameboth . ".sql'>Download SQL</a></td>\n");
            print("  <td><a href='backup/" . $filenameboth . ".zip'>Download ZIP</a></td>\n");
            print("  <td><a href='delete_file.php?file=" . $filenameboth . "'>Delete</a></td>\n");
            print("</tr>\n");
        }
    }
?>
</table>

As the last file to edit, we have delete_file.php.

Duplicate the Delete the file part, and change it as follows.

// Delete the SQL file
if (!is_dir("backup/" . $file . '.sql')) {
    unlink("backup/" . $file . '.sql');
}

// Delete the ZIP file
if (!is_dir("backup/" . $file . '.zip')) {
    unlink("backup/" . $file . '.zip');
}

Replace ‘backup_overview.php’ with ‘manage.php’ and you are done! Save it as delete.php.

The source code of this page

<?php
    // Get the filename to be deleted
    $file=$_GET['file'];

    // Check if the file has needed args
    if ($file==NULL){
        print("<script type='text/javascript'>window.alert('You have not provided a file to delete.')</script>");
        print("<script type='text/javascript'>window.location='manage.php'</script>");
        print("You have not provided a file to delete.<br>Click <a href='manage.php'>here</a> if your browser doesn't automatically redirect you.");
        die();
    }

    // Delete the SQL file
    if (!is_dir("backup/" . $file . '.sql')) {
        unlink("backup/" . $file . '.sql');
    }

    // Delete the ZIP file
    if (!is_dir("backup/" . $file . '.zip')) {
        unlink("backup/" . $file . '.zip');
    }

    // Redirect
    header("Location: manage.php");
?>

That’s it! You have fully functional (but still ugly though) Backup & Restore script. Remove the unnecessary files from your dir. I’m left with only the following ones: backup.php, config.php, delete.php, manage.php, pclzip.lib.php, restore.php and a single directory backup.

Cron Job

The whole point of this script is to make your (mine) life easier. So I wanted to create a Cron job, but then I realized that if I was only running backup.php, my backup dir will get full. So I’ve created a small script for Cron jobs.

This file will have three parts. The first part is going to delete all previous backups from the backup dir and nothing else. Second part will back-up MySQL DB, and the final part will back-up files from the server.

I also wanted to protect it from unauthorized access. So I’m adding password check at the beginning.

First I will get the password sent by the Cron job. Change the password to whatever you want, but DO change it. You can see that if passwords don’t match we are killing the script (with die()).

$pass = $argv[2];
if ($pass != 'your_own_custom_long_password_here') {
    die('Password is incorrect!');
}

Since this will be run from the command line (Cron Job), we can’t use $_GET[‘’], but instead we need to use $argv. Since I’m calling cron.php with following command in the Cron job settings

php /the/path/toyour/cronjob/file/cron.php -- password_you_changed

password is the third argument (first is file path and second is –), so I’ve set $argv array to element 2, since it starts counting from 0.

Now, I want to remove all previous backup files in the backup dir. So, I’m going to empty this dir.

foreach(glob('backup/' . '*.*') as $v){
    if (is_file($v)) {
        unlink($v);
    }
}

You can see that I’m removing all files (*.*) from the backup/ dir. I’m checking to see if they really are files and not subdirs.

I’ve simply copied backing-up MySQL and Files parts from the backup.php. This is simply because I wanted restore.php script to restore these cron backups, which are the same thing as manually called backups.

Next, I’ve changed a couple of things. I’ve added _cron_ to backup filename. So when I download backups on my local drive I will now, which are regular backups and which are after some big change I did. You don’t have to do this you can leave the filename as is.

I’ve also changed the message at the end of the file. Now it prints only “The backup has been successful!” and nothing else, because I don’t want to download backups or do anything else with these files. I will download manually these Cron backups.

I named this file cron.php, which has following code

<?php
    $pass = $argv[2];
    if ($pass != 'your_own_custom_long_password_here') {
        die('Password is incorrect!');
    }

    foreach(glob('backup/' . '*.*') as $v){
        if (is_file($v)) {
            unlink($v);
        }
    }

    // Include settings
    include("config.php");

    // Set the suffix of the backup filename
    if ($table == '*') {
        $extname = 'all';
    }else{
        $extname = str_replace(",", "_", $table);
        $extname = str_replace(" ", "_", $extname);
    }

    // Generate the filename for the backup file
    $filess = 'backup/dbbackup_' . date("d.m.Y_H:i:s") . '_cron_' . $extname;

    // Call the backup function for all tables in a DB
    backup_tables($DBhost,$DBuser,$DBpass,$DBName,$table,$extname,$filess);

    // Backup the table and save it to a sql file
    function backup_tables($host,$user,$pass,$name,$tables,$bckextname,$filess)
    {
        $link = mysql_connect($host,$user,$pass);
        mysql_select_db($name,$link);
        $return = "";

        // Get all of the tables
        if($tables == '*') {
            $tables = array();
            $result = mysql_query('SHOW TABLES');
            while($row = mysql_fetch_row($result)) {
                $tables[] = $row[0];
            }
        } else {
            if (is_array($tables)) {
                $tables = explode(',', $tables);
            }
        }

    // Cycle through each provided table
    foreach($tables as $table) {
        $result = mysql_query('SELECT * FROM '.$table);
        $num_fields = mysql_num_fields($result);

        // First part of the output – remove the table
        $return .= 'DROP TABLE ' . $table . ';<|||||||>';

        // Second part of the output – create table
        $row2 = mysql_fetch_row(mysql_query('SHOW CREATE TABLE '.$table));
        $return .= "\n\n" . $row2[1] . ";<|||||||>\n\n";

        // Third part of the output – insert values into new table
        for ($i = 0; $i < $num_fields; $i++) {
            while($row = mysql_fetch_row($result)) {
                $return.= 'INSERT INTO '.$table.' VALUES(';
                for($j=0; $j<$num_fields; $j++) {
                    $row[$j] = addslashes($row[$j]);
                    $row[$j] = ereg_replace("\n","\\n",$row[$j]);
                    if (isset($row[$j])) {
                        $return .= '"' . $row[$j] . '"';
                    } else {
                        $return .= '""';
                    }
                    if ($j<($num_fields-1)) {
                        $return.= ',';
                    }
                }
                $return.= ");<|||||||>\n";
            }
        }
        $return.="\n\n\n";
    }

    // Save the sql file
    $handle = fopen($filess.'.sql','w+');
    fwrite($handle,$return);
    fclose($handle);

    // Close MySQL Connection
    mysql_close();
    }

    require_once('pclzip.lib.php');
    $archive = new PclZip($filess.'.zip');
    $v_dir = dirname(getcwd()); // or dirname(__FILE__);
    $v_remove = $v_dir;
    $v_list = $archive->create($v_dir, PCLZIP_OPT_REMOVE_PATH, $v_remove);
    if ($v_list == 0) {
        die("Error : ".$archive->errorInfo(true));
    }

    // Print the message
    print('The backup has been created successfully!');
?>

We are done! Now, you can Backup & Restore your entire web site, manage your backups, download & remove them and set cron jobs.

Styling the Files

Since this is quite ugly (visually) I wanted to add some styling to the files so that they are nice to work with. The files in the download package are already styled, so you won’t have to bother with it.

Here are a few images of the styled script.

For styling I’ve used Transdmin Light Theme from http://www.transdmin.perspectived.com/.

I haven’t styled cron.php since it is not intended to be run within browser or to be worked with. It will only print a simple message that you will get in your email when executing cron.php as a cron job.

Usage

This script is free! You can download and use it as you wish. You are not even obligated to put a backlink! If you are using it for commercial purposes I kindly ask you to give some credit, but still it’s your free will.

Download

You can download the complete, styled source code, using the link below.



Social Share

   



39 Comments for this entry

Michael
February 20th, 2011 on 06:29

Hi,

good script and working fine, but there is one problem. My sql file is 6.2kb, ZIP – 130Mb….

Thanks
Michael

    Ivan Stambolic
    February 20th, 2011 on 13:09

    Thanks for the comment.
    Glad you like the script.

    About your question: The sql file is generally small file, since it’s a text backup of your MySQL database. You can open it using any text editor (notepad, vi) and you will see that it’s simple text – that’s why it’s very small. On the other hand, zip file is significantly larger, since it contains ALL the files from your server, every single php, html, images, videos, other zip files and any other files you have on your server (including the “just generated sql backup file”). So when all the files from your server are packed in a single zip file, the zip becomes 130 MB “heavy”, in your case.

    I hope I’ve clarified it.
    Thanks and cheers!
    Ivan

Goddy
March 8th, 2011 on 15:49

i received this message, i need help

Warning: fopen(backup/dbbackup_09.03.2011_03:36:30_all.sql) [function.fopen]: failed to open stream: Invalid argument in C:\AppServ\www\backit\backup.php on line 115

Warning: fwrite(): supplied argument is not a valid stream resource in C:\AppServ\www\backit\backup.php on line 116

Warning: fclose(): supplied argument is not a valid stream resource in C:\AppServ\www\backit\backup.php on line 117
Error : PCLZIP_ERR_READ_OPEN_FAIL (-2) : Unable to open archive ‘backup/dbbackup_09.03.2011_03:36:30_all.zip’ in wb mode

Igor Freire
April 14th, 2011 on 18:01

Goddy,

about the error “Error : PCLZIP_ERR_READ_OPEN_FAIL (-2) : Unable to open archive ‘backup/dbbackup_09.03.2011_03:36:30_all.zip’ in wb mode”, try to CHMOD 0777 in the backup folder.

Really good this script!

Qaysar
June 4th, 2011 on 21:39

Hello,

Nice script works fine but I have noticed three (3) little problems with the script.

1. When you create a second back of the website it also creates a backup of the backup folder so increasing the size of the complete file.

Is their a way to block the backup folder being included in the backup??

2. The following error message is displayed when the main folder is being extracted once saved on desktop

The following invalid filename was encountered in the archieve:
“backupscript\backup\dbbackup_04.06.2011_21:17:02_all.sql”

How can this be fixed, I thionk the sql file is not being zipped with this folder.

3. In the Sql file the following characters are added to the end of the table and also the insert statement

Your help is much apprecaited.

Thank you

Ivan Stambolic
June 4th, 2011 on 22:00

Hi Qaysar!
I’m glad you like the script. Thanks for the kind words.

Now, about your questions
1. Yes, native script will backup all dirs and files in the root dit (including all subdirs). All you need to do is to empty the dir ‘backup’. As you can see above, I have included this part in the Cron job script. I’m using this script only through Cron jobs, so it’s my mistake for not including it in the main script. Anyway, just use this code before all backing-up functions
foreach(glob(‘backup/’ . ‘*.*’) as $v){
if (is_file($v)) {
unlink($v);
}
}

Or, if you just use Cron Job script it will do this for you, so you don’t need to change anything.

2. This is a problem with Windows, because you can’t use colon characters in filenames. As you can see, the sql filename has date and time part, which is defined as
date(“d.m.Y_H:i:s”)
Just change colon chars to, let’s say, underscores and you will have no problem with sql filename.
date(“d.m.Y_H_i_s”)

3. I’m not sure I understand what the question is. Can you please elaborate a bit?
Thanks.

Thank you for using my script.

Cheers,
Ivan

Qaysar
June 5th, 2011 on 11:02

Hello Ivan,

thank you for such a fast response.

As for question 3, my mistake I forgot to put the characters in my question.

These characters are left at the end of the database structure and the insert statement.

Would this cause a problem when restoring the MySQL files??

Also wanted to ask if the site had no database can this script still be used to backup the website? Like just a basic HTML website?

Thank you for the hints but i’m not a programmer so will ask this sound like a dumb question but the code you provided

foreach(glob(‘backup/’ . ‘*.*’) as $v){
if (is_file($v)) {
unlink($v);
}
}

does this go in the backup.php file just before the first function?

foreach(glob(‘backup/’ . ‘*.*’) as $v){
if (is_file($v)) {
unlink($v);
}
}

// Backup the table and save it to a sql file
function backup_tables($host,$user,$pass,$name,$tables,$bckextname,$filess)
{
$link = mysql_connect($host,$user,$pass);
mysql_select_db($name,$link);
$return = “”;

Thank you for your help

Qaysar
June 5th, 2011 on 11:31

Hello Ivan,

Soory please ignore my question about where to put the following statement

foreach(glob(‘backup/’ . ‘*.*’) as $v){
if (is_file($v)) {
unlink($v);
}
}

I have found it and have tested it out.

I have noticed that it removes all the previuos backup with this statement rather than just exclude the backup folder from being included in the backup.

Can you please advise if their is a fix to just leave the backup folder out and keep backing up the website.

Also would their be a way to once you have like 10-15 backups done for the script to automatically delete like say the first 5 from, so everytime the backup reaches a certain number the script deletes the old backups.

Also would be an interesting feature if the zip files can be emailed to an email address each time the the cron job or manual backup is run??

Thank you for your kind help

Much apprecaited.

Qaysar

qaysar
June 5th, 2011 on 18:11

Hello Ivan,

Sorry my question 3 was the following characters are left at the end of the table

also have managed to add the code you provided in the correct place, again a BIG thank you for your help and script.

I wanted to ask if the following can be done.

Leave a certain sub directory out like the “backup”, I know the code you provided removes all the old backups from server but before it odes this a backup of the subfolder “backup is done” so this increases the size.

Have like 15 backups before the old ones are deleted like the first five

Email the zip files to an email address?

The email feature would be fantastic

Once again thank you very much.

Your help is much appreciated

Qaysar

Ivan Stambolic
June 6th, 2011 on 01:59

Hi,

if you are referring to the chars I added as a separator in the sql dump file, there is no problem with them. As I have written in the tutorial, they are there so the restore script would know where the sql statement ends.
I am using this script (on this very website) and there is not a single problem with sql restore. So, there is nothing to worry about.

If the website has no MySQL database(s) you can just comment-out the call for the backup_tables() function and the mysql backup code won’t be executed at all.

Cron Job php file isn’t archiving the old backups and then deleting them. It does the opposite: first deletes the old backups and archives everything else afterwards. Not really sure where you have placed the code to delete old backup files, but I’m not having any problems with archiving old backups.

About the dir exclusion: Since this script uses PclZip for the creation of the zip archives, you can find this in their User Guide, Class and Methods Part. From the top of my head, I would use a combination of PclZip::listContent() and PclZip::delete(), but not really sure. I would have to think about it, since I have never needed this.

Emailing function isn’t that much of a problem. You can use PHP Mail() Function. Just look around the web for sending emails with attachments.
Also, depending on the size of the website, you might run into issues while getting (or sending for that matter) large emails (emails with attachments).

I hope I’ve helped.
Cheers,
Ivan

Priya
August 19th, 2011 on 07:19

Hi,
I am getting this warnings and file is also not generated for backup.Please help to rectify this .
reply soon.

Warning: mysql_num_fields(): supplied argument is not a valid MySQL result resource in C:\alpstanda\xampp\htdocs\training\Eswari\backup.php on line 33

Warning: mysql_fetch_row(): supplied argument is not a valid MySQL result resource in C:\alpstanda\xampp\htdocs\training\Eswari\backup.php on line 37

Warning: fopen(jegga/dbbackup_19.08.2011_06:03:22_.sql) [function.fopen]: failed to open stream: Invalid argument in C:\alpstanda\xampp\htdocs\training\Eswari\backup.php on line 63

Warning: fwrite(): supplied argument is not a valid stream resource in C:\alpstanda\xampp\htdocs\training\Eswari\backup.php on line 65

Warning: fclose(): supplied argument is not a valid stream resource in C:\alpstanda\xampp\htdocs\training\Eswari\backup.php on line 65

    Reyz WHA
    November 18th, 2011 on 19:32

    Yeah, same thing. First, it’s deprecated. Second, I’ve get same error. How to solve that?

Mike
October 16th, 2011 on 22:42

Hi. I was looking for a MySQL backup for a site on a server than has disabled ‘system’ (so mysqldump is out) and won’t allow ‘select * into outfile ….” so used yours as a basis. Thanx … bit I think there are some problems:

Inside the main tables loop, there are three nested loops, over the number of fields, over the rows of data, and over the number of fields. This means the inserts will be repeated as many times as there are columns (fields) .. so if you have 5 columns and 100 rows, then 500 inserts will be output!

Second, it would be much safer to use mysql_real_escape_string to escape the text, rather than addslashes and ereg_replace; it will do the right thing for any binary date.

Third, you use “…” to quote values in the insert. This works OK with MySQL, but I think that its not standard SQL, which uses ‘…’ for text strings.

Fourth, it will convert nulls to the empty strings in the insert statement, which may or may not cause problems later!

Last (this is really only stylistic), I would accumulate values to insert into a list then then implode it with implode(‘,’, $values), then you don’t need the ” if ($j<($num_fields-1)) {$return.= ',';} test.

Regards
Mike

Daler
November 10th, 2011 on 10:03

Hi,

I was getting this error

“Deprecated: Function ereg_replace() is deprecated in C:\wamp\www\bckprstrcmplt\backup.php on line 97″

then is used preg_replace() instead of ereg_replace() but it shows error like below one

“Warning: preg_replace(): Empty regular expression in C:\wamp\www\bckprstrcmplt\backup.php on line 97″

Please Help!

Ivan Stambolic
December 3rd, 2011 on 16:43

@Mike
Inside the main tables loop, there are three nested loops, over the number of fields, over the rows of data, and over the number of fields. This means the inserts will be repeated as many times as there are columns (fields) .. so if you have 5 columns and 100 rows, then 500 inserts will be output!
We need to do this to save everything from the database, and nothing will be duplicated. I’m not sure if I understood you correctly.

Second, it would be much safer to use mysql_real_escape_string to escape the text, rather than addslashes and ereg_replace; it will do the right thing for any binary date.
So true. But then again, there is no real need for any security at all, since we don’t want to change any table values, just take them as they are and then restore them as they were originally. Those two functions are there only to bridge the differences between MySQL chars and chars saved to plain text file.
It wasn’t my intention here to add any security level of any kind.

Third, you use “…” to quote values in the insert. This works OK with MySQL, but I think that its not standard SQL, which uses ‘…’ for text strings.
I’m not really sure about this. You might be right, but even MySQL website has examples using double quotations. I never had the problem when using double quotations, instead of single ones, and as you have said it works. Will look it up more.

Fourth, it will convert nulls to the empty strings in the insert statement, which may or may not cause problems later!
Interesting… Will have to look a bit deeper in this one. Haven’t thought about it, to be honest.

@Daler
ereg_replace function has been deprecated as of PHP 5.3.0, so try using preg_replace function.

Booyah
January 18th, 2012 on 02:30

Thank you for the work put into this and for making it freely available. It looks to be exactly what I need. A word of warning…I have no particular training in writing my own scripts so I will apologize in advance if any answers are over my head.

I am making some test runs of this script and I am getting this error:

Error : PCLZIP_ERR_MISSING_FILE (-4) : File ‘/path/to/my/home/dir/stats/logs’ does not exist

First, I cannot figure out why I am getting this error. The stats dir indeed has no file called ‘logs’ nor should it…so what gives? Could it be a permissions problem with that folder?

Second, I am not really concerned with backing up the stats dir at all. Can a single dir be easily excluded from the backup process?

Richard
February 4th, 2012 on 22:26

Hello Ivan,

First, i would like to coment you on your scripting, it looks good!
I want to use this script for a very BIG website (7GB) with a DB of a few hundred MB…

I get this error when i run the script:

Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 33292274 bytes) in /home/backuptestdomain/public_html/test/backup.php on line 99

What can i do to solve this?

Thank you in advance!

Ivan Stambolic
February 9th, 2012 on 17:13

@Booyah I really have no clue about this one. The only thing that comes to mind is that it may be a hidden or a system’s file, to which the script has no access. I really have no idea. About excluding an individual dir, you’ll have to check on PclZip web page (http://www.phpconcept.net/pclzip). I haven’t really looked into it. It might be easier to add individual directories, depending on what exactly you need to backup.

@Richard It looks to me like you don’t have enough free space to create a backup. You should, in theory, have enough free space to place a copy of you website, which means at least 50% of space should be free.

    Richard
    February 18th, 2012 on 01:08

    I am trying to backup a DB of +/- 400 MB and the files are approx 7000MB, i have got plenty of room (2TB) left… the problem is the memory limit… in all my tests i could not backup more than approx 200 MB (tested on diferent servers). is there a way to split the files? for the DB it is even worse… it takes you memory limit (set on php/apache) and if that is reached the rest will not be backed up.

      Ivan Stambolic
      February 20th, 2012 on 14:07

      Memory limit can be a pain. ;)
      You can separate MySQL tables by defining table names in $table variable of backup_tables() function.
      Separate files can be backed up by defining the path in $v_dir var in backup.php. Just call the functions with diff paths/table names.
      Please, let me know if it works.

glueck
February 17th, 2012 on 16:26

please check your downloadfile -> http://campstamba.com/download/?id=bckprstrcmplt
i missed many of the phpfiles from the tutorial…
no backup_*.php .. i think the zipfile is not complete..
and: it doesn’t work… please check and upload it again! thx

    Ivan Stambolic
    February 20th, 2012 on 13:55

    I have just rechecked the download file and it seems that all files are in there. Try to redownload the file. I’m not sure which files you are missing, but there is backup.php file in the download archive.

Richard
February 20th, 2012 on 20:12

Is you can make is so that he doesn’t stop the backup but rather splits the files to combine them if he’s ready so that you don’t have problems with memory limits and that he does the table backup like you said automaticly i am willing to pay for the solution…

Richard
February 20th, 2012 on 20:28

without knowing the table structure so i can use it for multiple domains…

kiran singh
February 23rd, 2012 on 09:24

Warning: fopen(backup/dbbackup_23.02.2012_08:21:35_all.sql) [function.fopen]: failed to open stream: Invalid argument in D:\Program Files\VertrigoServ\www\bckprstrcmplt\backup.php on line 115
I received this message. Plz help me

Warning: fwrite(): supplied argument is not a valid stream resource in D:\Program Files\VertrigoServ\www\bckprstrcmplt\backup.php on line 116

Warning: fclose(): supplied argument is not a valid stream resource in D:\Program Files\VertrigoServ\www\bckprstrcmplt\backup.php on line 117
Error : PCLZIP_ERR_READ_OPEN_FAIL (-2) : Unable to open archive ‘backup/dbbackup_23.02.2012_08:21:35_all.zip’ in wb mode

kiran singh
February 23rd, 2012 on 09:51

I receive this message. I need help.

Warning: fopen(backup/dbbackup_23.02.2012_08:34:00_all.sql) [function.fopen]: failed to open stream: Invalid argument in D:\Program Files\VertrigoServ\www\bckprstrcmplt\backup.php on line 115

Warning: fwrite(): supplied argument is not a valid stream resource in D:\Program Files\VertrigoServ\www\bckprstrcmplt\backup.php on line 116

Warning: fclose(): supplied argument is not a valid stream resource in D:\Program Files\VertrigoServ\www\bckprstrcmplt\backup.php on line 117
Error : PCLZIP_ERR_READ_OPEN_FAIL (-2) : Unable to open archive ‘backup/dbbackup_23.02.2012_08:34:00_all.zip’ in wb mode

kiran singh
February 24th, 2012 on 06:28

I think you can’t solve this. so you are ignoring this error which i’ve received.

Warning: fopen(backup/dbbackup_24.02.2012_05:26:26_all.sql) [function.fopen]: failed to open stream: Invalid argument in D:\Program Files\VertrigoServ\www\bckprstrcmplt\backup.php on line 110

Warning: fwrite(): supplied argument is not a valid stream resource in D:\Program Files\VertrigoServ\www\bckprstrcmplt\backup.php on line 111

Warning: fclose(): supplied argument is not a valid stream resource in D:\Program Files\VertrigoServ\www\bckprstrcmplt\backup.php on line 112
Error : PCLZIP_ERR_READ_OPEN_FAIL (-2) : Unable to open archive ‘backup/dbbackup_24.02.2012_05:26:26_all.zip’ in wb mode

Dave
March 7th, 2012 on 02:31

Nice Script but how can a DIR be excluded from the backup process? Like my /images folder which is over 4GB.

Dave
March 7th, 2012 on 02:33

As I see, index.php in ZIP file has 0 bytes.

Catherine from Santa Barbara
March 13th, 2012 on 17:24

Wow! I am soooo grateful for this script! You have saved me hours of work. With just a few tweaks I had this working exactly the way I wanted. You are the best!

Enric
March 14th, 2012 on 05:51

Hi Ivan,

Firstly thanks for providing this script for free.

I’m in a bit of a situation here because I want to secure the contents of my hosting account and I have no clue on how to proceed with your script.

I have several WordPress sites and I would like to set up a procedure by which a backup of the files and the mySQL ddbbs is performed on a regular basis -without me having to initiate it every time!. To top it up, I am not familiar neither with PHP nor with Linux albeit I can follow basic instructions (I used to be a Classic ASP programmer myself!).

Could you possibly spare a few minutes to post a step-by-step check list, so to speak, on what is needed to do to properly set up your script?

If this script does what it says and it’s easy to implement in most situations then you’ve got here a gem!… and the answer to my problem too! :)

Thanks

Enric
March 14th, 2012 on 05:57

Also… is your index.php at bckprstrcmplt.zip meant to be blank?

nayan
March 28th, 2012 on 05:44

Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 33292273 bytes)

Gabriel Crowe
March 29th, 2012 on 11:47

To exclude paths from creation use this:

PCLZIP_OPT_REMOVE_PATH

like this:

require_once(‘pclzip.lib.php’);
$archive = new PclZip(‘archive.zip’);
$v_list = $archive->add(‘dev/file.txt’,
PCLZIP_OPT_REMOVE_PATH, ‘dev’);

Gabriel Crowe
March 29th, 2012 on 11:49

ah, ignore me, thats absolute nonesense. all this does is jigger up the folder tree.

RaiN Ingusan
March 29th, 2012 on 20:42

When I executed this scripts, all of my htdocs file are gone.
Please tell me what to do. I really need those files.

I’m begging u please TELL ME!

RaiN Ingusan
March 29th, 2012 on 20:46

When I executed this scripts, all of my files in htdocs are gone. please tell me what to do to get it back.
I badly need those files.
Please help me asap sir.
I’n begging you!

Ivan Stambolic
March 29th, 2012 on 21:11

@kiran As you can see I’m not ignoring anyone here (nor is any reply censored on this website and as you can see I approve everything that’s not spam), but I have been extremely busy and I barely have the time anymore.
On the other hand, I provide these scripts for free and in no way I’m obligated to help anyone out, as I’m not charging anyone for this, nor do i get any revenue from all of this. I do provide some support when I have the time, but that doesn’t mean that I have to! On the other hand I DO want to help people out when I can, and if I can’t I will say it, as I have several times so far (just read through my comments).

@Crowe Has pclZIP been changed recently? I don’t remember seeing PCLZIP_OPT_REMOVE_PATH before. I haven’t used the library in a while, though. I believe there is a FAQ question about removing the directory from archive, but I haven’t been on the pclZIP website in ages. If i remember correctly archive should already exist, so maybe you can create an archive with the directory in the backup archive and delete it afterwards – after the backing up has completed? Just tossing an idea in the air… No clue if it can work nor if I remember it correctly.

@kiran Try replacing “:” (colon) with underscore “_” in the backup file name, because I had several problems while transferring Linux files to Windows with colons in filename.

@RaiN Uhm… That’s a first! Did u modified the script in any way? Anything? If so can I see the modified script?

nj
April 14th, 2012 on 06:47

Hi Ivan,

Firstly thanks for providing this script for free.
I have run the script ,all the files are run properly but whenever restore.php file run ,it gives the message:
Warning: unlink(C:\wamp\www/bckprstrcmplt-updated/backup/dbbackup.sql) [function.unlink]: Permission denied in C:\wamp\www\bckprstrcmplt-updated\restore.php on line 111
Error : PCLZIP_ERR_MISSING_FILE (-4) : Missing archive file ‘backup/dbbackup.zip’

and all the files except restore.php gets deleted from the main folder and where the backup is restored I can’t find out.Is scheduling is possible in this so that after every 10 or more days backup will be created.
Plz reply soon .I need ur help….