3

I'm trying to create a backup class, with the main goal of customizing the class-call to make the final backup be filtered only on specific extensions, specific filesizes, or specific directories within a main directory, based on user choices.

Currently, the class lacks the compression algorithms, because I've been stuck on finding solutions about the .zip compression.

Here's an example of the file and folder structure that the class will use to be able to create a compressed archive ( zip, rar, gz, whatever ):

Array
(
    [0] => pclzip.lib.php
    [1] => index.php
    [style] => Array
        (
            [js] => Array
                (
                    [0] => jNice.js
                    [1] => jquery.js
                )

            [img] => Array
                (
                    [0] => input-shaddow-hover.gif
                    [1] => btn_right.gif
                    [2] => top-menu-bg.gif
                    [3] => top-menu-item-bg.gif
                    [4] => left-menu-bg.gif
                    [5] => button-submit.gif
                    [6] => btn_left.gif
                    [7] => select_right.gif
                    [8] => select_left.gif
                    [9] => transdmin-light.png
                    [10] => content.gif
                    [11] => input-shaddow.gif
                )

            [css] => Array
                (
                    [0] => transdmin.css
                    [1] => layout.css
                    [2] => ie7.css
                    [3] => hack.css
                    [4] => jNice.css
                    [5] => ie6.css
                    [6] => reset.css
                )

        )

    [2] => config.php
    [3] => delete.php
    [4] => restore.php
    [5] => cron.php
    [6] => manage.php
)

As you can see, the file and folder structure needs to be mantained in the backup file, so the list of file and folders to be backupped has been structured so that if an array leaf contains a sub-array, the leaf is a DIRECTORY, if a leaf contains no sub-arrays, the leaf is a FILE.

Here's what I came for in creating a procedure for recursively zip files and folders that ( should ) work for the structured array I presented before:

private function zipFileAndFolderStructureArray ( $backupContentsArray, $zipDestination, $backupRootDirectory ) {
    if ( $this -> zipObject == null ) {
        if ( extension_loaded ( 'zip' ) === true ) {
            $this -> zipObject = new ZipArchive();
            if ( ( $zipErrorCode = $this -> zipObject -> open ( $zipDestination, ZipArchive::CREATE ) ) !== true ) $this -> zipObject = null;
            else $this -> zipFileAndFolderStructureArray ( $backupContentsArray, $zipDestination, $backupRootDirectory );
        }
    }
    else if ( $this -> zipObject != null ) {
        foreach ( $backupContentsArray as $folder => $file_or_folder_list ) {
            $cwd = rtrim ( $backupRootDirectory, '/' ) . '/' . $folder . '/';
            if ( is_array ( $file_or_folder_list ) && is_dir ( $cwd . $folder ) ) {
                echo 'adding folder ' . $folder . ' in cwd ' . $cwd . '<br>';
                $this -> zipObject -> addEmptyDir ( $folder );
                $this -> zipFileAndFolderStructureArray ( $file_or_folder_list, $zipDestination, $cwd );
            }
            else if ( is_file ( $cwd . $file_or_folder_list ) ) {
                echo 'adding file ' . $file_or_folder_list . '<br>';
                $this -> zipObject -> addFromString ( $cwd . $file_or_folder_list );
            }
        }

        $this -> zipObject -> close ();
        return true;
    }
}

The problem is, I'm stuck at this point. This recursive function exists after the first folder leaf has been entered, creating an archive with only 1 empty folder inside of it.

Can you help me in figure out what's wrong?

Hints on possible classes I can use to extend the compression functionality to RAR and GZ, reusing as much as possible the same compression algorithm used for the ZIP one?

Thank you in advance

P.S Added the current function that creates the folder and directory structure

