Passed
Push — dev ( b198bc...26c2da )
by Vermeulen
02:03
created

FileManager::copyFile()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 29
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 17
nc 4
nop 2
dl 0
loc 29
ccs 17
cts 17
cp 1
crap 4
rs 9.7
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace bultonFr\Utils\Files;
6
7
use Exception;
8
9
/**
10
 * Some methods to manage files and folders
11
 *
12
 * @author bulton-fr <[email protected]>
13
 */
14
class FileManager
15
{
16
    /**
17
     * Exception code when the constructor's first parameter is not null and
18
     * is not an instance of \Psr\Log\LoggerInterface
19
     */
20
    const EXCEP_NOT_LOGGER_INTERFACE = 202001;
21
22
    /**
23
     * Exception code when a file already exist
24
     *
25
     * @const EXCEP_FILE_EXIST
26
     */
27
    const EXCEP_FILE_EXIST = 202002;
28
29
    /**
30
     * Exception code when a file not exist
31
     *
32
     * @const EXCEP_FILE_NOT_EXIST
33
     */
34
    const EXCEP_FILE_NOT_EXIST = 202003;
35
36
    /**
37
     * Exception code when the symlink target not exist
38
     *
39
     * @const EXCEP_LINK_TARGET_NOT_FOUND
40
     */
41
    const EXCEP_LINK_TARGET_NOT_FOUND = 202004;
42
43
    /**
44
     * Exception code if the creating of the symlink failed
45
     *
46
     * @const EXCEP_LINK_CREATION_FAILED
47
     */
48
    const EXCEP_LINK_CREATION_FAILED = 202005;
49
50
    /**
51
     * Exception code when the deleting of the symlink failed
52
     *
53
     * @const EXCEP_LINK_REMOVE_FAILED
54
     */
55
    const EXCEP_LINK_REMOVE_FAILED = 202006;
56
57
    /**
58
     * Exception code when a directory already exist
59
     *
60
     * @const EXCEP_DIRECTORY_EXIST
61
     */
62
    const EXCEP_DIRECTORY_EXIST = 202007;
63
64
    /**
65
     * Exception code when the directory creation failed
66
     *
67
     * @const EXCEP_DIRECTORY_CREATION_FAILED
68
     */
69
    const EXCEP_DIRECTORY_CREATION_FAILED = 202008;
70
71
    /**
72
     * Exception code when the file to copy not exist
73
     *
74
     * @const EXCEP_COPY_SOURCE_NOT_FOUND
75
     */
76
    const EXCEP_COPY_SOURCE_NOT_FOUND = 202009;
77
78
    /**
79
     * Exception code when a copy failed
80
     *
81
     * @const EXCEP_COPY_FAILED
82
     */
83
    const EXCEP_COPY_FAILED = 202010;
84
85
    /**
86
     * Exception code when a directory deletion failed
87
     *
88
     * @const EXCEP_DIRECTORY_REMOVE_FAIL
89
     */
90
    const EXCEP_DIRECTORY_REMOVE_FAIL = 202011;
91
92
    /**
93
     * The Logger instance where all debug message will be sent
94
     *
95
     * @var \Psr\Log\LoggerInterface|null
96
     */
97
    protected $logger;
98
99
    /**
100
     * The Logger level to use to send messages
101
     *
102
     * @var string
103
     */
104
    protected $loggerMsgType = '';
105
106
    /**
107
     * Constructor
108
     *
109
     * @param \Psr\Log\LoggerInterface|null $logger The Logger instance
110
     * where all debug message will be sent
111
     * @param string $loggerMsgType (default 'debug') The Logger level to use
112
     * to send messages
113
     */
114
    public function __construct($logger = null, string $loggerMsgType = 'debug')
115
    {
116 1
        if ($logger !== null && $logger instanceof \Psr\Log\LoggerInterface === false) {
117 1
            throw new Exception(
118 1
                'The constructor first parameter must be an instance of \Psr\Log\LoggerInterface.',
119 1
                static::EXCEP_NOT_LOGGER_INTERFACE
120
            );
121
        }
122
123 1
        $this->logger        = $logger;
124 1
        $this->loggerMsgType = $loggerMsgType;
125 1
    }
126
127
    /**
128
     * Get the value of logger
129
     *
130
     * @return \Psr\Log\LoggerInterface|null
131
     */
132
    public function getLogger()
133
    {
134 1
        return $this->logger;
135
    }
136
137
    /**
138
     * Get the value of loggerMsgType
139
     *
140
     * @return string
141
     */
142
    public function getLoggerMsgType(): string
143
    {
144 1
        return $this->loggerMsgType;
145
    }
146
147
    /**
148
     * Send a message to logger
149
     *
150
     * @param array ...$args Arguments passed to Logger function
151
     * @return void
152
     */
153
    protected function sendMsgInLogger(...$args)
154
    {
155 1
        if ($this->logger === null) {
156 1
            return;
157
        }
158
159 1
        $msgType = $this->loggerMsgType;
160 1
        $this->logger->{$msgType}(...$args);
161 1
    }
162
    
163
    /**
164
     * Create a symlink
165
     *
166
     * @param string $linkTarget The symlink target path
167
     * @param string $linkFile The symlink file path
168
     * @param boolean $tryRelative (default true) If system try to create the
169
     *  symlink with a relative path.
170
     *
171
     * @return void
172
     */
173
    public function createSymLink(
174
        string $linkTarget,
175
        string $linkFile,
176
        bool $tryRelative = true
177
    ) {
178
        $this->sendMsgInLogger(
179
            'FileManager - Create symlink',
180
            [
181
                'linkTarget' => $linkTarget,
182
                'linkFile'   => $linkFile
183
            ]
184
        );
185
        
186
        if (file_exists($linkFile)) {
187
            throw new Exception(
188
                'link file '.$linkFile.' already exist.',
189
                static::EXCEP_FILE_EXIST
190
            );
191
        }
192
193
        if (file_exists($linkTarget) === false) {
194
            throw new Exception(
195
                'link target '.$linkTarget.' not found.',
196
                static::EXCEP_LINK_TARGET_NOT_FOUND
197
            );
198
        }
199
200
        $targetPath = $linkTarget;
201
        if ($tryRelative === true) {
202
            $targetPath = Paths::absoluteToRelative($linkTarget, $linkFile);
203
        }
204
        
205
        $status = symlink($targetPath, $linkFile);
206
        if ($status === false) {
207
            throw new Exception(
208
                'link create failed for '.$linkFile.' -> '.$targetPath,
209
                static::EXCEP_LINK_CREATION_FAILED
210
            );
211
        }
212
    }
213
    
214
    /**
215
     * Remove a symlink
216
     *
217
     * @param string $linkFile The symlink file path
218
     *
219
     * @return void
220
     */
221
    public function removeSymLink(string $linkFile)
222
    {
223 1
        $this->sendMsgInLogger(
224 1
            'FileManager - Remove symlink',
225 1
            ['linkFile' => $linkFile]
226
        );
227
        
228 1
        if (file_exists($linkFile) === false) {
229 1
            throw new Exception(
230 1
                'link file '.$linkFile.' not found.',
231 1
                static::EXCEP_FILE_NOT_EXIST
232
            );
233
        }
234
        
235 1
        $status = unlink($linkFile);
236 1
        if ($status === false) {
237 1
            throw new Exception(
238 1
                'link remove failed for '.$linkFile,
239 1
                static::EXCEP_LINK_REMOVE_FAILED
240
            );
241
        }
242 1
    }
243
    
244
    /**
245
     * Create a new directory
246
     *
247
     * @param string $dirPath The directory path
248
     *
249
     * @return void
250
     */
251
    public function createDirectory(string $dirPath)
252
    {
253 1
        $this->sendMsgInLogger(
254 1
            'FileManager - Create directory',
255 1
            ['path' => $dirPath]
256
        );
257
        
258 1
        if (file_exists($dirPath) === true) {
259 1
            throw new Exception(
260 1
                'Directory '.$dirPath.' already exist.',
261 1
                static::EXCEP_DIRECTORY_EXIST
262
            );
263
        }
264
        
265 1
        $status = mkdir($dirPath, 0755);
266 1
        if ($status === false) {
267 1
            throw new Exception(
268 1
                'Directory '.$dirPath.' creation failed.',
269 1
                static::EXCEP_DIRECTORY_CREATION_FAILED
270
            );
271
        }
272 1
    }
273
    
274
    /**
275
     * Copy a file
276
     *
277
     * @param string $source The source file path
278
     * @param string $target The destination file path
279
     *
280
     * @return void
281
     */
282
    public function copyFile(string $source, string $target)
283
    {
284 1
        $this->sendMsgInLogger(
285 1
            'FileManager - Copy file',
286
            [
287 1
                'source' => $source,
288 1
                'target' => $target
289
            ]
290
        );
291
        
292 1
        if (file_exists($target)) {
293 1
            throw new Exception(
294 1
                'target file '.$target.' already exist.',
295 1
                static::EXCEP_FILE_EXIST
296
            );
297
        }
298
        
299 1
        if (file_exists($source) === false) {
300 1
            throw new Exception(
301 1
                'copy source '.$source.' not found.',
302 1
                static::EXCEP_COPY_SOURCE_NOT_FOUND
303
            );
304
        }
305
        
306 1
        $status = copy($source, $target);
307 1
        if ($status === false) {
308 1
            throw new Exception(
309 1
                'copy failed for '.$source.' -> '.$target,
310 1
                static::EXCEP_COPY_FAILED
311
            );
312
        }
313 1
    }
314
    
315
    /**
316
     * Remove folders recursively
317
     *
318
     * @see http://php.net/manual/fr/function.rmdir.php#110489
319
     *
320
     * @param string $dirPath Path to directory to remove
321
     *
322
     * @return boolean
323
     */
324
    public function removeRecursiveDirectory(string $dirPath)
325
    {
326 1
        $this->sendMsgInLogger(
327 1
            'FileManager - Remove files and directories',
328 1
            ['path' => $dirPath]
329
        );
330
        
331 1
        $itemList = array_diff(scandir($dirPath), ['.', '..']);
0 ignored issues
show
Bug introduced by
It seems like scandir($dirPath) can also be of type false; however, parameter $array1 of array_diff() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

331
        $itemList = array_diff(/** @scrutinizer ignore-type */ scandir($dirPath), ['.', '..']);
Loading history...
332
        
333 1
        foreach ($itemList as $itemName) {
334 1
            $itemPath = $dirPath.'/'.$itemName;
335
            
336 1
            if (is_dir($itemPath)) {
337 1
                $this->removeRecursiveDirectory($itemPath);
338
            } else {
339 1
                unlink($itemPath);
340
            }
341
        }
342
        
343 1
        $rmDirStatus = rmdir($dirPath);
344 1
        if ($rmDirStatus === false) {
345 1
            throw new Exception(
346 1
                'Directory deletion has failed for '.$dirPath,
347 1
                static::EXCEP_DIRECTORY_REMOVE_FAIL
348
            );
349
        }
350 1
    }
351
}
352