buildadmin/extend/ba/Filesystem.php
2023-10-17 05:38:27 +08:00

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));
}
}