From 61d27540a08e8c773cad0c880bb1d5e908cf80f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Sun, 9 Nov 2025 10:42:04 +0100 Subject: [PATCH 1/5] fix --- src/FS.php | 8 +- src/FileSystem.php | 90 ++------------- src/NativeFileSystem.php | 172 +++++++++++++++++++++++++++ src/SymfonyFileSystem.php | 236 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 419 insertions(+), 87 deletions(-) create mode 100644 src/NativeFileSystem.php create mode 100644 src/SymfonyFileSystem.php diff --git a/src/FS.php b/src/FS.php index 710e1ef..bff5052 100644 --- a/src/FS.php +++ b/src/FS.php @@ -59,18 +59,18 @@ */ class FS { - private static FileSystem $filesystem; + private static NativeFileSystem $filesystem; - public static function setInstance(FileSystem $filesystem): void + public static function setInstance(NativeFileSystem $filesystem): void { self::$filesystem = $filesystem; } - public static function getInstance(): FileSystem + public static function getInstance(): NativeFileSystem { /** @psalm-suppress RedundantPropertyInitializationCheck */ if (!isset(self::$filesystem)) { - self::$filesystem = new FileSystem(); + self::$filesystem = new NativeFileSystem(); } return self::$filesystem; diff --git a/src/FileSystem.php b/src/FileSystem.php index 58b058e..6906329 100644 --- a/src/FileSystem.php +++ b/src/FileSystem.php @@ -49,17 +49,8 @@ use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem; use Symfony\Component\Filesystem\Path; -use Webmozart\Assert\Assert; -use function error_get_last; -use function file_get_contents; -use function random_int; -use function realpath; -use function sprintf; -use function str_replace; -use function sys_get_temp_dir; -use const DIRECTORY_SEPARATOR; -class FileSystem extends SymfonyFilesystem +interface FileSystem extends SymfonyFilesystem { /** * Returns whether a path is relative. @@ -69,20 +60,11 @@ class FileSystem extends SymfonyFilesystem * @return bool returns true if the path is relative or empty, false if * it is absolute */ - public function isRelativePath(string $path): bool - { - return !$this->isAbsolutePath($path); - } + public function isRelativePath(string $path): bool; - public function escapePath(string $path): string - { - return str_replace('/', DIRECTORY_SEPARATOR, $path); - } + public function escapePath(string $path): string; - public function dumpFile(string $filename, $content = ''): void - { - parent::dumpFile($filename, $content); - } + public function dumpFile(string $filename, $content = ''): void; /** * Gets the contents of a file. @@ -93,26 +75,7 @@ public function dumpFile(string $filename, $content = ''): void * * @return string File contents */ - public function getFileContents(string $file): string - { - Assert::file($file); - Assert::readable($file); - - if (false === ($contents = @file_get_contents($file))) { - throw new IOException( - sprintf( - 'Failed to read file "%s": %s.', - $file, - error_get_last()['message'], - ), - 0, - null, - $file, - ); - } - - return $contents; - } + public function getFileContents(string $file): string; /** * Creates a temporary directory. @@ -122,51 +85,12 @@ public function getFileContents(string $file): string * * @return string the path to the created directory */ - public function makeTmpDir(string $namespace, string $className): string - { - $shortClass = false !== ($pos = mb_strrpos($className, '\\')) - ? mb_substr($className, $pos + 1) - : $className; - - $basePath = $this->getNamespacedTmpDir($namespace).'/'.$shortClass; - - $result = false; - $attempts = 0; - - do { - $tmpDir = $this->escapePath($basePath.random_int(10000, 99999)); - - if ($this->exists($tmpDir)) { - ++$attempts; - - continue; - } - - try { - $this->mkdir($tmpDir, 0o777); - - $result = true; - } catch (IOException) { - ++$attempts; - } - } while (false === $result && $attempts <= 10); - - return $tmpDir; - } + public function makeTmpDir(string $namespace, string $className): string; /** * Gets a namespaced temporary directory. * * @param string $namespace the directory path in the system's temporary directory */ - public function getNamespacedTmpDir(string $namespace): string - { - // Usage of realpath() is important if the temporary directory is a - // symlink to another directory (e.g. /var => /private/var on some Macs) - // We want to know the real path to avoid comparison failures with - // code that uses real paths only - $systemTempDir = str_replace('\\', '/', realpath(sys_get_temp_dir())); - - return $systemTempDir.'/'.$namespace; - } + public function getNamespacedTmpDir(string $namespace): string; } diff --git a/src/NativeFileSystem.php b/src/NativeFileSystem.php new file mode 100644 index 0000000..97048c3 --- /dev/null +++ b/src/NativeFileSystem.php @@ -0,0 +1,172 @@ + + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +declare(strict_types=1); + +/* + * This file is part of the box project. + * + * (c) Kevin Herrera + * Théo Fidry + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Fidry\FileSystem; + +use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem; +use Symfony\Component\Filesystem\Path; +use Webmozart\Assert\Assert; +use function error_get_last; +use function file_get_contents; +use function random_int; +use function realpath; +use function sprintf; +use function str_replace; +use function sys_get_temp_dir; +use const DIRECTORY_SEPARATOR; + +class NativeFileSystem extends SymfonyFilesystem +{ + /** + * Returns whether a path is relative. + * + * @param string $path a path string + * + * @return bool returns true if the path is relative or empty, false if + * it is absolute + */ + public function isRelativePath(string $path): bool + { + return !$this->isAbsolutePath($path); + } + + public function escapePath(string $path): string + { + return str_replace('/', DIRECTORY_SEPARATOR, $path); + } + + public function dumpFile(string $filename, $content = ''): void + { + parent::dumpFile($filename, $content); + } + + /** + * Gets the contents of a file. + * + * @param string $file File path + * + * @throws IOException If the file cannot be read + * + * @return string File contents + */ + public function getFileContents(string $file): string + { + Assert::file($file); + Assert::readable($file); + + if (false === ($contents = @file_get_contents($file))) { + throw new IOException( + sprintf( + 'Failed to read file "%s": %s.', + $file, + error_get_last()['message'], + ), + 0, + null, + $file, + ); + } + + return $contents; + } + + /** + * Creates a temporary directory. + * + * @param string $namespace the directory path in the system's temporary directory + * @param string $className the name of the test class + * + * @return string the path to the created directory + */ + public function makeTmpDir(string $namespace, string $className): string + { + $shortClass = false !== ($pos = mb_strrpos($className, '\\')) + ? mb_substr($className, $pos + 1) + : $className; + + $basePath = $this->getNamespacedTmpDir($namespace).'/'.$shortClass; + + $result = false; + $attempts = 0; + + do { + $tmpDir = $this->escapePath($basePath.random_int(10000, 99999)); + + if ($this->exists($tmpDir)) { + ++$attempts; + + continue; + } + + try { + $this->mkdir($tmpDir, 0o777); + + $result = true; + } catch (IOException) { + ++$attempts; + } + } while (false === $result && $attempts <= 10); + + return $tmpDir; + } + + /** + * Gets a namespaced temporary directory. + * + * @param string $namespace the directory path in the system's temporary directory + */ + public function getNamespacedTmpDir(string $namespace): string + { + // Usage of realpath() is important if the temporary directory is a + // symlink to another directory (e.g. /var => /private/var on some Macs) + // We want to know the real path to avoid comparison failures with + // code that uses real paths only + $systemTempDir = str_replace('\\', '/', realpath(sys_get_temp_dir())); + + return $systemTempDir.'/'.$namespace; + } +} diff --git a/src/SymfonyFileSystem.php b/src/SymfonyFileSystem.php new file mode 100644 index 0000000..fea685a --- /dev/null +++ b/src/SymfonyFileSystem.php @@ -0,0 +1,236 @@ + + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +declare(strict_types=1); + +namespace Fidry\FileSystem; + +use Symfony\Component\Filesystem\Exception\FileNotFoundException; +use Symfony\Component\Filesystem\Exception\IOException; +use Traversable; + +/** + * Is an interface that captures all the methods of the Symfony Filesystem. Purely internal, to facilitate + * moving this to Symfony eventually. + * + * @internal + */ +interface SymfonyFileSystem +{ + /** + * Copies a file. + * + * If the target file is older than the origin file, it's always overwritten. + * If the target file is newer, it is overwritten only when the + * $overwriteNewerFiles option is set to true. + * + * @throws FileNotFoundException When originFile doesn't exist + * @throws IOException When copy fails + */ + public function copy(string $originFile, string $targetFile, bool $overwriteNewerFiles = false): void; + + /** + * Creates a directory recursively. + * + * @throws IOException On any directory creation failure + */ + public function mkdir(string|iterable $dirs, int $mode = 0o777): void; + + /** + * Checks the existence of files or directories. + */ + public function exists(string|iterable $files): bool; + + /** + * Sets access and modification time of file. + * + * @param string|iterable $files + * @param int|null $time The touch time as a Unix timestamp, if not supplied the current system time is used + * @param int|null $atime The access time as a Unix timestamp, if not supplied the current system time is used + * + * @throws IOException When touch fails + */ + public function touch(string|iterable $files, ?int $time = null, ?int $atime = null): void; + + /** + * Removes files or directories. + * + * @throws IOException When removal fails + */ + public function remove(string|iterable $files): void; + + /** + * Change mode for an array of files or directories. + * + * @param int $mode The new mode (octal) + * @param int $umask The mode mask (octal) + * @param bool $recursive Whether change the mod recursively or not + * + * @throws IOException When the change fails + */ + public function chmod(string|iterable $files, int $mode, int $umask = 0o000, bool $recursive = false): void; + + /** + * Change the owner of an array of files or directories. + * + * This method always throws on Windows, as the underlying PHP function is not supported. + * + * @see https://www.php.net/chown + * + * @param string|int $user A user name or number + * @param bool $recursive Whether change the owner recursively or not + * + * @throws IOException When the change fails + */ + public function chown(string|iterable $files, string|int $user, bool $recursive = false): void; + + /** + * Change the group of an array of files or directories. + * + * This method always throws on Windows, as the underlying PHP function is not supported. + * + * @see https://www.php.net/chgrp + * + * @param string|iterable $files + * @param string|int $group A group name or number + * @param bool $recursive Whether change the group recursively or not + * + * @throws IOException When the change fails + */ + public function chgrp(string|iterable $files, string|int $group, bool $recursive = false): void; + + /** + * Renames a file or a directory. + * + * @throws IOException When target file or directory already exists + * @throws IOException When origin cannot be renamed + */ + public function rename(string $origin, string $target, bool $overwrite = false): void; + + /** + * Creates a symbolic link or copy a directory. + * + * @throws IOException When symlink fails + */ + public function symlink(string $originDir, string $targetDir, bool $copyOnWindows = false): void; + + /** + * Creates a hard link, or several hard links to a file. + * + * @param string|string[] $targetFiles The target file(s) + * + * @throws FileNotFoundException When original file is missing or not a file + * @throws IOException When link fails, including if link already exists + */ + public function hardlink(string $originFile, string|iterable $targetFiles): void; + + /** + * Resolves links in paths. + * + * With $canonicalize = false (default) + * - if $path does not exist or is not a link, returns null + * - if $path is a link, returns the next direct target of the link without considering the existence of the target + * + * With $canonicalize = true + * - if $path does not exist, returns null + * - if $path exists, returns its absolute fully resolved final version + */ + public function readlink(string $path, bool $canonicalize = false): ?string; + + /** + * Given an existing path, convert it to a path relative to a given starting path. + */ + public function makePathRelative(string $endPath, string $startPath): string; + + /** + * Mirrors a directory to another. + * + * Copies files and directories from the origin directory into the target directory. By default: + * + * - existing files in the target directory will be overwritten, except if they are newer (see the `override` option) + * - files in the target directory that do not exist in the source directory will not be deleted (see the `delete` option) + * + * @param Traversable|null $iterator Iterator that filters which files and directories to copy, if null a recursive iterator is created + * @param array $options An array of boolean options + * Valid options are: + * - $options['override'] If true, target files newer than origin files are overwritten (see copy(), defaults to false) + * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink(), defaults to false) + * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false) + * + * @throws IOException When a file type is unknown + */ + public function mirror(string $originDir, string $targetDir, ?Traversable $iterator = null, array $options = []): void; + + /** + * Returns whether the file path is an absolute path. + */ + public function isAbsolutePath(string $file): bool; + + /** + * Creates a temporary file with support for custom stream wrappers. + * + * @param string $prefix The prefix of the generated temporary filename + * Note: Windows uses only the first three characters of prefix + * @param string $suffix The suffix of the generated temporary filename + * + * @return string The new temporary filename (with path), or throw an exception on failure + */ + public function tempnam(string $dir, string $prefix, string $suffix = ''): string; + + /** + * Atomically dumps content into a file. + * + * @param string|resource $content The data to write into the file + * + * @throws IOException if the file cannot be written to + */ + public function dumpFile(string $filename, $content): void; + + /** + * Appends content to an existing file. + * + * @param string|resource $content The content to append + * @param bool $lock Whether the file should be locked when writing to it + * + * @throws IOException If the file is not writable + */ + public function appendToFile(string $filename, $content, bool $lock = false): void; + + /** + * Returns the content of a file as a string. + * + * @throws IOException If the file cannot be read + */ + public function readFile(string $filename): string; +} From f0113b613ed5cab1d5f9a17be3048a6dc2bf1f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Sun, 9 Nov 2025 10:45:40 +0100 Subject: [PATCH 2/5] fix --- src/FileSystem.php | 2 -- src/NativeFileSystem.php | 35 ++--------------------------------- 2 files changed, 2 insertions(+), 35 deletions(-) diff --git a/src/FileSystem.php b/src/FileSystem.php index 6906329..62435b4 100644 --- a/src/FileSystem.php +++ b/src/FileSystem.php @@ -47,8 +47,6 @@ namespace Fidry\FileSystem; use Symfony\Component\Filesystem\Exception\IOException; -use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem; -use Symfony\Component\Filesystem\Path; interface FileSystem extends SymfonyFilesystem { diff --git a/src/NativeFileSystem.php b/src/NativeFileSystem.php index 97048c3..374ae21 100644 --- a/src/NativeFileSystem.php +++ b/src/NativeFileSystem.php @@ -47,8 +47,7 @@ namespace Fidry\FileSystem; use Symfony\Component\Filesystem\Exception\IOException; -use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem; -use Symfony\Component\Filesystem\Path; +use Symfony\Component\Filesystem\Filesystem as NativeSymfonyFilesystem; use Webmozart\Assert\Assert; use function error_get_last; use function file_get_contents; @@ -59,16 +58,8 @@ use function sys_get_temp_dir; use const DIRECTORY_SEPARATOR; -class NativeFileSystem extends SymfonyFilesystem +class NativeFileSystem extends NativeSymfonyFilesystem implements FileSystem { - /** - * Returns whether a path is relative. - * - * @param string $path a path string - * - * @return bool returns true if the path is relative or empty, false if - * it is absolute - */ public function isRelativePath(string $path): bool { return !$this->isAbsolutePath($path); @@ -84,15 +75,6 @@ public function dumpFile(string $filename, $content = ''): void parent::dumpFile($filename, $content); } - /** - * Gets the contents of a file. - * - * @param string $file File path - * - * @throws IOException If the file cannot be read - * - * @return string File contents - */ public function getFileContents(string $file): string { Assert::file($file); @@ -114,14 +96,6 @@ public function getFileContents(string $file): string return $contents; } - /** - * Creates a temporary directory. - * - * @param string $namespace the directory path in the system's temporary directory - * @param string $className the name of the test class - * - * @return string the path to the created directory - */ public function makeTmpDir(string $namespace, string $className): string { $shortClass = false !== ($pos = mb_strrpos($className, '\\')) @@ -154,11 +128,6 @@ public function makeTmpDir(string $namespace, string $className): string return $tmpDir; } - /** - * Gets a namespaced temporary directory. - * - * @param string $namespace the directory path in the system's temporary directory - */ public function getNamespacedTmpDir(string $namespace): string { // Usage of realpath() is important if the temporary directory is a From b9873495fe6c74a39f955e7ce8b09ed1aa8d082e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Sun, 9 Nov 2025 10:50:54 +0100 Subject: [PATCH 3/5] fix --- src/FS.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/FS.php b/src/FS.php index bff5052..554e003 100644 --- a/src/FS.php +++ b/src/FS.php @@ -59,14 +59,14 @@ */ class FS { - private static NativeFileSystem $filesystem; + private static FileSystem $filesystem; - public static function setInstance(NativeFileSystem $filesystem): void + public static function setInstance(FileSystem $filesystem): void { self::$filesystem = $filesystem; } - public static function getInstance(): NativeFileSystem + public static function getInstance(): FileSystem { /** @psalm-suppress RedundantPropertyInitializationCheck */ if (!isset(self::$filesystem)) { From 331b3a11bea31ed90a65d3a6ea93eae4b7ab04ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Sun, 9 Nov 2025 10:57:20 +0100 Subject: [PATCH 4/5] fix typo --- src/FileSystem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FileSystem.php b/src/FileSystem.php index 62435b4..9a92144 100644 --- a/src/FileSystem.php +++ b/src/FileSystem.php @@ -48,7 +48,7 @@ use Symfony\Component\Filesystem\Exception\IOException; -interface FileSystem extends SymfonyFilesystem +interface FileSystem extends SymfonyFileSystem { /** * Returns whether a path is relative. From b139ba2ca98dd766d894dc0bad001db69454f4af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Sun, 9 Nov 2025 11:16:12 +0100 Subject: [PATCH 5/5] fix --- src/SymfonyFileSystem.php | 53 ++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/src/SymfonyFileSystem.php b/src/SymfonyFileSystem.php index fea685a..a14dd27 100644 --- a/src/SymfonyFileSystem.php +++ b/src/SymfonyFileSystem.php @@ -57,15 +57,19 @@ interface SymfonyFileSystem * * @throws FileNotFoundException When originFile doesn't exist * @throws IOException When copy fails + * + * @return void */ - public function copy(string $originFile, string $targetFile, bool $overwriteNewerFiles = false): void; + public function copy(string $originFile, string $targetFile, bool $overwriteNewerFiles = false); /** * Creates a directory recursively. * * @throws IOException On any directory creation failure + * + * @return void */ - public function mkdir(string|iterable $dirs, int $mode = 0o777): void; + public function mkdir(string|iterable $dirs, int $mode = 0o777); /** * Checks the existence of files or directories. @@ -80,15 +84,19 @@ public function exists(string|iterable $files): bool; * @param int|null $atime The access time as a Unix timestamp, if not supplied the current system time is used * * @throws IOException When touch fails + * + * @return void */ - public function touch(string|iterable $files, ?int $time = null, ?int $atime = null): void; + public function touch(string|iterable $files, ?int $time = null, ?int $atime = null); /** * Removes files or directories. * * @throws IOException When removal fails + * + * @return void */ - public function remove(string|iterable $files): void; + public function remove(string|iterable $files); /** * Change mode for an array of files or directories. @@ -98,8 +106,10 @@ public function remove(string|iterable $files): void; * @param bool $recursive Whether change the mod recursively or not * * @throws IOException When the change fails + * + * @return void */ - public function chmod(string|iterable $files, int $mode, int $umask = 0o000, bool $recursive = false): void; + public function chmod(string|iterable $files, int $mode, int $umask = 0o000, bool $recursive = false); /** * Change the owner of an array of files or directories. @@ -112,8 +122,10 @@ public function chmod(string|iterable $files, int $mode, int $umask = 0o000, boo * @param bool $recursive Whether change the owner recursively or not * * @throws IOException When the change fails + * + * @return void */ - public function chown(string|iterable $files, string|int $user, bool $recursive = false): void; + public function chown(string|iterable $files, string|int $user, bool $recursive = false); /** * Change the group of an array of files or directories. @@ -127,23 +139,29 @@ public function chown(string|iterable $files, string|int $user, bool $recursive * @param bool $recursive Whether change the group recursively or not * * @throws IOException When the change fails + * + * @return void */ - public function chgrp(string|iterable $files, string|int $group, bool $recursive = false): void; + public function chgrp(string|iterable $files, string|int $group, bool $recursive = false); /** * Renames a file or a directory. * * @throws IOException When target file or directory already exists * @throws IOException When origin cannot be renamed + * + * @return void */ - public function rename(string $origin, string $target, bool $overwrite = false): void; + public function rename(string $origin, string $target, bool $overwrite = false); /** * Creates a symbolic link or copy a directory. * * @throws IOException When symlink fails + * + * @return void */ - public function symlink(string $originDir, string $targetDir, bool $copyOnWindows = false): void; + public function symlink(string $originDir, string $targetDir, bool $copyOnWindows = false); /** * Creates a hard link, or several hard links to a file. @@ -152,8 +170,10 @@ public function symlink(string $originDir, string $targetDir, bool $copyOnWindow * * @throws FileNotFoundException When original file is missing or not a file * @throws IOException When link fails, including if link already exists + * + * @return void */ - public function hardlink(string $originFile, string|iterable $targetFiles): void; + public function hardlink(string $originFile, string|iterable $targetFiles); /** * Resolves links in paths. @@ -189,8 +209,10 @@ public function makePathRelative(string $endPath, string $startPath): string; * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false) * * @throws IOException When a file type is unknown + * + * @return void */ - public function mirror(string $originDir, string $targetDir, ?Traversable $iterator = null, array $options = []): void; + public function mirror(string $originDir, string $targetDir, ?Traversable $iterator = null, array $options = []); /** * Returns whether the file path is an absolute path. @@ -224,13 +246,8 @@ public function dumpFile(string $filename, $content): void; * @param bool $lock Whether the file should be locked when writing to it * * @throws IOException If the file is not writable - */ - public function appendToFile(string $filename, $content, bool $lock = false): void; - - /** - * Returns the content of a file as a string. * - * @throws IOException If the file cannot be read + * @return void */ - public function readFile(string $filename): string; + public function appendToFile(string $filename, $content, /*bool $lock = false*/); }