public function directory_list ( $directory_base_path, $filter_dir = false, $filter_files = false, $exclude_empty_dirs = true, $include_extensions = null, $exclude_extensions = null, $exclude_files = null, $recursive = true ) {
    $directory_base_path = rtrim ($directory_base_path, "/" ) . "/";
    if ( ! is_dir ( $directory_base_path ) ) return false;

    $result_list = array();
    if ( ! is_array ( $exclude_files ) ) $exclude_array = Array ( '.', '..' );
    else $exclude_array = array_merge ( Array ( '.', '..' ), $exclude_files );

    if ( ! $folder_handle = opendir ( $directory_base_path ) ) return false;
    else{
        while ( false !== ( $filename = readdir ( $folder_handle ) ) ) {
            if ( ! in_array ( $filename, $exclude_array ) ) {
                if ( is_dir ( $directory_base_path . $filename . "/" ) ) {
                    if ( $recursive && strcmp ( $filename, "." ) != 0 && strcmp ( $filename, ".." ) != 0 ) { // prevent infinite recursion
                        $result = self::directory_list("$directory_base_path$filename/", $filter_dir, $filter_files, $exclude_empty_dirs, $include_extensions, $exclude_extensions, $exclude_files, $recursive);
                        if ( $exclude_empty_dirs ) {
                            if ( count ( array_keys ( $result ) ) > 0 ) $result_list[$filename] = $result;
                        }
                        else $result_list[$filename] = $result;
                    }
                    else if ( ! $filter_dir ) $result_list[] = $filename;
                }
                else if ( ! $filter_files ) {
                    $extension = end ( explode ( '.', $filename ) );
                    if ( ! is_array ( $include_extensions ) && count ( $include_extensions ) == 0 && ! is_array ( $exclude_extensions ) && count ( $exclude_extensions ) == 0 ) if ( $filename != '.' && $filename != '..' ) $result_list[] = $filename;
                    if ( is_array ( $exclude_extensions ) && count ( $exclude_extensions ) > 0 && ! in_array ( $extension, $exclude_extensions ) ) $result_list[] = $filename;
                    else if ( is_array ( $include_extensions ) && count ( $include_extensions ) > 0 && strlen ( $extension ) > 0 && in_array ( $extension, $include_extensions ) ) $result_list[] = $filename;
                }
            }
        }
        closedir($folder_handle);
        return $result_list;
    }
}
Maurizio
  • 469
  • 1
  • 4
  • 11
  • What PHP version are you using here? This sounds like a job for the [RecursiveDirectoryIterator](http://php.net/class.recursivedirectoryiterator). It's a 5.3 thing, though the manual incorrectly says it's a 5.0 thing. – Charles Dec 19 '12 at 21:27
  • Currently running on 5.3.3 I've seen many examples of directory structure creators using RecursiveDirectoryIterator, but wanted to keep things compatible with older versions of PHP because I'd like to share this class to the public, once finished. So I came up with a different directory structure creator using an "older approach" procedure ( that I've added in the question above at the bottom ) – Maurizio Dec 20 '12 at 08:50
  • Given that the PHP maintainers have dropped support for everything prior to 5.3, and that 5.5 is just around the corner, it would probably be a good thing to not consider older versions... even at the cost of some people not being able to run it. – Charles Dec 20 '12 at 09:07
  • Yeah, I've read of that recently, even if I continue to think that I should develop it for the majority of configurations. I'm open to rewrite the procedures and make them based on RecursiveDirectoryIterator. What I have in mind is to keep the "directory structure creator" independent from the compression algorithm, so that if I want to add more compression algorithms in the future, I don't have to copy-and-paste code I've already wrote for the other compression algorithms. Can you guide me in how to accomplish such a thing, focusing on the compression algorithm itself? – Maurizio Dec 20 '12 at 09:50

1 Answers1

1

After having investigated and echoed almost everything in the recursive zip algorithm, it came out that folder structure was hard to handle and required a lot of fixing of slashes into paths.

Here's a fixed version of the same zipFileAndFolderStructureArray function

private function zipFileAndFolderStructureArray ( $backupContentsArray, $zipDestination, $backupRootDirectory, $currentWorkingDirectory = '' ) {
    if ( $this -> zipObject == null ) {
        if ( extension_loaded ( 'zip' ) === true ) {
            $this -> zipObject = new ZipArchive();
            if ( ( $zipErrorCode = $this -> zipObject -> open ( $zipDestination, ZipArchive::CREATE ) ) !== true ) $this -> zipObject = '#ERROR#';
            else $this -> zipFileAndFolderStructureArray ( $backupContentsArray, $zipDestination, $backupRootDirectory );
        }
    }
    else if ( $this -> zipObject == '#ERROR#' ) return false; // return false in case the zipArchive::open returned false on archive opening
    else {
        foreach ( $backupContentsArray as $folder => $file_or_folder_list ) {
            if ( is_array ( $file_or_folder_list ) ) {
                $current_working_folder = rtrim ( $currentWorkingDirectory, '/' ) . '/' . $folder . '/';
                if ( is_dir ( $backupRootDirectory . $current_working_folder ) ) {
                    // not necessary to add an empty folder in this case: the addFile will take care of creating the folder structure in the zip file
                    $this -> zipFileAndFolderStructureArray ( $file_or_folder_list, $zipDestination, $backupRootDirectory, rtrim ( $current_working_folder, '/' ) );
                }
            }
            else if ( is_file ( $backupRootDirectory . trim ( $currentWorkingDirectory, '/' ) . '/' . $file_or_folder_list ) ) {
                $file_insertion = $backupRootDirectory . trim ( $currentWorkingDirectory, '/' ) . '/' . $file_or_folder_list;
                $zip_filename = str_replace ( $backupRootDirectory, '', $file_insertion );
                if ( substr_count ( $zip_filename, '/' ) == 1 ) $zip_filename = trim ( $zip_filename, '/' ); // file in backup root directory fix
                $this -> zipObject -> addFile ( $file_insertion, $zip_filename );
            }
        }
        return true;
    }
}
Maurizio
  • 469
  • 1
  • 4
  • 11