257 lines
6.6 KiB
PHP
257 lines
6.6 KiB
PHP
<?php
|
|
|
|
namespace Dcat\Admin\Support;
|
|
|
|
/**
|
|
* Zip helper.
|
|
*
|
|
* @author Alexey Bobkov, Samuel Georges
|
|
*
|
|
* Usage:
|
|
*
|
|
* Zip::make('file.zip', '/some/path/*.php');
|
|
*
|
|
* Zip::make('file.zip', function($zip) {
|
|
*
|
|
* // Add all PHP files and directories
|
|
* $zip->add('/some/path/*.php');
|
|
*
|
|
* // Do not include subdirectories, one level only
|
|
* $zip->add('/non/recursive/*', ['recursive' => false]);
|
|
*
|
|
* // Add multiple paths
|
|
* $zip->add([
|
|
* '/collection/of/paths/*',
|
|
* '/a/single/file.php'
|
|
* ]);
|
|
*
|
|
* // Add all INI files to a zip folder "config"
|
|
* $zip->folder('/config', '/path/to/config/*.ini');
|
|
*
|
|
* // Add multiple paths to a zip folder "images"
|
|
* $zip->folder('/images', function($zip) {
|
|
* $zip->add('/my/gifs/*.gif', );
|
|
* $zip->add('/photo/reel/*.{png,jpg}', );
|
|
* });
|
|
*
|
|
* // Remove these files/folders from the zip
|
|
* $zip->remove([
|
|
* '.htaccess',
|
|
* 'config.php',
|
|
* 'some/folder'
|
|
* ]);
|
|
*
|
|
* });
|
|
*
|
|
* Zip::extract('file.zip', '/destination/path');
|
|
*/
|
|
|
|
use ZipArchive;
|
|
|
|
class Zip extends ZipArchive
|
|
{
|
|
/**
|
|
* @var string Folder prefix
|
|
*/
|
|
protected $folderPrefix = '';
|
|
|
|
/**
|
|
* Extract an existing zip file.
|
|
*
|
|
* @param string $source Path for the existing zip
|
|
* @param string $destination Path to extract the zip files
|
|
* @param array $options
|
|
* @return bool
|
|
*/
|
|
public static function extract($source, $destination, $options = [])
|
|
{
|
|
extract(array_merge([
|
|
'mask' => 0777,
|
|
], $options));
|
|
|
|
if (file_exists($destination) || mkdir($destination, $mask, true)) {
|
|
$zip = new ZipArchive;
|
|
if ($zip->open($source) === true) {
|
|
$zip->extractTo($destination);
|
|
$zip->close();
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Creates a new empty zip file.
|
|
*
|
|
* @param string $destination Path for the new zip
|
|
* @param mixed $source
|
|
* @param array $options
|
|
* @return self
|
|
*/
|
|
public static function make($destination, $source, $options = [])
|
|
{
|
|
$zip = new self;
|
|
$zip->open($destination, ZIPARCHIVE::CREATE | ZipArchive::OVERWRITE);
|
|
|
|
if (is_string($source)) {
|
|
$zip->add($source, $options);
|
|
} elseif (is_callable($source)) {
|
|
$source($zip);
|
|
} elseif (is_array($source)) {
|
|
foreach ($source as $_source) {
|
|
$zip->add($_source, $options);
|
|
}
|
|
}
|
|
|
|
$zip->close();
|
|
|
|
return $zip;
|
|
}
|
|
|
|
/**
|
|
* Includes a source to the Zip.
|
|
*
|
|
* @param mixed $source
|
|
* @param array $options
|
|
* @return self
|
|
*/
|
|
public function add($source, $options = [])
|
|
{
|
|
/*
|
|
* A directory has been supplied, convert it to a useful glob
|
|
*
|
|
* The wildcard for including hidden files:
|
|
* - isn't hidden with an '.'
|
|
* - is hidden with a '.' but is followed by a non '.' character
|
|
* - starts with '..' but has at least one character after it
|
|
*/
|
|
if (is_dir($source)) {
|
|
$includeHidden = isset($options['includeHidden']) && $options['includeHidden'];
|
|
$wildcard = $includeHidden ? '{*,.[!.]*,..?*}' : '*';
|
|
$source = implode('/', [dirname($source), Helper::basename($source), $wildcard]);
|
|
}
|
|
|
|
extract(array_merge([
|
|
'recursive' => true,
|
|
'includeHidden' => false,
|
|
'basedir' => dirname($source),
|
|
'baseglob' => Helper::basename($source),
|
|
], $options));
|
|
|
|
if (is_file($source)) {
|
|
$files = [$source];
|
|
$recursive = false;
|
|
} else {
|
|
$files = glob($source, GLOB_BRACE);
|
|
$folders = glob(dirname($source).'/*', GLOB_ONLYDIR);
|
|
}
|
|
|
|
foreach ($files as $file) {
|
|
if (! is_file($file)) {
|
|
continue;
|
|
}
|
|
|
|
$localpath = $this->removePathPrefix($basedir.'/', dirname($file).'/');
|
|
$localfile = $this->folderPrefix.$localpath.Helper::basename($file);
|
|
$this->addFile($file, $localfile);
|
|
}
|
|
|
|
if (! $recursive) {
|
|
return $this;
|
|
}
|
|
|
|
foreach ($folders as $folder) {
|
|
if (! is_dir($folder)) {
|
|
continue;
|
|
}
|
|
|
|
$localpath = $this->folderPrefix.$this->removePathPrefix($basedir.'/', $folder.'/');
|
|
$this->addEmptyDir($localpath);
|
|
$this->add($folder.'/'.$baseglob, array_merge($options, ['basedir' => $basedir]));
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Creates a new folder inside the Zip and adds source files (optional).
|
|
*
|
|
* @param string $name Folder name
|
|
* @param mixed $source
|
|
* @return self
|
|
*/
|
|
public function folder($name, $source = null)
|
|
{
|
|
$prefix = $this->folderPrefix;
|
|
$this->addEmptyDir($prefix.$name);
|
|
if ($source === null) {
|
|
return $this;
|
|
}
|
|
|
|
$this->folderPrefix = $prefix.$name.'/';
|
|
|
|
if (is_string($source)) {
|
|
$this->add($source);
|
|
} elseif (is_callable($source)) {
|
|
$source($this);
|
|
} elseif (is_array($source)) {
|
|
foreach ($source as $_source) {
|
|
$this->add($_source);
|
|
}
|
|
}
|
|
|
|
$this->folderPrefix = $prefix;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Removes a file or folder from the zip collection.
|
|
* Does not support wildcards.
|
|
*
|
|
* @param string $source
|
|
* @return self
|
|
*/
|
|
public function remove($source)
|
|
{
|
|
if (is_array($source)) {
|
|
foreach ($source as $_source) {
|
|
$this->remove($_source);
|
|
}
|
|
}
|
|
|
|
if (! is_string($source)) {
|
|
return $this;
|
|
}
|
|
|
|
if (substr($source, 0, 1) == '/') {
|
|
$source = substr($source, 1);
|
|
}
|
|
|
|
for ($i = 0; $i < $this->numFiles; $i++) {
|
|
$stats = $this->statIndex($i);
|
|
if (substr($stats['name'], 0, strlen($source)) == $source) {
|
|
$this->deleteIndex($i);
|
|
}
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Removes a prefix from a path.
|
|
*
|
|
* @param string $prefix /var/sites/
|
|
* @param string $path /var/sites/moo/cow/
|
|
* @return string moo/cow/
|
|
*/
|
|
protected function removePathPrefix($prefix, $path)
|
|
{
|
|
return (strpos($path, $prefix) === 0)
|
|
? substr($path, strlen($prefix))
|
|
: $path;
|
|
}
|
|
}
|