PHP – WordPress Backup (MySQL & Files)

| | | | |

After writing WordPress – Backup & Restore I came across a site that I do not have SSH/CLI access to so I needed to have another option. If you have SSH/CLI access it’s best to use the process on WordPress – Backup & Restore.

Sometimes you just need a quick and dirty way to backup the WordPress site that you’ve taken over, this will help with that.

It will read the database settings from the wp-config.php file and then do a MySQL backup and a full site file backup.

Full disclosure I merged the two references together into one file to make this work. I did make minor changes but most of the credit goes to the two links below.

Make sure the OUTPUT_DIR variable is not within your web folder as someone could download your backups.

Note: At this time there isn’t any security around this file so it’s best to create the file, get the backups done and then remove the file.

<?php
// Report all errors
error_reporting(E_ALL);

$start_time = array_sum(explode(chr(32), microtime()));
 
$path = getenv('PWD');
if ($path == "/")
{
    $path = $_SERVER['DOCUMENT_ROOT'];
}
 
require($path.'/wp-config.php');
 
//echo $path . "<br/>";
//echo "DB_HOST :".DB_HOST. "<br/>";
//echo "DB_USER :".DB_USER. "<br/>";
 
$dbc = mysql_connect(DB_HOST, DB_USER, DB_PASSWORD);
if ( !$dbc ) {
    die( 'Not Connected: ' . mysql_error());
}
else
{
    echo "MySQL DB Connected<br/>";
}
 
define("OUTPUT_DIR", '/var/www/backup'); // Make sure you define a valid directory
define("TABLES", '*'); // By default we want all tables
 
// MySQL Backup - Start
$backupDatabase = new Backup_Database(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
$status = $backupDatabase->backupTables(TABLES, OUTPUT_DIR) ? 'OK' : 'KO';
echo "MySQL Backup result: ".$status . "<br/>";
// MySQL Backup - Stop
 
// File Backup - Start
function filesize_recursive($path)
    {
        $ret = 0;
 
        try
        {
            if(!file_exists($path)) return 0;
            if(is_file($path)) return filesize($path);
             
            foreach(glob($path."/*") as $fn)
            $ret += filesize_recursive($fn);
        }
        catch(Exception $e)
        {
            echo 'Message: ' .$e->getMessage();
        }
        return $ret;
    }

if (filesize_recursive('/var/www/html') < 4080218931) // If directory size is less than 3.8 GB (4080218931 Bytes) Zip will be used, otherwise use Tar
	{
		echo "Zip Compression Selected";
	
		$zip = new zipConverter();
		$zip->setRecursiveness(true); //default is false
		$zip->addFolder(
			array(
				'/var/www/html'  // WordPress Site Directory Path, make sure you define the proper path
			)
		);

		$zip->setZipPath(OUTPUT_DIR . '/site-backup-'.date('m-d-Y_hisa').'.zip'); //Set your Zip Path with your Zip File's Name
		$result = $zip->createArchive();
 
		if ($result["success"]==true)
			{
				$backupresult= "OK";
			}
		else
			{
				$backupresult= "Failed";    
			}

		echo "<br/>File Backup result: " . $backupresult . "<br/>Files In Backup: " . $result["statistics"]["Num Files"];
	}
else
	{
		echo "Tar Compression Selected<pre>";
		if(system("tar -czvf /var/www/backup/site-backup-".date('m-d-Y_hisa').".tar.gz /var/www/html/"))
			{
				echo '</pre><b>Directory compressed successfully!</b>';
			}
		else
			{
				echo '</pre><b>Unable To Tar</b>';
			}
	}

// File Backup - Stop

$finish_time = array_sum(explode(chr(32), microtime()));
printf("<p>The time taken: %f seconds</p>", ($finish_time - $start_time));
 
class Backup_Database {
    /**
     * Host where database is located
     */
    var $host = '';
 
    /**
     * Username used to connect to database
     */
    var $username = '';
 
    /**
     * Password used to connect to database
     */
    var $passwd = '';
 
    /**
     * Database to backup
     */
    var $dbName = '';
 
    /**
     * Database charset
     */
    var $charset = '';
 
    /**
     * Constructor initializes database
     */
    function Backup_Database($host, $username, $passwd, $dbName, $charset = 'utf8')
    {
        $this->host     = $host;
        $this->username = $username;
        $this->passwd   = $passwd;
        $this->dbName   = $dbName;
        $this->charset  = $charset;
 
        $this->initializeDatabase();
    }
 
    protected function initializeDatabase()
    {
        $conn = mysql_connect($this->host, $this->username, $this->passwd);
        mysql_select_db($this->dbName, $conn);
        if (! mysql_set_charset ($this->charset, $conn))
        {
            mysql_query('SET NAMES '.$this->charset);
        }
    }
 
    /**
     * Backup the whole database or just some tables
     * Use '*' for whole database or 'table1 table2 table3...'
     * @param string $tables
     */
    public function backupTables($tables = '*', $outputDir = '.')
    {
        try
        {
            /**
            * Tables to export
            */
            if($tables == '*')
            {
                $tables = array();
                $result = mysql_query('SHOW TABLES');
                while($row = mysql_fetch_row($result))
                {
                    $tables[] = $row[0];
                }
            }
            else
            {
                $tables = is_array($tables) ? $tables : explode(',',$tables);
            }
 
            $sql = 'CREATE DATABASE IF NOT EXISTS '.$this->dbName.";\n\n";
            $sql .= 'USE '.$this->dbName.";\n\n";
 
            /**
            * Iterate tables
            */
            foreach($tables as $table)
            {
                echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Backing up ".$table." table...";
 
                $result = mysql_query('SELECT * FROM '.$table);
                $numFields = mysql_num_fields($result);
 
                $sql .= 'DROP TABLE IF EXISTS '.$table.';';
                $row2 = mysql_fetch_row(mysql_query('SHOW CREATE TABLE '.$table));
                $sql.= "\n\n".$row2[1].";\n\n";
 
                for ($i = 0; $i < $numFields; $i++) 
                {
                    while($row = mysql_fetch_row($result))
                    {
                        $sql .= 'INSERT INTO '.$table.' VALUES(';
                        for($j=0; $j<$numFields; $j++) 
                        {
                            $row[$j] = addslashes($row[$j]);
                            $row[$j] = ereg_replace("\n","\\n",$row[$j]);
                            if (isset($row[$j]))
                            {
                                $sql .= '"'.$row[$j].'"' ;
                            }
                            else
                            {
                                $sql.= '""';
                            }
 
                            if ($j < ($numFields-1))
                            {
                                $sql .= ',';
                            }
                        }
 
                        $sql.= ");\n";
                    }
                }
 
                $sql.="\n\n\n";
 
                echo " OK" . "<br/>";
            }
        }
        catch (Exception $e)
        {
            var_dump($e->getMessage());
            return false;
        }
 
        return $this->saveFile($sql, $outputDir);
    }
 
    /**
     * Save SQL to file
     * @param string $sql
     */
    protected function saveFile(&$sql, $outputDir = '.')
    {
        if (!$sql) return false;
 
        try
        {
        date_default_timezone_set("EST");
        //echo "<br/>".date("m-d-Y_hisa", time())."<br/>";
            $handle = fopen($outputDir.'/db-backup-'.$this->dbName.'-'.date("m-d-Y_hisa", time()).'.sql','w+');
            fwrite($handle, $sql);
            fclose($handle);
        }
        catch (Exception $e)
        {
            var_dump($e->getMessage());
            return false;
        }
 
        return true;
    }
}

class zipConverter
{
    private $_isRecursive = false;
    private $_folderList = array();
    private $_zipPath = '';
    private $_zip;
 
    /**
     * Class Constructor
     */
    function __construct() {
        $this->_zip = new ZipArchive();
    }
 
    /**
     * Sets Recursive Behaviour in a folder
     * 
     * @param boolean $isRecursive Indicates if folders will be scaned recursively or not
     * 
     * @return void
     */
    function setRecursiveness($isRecursive)
    {
        $this->_isRecursive = $isRecursive;
    }
 
    /**
     * Adds folder to be included into the Zip Archive
     * 
     * @param array $folderList Array of Folder Paths
     * 
     * @return void
     */
    function addFolder(array $folderList)
    {
        $this->_folderList = array_merge($this->_folderList, $folderList);
    }
 
    /**
     * Sets Zip file Name with its Path
     * 
     * @param string $path Path of the zip with its name
     * 
     * @return array
     */
    function setZipPath($path)
    {
        if ($this->_zip->open($path, ZipArchive::CREATE)!==TRUE) {
            return array("error"=>true, "msg"=>"Can not open or create <$path>");
        } else {
            $this->_zipPath = $path;
            return array("success"=>true);
        }
    }
 
    /**
     * Creates Zip Archive from your Provided Folders
     * 
     * @return array
     */
    function createArchive()
    {
        if (count($this->_folderList) == 0) {
            return array("error"=>true, "msg"=>"You did not set Folder(s) which needs to be added into Zip Archive");
        }
 
        if ($this->_zipPath == '') {
            return array("error"=>true, "msg"=>"Please set Zip Path first before Conversion process.");
        }
 
        ini_set('memory_limit', '-1');
        foreach ($this->_folderList as $folder) {
            $parent = substr($folder, (strlen(dirname($folder))+1)-strlen($folder));
            if ($parent==$folder) {
                $parent = '';
            } else {
                $this->_zip->addEmptyDir($parent);
            }
            $result = $this->_scanDir($folder, $parent);
            if (is_array($result) && isset($result['error'])) {
                return $result;
            }
        }
 
      $msg["Num Files"] = $this->_zip->numFiles;
 
        $this->_zip->close();
        return array("success"=>true, "statistics"=>$msg);
    }
 
    /**
     * Scans Folder Recursively
     * 
     * @param string $dir       Directory to be Scaned
     * @param string $parentDir Parent Directory
     * 
     * @return array | boolean
     */
    private function _scanDir($dir, $parentDir='')
    {
        $cdir = array_diff(scandir($dir), array('..', '.'));
        foreach ($cdir as $key => $value) {
            if (is_dir($dir . DIRECTORY_SEPARATOR . $value)) {
                if ($this->_isRecursive) {
                    if ($parentDir=='') {
                        $newDir = $value;
                    } else {
                        $newDir = $parentDir. DIRECTORY_SEPARATOR . $value;
                    }
                    if($this->_zip->addEmptyDir($newDir)) {
                        $this->_scanDir($dir . DIRECTORY_SEPARATOR . $value, $newDir);
                    } else {
                        $this->_zip->close();
                        return array(
                            "error"=>true, 
                            "msg"=>"Could not create <$parentDir". DIRECTORY_SEPARATOR . "$value> folder in Zip Archive."
                        );
                    }
                }
            } elseif (is_file($dir . DIRECTORY_SEPARATOR . $value)) {
                if ($parentDir=='') {
                    $newDir = $value;
                    $newName = '';
                } else {
                    $newDir = $dir. DIRECTORY_SEPARATOR . $value;
                    $newName = $parentDir. DIRECTORY_SEPARATOR . $value;
                }
                $result = $this->_zip->addFile($newDir, $newName);
            }
        }
 
        return true;
    }
}
?>

References:
http://www.azanweb.com/en/using-php-to-backup-mysql-databases/
http://www.phpclasses.org/package/8998-PHP-Create-ZIP-archive-with-the-files-of-a-directory.html

Originally Posted on February 22, 2016
Last Updated on February 23, 2016
All information on this site is shared with the intention to help. Before any source code or program is ran on a production (non-development) system it is suggested you test it and fully understand what it is doing not just what it appears it is doing. I accept no responsibility for any damage you may do with this code.