Final Class Yiisoft\Files\FileHelper
| Inheritance | Yiisoft\Files\FileHelper |
|---|
Provides useful methods to manage files and directories.
Public Methods
| Method | Description | Defined By |
|---|---|---|
| clearDirectory() | Clears all directory content. | Yiisoft\Files\FileHelper |
| copyDirectory() | Copies a whole directory as another one. | Yiisoft\Files\FileHelper |
| copyFile() | Copies files with some options. | Yiisoft\Files\FileHelper |
| ensureDirectory() | Ensures directory exists and has specific permissions. | Yiisoft\Files\FileHelper |
| findDirectories() | Returns the directories found under the specified directory and subdirectories. | Yiisoft\Files\FileHelper |
| findFiles() | Returns the files found under the specified directory and subdirectories. | Yiisoft\Files\FileHelper |
| isEmptyDirectory() | Tells whether the path is an empty directory. | Yiisoft\Files\FileHelper |
| lastModifiedTime() | Returns the last modification time for the given paths. | Yiisoft\Files\FileHelper |
| normalizePath() | Normalizes a file/directory path. | Yiisoft\Files\FileHelper |
| openFile() | Opens a file or URL. | Yiisoft\Files\FileHelper |
| removeDirectory() | Removes a directory (and all its content) recursively. Does nothing if directory does not exist. | Yiisoft\Files\FileHelper |
| unlink() | Removes a file or symlink in a cross-platform way. | Yiisoft\Files\FileHelper |
Method Details
Clears all directory content.
| public static void clearDirectory ( string $directory, array $options = [] ) | ||
| $directory | string |
The directory to be cleared. |
| $options | array |
Options for directory clear. Valid options are:
|
| throws | RuntimeException |
if unable to open directory. |
|---|---|---|
public static function clearDirectory(string $directory, array $options = []): void
{
$filter = self::getFilter($options);
$handle = self::openDirectory($directory);
if (!empty($options['traverseSymlinks']) || !is_link($directory)) {
while (($file = readdir($handle)) !== false) {
if ($file === '.' || $file === '..') {
continue;
}
$path = $directory . '/' . $file;
if ($filter === null || $filter->match($path)) {
if (is_dir($path)) {
self::clearDirectory($path, $options);
if (is_link($path) || self::isEmptyDirectory($path)) {
self::removeLinkOrEmptyDirectory($path);
}
} else {
self::unlink($path);
}
}
}
closedir($handle);
}
}
Copies a whole directory as another one.
The files and subdirectories will also be copied over.
| public static void copyDirectory ( string $source, string $destination, array $options = [] ) | ||
| $source | string |
The source directory. |
| $destination | string |
The destination directory. |
| $options | array |
Options for directory copy. Valid options are:
|
| throws | RuntimeException |
if unable to open directory |
|---|---|---|
public static function copyDirectory(string $source, string $destination, array $options = []): void
{
$source = self::normalizePath($source);
$destination = self::normalizePath($destination);
self::assertNotSelfDirectory($source, $destination);
if (self::processCallback($options['beforeCopy'] ?? null, $source, $destination) === false) {
return;
}
$filter = self::getFilter($options);
$recursive = !array_key_exists('recursive', $options) || $options['recursive'];
$copyEmptyDirectories = !array_key_exists('copyEmptyDirectories', $options) || $options['copyEmptyDirectories'];
if (!isset($options['dirMode'])) {
$options['dirMode'] = 0755;
}
if ($copyEmptyDirectories && !is_dir($destination)) {
self::ensureDirectory($destination, $options['dirMode']);
}
$handle = self::openDirectory($source);
if (!array_key_exists('basePath', $options)) {
$options['basePath'] = realpath($source);
}
while (($file = readdir($handle)) !== false) {
if ($file === '.' || $file === '..') {
continue;
}
$from = $source . '/' . $file;
$to = $destination . '/' . $file;
if ($filter === null || $filter->match($from)) {
/** @psalm-suppress InvalidArgument $options is compatible with `copyFile()` and `copyDirectory()` */
if (is_file($from)) {
self::copyFile($from, $to, $options);
} elseif ($recursive) {
self::copyDirectory($from, $to, $options);
}
}
}
closedir($handle);
self::processCallback($options['afterCopy'] ?? null, $source, $destination);
}
Copies files with some options.
- dirMode: integer or null, the permission to be set for newly copied directories. Defaults to null. When null - directory will be not created
- fileMode: integer, the permission to be set for newly copied files. Defaults to the current environment setting.
- beforeCopy: callback, a PHP callback that is called before copying file. If the callback
returns false, the copy operation for file will be cancelled. The signature of the
callback should be:
function ($from, $to), where$fromis the file to be copied from, while$tois the copy target. - afterCopy: callback, a PHP callback that is called after file if successfully copied.
The signature of the callback should be:
function ($from, $to), where$fromis the file copied from, while$tois the copy target.
| public static void copyFile ( string $source, string $destination, array $options = [] ) | ||
| $source | string |
The source file |
| $destination | string |
The destination filename |
| $options | array | |
public static function copyFile(string $source, string $destination, array $options = []): void
{
if (!is_file($source)) {
throw new InvalidArgumentException('Argument $source must be an existing file.');
}
if (self::processCallback($options['beforeCopy'] ?? null, $source, $destination) === false) {
return;
}
$dirname = dirname($destination);
$dirMode = $options['dirMode'] ?? 0755;
$fileMode = $options['fileMode'] ?? null;
if (!is_dir($dirname)) {
self::ensureDirectory($dirname, $dirMode);
}
if (!copy($source, $destination)) {
throw new RuntimeException('Failed to copy the file.');
}
if ($fileMode !== null && !chmod($destination, $fileMode)) {
throw new RuntimeException(sprintf('Unable to set mode "%s" for "%s".', $fileMode, $destination));
}
self::processCallback($options['afterCopy'] ?? null, $source, $destination);
}
Ensures directory exists and has specific permissions.
This method is similar to the PHP {@see \Yiisoft\Files\mkdir()} function with some differences:
- It does not fail if directory already exists.
- It uses {@see \Yiisoft\Files\chmod()} to set the permission of the created directory in order to avoid the impact
of the
umasksetting. - It throws exceptions instead of returning false and emitting {@see \Yiisoft\Files\E_WARNING}.
| public static void ensureDirectory ( string $path, integer $mode = 0775 ) | ||
| $path | string |
Path of the directory to be created. |
| $mode | integer |
The permission to be set for the created directory. |
public static function ensureDirectory(string $path, int $mode = 0775): void
{
$path = self::normalizePath($path);
if (!is_dir($path)) {
set_error_handler(static function (int $errorNumber, string $errorString) use ($path): bool {
// Handle race condition.
// See https://github.com/kalessil/phpinspectionsea/blob/master/docs/probable-bugs.md#mkdir-race-condition
if (!is_dir($path)) {
throw new RuntimeException(
sprintf('Failed to create directory "%s". ', $path) . $errorString,
$errorNumber
);
}
return true;
});
try {
mkdir($path, $mode, true);
} finally {
restore_error_handler();
}
}
if (!chmod($path, $mode)) {
throw new RuntimeException(sprintf('Unable to set mode "%s" for "%s".', $mode, $path));
}
}
Returns the directories found under the specified directory and subdirectories.
| public static string[] findDirectories ( string $directory, array $options = [] ) | ||
| $directory | string |
The directory under which the files will be looked for. |
| $options | array |
Options for directory searching. Valid options are:
|
| return | string[] |
Directories found under the directory specified, in no particular order. Ordering depends on the file system used. |
|---|---|---|
| throws | InvalidArgumentException |
If the directory is invalid. |
public static function findDirectories(string $directory, array $options = []): array
{
$filter = self::getFilter($options);
$recursive = !array_key_exists('recursive', $options) || $options['recursive'];
$directory = self::normalizePath($directory);
$result = [];
$handle = self::openDirectory($directory);
while (false !== $file = readdir($handle)) {
if ($file === '.' || $file === '..') {
continue;
}
$path = $directory . '/' . $file;
if (is_file($path)) {
continue;
}
if ($filter === null || $filter->match($path)) {
$result[] = $path;
}
if ($recursive) {
$result = array_merge($result, self::findDirectories($path, $options));
}
}
closedir($handle);
return $result;
}
Returns the files found under the specified directory and subdirectories.
| public static string[] findFiles ( string $directory, array $options = [] ) | ||
| $directory | string |
The directory under which the files will be looked for. |
| $options | array |
Options for file searching. Valid options are:
|
| return | string[] |
Files found under the directory specified, in no particular order. Ordering depends on the files system used. |
|---|---|---|
| throws | InvalidArgumentException |
If the directory is invalid. |
public static function findFiles(string $directory, array $options = []): array
{
$filter = self::getFilter($options);
$recursive = !array_key_exists('recursive', $options) || $options['recursive'];
$directory = self::normalizePath($directory);
$result = [];
$handle = self::openDirectory($directory);
while (false !== $file = readdir($handle)) {
if ($file === '.' || $file === '..') {
continue;
}
$path = $directory . '/' . $file;
if (is_file($path)) {
if ($filter === null || $filter->match($path)) {
$result[] = $path;
}
continue;
}
if ($recursive) {
$result = array_merge($result, self::findFiles($path, $options));
}
}
closedir($handle);
return $result;
}
Tells whether the path is an empty directory.
| public static boolean isEmptyDirectory ( string $path ) | ||
| $path | string |
Path to check for being an empty directory. |
public static function isEmptyDirectory(string $path): bool
{
if (!is_dir($path)) {
return false;
}
return !(new FilesystemIterator($path))->valid();
}
Returns the last modification time for the given paths.
If the path is a directory, any nested files/directories will be checked as well.
| public static integer|null lastModifiedTime ( RecursiveDirectoryIterator[]|string[] $paths ) | ||
| $paths | RecursiveDirectoryIterator[]|string[] |
The directories to be checked. |
| return | integer|null |
Unix timestamp representing the last modification time. |
|---|---|---|
| throws | LogicException |
If path is not set. |
public static function lastModifiedTime(string|RecursiveDirectoryIterator ...$paths): ?int
{
if (empty($paths)) {
throw new LogicException('Path is required.');
}
$time = null;
foreach ($paths as $path) {
if (is_string($path)) {
$timestamp = self::modifiedTime($path);
if ($timestamp > $time) {
$time = $timestamp;
}
if (is_file($path)) {
continue;
}
$path = new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS);
}
/** @var iterable<string, string> $iterator */
$iterator = new RecursiveIteratorIterator(
$path,
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $path => $_info) {
$timestamp = self::modifiedTime($path);
if ($timestamp > $time) {
$time = $timestamp;
}
}
}
return $time;
}
Normalizes a file/directory path.
The normalization does the following work:
- Convert all directory separators into
/(e.g. "\a/b\c" becomes "/a/b/c") - Remove trailing directory separators (e.g. "/a/b/c/" becomes "/a/b/c")
- Turn multiple consecutive slashes into a single one (e.g. "/a///b/c" becomes "/a/b/c")
- Remove ".." and "." based on their meanings (e.g. "/a/./b/../c" becomes "/a/c")
| public static string normalizePath ( string $path ) | ||
| $path | string |
The file/directory path to be normalized. |
| return | string |
The normalized file/directory path. |
|---|---|---|
public static function normalizePath(string $path): string
{
$isWindowsShare = str_starts_with($path, '\\\\');
if ($isWindowsShare) {
$path = substr($path, 2);
}
$path = rtrim(strtr($path, '\\', '/'), '/');
if (!str_contains('/' . $path, '/.') && !str_contains($path, '//')) {
return $isWindowsShare ? "\\\\$path" : $path;
}
$parts = [];
foreach (explode('/', $path) as $part) {
if ($part === '..' && !empty($parts) && end($parts) !== '..') {
array_pop($parts);
} elseif ($part !== '.' && ($part !== '' || empty($parts))) {
$parts[] = $part;
}
}
$path = implode('/', $parts);
if ($isWindowsShare) {
$path = '\\\\' . $path;
}
return $path === '' ? '.' : $path;
}
Opens a file or URL.
This method is similar to the PHP {@see \Yiisoft\Files\fopen()} function, except that it suppresses the {@see \Yiisoft\Files\E_WARNING} level error and throws the {@see \RuntimeException} exception if it can't open the file.
| public static resource openFile ( string $filename, string $mode, boolean $useIncludePath = false, resource|null $context = null ) | ||
| $filename | string |
The file or URL. |
| $mode | string |
The type of access. |
| $useIncludePath | boolean |
Whether to search for a file in the include path. |
| $context | resource|null |
The stream context or |
| return | resource |
The file pointer resource. |
|---|---|---|
| throws | RuntimeException |
If the file could not be opened. |
public static function openFile(string $filename, string $mode, bool $useIncludePath = false, $context = null)
{
/** @psalm-suppress InvalidArgument, MixedArgumentTypeCoercion */
set_error_handler(static function (int $errorNumber, string $errorString) use ($filename): bool {
throw new RuntimeException(
sprintf('Failed to open a file "%s". ', $filename) . $errorString,
$errorNumber
);
});
try {
$filePointer = fopen($filename, $mode, $useIncludePath, $context);
} finally {
restore_error_handler();
}
if ($filePointer === false) {
throw new RuntimeException(sprintf('Failed to open a file "%s". ', $filename));
}
return $filePointer;
}
Removes a directory (and all its content) recursively. Does nothing if directory does not exist.
| public static void removeDirectory ( string $directory, array $options = [] ) | ||
| $directory | string |
The directory to be deleted recursively. |
| $options | array |
Options for directory remove. Valid options are:
|
| throws | RuntimeException |
When unable to remove directory. |
|---|---|---|
public static function removeDirectory(string $directory, array $options = []): void
{
if (!file_exists($directory)) {
return;
}
self::clearDirectory(
$directory,
['traverseSymlinks' => $options['traverseSymlinks'] ?? false]
);
self::removeLinkOrEmptyDirectory($directory);
}
Removes a file or symlink in a cross-platform way.
| public static void unlink ( string $path ) | ||
| $path | string |
Path to unlink. |
public static function unlink(string $path): void
{
/** @psalm-suppress InvalidArgument, MixedArgumentTypeCoercion */
set_error_handler(static function (int $errorNumber, string $errorString) use ($path): bool {
throw new RuntimeException(
sprintf('Failed to unlink "%s". ', $path) . $errorString,
$errorNumber
);
});
try {
$isWindows = DIRECTORY_SEPARATOR === '\\';
if ($isWindows) {
if (is_link($path)) {
try {
unlink($path);
} catch (RuntimeException) {
rmdir($path);
}
} else {
if (file_exists($path) && !is_writable($path)) {
chmod($path, 0777);
}
unlink($path);
}
} else {
unlink($path);
}
} finally {
restore_error_handler();
}
}
Signup or Login in order to comment.