Milos Stankovic
Milos Stankovic

Reputation: 611

PHP ZipArchive file permissions

When I make ZIP archive using PHP ZipArchive object, all files inside the archive have permissions set to 666, although original files have permissions set to 644.

My script makes the zip archives properly, just the permissions are messed up.

////// Make Template archive object
$templateArchive = new ZipArchive();
$templateArchive->open(PATH_TO_TEMPLATES.'_files/'.$templateName.'/_pack/'.$templateName.'.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);

$files = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($templateDir."/templates/".$template_archive_name),
    RecursiveIteratorIterator::LEAVES_ONLY
);

foreach ($files as $name => $file)
{

    // Skip directories (they would be added automatically)
    if (!$file->isDir())
    {
        // Get real and relative path for current file
        $filePath = $file->getRealPath();

        // relative path is full path, reduced with length of templateDir string and 11 more chars for /templates/
        $relativePath = substr($filePath, strlen($templateDir) + 11);

        // Add current file to archive
        $templateArchive->addFile($filePath, $relativePath);
    }
}

// Template Zip archive will be created only after closing object
$templateArchive->close();

P.S. I am working of MAMP on Mac. I just found out that issue is only when PHP 5.6.10 version is selected. When I select 5.5.26, the permissions of files are correct.

Upvotes: 2

Views: 4212

Answers (1)

Deltik
Deltik

Reputation: 1137

You can preserve the Unix permissions in the files you archive by adding the following code after your ZipArchive::addFile() statement:

$templateArchive->setExternalAttributesName($relativePath,
                                            ZipArchive::OPSYS_UNIX,
                                            fileperms($filePath) << 16);

This updates the external attributes of the entry at $filePath with the actual file's permissions.

Unix permissions can be stored in ZIP files in each entry's external attribute, but you have to shift 16 bits to store the permissions in the right place in the external attribute, which is why << 16 is applied to the output of fileperms($filePath).

The documentation of ZipArchive::setExternalAttributesName() also has an example that features Unix permission setting: https://secure.php.net/manual/en/ziparchive.setexternalattributesname.php

As for the permissions of the directories, you'll need to add the directories with ZipArchive::addEmptyDir() (because ZipArchive::addFile() on a directory will result in an error) and then apply the permissions with ZipArchive::setExternalAttributesName() the same way you did for the regular files.

Due to the way that RecursiveDirectoryIterator works, the current directory name will end with /., and due to the way that ZipArchive::addEmptyDir() works, stored directories will end with /. To apply ZipArchive::setExternalAttributesName(), you have to provide the exact entry name, so I suggest lopping off the trailing dot for both methods.

Here is your code edited to support the preservation of permissions of files and directories:

////// Make Template archive object
$templateArchive = new ZipArchive();
$templateArchive->open(PATH_TO_TEMPLATES.'_files/'.$templateName.'/_pack/'.$templateName.'.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);

$files = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($templateDir."/templates/".$template_archive_name),
        RecursiveIteratorIterator::LEAVES_ONLY
);      

foreach ($files as $name => $file)
{               

        // Get real and relative path for current file
        $filePath = $file->getRealPath();

        // relative path is full path, reduced with length of templateDir string and 11 more chars for /templates/
        $relativePath = substr($filePath, strlen($templateDir) + 11);

        // Add regular files
        if (!$file->isDir())
        {
                // Add current file to archive
                $templateArchive->addFile($filePath, $relativePath);
        }
        elseif (substr($relativePath, -2) === "/.")
        {
                // Remove the dot representing the current directory
                $relativePath = substr($relativePath, 0, -1);
                // Add current directory to archive
                $templateArchive->addEmptyDir($relativePath);
        }
        else
        {
                continue;
        }

        $templateArchive->setExternalAttributesName($relativePath,
                ZipArchive::OPSYS_UNIX,
                fileperms($filePath) << 16);
}

// Template Zip archive will be created only after closing object
$templateArchive->close();

Upvotes: 2

Related Questions