简体   繁体   中英

creating a backup using php on localhost and server

I created my own backup system for backing up my database and various folders that are subject to change on my site (for simplicity assume one folder -> folder_to_backup and the files and folders therein that I want to preserve ). The only issue is, it doesn't work on local environments (localhost) and this is something I would like to address. Heres the class that does the actual zipping (forgot where I got it from):

    function __construct($file, $folders = array(), $ignored = NULL)
    {
        $this->zip = new ZipArchive();
        $this->ignored_names = is_array($ignored) ? $ignored : $ignored ? array(
            $ignored
        ) : array();
        if ($this->zip->open($file, ZIPARCHIVE::CREATE) !== TRUE) {
            return FALSE;
        }
        $folder = substr($folder, -1) == '/' ? substr($folder, 0, strlen($folder) - 1) : $folder;
        if (strstr($folder, '/')) {
            $this->root = substr($folder, 0, strrpos($folder, '/') + 1);
            $folder = substr($folder, strrpos($folder, '/') + 1);
        }
        foreach ($folders as $folder) {
            $this->zip($folder);
        }
        $this->zip->close();
    }
    function zip($folder, $parent = NULL)
    {
        $full_path = $this->root . $parent . $folder;
        $zip_path = $parent . $folder;
        $this->zip->addEmptyDir($zip_path);
        $dir = new DirectoryIterator($full_path);
        foreach ($dir as $file) {
            if (!$file->isDot()) {
                $filename = $file->getFilename();
                if (!in_array($filename, $this->ignored_names)) {
                    if ($file->isDir()) {
                        $this->zip($filename, $zip_path . '/');
                    } else {
                        $this->zip->addFile($full_path . '/' . $filename, $zip_path . '/' . $filename);
                    }
                }
            }
        }
    }

So using the above code I create my zip files, however I have to send the $folder with a realpath() for the iterator to work, and thus on a local environment I get something like this:

C:\\xampp\\htdocs\\sitename\\cms\\files\\folder_to_backup\\

and on a http environment:

/opt/www/prezent/sitename/HTML/cms/files/folder_to_backup/

So say I wanted to update the localhost copy of my site I download the file and for obvious reasons I cannot unzip it as the directory structure isn't compatible, nor are the directory separators compatible.

So I figured I could isolate the commonality being 'cms' . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR . 'folder_to_backup 'cms' . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR . 'folder_to_backup 'cms' . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR . 'folder_to_backup . So essentially instead of having all the folders leading up to the folder cms I would just have the structure

DIR
   files /
      folder_to_backup /
         some files and folders ... etc

and then on restore instead of extracting it to the /opt/ or C:\\ via realpath(DIRECTORY_SEPARATOR) . I would use something similar to realpath(dirname(__FILE__)) or what have you.

So to be succinct my question is how can I obtain the above file structure (excluding the realpath up to /files/ , knowing that I have to feed the zip creator a realpath so that it can find the files?

Use This script:

1) Set execution time to 0 if in case your site is huge and will take much time for back-up as default execution time for PHP is 30 seconds.

ini_set("max_execution_time", 0);

2) By using below code we are creating a directory in which you're going to stored your zip. In my case my directory name is 'site-backup-stark'.

$dir = "site-backup-stark";
if(!(file_exists($dir))) {
mkdir($dir, 0777);
}

3) Set your site credentials.

$host = "localhost"; //host name
$username = "root"; //username
$password = ""; // your password
$dbname = "wp_test"; // database name

4) We are going to save our backup in zip format so create an object of zip.

$zip = new ZipArchive();

5) Call the function backup_tables.

backup_tables($host, $username, $password, $dbname);

6) Define the function backup_tables which will create a database sql file.

/* backup the db OR just a table */
function backup_tables($host,$user,$pass,$name,$tables = '*')
{
$con = mysql_connect($host,$user,$pass);
mysql_select_db($name,$con);

//get all of the tables
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);
}
$return = "";

//cycle through
foreach($tables as $table)
{
$result = mysql_query('SELECT * FROM '.$table);
$num_fields = mysql_num_fields($result);
$return.= 'DROP TABLE '.$table.';';
$row2 = mysql_fetch_row(mysql_query('SHOW CREATE TABLE '.$table));
$return.= "nn".$row2[1].";nn";

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] = preg_replace("#n#","n",$row[$j]);
if (isset($row[$j])) { $return.= '"'.$row[$j].'"' ; } else { $return.= '""'; }
if ($j<($num_fields-1)) { $return.= ','; }
}
$return.= ");n";
}
$return.="nnn";
}

//save file
$handle = fopen('db-backup-'.time().'-'.(md5(implode(',',$tables))).'.sql','w+');
fwrite($handle,$return);
fclose($handle);
}

7) Convert .sql file in .sql.zip file and remove the .sql file.

