mirror of
https://github.com/Ne-Lexa/php-zip.git
synced 2025-08-27 08:54:25 +02:00
solving problems with lack of memory, the new ZipReader and ZipWriter class, adds .phpstorm.meta.php
#13 #16 #27 #31 #41
This commit is contained in:
77
src/Util/CryptoUtil.php
Normal file
77
src/Util/CryptoUtil.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace PhpZip\Util;
|
||||
|
||||
use PhpZip\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* Crypto Utils.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class CryptoUtil
|
||||
{
|
||||
/**
|
||||
* Returns random bytes.
|
||||
*
|
||||
* @param int $length
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @deprecated Use random_bytes()
|
||||
*/
|
||||
final public static function randomBytes($length)
|
||||
{
|
||||
return random_bytes($length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt AES-CTR.
|
||||
*
|
||||
* @param string $data Encrypted data
|
||||
* @param string $key Aes key
|
||||
* @param string $iv Aes IV
|
||||
*
|
||||
* @return string Raw data
|
||||
*/
|
||||
public static function decryptAesCtr($data, $key, $iv)
|
||||
{
|
||||
if (\extension_loaded('openssl')) {
|
||||
$numBits = \strlen($key) * 8;
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
return openssl_decrypt($data, 'AES-' . $numBits . '-CTR', $key, \OPENSSL_RAW_DATA, $iv);
|
||||
}
|
||||
|
||||
if (\extension_loaded('mcrypt')) {
|
||||
return mcrypt_decrypt(\MCRYPT_RIJNDAEL_128, $key, $data, 'ctr', $iv);
|
||||
}
|
||||
|
||||
throw new RuntimeException('Extension openssl or mcrypt not loaded');
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt AES-CTR.
|
||||
*
|
||||
* @param string $data Raw data
|
||||
* @param string $key Aes key
|
||||
* @param string $iv Aes IV
|
||||
*
|
||||
* @return string Encrypted data
|
||||
*/
|
||||
public static function encryptAesCtr($data, $key, $iv)
|
||||
{
|
||||
if (\extension_loaded('openssl')) {
|
||||
$numBits = \strlen($key) * 8;
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
return openssl_encrypt($data, 'AES-' . $numBits . '-CTR', $key, \OPENSSL_RAW_DATA, $iv);
|
||||
}
|
||||
|
||||
if (\extension_loaded('mcrypt')) {
|
||||
return mcrypt_encrypt(\MCRYPT_RIJNDAEL_128, $key, $data, 'ctr', $iv);
|
||||
}
|
||||
|
||||
throw new RuntimeException('Extension openssl or mcrypt not loaded');
|
||||
}
|
||||
}
|
80
src/Util/DateTimeConverter.php
Normal file
80
src/Util/DateTimeConverter.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace PhpZip\Util;
|
||||
|
||||
/**
|
||||
* Convert unix timestamp values to DOS date/time values and vice versa.
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class DateTimeConverter
|
||||
{
|
||||
/**
|
||||
* Smallest supported DOS date/time value in a ZIP file,
|
||||
* which is January 1st, 1980 AD 00:00:00 local time.
|
||||
*/
|
||||
const MIN_DOS_TIME = 0x210000; // (1 << 21) | (1 << 16)
|
||||
|
||||
/**
|
||||
* Largest supported DOS date/time value in a ZIP file,
|
||||
* which is December 31st, 2107 AD 23:59:58 local time.
|
||||
*/
|
||||
const MAX_DOS_TIME = 0xff9fbf7d; // ((2107 - 1980) << 25) | (12 << 21) | (31 << 16) | (23 << 11) | (59 << 5) | (58 >> 1);
|
||||
|
||||
/**
|
||||
* Convert a 32 bit integer DOS date/time value to a UNIX timestamp value.
|
||||
*
|
||||
* @param int $dosTime Dos date/time
|
||||
*
|
||||
* @return int Unix timestamp
|
||||
*/
|
||||
public static function toUnixTimestamp($dosTime)
|
||||
{
|
||||
if ($dosTime < self::MIN_DOS_TIME) {
|
||||
$dosTime = self::MIN_DOS_TIME;
|
||||
} elseif ($dosTime > self::MAX_DOS_TIME) {
|
||||
$dosTime = self::MAX_DOS_TIME;
|
||||
}
|
||||
|
||||
return mktime(
|
||||
($dosTime >> 11) & 0x1f, // hour
|
||||
($dosTime >> 5) & 0x3f, // minute
|
||||
2 * ($dosTime & 0x1f), // second
|
||||
($dosTime >> 21) & 0x0f, // month
|
||||
($dosTime >> 16) & 0x1f, // day
|
||||
1980 + (($dosTime >> 25) & 0x7f) // year
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a UNIX timestamp value to a DOS date/time value.
|
||||
*
|
||||
* @param int $unixTimestamp the number of seconds since midnight, January 1st,
|
||||
* 1970 AD UTC
|
||||
*
|
||||
* @return int a DOS date/time value reflecting the local time zone and
|
||||
* rounded down to even seconds
|
||||
* and is in between DateTimeConverter::MIN_DOS_TIME and DateTimeConverter::MAX_DOS_TIME
|
||||
*/
|
||||
public static function toDosTime($unixTimestamp)
|
||||
{
|
||||
if ($unixTimestamp < 0) {
|
||||
throw new \InvalidArgumentException('Negative unix timestamp: ' . $unixTimestamp);
|
||||
}
|
||||
|
||||
$date = getdate($unixTimestamp);
|
||||
|
||||
if ($date['year'] < 1980) {
|
||||
return self::MIN_DOS_TIME;
|
||||
}
|
||||
|
||||
$date['year'] -= 1980;
|
||||
|
||||
return $date['year'] << 25 | $date['mon'] << 21 |
|
||||
$date['mday'] << 16 | $date['hours'] << 11 |
|
||||
$date['minutes'] << 5 | $date['seconds'] >> 1;
|
||||
}
|
||||
}
|
108
src/Util/FileAttribUtil.php
Normal file
108
src/Util/FileAttribUtil.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace PhpZip\Util;
|
||||
|
||||
use PhpZip\Constants\DosAttrs;
|
||||
use PhpZip\Constants\UnixStat;
|
||||
|
||||
/**
|
||||
* Class FileAttribUtil.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class FileAttribUtil implements DosAttrs, UnixStat
|
||||
{
|
||||
/**
|
||||
* Get DOS mode,.
|
||||
*
|
||||
* @param int $xattr
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getDosMode($xattr)
|
||||
{
|
||||
$xattr = (int) $xattr;
|
||||
|
||||
$mode = (($xattr & self::DOS_DIRECTORY) === self::DOS_DIRECTORY) ? 'd' : '-';
|
||||
$mode .= (($xattr & self::DOS_ARCHIVE) === self::DOS_ARCHIVE) ? 'a' : '-';
|
||||
$mode .= (($xattr & self::DOS_READ_ONLY) === self::DOS_READ_ONLY) ? 'r' : '-';
|
||||
$mode .= (($xattr & self::DOS_HIDDEN) === self::DOS_HIDDEN) ? 'h' : '-';
|
||||
$mode .= (($xattr & self::DOS_SYSTEM) === self::DOS_SYSTEM) ? 's' : '-';
|
||||
$mode .= (($xattr & self::DOS_LABEL) === self::DOS_LABEL) ? 'l' : '-';
|
||||
|
||||
return $mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $permission
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getUnixMode($permission)
|
||||
{
|
||||
$mode = '';
|
||||
$permission = (int) $permission;
|
||||
switch ($permission & self::UNX_IFMT) {
|
||||
case self::UNX_IFDIR:
|
||||
$mode .= 'd';
|
||||
break;
|
||||
|
||||
case self::UNX_IFREG:
|
||||
$mode .= '-';
|
||||
break;
|
||||
|
||||
case self::UNX_IFLNK:
|
||||
$mode .= 'l';
|
||||
break;
|
||||
|
||||
case self::UNX_IFBLK:
|
||||
$mode .= 'b';
|
||||
break;
|
||||
|
||||
case self::UNX_IFCHR:
|
||||
$mode .= 'c';
|
||||
break;
|
||||
|
||||
case self::UNX_IFIFO:
|
||||
$mode .= 'p';
|
||||
break;
|
||||
|
||||
case self::UNX_IFSOCK:
|
||||
$mode .= 's';
|
||||
break;
|
||||
|
||||
default:
|
||||
$mode .= '?';
|
||||
break;
|
||||
}
|
||||
$mode .= ($permission & self::UNX_IRUSR) ? 'r' : '-';
|
||||
$mode .= ($permission & self::UNX_IWUSR) ? 'w' : '-';
|
||||
|
||||
if ($permission & self::UNX_IXUSR) {
|
||||
$mode .= ($permission & self::UNX_ISUID) ? 's' : 'x';
|
||||
} else {
|
||||
$mode .= ($permission & self::UNX_ISUID) ? 'S' : '-'; // S==undefined
|
||||
}
|
||||
$mode .= ($permission & self::UNX_IRGRP) ? 'r' : '-';
|
||||
$mode .= ($permission & self::UNX_IWGRP) ? 'w' : '-';
|
||||
|
||||
if ($permission & self::UNX_IXGRP) {
|
||||
$mode .= ($permission & self::UNX_ISGID) ? 's' : 'x';
|
||||
} // == self::UNX_ENFMT
|
||||
else {
|
||||
$mode .= ($permission & self::UNX_ISGID) ? 'S' : '-';
|
||||
} // SunOS 4.1.x
|
||||
|
||||
$mode .= ($permission & self::UNX_IROTH) ? 'r' : '-';
|
||||
$mode .= ($permission & self::UNX_IWOTH) ? 'w' : '-';
|
||||
|
||||
if ($permission & self::UNX_IXOTH) {
|
||||
$mode .= ($permission & self::UNX_ISVTX) ? 't' : 'x';
|
||||
} // "sticky bit"
|
||||
else {
|
||||
$mode .= ($permission & self::UNX_ISVTX) ? 'T' : '-';
|
||||
} // T==undefined
|
||||
|
||||
return $mode;
|
||||
}
|
||||
}
|
466
src/Util/FilesUtil.php
Normal file
466
src/Util/FilesUtil.php
Normal file
@@ -0,0 +1,466 @@
|
||||
<?php
|
||||
|
||||
namespace PhpZip\Util;
|
||||
|
||||
use PhpZip\Util\Iterator\IgnoreFilesFilterIterator;
|
||||
use PhpZip\Util\Iterator\IgnoreFilesRecursiveFilterIterator;
|
||||
|
||||
/**
|
||||
* Files util.
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class FilesUtil
|
||||
{
|
||||
/**
|
||||
* Is empty directory.
|
||||
*
|
||||
* @param string $dir Directory
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isEmptyDir($dir)
|
||||
{
|
||||
if (!is_readable($dir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return \count(scandir($dir)) === 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove recursive directory.
|
||||
*
|
||||
* @param string $dir directory path
|
||||
*/
|
||||
public static function removeDir($dir)
|
||||
{
|
||||
$files = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
\RecursiveIteratorIterator::CHILD_FIRST
|
||||
);
|
||||
|
||||
foreach ($files as $fileInfo) {
|
||||
$function = ($fileInfo->isDir() ? 'rmdir' : 'unlink');
|
||||
$function($fileInfo->getRealPath());
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert glob pattern to regex pattern.
|
||||
*
|
||||
* @param string $globPattern
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function convertGlobToRegEx($globPattern)
|
||||
{
|
||||
// Remove beginning and ending * globs because they're useless
|
||||
$globPattern = trim($globPattern, '*');
|
||||
$escaping = false;
|
||||
$inCurrent = 0;
|
||||
$chars = str_split($globPattern);
|
||||
$regexPattern = '';
|
||||
|
||||
foreach ($chars as $currentChar) {
|
||||
switch ($currentChar) {
|
||||
case '*':
|
||||
$regexPattern .= ($escaping ? '\\*' : '.*');
|
||||
$escaping = false;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
$regexPattern .= ($escaping ? '\\?' : '.');
|
||||
$escaping = false;
|
||||
break;
|
||||
|
||||
case '.':
|
||||
case '(':
|
||||
case ')':
|
||||
case '+':
|
||||
case '|':
|
||||
case '^':
|
||||
case '$':
|
||||
case '@':
|
||||
case '%':
|
||||
$regexPattern .= '\\' . $currentChar;
|
||||
$escaping = false;
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
if ($escaping) {
|
||||
$regexPattern .= '\\\\';
|
||||
$escaping = false;
|
||||
} else {
|
||||
$escaping = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case '{':
|
||||
if ($escaping) {
|
||||
$regexPattern .= '\\{';
|
||||
} else {
|
||||
$regexPattern = '(';
|
||||
$inCurrent++;
|
||||
}
|
||||
$escaping = false;
|
||||
break;
|
||||
|
||||
case '}':
|
||||
if ($inCurrent > 0 && !$escaping) {
|
||||
$regexPattern .= ')';
|
||||
$inCurrent--;
|
||||
} elseif ($escaping) {
|
||||
$regexPattern = '\\}';
|
||||
} else {
|
||||
$regexPattern = '}';
|
||||
}
|
||||
$escaping = false;
|
||||
break;
|
||||
|
||||
case ',':
|
||||
if ($inCurrent > 0 && !$escaping) {
|
||||
$regexPattern .= '|';
|
||||
} elseif ($escaping) {
|
||||
$regexPattern .= '\\,';
|
||||
} else {
|
||||
$regexPattern = ',';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$escaping = false;
|
||||
$regexPattern .= $currentChar;
|
||||
}
|
||||
}
|
||||
|
||||
return $regexPattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search files.
|
||||
*
|
||||
* @param string $inputDir
|
||||
* @param bool $recursive
|
||||
* @param array $ignoreFiles
|
||||
*
|
||||
* @return array Searched file list
|
||||
*/
|
||||
public static function fileSearchWithIgnore($inputDir, $recursive = true, array $ignoreFiles = [])
|
||||
{
|
||||
if ($recursive) {
|
||||
$directoryIterator = new \RecursiveDirectoryIterator($inputDir);
|
||||
|
||||
if (!empty($ignoreFiles)) {
|
||||
$directoryIterator = new IgnoreFilesRecursiveFilterIterator($directoryIterator, $ignoreFiles);
|
||||
}
|
||||
$iterator = new \RecursiveIteratorIterator($directoryIterator);
|
||||
} else {
|
||||
$directoryIterator = new \DirectoryIterator($inputDir);
|
||||
|
||||
if (!empty($ignoreFiles)) {
|
||||
$directoryIterator = new IgnoreFilesFilterIterator($directoryIterator, $ignoreFiles);
|
||||
}
|
||||
$iterator = new \IteratorIterator($directoryIterator);
|
||||
}
|
||||
|
||||
$fileList = [];
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
if ($file instanceof \SplFileInfo) {
|
||||
$fileList[] = $file->getPathname();
|
||||
}
|
||||
}
|
||||
|
||||
return $fileList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search files from glob pattern.
|
||||
*
|
||||
* @param string $globPattern
|
||||
* @param int $flags
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array Searched file list
|
||||
*/
|
||||
public static function globFileSearch($globPattern, $flags = 0, $recursive = true)
|
||||
{
|
||||
$flags = (int) $flags;
|
||||
$recursive = (bool) $recursive;
|
||||
$files = glob($globPattern, $flags);
|
||||
|
||||
if (!$recursive) {
|
||||
return $files;
|
||||
}
|
||||
|
||||
foreach (glob(\dirname($globPattern) . '/*', \GLOB_ONLYDIR | \GLOB_NOSORT) as $dir) {
|
||||
// Unpacking the argument via ... is supported starting from php 5.6 only
|
||||
/** @noinspection SlowArrayOperationsInLoopInspection */
|
||||
$files = array_merge($files, self::globFileSearch($dir . '/' . basename($globPattern), $flags, $recursive));
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search files from regex pattern.
|
||||
*
|
||||
* @param string $folder
|
||||
* @param string $pattern
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array Searched file list
|
||||
*/
|
||||
public static function regexFileSearch($folder, $pattern, $recursive = true)
|
||||
{
|
||||
if ($recursive) {
|
||||
$directoryIterator = new \RecursiveDirectoryIterator($folder);
|
||||
$iterator = new \RecursiveIteratorIterator($directoryIterator);
|
||||
} else {
|
||||
$directoryIterator = new \DirectoryIterator($folder);
|
||||
$iterator = new \IteratorIterator($directoryIterator);
|
||||
}
|
||||
|
||||
$regexIterator = new \RegexIterator($iterator, $pattern, \RegexIterator::MATCH);
|
||||
$fileList = [];
|
||||
|
||||
foreach ($regexIterator as $file) {
|
||||
if ($file instanceof \SplFileInfo) {
|
||||
$fileList[] = $file->getPathname();
|
||||
}
|
||||
}
|
||||
|
||||
return $fileList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert bytes to human size.
|
||||
*
|
||||
* @param int $size Size bytes
|
||||
* @param string|null $unit Unit support 'GB', 'MB', 'KB'
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function humanSize($size, $unit = null)
|
||||
{
|
||||
if (($unit === null && $size >= 1 << 30) || $unit === 'GB') {
|
||||
return number_format($size / (1 << 30), 2) . 'GB';
|
||||
}
|
||||
|
||||
if (($unit === null && $size >= 1 << 20) || $unit === 'MB') {
|
||||
return number_format($size / (1 << 20), 2) . 'MB';
|
||||
}
|
||||
|
||||
if (($unit === null && $size >= 1 << 10) || $unit === 'KB') {
|
||||
return number_format($size / (1 << 10), 2) . 'KB';
|
||||
}
|
||||
|
||||
return number_format($size) . ' bytes';
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes zip path.
|
||||
*
|
||||
* @param string $path Zip path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function normalizeZipPath($path)
|
||||
{
|
||||
return implode(
|
||||
'/',
|
||||
array_filter(
|
||||
explode('/', (string) $path),
|
||||
static function ($part) {
|
||||
return $part !== '.' && $part !== '..';
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the file path is an absolute path.
|
||||
*
|
||||
* @param string $file A file path
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @see source symfony filesystem component
|
||||
*/
|
||||
public static function isAbsolutePath($file)
|
||||
{
|
||||
return strspn($file, '/\\', 0, 1)
|
||||
|| (
|
||||
\strlen($file) > 3 && ctype_alpha($file[0])
|
||||
&& $file[1] === ':'
|
||||
&& strspn($file, '/\\', 2, 1)
|
||||
)
|
||||
|| parse_url($file, \PHP_URL_SCHEME) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $linkPath
|
||||
* @param string $target
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function symlink($target, $linkPath)
|
||||
{
|
||||
if (\DIRECTORY_SEPARATOR === '\\') {
|
||||
$linkPath = str_replace('/', '\\', $linkPath);
|
||||
$target = str_replace('/', '\\', $target);
|
||||
$abs = null;
|
||||
|
||||
if (!self::isAbsolutePath($target)) {
|
||||
$abs = realpath(\dirname($linkPath) . \DIRECTORY_SEPARATOR . $target);
|
||||
|
||||
if (\is_string($abs)) {
|
||||
$target = $abs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!symlink($target, $linkPath)) {
|
||||
if (\DIRECTORY_SEPARATOR === '\\' && is_file($target)) {
|
||||
return copy($target, $linkPath);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isBadCompressionFile($file)
|
||||
{
|
||||
$badCompressFileExt = [
|
||||
'dic',
|
||||
'dng',
|
||||
'f4v',
|
||||
'flipchart',
|
||||
'h264',
|
||||
'lrf',
|
||||
'mobi',
|
||||
'mts',
|
||||
'nef',
|
||||
'pspimage',
|
||||
];
|
||||
|
||||
$ext = strtolower(pathinfo($file, \PATHINFO_EXTENSION));
|
||||
|
||||
if (\in_array($ext, $badCompressFileExt, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$mimeType = self::getMimeTypeFromFile($file);
|
||||
|
||||
return self::isBadCompressionMimeType($mimeType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $mimeType
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isBadCompressionMimeType($mimeType)
|
||||
{
|
||||
static $badDeflateCompMimeTypes = [
|
||||
'application/epub+zip',
|
||||
'application/gzip',
|
||||
'application/vnd.debian.binary-package',
|
||||
'application/vnd.oasis.opendocument.graphics',
|
||||
'application/vnd.oasis.opendocument.presentation',
|
||||
'application/vnd.oasis.opendocument.text',
|
||||
'application/vnd.oasis.opendocument.text-master',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'application/vnd.rn-realmedia',
|
||||
'application/x-7z-compressed',
|
||||
'application/x-arj',
|
||||
'application/x-bzip2',
|
||||
'application/x-hwp',
|
||||
'application/x-lzip',
|
||||
'application/x-lzma',
|
||||
'application/x-ms-reader',
|
||||
'application/x-rar',
|
||||
'application/x-rpm',
|
||||
'application/x-stuffit',
|
||||
'application/x-tar',
|
||||
'application/x-xz',
|
||||
'application/zip',
|
||||
'application/zlib',
|
||||
'audio/flac',
|
||||
'audio/mpeg',
|
||||
'audio/ogg',
|
||||
'audio/vnd.dolby.dd-raw',
|
||||
'audio/webm',
|
||||
'audio/x-ape',
|
||||
'audio/x-hx-aac-adts',
|
||||
'audio/x-m4a',
|
||||
'audio/x-m4a',
|
||||
'audio/x-wav',
|
||||
'image/gif',
|
||||
'image/heic',
|
||||
'image/jp2',
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/vnd.djvu',
|
||||
'image/webp',
|
||||
'image/x-canon-cr2',
|
||||
'video/ogg',
|
||||
'video/webm',
|
||||
'video/x-matroska',
|
||||
'video/x-ms-asf',
|
||||
'x-epoc/x-sisx-app',
|
||||
];
|
||||
|
||||
if (\in_array($mimeType, $badDeflateCompMimeTypes, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpComposerExtensionStubsInspection
|
||||
*/
|
||||
public static function getMimeTypeFromFile($file)
|
||||
{
|
||||
if (\function_exists('mime_content_type')) {
|
||||
return mime_content_type($file);
|
||||
}
|
||||
|
||||
return 'application/octet-stream';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $contents
|
||||
*
|
||||
* @return string
|
||||
* @noinspection PhpComposerExtensionStubsInspection
|
||||
*/
|
||||
public static function getMimeTypeFromString($contents)
|
||||
{
|
||||
$contents = (string) $contents;
|
||||
$finfo = new \finfo(\FILEINFO_MIME);
|
||||
$mimeType = $finfo->buffer($contents);
|
||||
|
||||
if ($mimeType === false) {
|
||||
$mimeType = 'application/octet-stream';
|
||||
}
|
||||
|
||||
return explode(';', $mimeType)[0];
|
||||
}
|
||||
}
|
66
src/Util/Iterator/IgnoreFilesFilterIterator.php
Normal file
66
src/Util/Iterator/IgnoreFilesFilterIterator.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace PhpZip\Util\Iterator;
|
||||
|
||||
use PhpZip\Util\StringUtil;
|
||||
|
||||
/**
|
||||
* Iterator for ignore files.
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*/
|
||||
class IgnoreFilesFilterIterator extends \FilterIterator
|
||||
{
|
||||
/**
|
||||
* Ignore list files.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $ignoreFiles = ['..'];
|
||||
|
||||
/**
|
||||
* @param \Iterator $iterator
|
||||
* @param array $ignoreFiles
|
||||
*/
|
||||
public function __construct(\Iterator $iterator, array $ignoreFiles)
|
||||
{
|
||||
parent::__construct($iterator);
|
||||
$this->ignoreFiles = array_merge($this->ignoreFiles, $ignoreFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the current element of the iterator is acceptable.
|
||||
*
|
||||
* @see http://php.net/manual/en/filteriterator.accept.php
|
||||
*
|
||||
* @return bool true if the current element is acceptable, otherwise false
|
||||
*
|
||||
* @since 5.1.0
|
||||
*/
|
||||
public function accept()
|
||||
{
|
||||
/**
|
||||
* @var \SplFileInfo $fileInfo
|
||||
*/
|
||||
$fileInfo = $this->current();
|
||||
$pathname = str_replace('\\', '/', $fileInfo->getPathname());
|
||||
|
||||
foreach ($this->ignoreFiles as $ignoreFile) {
|
||||
// handler dir and sub dir
|
||||
if ($fileInfo->isDir()
|
||||
&& StringUtil::endsWith($ignoreFile, '/')
|
||||
&& StringUtil::endsWith($pathname, substr($ignoreFile, 0, -1))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// handler filename
|
||||
if (StringUtil::endsWith($pathname, $ignoreFile)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
74
src/Util/Iterator/IgnoreFilesRecursiveFilterIterator.php
Normal file
74
src/Util/Iterator/IgnoreFilesRecursiveFilterIterator.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace PhpZip\Util\Iterator;
|
||||
|
||||
use PhpZip\Util\StringUtil;
|
||||
|
||||
/**
|
||||
* Recursive iterator for ignore files.
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*/
|
||||
class IgnoreFilesRecursiveFilterIterator extends \RecursiveFilterIterator
|
||||
{
|
||||
/**
|
||||
* Ignore list files.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $ignoreFiles = ['..'];
|
||||
|
||||
/**
|
||||
* @param \RecursiveIterator $iterator
|
||||
* @param array $ignoreFiles
|
||||
*/
|
||||
public function __construct(\RecursiveIterator $iterator, array $ignoreFiles)
|
||||
{
|
||||
parent::__construct($iterator);
|
||||
$this->ignoreFiles = array_merge($this->ignoreFiles, $ignoreFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the current element of the iterator is acceptable.
|
||||
*
|
||||
* @see http://php.net/manual/en/filteriterator.accept.php
|
||||
*
|
||||
* @return bool true if the current element is acceptable, otherwise false
|
||||
*
|
||||
* @since 5.1.0
|
||||
*/
|
||||
public function accept()
|
||||
{
|
||||
/**
|
||||
* @var \SplFileInfo $fileInfo
|
||||
*/
|
||||
$fileInfo = $this->current();
|
||||
$pathname = str_replace('\\', '/', $fileInfo->getPathname());
|
||||
|
||||
foreach ($this->ignoreFiles as $ignoreFile) {
|
||||
// handler dir and sub dir
|
||||
if ($fileInfo->isDir()
|
||||
&& $ignoreFile[\strlen($ignoreFile) - 1] === '/'
|
||||
&& StringUtil::endsWith($pathname, substr($ignoreFile, 0, -1))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// handler filename
|
||||
if (StringUtil::endsWith($pathname, $ignoreFile)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return IgnoreFilesRecursiveFilterIterator
|
||||
*/
|
||||
public function getChildren()
|
||||
{
|
||||
return new self($this->getInnerIterator()->getChildren(), $this->ignoreFiles);
|
||||
}
|
||||
}
|
69
src/Util/PackUtil.php
Normal file
69
src/Util/PackUtil.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace PhpZip\Util;
|
||||
|
||||
/**
|
||||
* Pack util.
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class PackUtil
|
||||
{
|
||||
/**
|
||||
* @param int $longValue
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function packLongLE($longValue)
|
||||
{
|
||||
if (\PHP_VERSION_ID >= 506030) {
|
||||
return pack('P', $longValue);
|
||||
}
|
||||
|
||||
$left = 0xffffffff00000000;
|
||||
$right = 0x00000000ffffffff;
|
||||
|
||||
$r = ($longValue & $left) >> 32;
|
||||
$l = $longValue & $right;
|
||||
|
||||
return pack('VV', $l, $r);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function unpackLongLE($value)
|
||||
{
|
||||
if (\PHP_VERSION_ID >= 506030) {
|
||||
return unpack('P', $value)[1];
|
||||
}
|
||||
$unpack = unpack('Va/Vb', $value);
|
||||
|
||||
return $unpack['a'] + ($unpack['b'] << 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast to signed int 32-bit.
|
||||
*
|
||||
* @param int $int
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function toSignedInt32($int)
|
||||
{
|
||||
if (\PHP_INT_SIZE === 8) {
|
||||
$int &= 0xffffffff;
|
||||
|
||||
if ($int & 0x80000000) {
|
||||
return $int - 0x100000000;
|
||||
}
|
||||
}
|
||||
|
||||
return $int;
|
||||
}
|
||||
}
|
54
src/Util/StringUtil.php
Normal file
54
src/Util/StringUtil.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace PhpZip\Util;
|
||||
|
||||
/**
|
||||
* String Util.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class StringUtil
|
||||
{
|
||||
/**
|
||||
* @param string $haystack
|
||||
* @param string $needle
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function startsWith($haystack, $needle)
|
||||
{
|
||||
return $needle === '' || strrpos($haystack, $needle, -\strlen($haystack)) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $haystack
|
||||
* @param string $needle
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function endsWith($haystack, $needle)
|
||||
{
|
||||
return $needle === '' || (($temp = \strlen($haystack) - \strlen($needle)) >= 0
|
||||
&& strpos($haystack, $needle, $temp) !== false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isBinary($string)
|
||||
{
|
||||
return strpos($string, "\0") !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isASCII($name)
|
||||
{
|
||||
return preg_match('~[^\x20-\x7e]~', (string) $name) === 0;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user