0 follower

Final Class Yiisoft\Files\FileHelper

InheritanceYiisoft\Files\FileHelper

Provides useful methods to manage files and directories.

Public Methods

Hide inherited 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

Hide inherited methods

clearDirectory() public static method

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:

  • traverseSymlinks: boolean, whether symlinks to the directories should be traversed too. Defaults to false, meaning the content of the symlinked directory would not be deleted. Only symlink would be removed in that default case.
  • filter: a filter to apply while deleting files. It should be an instance of {@see \Yiisoft\Files\PathMatcher\PathMatcherInterface}.
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);
    }
}

            
copyDirectory() public static method

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:

  • dirMode: integer, the permission to be set for newly copied directories. Defaults to 0775.
  • fileMode: integer, the permission to be set for newly copied files. Defaults to the current environment setting.
  • filter: a filter to apply while copying files. It should be an instance of {@see \Yiisoft\Files\PathMatcher\PathMatcherInterface}.
  • recursive: boolean, whether the files under the subdirectories should also be copied. Defaults to true.
  • beforeCopy: callback, a PHP callback that is called before copying each subdirectory or file. If the callback returns false, the copy operation for the subdirectory or file will be cancelled. The signature of the callback should be: function ($from, $to), where $from is the subdirectory or file to be copied from, while $to is the copy target.
  • afterCopy: callback, a PHP callback that is called after each subdirectory or file is successfully copied. The signature of the callback should be: function ($from, $to), where $from is the subdirectory or file copied from, while $to is the copy target.
  • copyEmptyDirectories: boolean, whether to copy empty directories. Set this to false to avoid creating directories that do not contain files. This affects directories that do not contain files initially as well as directories that do not contain files at the target destination because files have been filtered via only or except. Defaults to true.
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);
}

            
copyFile() public static method

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 $from is the file to be copied from, while $to is 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 $from is the file copied from, while $to is 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);
}

            
ensureDirectory() public static method

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 umask setting.
  • 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));
    }
}

            
findDirectories() public static method

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:

  • filter: a filter to apply while looked directories. It should be an instance of {@see \Yiisoft\Files\PathMatcher\PathMatcherInterface}.
  • recursive: boolean, whether the subdirectories should also be looked for. Defaults to true.
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;
}

            
findFiles() public static method

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:

  • filter: a filter to apply while looked files. It should be an instance of {@see \Yiisoft\Files\PathMatcher\PathMatcherInterface}.
  • recursive: boolean, whether the files under the subdirectories should also be looked for. Defaults to true.
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;
}

            
isEmptyDirectory() public static method

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();
}

            
lastModifiedTime() public static method

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;
}

            
normalizePath() public static method

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;
}

            
openFile() public static method

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 null.

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;
}

            
removeDirectory() public static method

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:

  • traverseSymlinks: boolean, whether symlinks to the directories should be traversed too. Defaults to false, meaning the content of the symlinked directory would not be deleted. Only symlink would be removed in that default case.
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);
}

            
unlink() public static method

Removes a file or symlink in a cross-platform way.

public static void unlink ( string $path )
$path string

Path to unlink.