Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
14 / 14
CRAP
100.00% covered (success)
100.00%
1 / 1
Filesystem
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
14 / 14
16
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 exists
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 readFile
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 dumpFile
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 copy
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 chmod
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 remove
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 symlink
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 readlink
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAbsolutePath
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 mkdir
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 makePathRelative
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getBasename
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDirectory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5/**
6 * Fast Forward Development Tools for PHP projects.
7 *
8 * This file is part of fast-forward/dev-tools project.
9 *
10 * @author   Felipe SayĆ£o Lobato Abreu <github@mentordosnerds.com>
11 * @license  https://opensource.org/licenses/MIT MIT License
12 *
13 * @see      https://github.com/php-fast-forward/
14 * @see      https://github.com/php-fast-forward/dev-tools
15 * @see      https://github.com/php-fast-forward/dev-tools/issues
16 * @see      https://php-fast-forward.github.io/dev-tools/
17 * @see      https://datatracker.ietf.org/doc/html/rfc2119
18 */
19
20namespace FastForward\DevTools\Filesystem;
21
22use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem;
23use Symfony\Component\Filesystem\Path;
24use function Safe\getcwd;
25
26/**
27 * Concrete implementation of the standard filesystem interface.
28 *
29 * This class wraps over the Symfony Filesystem component, automatically
30 * converting provided paths to absolute representations when a base path is supplied or
31 * dynamically inferred from the generic working directory.
32 */
33  class Filesystem implements FilesystemInterface
34{
35    /**
36     * @param SymfonyFilesystem $filesystem
37     */
38    public function __construct(
39        private SymfonyFilesystem $filesystem = new SymfonyFilesystem(),
40    ) {}
41
42    /**
43     * Checks whether a file or directory exists.
44     *
45     * @param iterable<string>|string $files the file(s) or directory(ies) to check
46     * @param string|null $basePath the base path used to resolve relative paths
47     *
48     * @return bool true if the path exists, false otherwise
49     */
50    public function exists(string|iterable $files, ?string $basePath = null): bool
51    {
52        return $this->filesystem->exists($this->getAbsolutePath($files, $basePath));
53    }
54
55    /**
56     * Reads the entire content of a file.
57     *
58     * @param string $filename the target filename to read
59     * @param string|null $path the optional base path to resolve the filename against
60     *
61     * @return string the content of the file
62     */
63    public function readFile(string $filename, ?string $path = null): string
64    {
65        return $this->filesystem->readFile($this->getAbsolutePath($filename, $path));
66    }
67
68    /**
69     * Writes content to a file, overriding it if it already exists.
70     *
71     * @param string $filename the filename to write to
72     * @param mixed $content the content to write
73     * @param string|null $path the optional base path to resolve the filename against
74     */
75    public function dumpFile(string $filename, mixed $content, ?string $path = null): void
76    {
77        $this->filesystem->dumpFile($this->getAbsolutePath($filename, $path), $content);
78    }
79
80    /**
81     * Copies a file to a target path.
82     *
83     * @param string $originFile the source file path to copy
84     * @param string $targetFile the target file path to create
85     * @param bool $overwriteNewerFiles whether newer target files MAY be overwritten
86     */
87    public function copy(string $originFile, string $targetFile, bool $overwriteNewerFiles = false): void
88    {
89        $this->filesystem->copy(
90            $this->getAbsolutePath($originFile),
91            $this->getAbsolutePath($targetFile),
92            $overwriteNewerFiles
93        );
94    }
95
96    /**
97     * Changes the permission mode for one or more files.
98     *
99     * @param iterable<string>|string $files the target file paths
100     * @param int $mode the permission mode to apply
101     * @param int $umask the umask to apply
102     * @param bool $recursive whether permissions SHOULD be applied recursively
103     */
104    public function chmod(string|iterable $files, int $mode, int $umask = 0o000, bool $recursive = false): void
105    {
106        $this->filesystem->chmod($this->getAbsolutePath($files), $mode, $umask, $recursive);
107    }
108
109    /**
110     * Removes files, symbolic links, or directories.
111     *
112     * @param iterable<string>|string $files the file(s), link(s), or directory(ies) to remove
113     */
114    public function remove(string|iterable $files): void
115    {
116        $this->filesystem->remove($this->getAbsolutePath($files));
117    }
118
119    /**
120     * Creates a symbolic link.
121     *
122     * @param string $originDir the origin path the link MUST point to
123     * @param string $targetDir the link path to create
124     * @param bool $copyOnWindows whether directories SHOULD be copied on Windows instead of linked
125     */
126    public function symlink(string $originDir, string $targetDir, bool $copyOnWindows = false): void
127    {
128        $this->filesystem->symlink($originDir, $this->getAbsolutePath($targetDir), $copyOnWindows);
129    }
130
131    /**
132     * Reads a symbolic link target.
133     *
134     * @param string $path the symbolic link path
135     * @param bool $canonicalize whether the returned path SHOULD be canonicalized
136     *
137     * @return string|null the link target, or null when the path is not a symbolic link
138     */
139    public function readlink(string $path, bool $canonicalize = false): ?string
140    {
141        return $this->filesystem->readlink($this->getAbsolutePath($path), $canonicalize);
142    }
143
144    /**
145     * Resolves a path or iterable of paths into their absolute path representation.
146     *
147     * @param iterable<string>|string $files the path(s) to resolve
148     * @param string|null $basePath the base path for relative path resolution
149     *
150     * @return iterable<string>|string the resolved absolute path(s)
151     */
152    public function getAbsolutePath(string|iterable $files, ?string $basePath = null): string|iterable
153    {
154        $basePath ??= getcwd();
155
156        if (! Path::isAbsolute($basePath)) {
157            $basePath = Path::makeAbsolute($basePath, getcwd());
158        }
159
160        if (\is_string($files)) {
161            return Path::makeAbsolute($files, $basePath);
162        }
163
164        return array_map(static fn(string $file): string => Path::makeAbsolute($file, $basePath), $files);
165    }
166
167    /**
168     * Creates a directory recursively.
169     *
170     * @param iterable<string>|string $dirs the directory path(s) to create
171     * @param int $mode the permissions mode (defaults to 0777)
172     */
173    public function mkdir(string|iterable $dirs, int $mode = 0o777): void
174    {
175        $this->filesystem->mkdir($this->getAbsolutePath($dirs), $mode);
176    }
177
178    /**
179     * Computes the relative path from the base path to the target path.
180     *
181     * @param string $path the target absolute or relative path
182     * @param string|null $basePath the origin point; defaults to the current working directory
183     *
184     * @return string the computed relative path
185     */
186    public function makePathRelative(string $path, ?string $basePath = null): string
187    {
188        return $this->filesystem->makePathRelative($this->getAbsolutePath($path, $basePath), $basePath ?? getcwd());
189    }
190
191    /**
192     * Returns the trailing name component of a path.
193     *
194     * @param string $path the path to process
195     * @param string $suffix an optional suffix to strip from the returned basename
196     *
197     * @return string the base name of the given path
198     */
199    public function getBasename(string $path, string $suffix = ''): string
200    {
201        return basename($path, $suffix);
202    }
203
204    /**
205     * Returns a parent directory's path.
206     *
207     * @param string $path the path to evaluate
208     * @param int $levels the number of parent directories to go up
209     *
210     * @return string the parent path name
211     */
212    public function getDirectory(string $path, int $levels = 1): string
213    {
214        return \dirname($path, $levels);
215    }
216}