isDir() || $ext === '' || strtolower($info->getExtension()) === strtolower($ext); }, $short); } /** * 扫描目录并返回文件路径数组 * @param string $path 扫描目录 * @param ?integer $depth 扫描深度 * @param ?Closure $filter 文件过滤,返回 false 表示放弃 * @param boolean $short 是否返回相对于给定路径的短路径 * @return array 包含文件路径的数组 */ public static function find(string $path, ?int $depth = null, ?Closure $filter = null, bool $short = true): array { [$info, $files] = [new SplFileInfo($path), []]; if ($info->isDir() || $info->isFile()) { if ($info->isFile() && ($filter === null || $filter($info) !== false)) { $files[] = $short ? $info->getBasename() : $info->getPathname(); } if ($info->isDir()) foreach (static::findFilesYield($info->getPathname(), $depth, $filter) as $file) { $files[] = $short ? substr($file->getPathname(), strlen($info->getPathname()) + 1) : $file->getPathname(); } } 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 对象。 * @param string $path 目录路径。 * @param ?integer $depth 扫描深度 * @param \Closure|null $filter 文件过滤,返回 false 表示放弃 * @param boolean $appendPath 是否包含目录本身在结果中。 * @param integer $currDepth 当前深度,临时变量递归时使用。 * @return \Generator 返回 SplFileInfo 对象的生成器。 */ public 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_null($depth) || $currDepth <= $depth)) { foreach (new FilesystemIterator($path, FilesystemIterator::SKIP_DOTS) as $item) { if ($filter !== null && $filter($item) === false) continue; if ($item->isDir() && !$item->isLink()) { $appendPath && yield $item; yield from static::findFilesYield($item->getPathname(), $depth, $filter, $appendPath, $currDepth + 1); } else { yield $item; } } } } }