fix: 重构 ToolsExtend 文件系统操作工具

This commit is contained in:
邹景立 2024-10-17 09:50:08 +08:00
parent d8b984b2dc
commit aef4e36c19
6 changed files with 103 additions and 78 deletions

View File

@ -108,7 +108,7 @@ class Library extends Service
{ {
// 动态加载全局配置 // 动态加载全局配置
[$dir, $ext] = [$this->app->getBasePath(), $this->app->getConfigExt()]; [$dir, $ext] = [$this->app->getBasePath(), $this->app->getConfigExt()];
ToolsExtend::findFilesArray($dir, 2, function (SplFileInfo $info) use ($ext) { ToolsExtend::find($dir, 2, function (SplFileInfo $info) use ($ext) {
$info->isFile() && $info->getBasename() === "sys{$ext}" && include_once $info->getPathname(); $info->isFile() && $info->getBasename() === "sys{$ext}" && include_once $info->getPathname();
}); });
if (is_file($file = "{$dir}common{$ext}")) include_once $file; if (is_file($file = "{$dir}common{$ext}")) include_once $file;

View File

@ -338,13 +338,13 @@ CODE;
private static function nextFile(string $class): string private static function nextFile(string $class): string
{ {
[$snake, $items] = [Str::snake($class), [20010000000000]]; [$snake, $items] = [Str::snake($class), [20010000000000]];
ToolsExtend::findFilesArray(syspath('database/migrations'), 1, function (SplFileInfo $info) use ($snake, &$items) { ToolsExtend::find(syspath('database/migrations'), 1, function (SplFileInfo $info) use ($snake, &$items) {
if ($info->isFile()) { if ($info->isFile()) {
$bname = pathinfo($info->getBasename(), PATHINFO_FILENAME); $bname = pathinfo($info->getBasename(), PATHINFO_FILENAME);
$items[] = $version = intval(substr($bname, 0, 14)); $items[] = $version = intval(substr($bname, 0, 14));
if ($snake === substr($bname, 15) && unlink($info->getRealPath())) { if ($snake === substr($bname, 15) && unlink($info->getRealPath())) {
if (is_dir($dataPath = $info->getPath() . DIRECTORY_SEPARATOR . $version)) { if (is_dir($dataPath = $info->getPath() . DIRECTORY_SEPARATOR . $version)) {
ToolsExtend::removeEmptyDirectory($dataPath); ToolsExtend::remove($dataPath);
} }
} }
} }

View File

@ -22,6 +22,7 @@ use Closure;
use FilesystemIterator; use FilesystemIterator;
use Generator; use Generator;
use SplFileInfo; use SplFileInfo;
use think\admin\Exception;
/** /**
* 通用工具扩展 * 通用工具扩展
@ -30,82 +31,31 @@ use SplFileInfo;
*/ */
class ToolsExtend class ToolsExtend
{ {
/**
* 深度拷贝到指定目录
* @param string $frdir 来源目录
* @param string $todir 目标目录
* @param array $files 指定文件
* @param boolean $force 强制替换
* @param boolean $remove 删除文件
* @return boolean
*/
public static function copyfile(string $frdir, string $todir, array $files = [], bool $force = true, bool $remove = true): bool
{
$frdir = rtrim($frdir, '\\/') . DIRECTORY_SEPARATOR;
$todir = rtrim($todir, '\\/') . DIRECTORY_SEPARATOR;
// 扫描目录文件
if (empty($files) && is_dir($frdir)) {
$files = static::findFilesArray($frdir, null, function (SplFileInfo $info) {
return $info->getBasename()[0] !== '.';
});
}
// 复制文件列表
foreach ($files as $target) {
[$fromPath, $destPath] = [$frdir . $target, $todir . $target];
if ($force || !is_file($destPath)) {
is_dir($dir = dirname($destPath)) || mkdir($dir, 0777, true);
copy($fromPath, $destPath);
}
// 删除来源文件
$remove && unlink($fromPath);
}
// 删除来源目录
$remove && static::removeEmptyDirectory($frdir);
return true;
}
/** /**
* 移除清空目录 * 扫描目录下的文件列表
* @param string $path
* @return boolean
*/
public static function removeEmptyDirectory(string $path): bool
{
$dirs = [$path];
iterator_to_array(self::findFilesYield($path, null, function (SplFileInfo $file) use (&$dirs) {
$file->isDir() ? $dirs[] = $file->getPathname() : unlink($file->getPathname());
}));
usort($dirs, function ($a, $b) {
return strlen($b) <=> strlen($a);
});
foreach ($dirs as $dir) rmdir($dir);
return !file_exists($path);
}
/**
* 扫描目录列表
* @param string $path 扫描目录 * @param string $path 扫描目录
* @param ?integer $depth 扫描深度 * @param ?integer $depth 扫描深度
* @param string $filterExt 筛选后缀 * @param string $ext 筛选后缀
* @param boolean $shortPath 相对路径 * @param boolean $short 相对路径
* @return array * @return array
*/ */
public static function scanDirectory(string $path, ?int $depth = null, string $filterExt = '', bool $shortPath = true): array public static function scan(string $path, ?int $depth = null, string $ext = '', bool $short = true): array
{ {
return static::findFilesArray($path, $depth, function (SplFileInfo $info) use ($filterExt, &$files) { return static::find($path, $depth, function (SplFileInfo $info) use ($ext) {
return $info->isDir() || $filterExt === '' || strtolower($info->getExtension()) === strtolower($filterExt); return $info->isDir() || $ext === '' || strtolower($info->getExtension()) === strtolower($ext);
}, $shortPath); }, $short);
} }
/** /**
* 扫描指定目录并返回文件路径数组 * 扫描目录并返回文件路径数组
* @param string $path 扫描目录 * @param string $path 扫描目录
* @param ?integer $depth 扫描深度 * @param ?integer $depth 扫描深度
* @param ?Closure $filter 文件过滤,返回 false 表示放弃 * @param ?Closure $filter 文件过滤,返回 false 表示放弃
* @param boolean $short 是否返回相对于给定路径的短路径 * @param boolean $short 是否返回相对于给定路径的短路径
* @return array 包含文件路径的数组 * @return array 包含文件路径的数组
*/ */
public static function findFilesArray(string $path, ?int $depth = null, ?Closure $filter = null, bool $short = true): array public static function find(string $path, ?int $depth = null, ?Closure $filter = null, bool $short = true): array
{ {
[$info, $files] = [new SplFileInfo($path), []]; [$info, $files] = [new SplFileInfo($path), []];
if ($info->isDir() || $info->isFile()) { if ($info->isDir() || $info->isFile()) {
@ -119,6 +69,82 @@ class ToolsExtend
return $files; return $files;
} }
/**
* 深度拷贝到指定目录
* @param string $frdir 来源目录
* @param string $todir 目标目录
* @param array $files 指定文件
* @param boolean $force 强制替换
* @param boolean $remove 删除文件
* @return boolean
*/
public static function copy(string $frdir, string $todir, array $files = [], bool $force = true, bool $remove = true): bool
{
$frdir = rtrim($frdir, '\\/') . DIRECTORY_SEPARATOR;
$todir = rtrim($todir, '\\/') . DIRECTORY_SEPARATOR;
// 扫描目录文件
if (empty($files) && is_dir($frdir)) {
$files = static::find($frdir, null, function (SplFileInfo $info) {
return $info->getBasename()[0] !== '.';
});
}
// 复制文件列表
foreach ($files as $target) {
[$fromPath, $destPath] = [$frdir . $target, $todir . $target];
if ($force || !is_file($destPath)) {
is_dir($dir = dirname($destPath)) || mkdir($dir, 0777, true);
copy($fromPath, $destPath);
}
// 删除来源文件
$remove && unlink($fromPath);
}
// 删除来源目录
$remove && static::remove($frdir);
return true;
}
/**
* 移除清空目录
* @param string $path
* @return boolean
*/
public static function remove(string $path): bool
{
if (!file_exists($path)) return true;
if (is_file($path)) return unlink($path);
$dirs = [$path];
iterator_to_array(self::findFilesYield($path, null, function (SplFileInfo $file) use (&$dirs) {
$file->isDir() ? $dirs[] = $file->getPathname() : unlink($file->getPathname());
}));
usort($dirs, function ($a, $b) {
return strlen($b) <=> strlen($a);
});
foreach ($dirs as $dir) file_exists($dir) && is_dir($dir) && rmdir($dir);
return !file_exists($path);
}
/**
* 兼容旧方式调用
* @param string $method
* @param array $arguments
* @return array|bool
* @throws \think\admin\Exception
*/
public static function __callStatic(string $method, array $arguments)
{
$methods = [
'copyfile' => 'copy',
'scandirectory' => 'scan',
'findfilesarray' => 'find',
'removeemptydirectory' => 'remove',
];
if ($real = $methods[strtolower($method)] ?? null) {
return self::{$real}(...$arguments);
} else {
throw new Exception("method not exists: ToolsExtend::{$method}()");
}
}
/** /**
* 递归扫描指定目录,返回文件或目录的 SplFileInfo 对象。 * 递归扫描指定目录,返回文件或目录的 SplFileInfo 对象。
* @param string $path 目录路径。 * @param string $path 目录路径。
@ -130,15 +156,14 @@ class ToolsExtend
*/ */
private static function findFilesYield(string $path, ?int $depth = null, ?Closure $filter = null, bool $appendPath = false, int $currDepth = 1): Generator private static function findFilesYield(string $path, ?int $depth = null, ?Closure $filter = null, bool $appendPath = false, int $currDepth = 1): Generator
{ {
if (file_exists($path) && is_dir($path) && (!is_numeric($depth) || $currDepth <= $depth)) { if (file_exists($path) && is_dir($path) && (is_null($depth) || $currDepth <= $depth)) {
foreach (new FilesystemIterator($path, FilesystemIterator::SKIP_DOTS) as $item) { foreach (new FilesystemIterator($path, FilesystemIterator::SKIP_DOTS) as $item) {
if ($filter === null || $filter($item) !== false) { if ($filter !== null && $filter($item) === false) continue;
if ($item->isDir() && !$item->isLink()) { if ($item->isDir() && !$item->isLink()) {
$appendPath && yield $item; $appendPath && yield $item;
yield from static::findFilesYield($item->getPathname(), $depth, $filter, $appendPath, $currDepth + 1); yield from static::findFilesYield($item->getPathname(), $depth, $filter, $appendPath, $currDepth + 1);
} else { } else {
yield $item; yield $item;
}
} }
} }
} }

View File

@ -119,7 +119,7 @@ class NodeService extends Service
$ignoreMethods = get_class_methods('\think\admin\Controller'); $ignoreMethods = get_class_methods('\think\admin\Controller');
$ignoreAppNames = Library::$sapp->config->get('app.rbac_ignore', []); $ignoreAppNames = Library::$sapp->config->get('app.rbac_ignore', []);
// 扫描所有代码控制器节点,更新节点缓存 // 扫描所有代码控制器节点,更新节点缓存
foreach (ToolsExtend::scanDirectory(Library::$sapp->getBasePath(), null, 'php') as $name) { foreach (ToolsExtend::scan(Library::$sapp->getBasePath(), null, 'php') as $name) {
if (preg_match("|^(\w+)/controller/(.+)\.php$|i", strtr($name, '\\', '/'), $matches)) { if (preg_match("|^(\w+)/controller/(.+)\.php$|i", strtr($name, '\\', '/'), $matches)) {
[, $appName, $className] = $matches; [, $appName, $className] = $matches;
if (in_array($appName, $ignoreAppNames)) continue; if (in_array($appName, $ignoreAppNames)) continue;
@ -130,7 +130,7 @@ class NodeService extends Service
foreach (Plugin::get() as $appName => $plugin) { foreach (Plugin::get() as $appName => $plugin) {
if (in_array($appName, $ignoreAppNames)) continue; if (in_array($appName, $ignoreAppNames)) continue;
[$appPath, $appSpace] = [$plugin['path'], $plugin['space']]; [$appPath, $appSpace] = [$plugin['path'], $plugin['space']];
foreach (ToolsExtend::scanDirectory($appPath, null, 'php') as $name) { foreach (ToolsExtend::scan($appPath, null, 'php') as $name) {
if (preg_match("|^.*?controller/(.+)\.php$|i", strtr($name, '\\', '/'), $matches)) { if (preg_match("|^.*?controller/(.+)\.php$|i", strtr($name, '\\', '/'), $matches)) {
static::_parseClass($appName, $appSpace, $matches[1], $ignoreMethods, $data); static::_parseClass($appName, $appSpace, $matches[1], $ignoreMethods, $data);
} }
@ -208,7 +208,7 @@ class NodeService extends Service
public static function __callStatic(string $name, array $arguments) public static function __callStatic(string $name, array $arguments)
{ {
if ($name === 'scanDirectory') { if ($name === 'scanDirectory') {
return ToolsExtend::scanDirectory(...$arguments); return ToolsExtend::scan(...$arguments);
} elseif ($name === 'getModules') { } elseif ($name === 'getModules') {
return ModuleService::getModules(...$arguments); return ModuleService::getModules(...$arguments);
} else { } else {

View File

@ -87,15 +87,15 @@ class Publish extends Command
{ {
// 复制系统配置文件 // 复制系统配置文件
$frdir = rtrim($copy, '\\/') . DIRECTORY_SEPARATOR . 'config'; $frdir = rtrim($copy, '\\/') . DIRECTORY_SEPARATOR . 'config';
ToolsExtend::copyfile($frdir, syspath('config'), [], $force, false); ToolsExtend::copy($frdir, syspath('config'), [], $force, false);
// 复制静态资料文件 // 复制静态资料文件
$frdir = rtrim($copy, '\\/') . DIRECTORY_SEPARATOR . 'public'; $frdir = rtrim($copy, '\\/') . DIRECTORY_SEPARATOR . 'public';
ToolsExtend::copyfile($frdir, syspath('public'), [], true, false); ToolsExtend::copy($frdir, syspath('public'), [], true, false);
// 复制数据库脚本 // 复制数据库脚本
$frdir = rtrim($copy, '\\/') . DIRECTORY_SEPARATOR . 'database'; $frdir = rtrim($copy, '\\/') . DIRECTORY_SEPARATOR . 'database';
ToolsExtend::copyfile($frdir, syspath('database/migrations'), [], $force, false); ToolsExtend::copy($frdir, syspath('database/migrations'), [], $force, false);
} }
/** /**

View File

@ -169,7 +169,7 @@ class MultAccess
// 加载应用函数文件 // 加载应用函数文件
if (is_file($file = "{$appPath}common{$ext}")) include_once $file; if (is_file($file = "{$appPath}common{$ext}")) include_once $file;
// 加载应用配置文件 // 加载应用配置文件
ToolsExtend::findFilesArray($appPath . 'config', 1, function (SplFileInfo $info) use ($ext) { ToolsExtend::find($appPath . 'config', 1, function (SplFileInfo $info) use ($ext) {
if ($info->isFile() && strtolower(".{$info->getExtension()}") === $ext) { if ($info->isFile() && strtolower(".{$info->getExtension()}") === $ext) {
$this->app->config->load($info->getPathname(), $info->getBasename($ext)); $this->app->config->load($info->getPathname(), $info->getBasename($ext));
} }