<?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
|
);
|
|
/** @var \SplFileInfo $fileInfo */
|
foreach ($files as $fileInfo) {
|
$function = ($fileInfo->isDir() ? 'rmdir' : 'unlink');
|
$function($fileInfo->getPathname());
|
}
|
@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) . \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.
|
*
|
* @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(
|
\DIRECTORY_SEPARATOR,
|
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 $target
|
* @param string $path
|
* @param bool $allowSymlink
|
*
|
* @return bool
|
*/
|
public static function symlink($target, $path, $allowSymlink)
|
{
|
if (\DIRECTORY_SEPARATOR === '\\' || !$allowSymlink) {
|
return file_put_contents($path, $target) !== false;
|
}
|
|
return symlink($target, $path);
|
}
|
|
/**
|
* @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];
|
}
|
}
|