mirror of
https://gitee.com/wonderful-code/buildadmin
synced 2024-11-21 14:41:29 +00:00
244 lines
7.0 KiB
PHP
244 lines
7.0 KiB
PHP
<?php
|
|
|
|
namespace ba;
|
|
|
|
use Throwable;
|
|
use PhpZip\ZipFile;
|
|
use FilesystemIterator;
|
|
use RecursiveIteratorIterator;
|
|
use RecursiveDirectoryIterator;
|
|
|
|
/**
|
|
* 访问和操作文件系统
|
|
*/
|
|
class Filesystem
|
|
{
|
|
/**
|
|
* 是否是空目录
|
|
*/
|
|
public static function dirIsEmpty(string $dir): bool
|
|
{
|
|
if (!file_exists($dir)) return true;
|
|
$handle = opendir($dir);
|
|
while (false !== ($entry = readdir($handle))) {
|
|
if ($entry != "." && $entry != "..") {
|
|
closedir($handle);
|
|
return false;
|
|
}
|
|
}
|
|
closedir($handle);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 递归删除目录
|
|
* @param string $dir 目录路径
|
|
* @param bool $delSelf 是否删除传递的目录本身
|
|
* @return bool
|
|
*/
|
|
public static function delDir(string $dir, bool $delSelf = true): bool
|
|
{
|
|
if (!is_dir($dir)) {
|
|
return false;
|
|
}
|
|
$files = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS),
|
|
RecursiveIteratorIterator::CHILD_FIRST
|
|
);
|
|
foreach ($files as $fileInfo) {
|
|
if ($fileInfo->isDir()) {
|
|
self::delDir($fileInfo->getRealPath());
|
|
} else {
|
|
@unlink($fileInfo->getRealPath());
|
|
}
|
|
}
|
|
if ($delSelf) {
|
|
@rmdir($dir);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 删除一个路径下的所有相对空文件夹(删除此路径中的所有空文件夹)
|
|
* @param string $path 相对于根目录的文件夹路径 如`c:BuildAdmin/a/b/`
|
|
* @return void
|
|
*/
|
|
public static function delEmptyDir(string $path): void
|
|
{
|
|
$path = str_replace(root_path(), '', rtrim(self::fsFit($path), DIRECTORY_SEPARATOR));
|
|
$path = array_filter(explode(DIRECTORY_SEPARATOR, $path));
|
|
for ($i = count($path) - 1; $i >= 0; $i--) {
|
|
$dirPath = root_path() . implode(DIRECTORY_SEPARATOR, $path);
|
|
if (!is_dir($dirPath)) {
|
|
unset($path[$i]);
|
|
continue;
|
|
}
|
|
if (self::dirIsEmpty($dirPath)) {
|
|
self::delDir($dirPath);
|
|
unset($path[$i]);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 检查目录/文件是否可写
|
|
* @param $path
|
|
* @return bool
|
|
*/
|
|
public static function pathIsWritable($path): bool
|
|
{
|
|
if (DIRECTORY_SEPARATOR == '/' && !@ini_get('safe_mode')) {
|
|
return is_writable($path);
|
|
}
|
|
|
|
if (is_dir($path)) {
|
|
$path = rtrim($path, '/') . '/' . md5(mt_rand(1, 100) . mt_rand(1, 100));
|
|
if (($fp = @fopen($path, 'ab')) === false) {
|
|
return false;
|
|
}
|
|
|
|
fclose($fp);
|
|
@chmod($path, 0777);
|
|
@unlink($path);
|
|
|
|
return true;
|
|
} elseif (!is_file($path) || ($fp = @fopen($path, 'ab')) === false) {
|
|
return false;
|
|
}
|
|
|
|
fclose($fp);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 路径分隔符根据当前系统分隔符适配
|
|
* @param string $path 路径
|
|
* @return string 转换后的路径
|
|
*/
|
|
public static function fsFit(string $path): string
|
|
{
|
|
return str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
|
|
}
|
|
|
|
/**
|
|
* 解压Zip
|
|
* @param string $file ZIP文件路径
|
|
* @param string $dir 解压路径
|
|
* @return string 解压后的路径
|
|
* @throws Throwable
|
|
*/
|
|
public static function unzip(string $file, string $dir = ''): string
|
|
{
|
|
if (!file_exists($file)) {
|
|
throw new Exception("Zip file not found");
|
|
}
|
|
|
|
$zip = new ZipFile();
|
|
try {
|
|
$zip->openFile($file);
|
|
} catch (Throwable $e) {
|
|
$zip->close();
|
|
throw new Exception('Unable to open the zip file', 0, ['msg' => $e->getMessage()]);
|
|
}
|
|
|
|
$dir = $dir ?: substr($file, 0, strripos($file, '.zip'));
|
|
if (!is_dir($dir)) {
|
|
@mkdir($dir, 0755);
|
|
}
|
|
|
|
try {
|
|
$zip->extractTo($dir);
|
|
} catch (Throwable $e) {
|
|
throw new Exception('Unable to extract ZIP file', 0, ['msg' => $e->getMessage()]);
|
|
} finally {
|
|
$zip->close();
|
|
}
|
|
return $dir;
|
|
}
|
|
|
|
/**
|
|
* 创建ZIP
|
|
* @param array $files 文件路径列表
|
|
* @param string $fileName ZIP文件名称
|
|
* @return bool
|
|
* @throws Throwable
|
|
*/
|
|
public static function zip(array $files, string $fileName): bool
|
|
{
|
|
$zip = new ZipFile();
|
|
try {
|
|
foreach ($files as $v) {
|
|
if (is_array($v) && isset($v['file']) && isset($v['name'])) {
|
|
$zip->addFile(root_path() . str_replace(root_path(), '', Filesystem::fsFit($v['file'])), $v['name']);
|
|
} else {
|
|
$saveFile = str_replace(root_path(), '', Filesystem::fsFit($v));
|
|
$zip->addFile(root_path() . $saveFile, $saveFile);
|
|
}
|
|
}
|
|
$zip->saveAsFile($fileName);
|
|
} catch (Throwable $e) {
|
|
throw new Exception('Unable to package zip file', 0, ['msg' => $e->getMessage(), 'file' => $fileName]);
|
|
} finally {
|
|
$zip->close();
|
|
}
|
|
if (file_exists($fileName)) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 递归创建目录
|
|
* @param string $dir 目录路径
|
|
* @return bool
|
|
*/
|
|
public static function mkdir(string $dir): bool
|
|
{
|
|
if (!is_dir($dir)) {
|
|
return mkdir($dir, 0755, true);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 获取一个目录内的文件列表
|
|
* @param string $dir 目录路径
|
|
* @param array $suffix 要获取的文件列表的后缀
|
|
* @return array
|
|
*/
|
|
public static function getDirFiles(string $dir, array $suffix = ['php']): array
|
|
{
|
|
$files = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($dir), RecursiveIteratorIterator::LEAVES_ONLY
|
|
);
|
|
|
|
$fileList = [];
|
|
foreach ($files as $file) {
|
|
if (!$file->isDir() && in_array($file->getExtension(), $suffix)) {
|
|
$filePath = $file->getRealPath();
|
|
$name = str_replace($dir, '', $filePath);
|
|
$name = str_replace(DIRECTORY_SEPARATOR, "/", $name);
|
|
$fileList[$name] = $name;
|
|
}
|
|
}
|
|
return $fileList;
|
|
}
|
|
|
|
/**
|
|
* 将一个文件单位转为字节
|
|
* @param string $unit 将b、kb、m、mb、g、gb的单位转为 byte
|
|
* @return int byte
|
|
*/
|
|
public static function fileUnitToByte(string $unit): int
|
|
{
|
|
preg_match('/([0-9.]+)(\w+)/', $unit, $matches);
|
|
if (!$matches) {
|
|
return 0;
|
|
}
|
|
$typeDict = ['b' => 0, 'k' => 1, 'kb' => 1, 'm' => 2, 'mb' => 2, 'gb' => 3, 'g' => 3];
|
|
return (int)($matches[1] * pow(1024, $typeDict[strtolower($matches[2])] ?? 0));
|
|
}
|
|
} |