- 框架初始化
 - 安装插件
 - 修复PHP8.4报错
This commit is contained in:
2025-04-19 17:21:20 +08:00
commit c6a4e1f5f6
5306 changed files with 967782 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Util;
use PhpZip\Exception\RuntimeException;
/**
* Crypto Utils.
*
* @internal
*/
final class CryptoUtil
{
/**
* 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(string $data, ?string $key, ?string $iv): string
{
if (\extension_loaded('openssl')) {
$numBits = \strlen($key) * 8;
/** @noinspection PhpComposerExtensionStubsInspection */
return openssl_decrypt($data, 'AES-' . $numBits . '-CTR', $key, \OPENSSL_RAW_DATA, $iv);
}
throw new RuntimeException('Openssl extension 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(string $data, ?string $key, ?string $iv): string
{
if (\extension_loaded('openssl')) {
$numBits = \strlen($key) * 8;
/** @noinspection PhpComposerExtensionStubsInspection */
return openssl_encrypt($data, 'AES-' . $numBits . '-CTR', $key, \OPENSSL_RAW_DATA, $iv);
}
throw new RuntimeException('Openssl extension not loaded');
}
}

View File

@@ -0,0 +1,109 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Util;
/**
* Convert unix timestamp values to DOS date/time values and vice versa.
*
* The DOS date/time format is a bitmask:
*
* 24 16 8 0
* +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
* |Y|Y|Y|Y|Y|Y|Y|M| |M|M|M|D|D|D|D|D| |h|h|h|h|h|m|m|m| |m|m|m|s|s|s|s|s|
* +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
* \___________/\________/\_________/ \________/\____________/\_________/
* year month day hour minute second
*
* The year is stored as an offset from 1980.
* Seconds are stored in two-second increments.
* (So if the "second" value is 15, it actually represents 30 seconds.)
*
* @see https://docs.microsoft.com/ru-ru/windows/win32/api/winbase/nf-winbase-filetimetodosdatetime?redirectedfrom=MSDN
*
* @internal
*/
class DateTimeConverter
{
/**
* Smallest supported DOS date/time value in a ZIP file,
* which is January 1st, 1980 AD 00:00:00 local time.
*
* @var int
*/
public const MIN_DOS_TIME = (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.
*
* @var int
*/
public const MAX_DOS_TIME = ((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 msDosToUnix(int $dosTime): int
{
if ($dosTime <= self::MIN_DOS_TIME) {
$dosTime = 0;
} elseif ($dosTime > self::MAX_DOS_TIME) {
$dosTime = self::MAX_DOS_TIME;
}
// date_default_timezone_set('UTC');
return mktime(
(($dosTime >> 11) & 0x1F), // hours
(($dosTime >> 5) & 0x3F), // minutes
(($dosTime << 1) & 0x3E), // seconds
(($dosTime >> 21) & 0x0F), // month
(($dosTime >> 16) & 0x1F), // day
((($dosTime >> 25) & 0x7F) + 1980) // 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 unixToMsDos(int $unixTimestamp): int
{
if ($unixTimestamp < 0) {
throw new \InvalidArgumentException('Negative unix timestamp: ' . $unixTimestamp);
}
$date = getdate($unixTimestamp);
$dosTime = (
(($date['year'] - 1980) << 25)
| ($date['mon'] << 21)
| ($date['mday'] << 16)
| ($date['hours'] << 11)
| ($date['minutes'] << 5)
| ($date['seconds'] >> 1)
);
if ($dosTime <= self::MIN_DOS_TIME) {
$dosTime = 0;
}
return $dosTime;
}
}

View File

@@ -0,0 +1,106 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Util;
use PhpZip\Constants\DosAttrs;
use PhpZip\Constants\UnixStat;
/**
* @internal
*/
class FileAttribUtil implements DosAttrs, UnixStat
{
/**
* Get DOS mode.
*/
public static function getDosMode(int $xattr): string
{
$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;
}
/**
* @noinspection DuplicatedCode
*/
public static function getUnixMode(int $permission): string
{
$mode = '';
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;
}
}

399
vendor/nelexa/zip/src/Util/FilesUtil.php vendored Normal file
View File

@@ -0,0 +1,399 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Util;
use PhpZip\Util\Iterator\IgnoreFilesFilterIterator;
use PhpZip\Util\Iterator\IgnoreFilesRecursiveFilterIterator;
/**
* Files util.
*
* @internal
*/
final class FilesUtil
{
/**
* Is empty directory.
*
* @param string $dir Directory
*/
public static function isEmptyDir(string $dir): bool
{
if (!is_readable($dir)) {
return false;
}
return \count(scandir($dir)) === 2;
}
/**
* Remove recursive directory.
*
* @param string $dir directory path
*/
public static function removeDir(string $dir): void
{
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::CHILD_FIRST
);
/** @var \SplFileInfo $fileInfo */
foreach ($files as $fileInfo) {
$function = ($fileInfo->isDir() ? 'rmdir' : 'unlink');
$function($fileInfo->getPathname());
}
@rmdir($dir);
}
/**
* Convert glob pattern to regex pattern.
*/
public static function convertGlobToRegEx(string $globPattern): string
{
// 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.
*
* @return array Searched file list
*/
public static function fileSearchWithIgnore(string $inputDir, bool $recursive = true, ?array $ignoreFiles = []): array
{
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.
*
* @return array Searched file list
*/
public static function globFileSearch(string $globPattern, int $flags = 0, bool $recursive = true): array
{
$files = glob($globPattern, $flags);
if (!$recursive) {
return $files;
}
foreach (glob(\dirname($globPattern) . \DIRECTORY_SEPARATOR . '*', \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 . \DIRECTORY_SEPARATOR . basename($globPattern), $flags, $recursive));
}
return $files;
}
/**
* Search files from regex pattern.
*
* @return array Searched file list
*/
public static function regexFileSearch(string $folder, ?string $pattern, bool $recursive = true): array
{
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'
*/
public static function humanSize(int $size, ?string $unit = null): string
{
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
*/
public static function normalizeZipPath(string $path): string
{
return implode(
\DIRECTORY_SEPARATOR,
array_filter(
explode('/', $path),
static fn ($part) => $part !== '.' && $part !== '..'
)
);
}
/**
* Returns whether the file path is an absolute path.
*
* @param string $file A file path
*
* @see source symfony filesystem component
*/
public static function isAbsolutePath(string $file): bool
{
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;
}
public static function symlink(string $target, ?string $path, bool $allowSymlink): bool
{
if (\DIRECTORY_SEPARATOR === '\\' || !$allowSymlink) {
return file_put_contents($path, $target) !== false;
}
return symlink($target, $path);
}
public static function isBadCompressionFile(string $file): bool
{
$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);
}
public static function isBadCompressionMimeType(string $mimeType): bool
{
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',
];
return \in_array($mimeType, $badDeflateCompMimeTypes, true);
}
/**
* @noinspection PhpComposerExtensionStubsInspection
*/
public static function getMimeTypeFromFile(string $file): string
{
if (\function_exists('mime_content_type')) {
return mime_content_type($file);
}
return 'application/octet-stream';
}
/**
* @noinspection PhpComposerExtensionStubsInspection
*/
public static function getMimeTypeFromString(string $contents): string
{
$finfo = new \finfo(\FILEINFO_MIME);
$mimeType = $finfo->buffer($contents);
if ($mimeType === false) {
$mimeType = 'application/octet-stream';
}
return explode(';', $mimeType)[0];
}
}

View File

@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Util\Iterator;
use PhpZip\Util\StringUtil;
/**
* Iterator for ignore files.
*/
class IgnoreFilesFilterIterator extends \FilterIterator
{
/** Ignore list files. */
private 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
*/
public function accept(): bool
{
/**
* @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;
}
}

View File

@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Util\Iterator;
use PhpZip\Util\StringUtil;
/**
* Recursive iterator for ignore files.
*/
class IgnoreFilesRecursiveFilterIterator extends \RecursiveFilterIterator
{
/** Ignore list files. */
private 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
*/
public function accept(): bool
{
/**
* @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
* @psalm-suppress UndefinedInterfaceMethod
* @noinspection PhpPossiblePolymorphicInvocationInspection
*/
public function getChildren(): self
{
return new self($this->getInnerIterator()->getChildren(), $this->ignoreFiles);
}
}

36
vendor/nelexa/zip/src/Util/MathUtil.php vendored Normal file
View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Util;
/**
* Math util.
*
* @internal
*/
final class MathUtil
{
/**
* Cast to signed int 32-bit.
*/
public static function toSignedInt32(int $int): int
{
if (\PHP_INT_SIZE === 8) {
$int &= 0xFFFFFFFF;
if ($int & 0x80000000) {
return $int - 0x100000000;
}
}
return $int;
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Util;
/**
* String Util.
*
* @internal
*/
final class StringUtil
{
public static function endsWith(string $haystack, ?string $needle): bool
{
return $needle === '' || ($haystack !== '' && substr_compare($haystack, $needle, -\strlen($needle)) === 0);
}
public static function isBinary(string $string): bool
{
return strpos($string, "\0") !== false;
}
public static function isASCII(string $name): bool
{
return preg_match('~[^\x20-\x7e]~', $name) === 0;
}
}