"Директория не существует или нет доступа на запись!", 'error_deleting_file_or_link' => 'Ошибка удаления файла или ссылки: %s!', 'error_deleting_directory' => 'Ошибка удаления каталога: %s. Код возврата: %d!', 'unhandled_error' => 'Ошибка удаления директории %s: %s!' ]; /** * @var array Массив сообщений об ошибках при получении размера файла. */ public const array FILE_SIZE_ERROR_MESSAGES = [ 'file_not_exist' => 'Файл не существует!', 'not_a_file' => 'Указанный путь не является файлом!', 'cannot_get_size' => 'Не удалось получить размер файла!' ]; /** * @var array Массив локализации размеров файлов. */ public const array FILE_SIZE_UNITS = ['байт', 'КБ', 'МБ', 'ГБ', 'ТБ']; /** * Получает список файлов в директории и поддиректориях, соответствующей шаблону $pattern. * * @param string $dir Родительская директория * @param string $pattern Шаблон имени файла * * @return false|array Список файлов или false в случае ошибки */ public static function FindFiles (string $dir, string $pattern = '*.php'): false|array { // Получаем список файлов и каталогов в текущей директории $files = glob("$dir/$pattern"); // Если произошла ошибка if ($files === false) // - то возвращаем false return false; // Перебираем поддиректории foreach (glob("$dir/*", GLOB_ONLYDIR | GLOB_NOSORT) as $subDir) { // - если ошибка if ($subDir === false) // - то пропускаем continue; // - рекурсивный вызов для каждой поддиректории $files = array_merge($files, self::FindFiles($subDir, $pattern)); } // Возвращаем список файлов return $files; } /** * Получает имя файла без пути к нему и расширения. * * @param string $fileName Полное имя файла с путем к нему. * * @return string Имя файла без пути к нему и расширения. */ public static function ExtractFileNameWithoutExtension (string $fileName): string { // Имя файла без пути к нему $fileNameOnly = self::ExtractFileName($fileName); // Расширение файла $fileExtension = self::ExtractFileExtension($fileName); // Возвращаем имя файла без пути к нему и расширения. return substr($fileNameOnly, 0, -strlen($fileExtension) - 1); } /** * Получает имя файла без пути к нему, но с расширением. * * @param string $fileName Полное имя файла с путем к нему. * * @return string Имя файла без пути к нему, но с расширением. */ public static function ExtractFileName (string $fileName): string { return basename($fileName); } /** * Получает расширение файла. * * @param string $fileName Имя файла с путем к нему. * * @return string Расширение файла. */ public static function ExtractFileExtension (string $fileName): string { return pathinfo($fileName, PATHINFO_EXTENSION); } /** * Получает относительный путь к файлу, относительно заданной папки * * @param string $fullPath Полный путь к файлу * @param string $basePath Вырезаемый путь (с начала) * * @return false|string Относительный путь к файлу */ public static function GetRelativePath (string $fullPath, string $basePath): false|string { return stripos($fullPath, $basePath) !== false ? str_replace($basePath, "", $fullPath) : false; } /** * Удаляет директорию вместе со всеми файлами и поддиректориями. * * @param string $directory Полный путь к директории. * @param array $errorMessages Сообщения об ошибках удаления (по умолчанию, см. * {@link REMOVE_DIRECTORY_ERROR_MESSAGES}). * * @return ActionState Результат удаления. */ public static function RemoveDir (string $directory, array $errorMessages = self::REMOVE_DIRECTORY_ERROR_MESSAGES): ActionState { // Создаю результат $result = new ActionState(false); try { // Проверяем наличие директории и доступ на запись if (!self::DirectoryExists(directory: $directory, checkWriteAccess: true)) { // - если нет, то добавляем ошибку $result->AddError($errorMessages['directory_not_exist']); // - и возвращаем результат return $result; } // Создаем рекурсивный итерационный объект для перебора всего дерева каталогов $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($directory), RecursiveIteratorIterator::CHILD_FIRST ); // Проходим по каждому элементу (каталогам и файлам) foreach ($iterator as $item) { // - получаем путь к файлу $realPath = $item->getRealPath(); // - если это файл или ссылка if ($item->isFile() || $item->isLink()) // -- то удаляем его if (!@unlink($realPath)) { // --- если не удалось удалить, то добавляем ошибку $result->AddError(sprintf($errorMessages['error_deleting_file_or_link'], $realPath)); // --- и возвращаем результат return $result; } } // Определение текущей операционной системы $os = strtolower(PHP_OS_FAMILY); // Экранируем аргумент для предотвращения инъекций $escapedDirectory = escapeshellarg($directory); // Дальнейшие действия зависят от операционной системы switch ($os) { // - для Windows case 'windows': // -- выполняем команду Windows exec("rd /s /q $escapedDirectory", $output, $returnCode); break; // - для Linux/macOS default: // -- выполняем команду Linux/macOS exec("rm -rf $escapedDirectory", $output, $returnCode); break; } // Проверяем код возврата if ($returnCode !== 0) { // - если не удалось удалить, то добавляем ошибку $result->AddError(sprintf($errorMessages['error_deleting_directory'], $directory, $returnCode)); // --- и возвращаем результат return $result; } // Если все прошло успешно (а если мы сюда попали, то все должно быть хорошо), то добавляем результат true $result->Value = true; // - и возвращаем его return $result; } catch (Exception $exception) { // Если произошла ошибка, то добавляем ошибку $result->AddError(sprintf($errorMessages['unhandled_error'], $directory, $exception->getMessage())); // - задаем результат false $result->Value = false; // - и возвращаем его return $result; } } /** * Проверяет, существует ли директория. * * @param string $directory Путь к директории. * @param bool $checkReadAccess Проверять ли доступ на чтение директории (по умолчанию true). * @param bool $checkWriteAccess Проверять ли доступ на запись директории (по умолчанию false). * * @return bool Результат проверки. */ public static function DirectoryExists (string $directory, bool $checkReadAccess = true, bool $checkWriteAccess = false): bool { // Очищаем кэш clearstatcache(); // Проверяем, существует ли директория if (!file_exists($directory)) // - если нет, то возвращаем false return false; // Проверяем, является ли директория директорией, а не файлом if (!is_dir($directory)) // - если нет, то возвращаем false return false; // Проверяем, есть ли доступ на чтение директории if ($checkReadAccess && !is_readable($directory)) // - если нет, то возвращаем false return false; // Проверяем, есть ли доступ на запись директории if ($checkWriteAccess && !is_writable($directory)) // - если нет, то возвращаем false return false; // Если все проверки пройдены успешно, то возвращаем true return true; } /** * Получает размер файла в байтах. * * @param string $filename Имя файла. * @param array $errorLocalization Массив сообщений об ошибках при получении размера файла (по умолчанию, см. * {@link FILE_SIZE_ERROR_MESSAGES}). * * @return ActionState Результат с размером файла в байтах. */ public static function FileSize (string $filename, array $errorLocalization = self::FILE_SIZE_ERROR_MESSAGES): ActionState { // Очищаем кэш clearstatcache(); // Создаём результат $result = new ActionState(-1); // Проверяем, существует ли файл if (!file_exists($filename)) { // - если нет, то добавляем ошибку $result->AddError($errorLocalization['file_not_exist']); // - и возвращаем результат return $result; } // Проверяем, является ли $filename файлом if (!is_file($filename)) { // - если нет, то добавляем ошибку $result->AddError($errorLocalization['not_a_file']); // - и возвращаем результат return $result; } // Получаем размер файла $size = filesize($filename); // Проверяем, получилось ли получить размер файла if ($size === false) { // - если нет, то добавляем ошибку $result->AddError($errorLocalization['cannot_get_size']); // - и возвращаем результат return $result; } // Устанавливаем значение результата $result->Value = $size; // Возвращаем результат return $result; } /** * Преобразует размер файла в байтах в красивое строковое представление. * * @param int $fileSize Размер файла в байтах. * @param array $fileSizeUnits Локализованные единицы измерения размера файла (по умолчанию, см. * {@link FILE_SIZE_UNITS}). * @param string $decimalSeparator Разделитель десятичной части (по умолчанию, запятая). * * @return string Размер файла в красивом строковом представлении. Например, если размер файла составляет 1500 * байт, вывод будет «1.46 КБ». */ public static function FileSizeToString (int $fileSize, array $fileSizeUnits = self::FILE_SIZE_UNITS, string $decimalSeparator = ','): string { /** * Вычисление степени для преобразования: берём минимум из 4 и результата округления до ближайшего целого числа * в меньшую сторону логарифма размера файла в байтах по основанию 1024 (это показывает, сколько раз нужно * разделить размер файла на 1024, чтобы получить значение в более крупных единицах измерения). Ограничение в 4 * необходимо для того, чтобы соответствовать единице измерения ТБ (терабайт). */ $power = min(4, floor(log($fileSize, 1024))); /** * Преобразование размера файла: размер файла делим на 1024 в степени, равной степени $power, * затем округляем полученное до 2 цифр после запятой. */ $size = number_format(round($fileSize / pow(1024, $power), 2), 2, $decimalSeparator); // Возвращаем преобразованное значение вместе с единицей измерения return "$size $fileSizeUnits[$power]"; } }