PHP Classes

Updated Code

Recommend this page to a friend!

      Zip Stream  >  All threads  >  Updated Code  >  (Un) Subscribe thread alerts  
Subject:Updated Code
Summary:I had a few problems and fixed them
Messages:3
Author:James Sefton
Date:2011-10-07 00:58:18
Update:2011-10-07 06:30:34
 

  1. Updated Code   Reply   Report abuse  
Picture of James Sefton James Sefton - 2011-10-07 00:58:20
Hi,

Thank you so much for publishing this class. I have a little personal project that needed something just like this.

I came across a couple of issues when using the addDirectoryContent() function.

The first was that when the directory contained empty subdirectories, they were not reproduced in the resulting zip file.

The second was that if there happened to be a symbolic link within the directory that pointed to a file to which the web user does not have access - the code seemed to go into an infinite loop of some sort and take down the server. (just ate all the memory and cpu)


My work around for the first was to add a call to addDirectory() at the start of addDirectoryContent. It would probably be better to only do this if the directory was empty but this is good enough for me.

My work around for the second was to use the file_exists() PHP statement to check if the file is accessible before trying to open it. This change is also in the addDirectoryContent function. The file_exists() statement returns false if the script is not going to be able to open the file for any reason.


Hope this is useful to someone. Here is the function with the two extra lines I added.


/**
* Add the content to a directory.
*
* @author Adam Schmalhofer <hide@address.com>
* @author A. Grandt
*
* @param String $realPath Path on the file system.
* @param String $zipPath Filepath and name to be used in the archive.
* @param bool $zipPath Add content recursively, default is TRUE.
*/
public function addDirectoryContent($realPath, $zipPath, $recursive = TRUE) {
$this->addDirectory($zipPath);
$iter = new DirectoryIterator($realPath);
foreach ($iter as $file) {
if ($file->isDot()) {
continue;
}
$newRealPath = $file->getPathname();
$newZipPath = self::pathJoin($zipPath, $file->getFilename());
if ($file->isFile()) {
if(file_exists($newRealPath))
$this->addLargeFile($newRealPath, $newZipPath);
} else if ($recursive === TRUE) {
$this->addDirectoryContent($newRealPath, $newZipPath, $recursive);
}
}
}

  2. Re: Updated Code   Reply   Report abuse  
Picture of Asbjorn Grandt Asbjorn Grandt - 2011-10-07 06:18:58 - In reply to message 1 from James Sefton
Thanks :)

You are correct, addDirectory is a good solution, and for some reason it never occurred to me to test the symlinks.

The symlinks are quite dangerous though. A test is needed for the scenario where a symlinks points to a parent directory. Else we'll get an infinite loop again.

I took your suggestions and added the $followSymlinks option. The $addedFiles array is used to keep track of which files have been added so far. Both of these are optional.
It still needs testing, but I think this will solve the problems you mentioned quite nicely :)

/**
* Add the content to a directory.
*
* @author Adam Schmalhofer <Adam.Schmalhofer@gmx.de>
* @author A. Grandt
*
* @param String $realPath Path on the file system.
* @param String $zipPath Filepath and name to be used in the archive.
* @param bool $recursive Add content recursively, default is TRUE.
* @param bool $followSymlinks Follow and add symbolic links, if they are accessible, default is TRUE.
* @param array $addedFiles Reference to the added files, this is used to prevent duplicates, efault is an empty array.
* If you start the function by parsing an array, the array will be populated with the realPath
* and zipPath kay/value pairs added to the archive by the function.
*/
public function addDirectoryContent($realPath, $zipPath, $recursive = TRUE, $followSymlinks = TRUE, &$addedFiles = array()) {
if (file_exists($realPath)) {
$this->addDirectory($zipPath);

if (isset($addedFiles[realpath($realPath)])) {
return;
}
$addedFiles[realpath($realPath)] = $zipPath;

$iter = new DirectoryIterator($realPath);
foreach ($iter as $file) {
if ($file->isDot()) {
continue;
}
$newRealPath = $file->getPathname();
$newZipPath = self::pathJoin($zipPath, $file->getFilename());

if(file_exists($newRealPath) && ($folowSymliks === TRUE || !is_link($newRealPath))) {
if ($file->isFile()) {
$addedFiles[realpath($newRealPath)] = $newZipPath;
$this->addLargeFile($newRealPath, $newZipPath);
} else if ($recursive === TRUE) {
$this->addDirectoryContent($newRealPath, $newZipPath, $recursive);
} else {
$this->addDirectory($zipPath);
}
}
}
}
}

  3. Re: Updated Code   Reply   Report abuse  
Picture of Asbjorn Grandt Asbjorn Grandt - 2011-10-07 06:30:34 - In reply to message 2 from Asbjorn Grandt
One little update.

The first few lines in the function in my previous post should be changed from
...
if (file_exists($realPath)) {
$this->addDirectory($zipPath);

if (isset($addedFiles[realpath($realPath)])) {
return;
}
$addedFiles[realpath($realPath)] = $zipPath;
...

to

...
if (file_exists($realPath) && !isset($addedFiles[realpath($realPath)])) {
if (is_dir($realPath)) {
$this->addDirectory($zipPath);
}

$addedFiles[realpath($realPath)] = $zipPath;
...

I'll update my GitHub versions to these new versions of Zip and ZipStream, and once I'm happy with them, I'll update PHPClasses.
github.com/Grandt/PHPZip