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