if (glob("*.sql") != false)
{
$filecount = count(glob("*.sql"));
$arr_file = glob("*.sql");

for($j=0;$j<$filecount;$j++)
{
$res = $zip->open($arr_file[$j].".zip", ZipArchive::CREATE);
if ($res === TRUE)
{
$zip->addFile($arr_file[$j]);
$zip->close();
unlink($arr_file[$j]);
}
}
}

8) Now we have to get current folder name in which our php file is present. This is because when we have to move all folders and files from root directory to our zip.

//get the current folder name-start
$path = dirname($_SERVER['PHP_SELF']);
$position = strrpos($path,'/') + 1;
$folder_name = substr($path,$position);
//get the current folder name-end

9) Create a name for zip file. I have created it based on today's date so that we can easily find date of last backup. Also I have append 'stark-' to a name of zip file which we use in next procedures.

$zipname = date('Y/m/d');
$str = "stark-".$zipname.".zip";
$str = str_replace("/", "-", $str);

10) Add all files from current folder in newly created zip file.

// open archive
if ($zip->open($str, ZIPARCHIVE::CREATE) !== TRUE) {
die ("Could not open archive");
}
// initialize an iterator
// pass it the directory to be processed
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator("../$folder_name    /"));
// iterate over the directory
// add each file found to the archive

foreach ($iterator as $key=>$value) {
if( strstr(realpath($key), "stark") == FALSE) {
$zip->addFile(realpath($key), $key) or die ("ERROR: Could not add file: $key");
}

}
// close and save archive
$zip->close();
echo "Archive created successfully.";

11) Remove all our .sql.zip files from root directory as we moved it in another folder.

if(glob("*.sql.zip") != false) {
$filecount = count(glob("*.sql.zip"));
$arr_file = glob("*.sql.zip");

for($j=0;$j<$filecount;$j++)
{
unlink($arr_file[$j]);
}
}

12) Now move a copy of our zip file to a direcotry which we have created in step 2.

//get the array of zip files
if(glob("*.zip") != false) {
$arr_zip = glob("*.zip");
}

//copy the backup zip file to site-backup-stark folder
foreach ($arr_zip as $key => $value) {
if (strstr($value, "stark")) {
$delete_zip[] = $value;
copy("$value", "$dir/$value");
}
}

13) Delete a zip file from root directory as we moved it in another directory.

for ($i=0; $i < count($delete_zip); $i++) {
unlink($delete_zip[$i]);
}

So because I couldn't find a direct answer, and was getting nowhere, I did alot of research on this site and by looking up other backup methodologies and I've come to create a class that combines many answers on stack related to this into one class. I've even added a way to ignore files and folders, and a sort of automated way to find out what directory the actual zip architecture should start in. I have confirmed that it works on windows (via xampp) and my site server. Hopefully this saves someone some headache!

Usage outlined at the end of the class! If anyone wants to refactor it, feel free, but test it and then update my answer!

<?php
class ZipFolder
{
    /** NON CONFIGURABLE **/
    protected $zip;
    protected $SQLinit = false;
    /** CONFIGURABLE **/
    protected $overwrite = true;                    // overwrite file if exists (just incase unlink doesnt work)
    protected $nback = 1;                           /* if you have a folder such as /HTML/ in your doc root and
                                                       your website index.html begings in the /HTML/ dir this
                                                       number should be one. add +1 for every directory until your
                                                       website frontend homepage */
    protected $sqlDirInZip = 'sql';                 // sql dir inside
    protected $ignored_files = array('.DS_STORE');  // specific files to ignore (Note: also works with extensions)
    protected $ignored_folders = array('_notes');   // folders to ignore

