AzuraCast/src/Utilities/File.php

196 lines
5.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Utilities;
use FilesystemIterator;
use InvalidArgumentException;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SplFileInfo;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Path;
use function stripos;
use function strlen;
/**
* Static class that facilitates the uploading, reading and deletion of files in a controlled directory.
*/
final class File
{
/**
* @param string $path
*/
public static function sanitizePathPrefix(string $path): string
{
$pattern = '/:\/\//';
$path = preg_replace($pattern, '', $path) ?? $path;
if (preg_match($pattern, $path)) {
return self::sanitizePathPrefix($path);
}
return $path;
}
/**
* Sanitize a user-specified filename for storage.
* Credit to: http://stackoverflow.com/a/19018736
*
* @param string $str
*/
public static function sanitizeFileName(string $str): string
{
return Strings::getProgrammaticString($str);
}
public static function generateTempPath(string $pattern = ''): string
{
$prefix = Path::getFilenameWithoutExtension($pattern) ?: 'temp';
$extension = Path::getExtension($pattern) ?: 'log';
return (new Filesystem())->tempnam(
sys_get_temp_dir(),
$prefix . '_',
'.' . $extension
);
}
public static function validateTempPath(string $path): string
{
$tempDir = sys_get_temp_dir();
$fullPath = Path::makeAbsolute($path, $tempDir);
if (!Path::isBasePath($tempDir, $fullPath)) {
throw new InvalidArgumentException(
sprintf('Path "%s" is not within "%s".', $fullPath, $tempDir)
);
}
return $fullPath;
}
/**
* Given a "from" and "to" directory, update the given path to be relative to the "to" directory.
* If $preserveFolderStructure is set to "true" (default), this will preserve the folder structure
* that's underneath "fromDir". If it's set to "false", it will use the basename of the files and
* drop them directly into the "toDir".
*/
public static function renameDirectoryInPath(
string $path,
string $fromDir,
string $toDir,
bool $preserveFolderStructure = true
): string {
if (!$preserveFolderStructure) {
$basename = basename($path);
return ('' === $toDir)
? $basename
: $toDir . '/' . $basename;
}
if ('' === $fromDir && '' !== $toDir) {
// Just prepend the new directory.
return $toDir . '/' . $path;
}
if (0 === stripos($path, $fromDir)) {
$newBasePath = ltrim(substr($path, strlen($fromDir)), '/');
if ('' !== $toDir) {
return $toDir . '/' . $newBasePath;
}
return $newBasePath;
}
return $path;
}
/**
* Clear all contents of a directory, without removing the directory itself.
*/
public static function clearDirectoryContents(string $targetDir): void
{
$targetDir = rtrim($targetDir, '/\\');
$flags = FilesystemIterator::SKIP_DOTS;
$deleteIterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($targetDir, $flags),
RecursiveIteratorIterator::CHILD_FIRST
);
$fsUtils = new Filesystem();
/** @var SplFileInfo $file */
foreach ($deleteIterator as $file) {
$fsUtils->remove((string)$file);
}
}
public static function moveDirectoryContents(
string $originDir,
string $targetDir,
bool $clearDirectoryFirst = false
): void {
if ($clearDirectoryFirst) {
self::clearDirectoryContents($targetDir);
}
$targetDir = rtrim($targetDir, '/\\');
$originDir = rtrim($originDir, '/\\');
$originDirLen = strlen($originDir);
$flags = FilesystemIterator::SKIP_DOTS;
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($originDir, $flags),
RecursiveIteratorIterator::SELF_FIRST
);
$fsUtils = new Filesystem();
$fsUtils->mkdir($targetDir);
/** @var SplFileInfo $file */
foreach ($iterator as $file) {
if (
$file->getPathname() === $targetDir
|| $file->getRealPath() === $targetDir
) {
continue;
}
$target = $targetDir . substr($file->getPathname(), $originDirLen);
if (is_link((string)$file)) {
$fsUtils->symlink($file->getLinkTarget(), $target);
} elseif (is_dir((string)$file)) {
$fsUtils->mkdir($target);
} elseif (is_file((string)$file)) {
$fsUtils->rename((string)$file, $target, true);
}
}
}
public static function getFirstExistingFile(array $files): string
{
foreach ($files as $file) {
if (file_exists($file)) {
return $file;
}
}
throw new InvalidArgumentException('No existing files found.');
}
public static function getFirstExistingDirectory(array $dirs): string
{
foreach ($dirs as $dir) {
if (is_dir($dir)) {
return $dir;
}
}
throw new InvalidArgumentException('No existing directories found.');
}
}