    function __construct($destination, $sources = array(), $SQLfile = NULL)
    {
        if (!extension_loaded('zip')) {
            return false;
        }
        $this->zip = new ZipArchive();
        if (file_exists($destination)) {
            @unlink($destination);
        }
        // ZIPARCHIVE::OVERWRITE ??
        if ($this->zip->open($destination, $this->overwrite == true ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== TRUE) {
            return false;
        }
        if (isset($SQLfile)) {
            // zip sql file in sql directory
            $this->SQLinit = true;
            $SQLfile = str_replace('\\', DIRECTORY_SEPARATOR, realpath($SQLfile));
            $this->zip->addEmptyDir($this->sqlDirInZip);
            $this->zip->addFromString($this->sqlDirInZip . DIRECTORY_SEPARATOR . basename($SQLfile), file_get_contents($SQLfile));
        }
        foreach ($sources as $source) {
            $this->zippy($source, true);
        }
        if ($this->SQLinit) {
            if (!$this->verifySQL($destination)) {
                // couldn't verify that sql made it we close dir and remove it, return a false
                $this->zip->close();
                unlink($destination);
                return false;
            }
        }
        $this->zip->close();
        return true;
    }
    /**
     *
     * Count Slashes
     *
     * Given a $str will return the amount of slashes in the string
     * We can use this to find the diff (new func needed) between 
     *  /HTML/ and where we are now, and then send that number to minus
     *
     * string   $str
     * boolean  $normalizeHttp  will normalize to \ slash
     */
    function count_slashes($str, $normalizeHttp = false)
    {
        $slash = $normalizeHttp ? '\\' : '/';
        $n = 0;
        for ($i = 0; $i < strlen($str); $i++)
        { 
            if ($str[$i] == $slash)
                $n++;
        }
        return $n;
    }
    /**
     * Directory Difference
     *
     * Will find the diffrence between the document root, and the source
     * with is the relative path to this class, and return a number based
     * on the number of slashes in the string that makes up the diffrence
     *
     * string   $source
     * int      $offset     value of 1 offeset directory forward one directory 
     *                      from /HTML/foo/bar/ to /foo/bar/
     *
     */
    function dirDiff($source, $offset = 0) {
        $docRoot = str_replace('/', '\\', $_SERVER['DOCUMENT_ROOT']); // normalize to http
        $diff = str_replace($docRoot, '', $source);
        return $this->count_slashes($diff, true) - $offset; // removed ltrim to trim beginning slash of diff
    }
    /**
     *
     * zippy
     *
     * Creates a zip file from a $source directory
     * $include_dir = true means we get the parent directories up until the root directory
     *  otherwise, we just put in the $source dir and its children.
     *
     * string   $source
     * boolean  $include_dir
     *
     */
    function zippy($source, $include_dir = false)
    {
        $source = str_replace('\\', DIRECTORY_SEPARATOR, realpath($source));
        if (is_dir($source) === true) {
            $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);
            // this is where we figure out where to start and how to format the directories
            if ($include_dir) {
                $goForward = $this->dirDiff($source, $this->nback);
                $arrDir = explode(DIRECTORY_SEPARATOR, $source);
                $maindir = $arrDir[count($arrDir) - $goForward];
                $this->zip->addEmptyDir($maindir);
                $source = "";
                for ($i = 0; $i < count($arrDir) - $goForward; $i++) {
                    $source .= DIRECTORY_SEPARATOR . $arrDir[$i];
                }
                $source = substr($source, 1);

            }
            foreach ($files as $file) {
                $file = str_replace('\\', DIRECTORY_SEPARATOR, $file);
                // ignore "." and ".." folders (Note: isDot() fails to work)
                if (in_array(substr($file, strrpos($file, DIRECTORY_SEPARATOR) + 1), array('.', '..'))) {
                    continue; // exit out of loop
                }
                $file = realpath($file);
                if (is_dir($file) === true) {
                    if (!in_array(basename($file), $this->ignored_folders)) {
                        $this->zip->addEmptyDir(str_replace($source . DIRECTORY_SEPARATOR, '', $file . DIRECTORY_SEPARATOR));
                    } else {
                        continue;
                    }
                } elseif (is_file($file) === true) {
                    // for some reason addFromString iterates through dirs that are exlcuded folders
                    // so we remove ignored folders here to via a strpos hack
                    if (!in_array(basename($file), $this->ignored_files) && $this->strposArray($file, $this->ignored_folders, 1) == false) {
                        $this->zip->addFromString(str_replace($source . DIRECTORY_SEPARATOR, '', $file), file_get_contents($file));
                    } else {
                        continue;
                    }
                }
            }
        } else if (is_file($source) === true) {
            $this->zip->addFromString(basename($source), file_get_contents($source));
        }
    }
    /**
     *
     * strposArray
     *
     * Array usage of strpos
     * Returns false on first true matching result from the given array
     *
     * string           $haystack
     * string || array  $needle
     * int              $offset
     *
     */
    function strposArray($haystack, $needle, $offset= 0 ) {
        if(!is_array($needle)) {
            $needle = array($needle);
        }
        foreach($needle as $query) {
            if(strpos($haystack, $query, $offset) !== false) return true; // stop on first true result
        }
        return false;
    }
    /**
     * verifySQL
     *
     * Called to check that the $sqlDirInZip exists
     * Returns false on failure to affirm the above
     *
     */
    function verifySQL()
    {
        // the '/sql' dir exists
        // we want to use '/' instead of directory sep bc windows looks up in zips / instead of \
        if ($this->zip->statName($this->sqlDirInZip . '/') !== false) {
            return true;
        } else {
            // no sql file
            return false;
        }
    }

}

// all of the following paths in either as a destination or source can be real or absolute

$destination = 'newzippyzip.zip';
$sql = '../backup/test.sql';

if (new ZipFolder($destination, array('../images/', '../../images/uploads/'), $sql)) {
    echo 'Done!';
}

?>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM