Passed
Push — dev ( 26c2da...b953b4 )
by Vermeulen
08:56
created

FileManager::createSymLink()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 45
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
eloc 26
nc 10
nop 3
dl 0
loc 45
ccs 21
cts 21
cp 1
crap 6
rs 8.8817
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 bool $tryRelative (default true) If system try to resolv paths
169
     *  to use a relative path for target.
170
     *
171
     * @return void
172
     */
173 1
    public function createSymLink(
174 1
        string $linkTarget,
175
        string $linkFile,
176 1
        bool $tryRelative = true
177 1
    ) {
178
        $this->sendMsgInLogger(
179
            'FileManager - Create symlink',
180
            [
181 1
                'linkTarget' => $linkTarget,
182 1
                'linkFile'   => $linkFile
183 1
            ]
184 1
        );
185
        
186
        if (file_exists($linkFile)) {
187
            throw new Exception(
188 1
                'link file '.$linkFile.' already exist.',
189 1
                static::EXCEP_FILE_EXIST
190 1
            );
191 1
        }
192
        
193
        if (file_exists($linkTarget) === false) {
194
            throw new Exception(
195 1
                'link target '.$linkTarget.' not found.',
196 1
                static::EXCEP_LINK_TARGET_NOT_FOUND
197 1
            );
198 1
        }
199 1
200
        $usedTarget = $linkTarget;
201
        if ($tryRelative === true) {
202 1
            try {
203
                $usedTarget = Paths::absoluteToRelative($linkTarget, $linkFile);
204
                $this->sendMsgInLogger(
205
                    'FileManager - Create symlink - Use relative path',
206
                    ['target' => $usedTarget]
207
                );
208
            } catch (Exception $e) {
209
                $usedTarget = $linkTarget;
210
            }
211
        }
212
        
213 1
        $status = symlink($usedTarget, $linkFile);
214 1
        if ($status === false) {
215 1
            throw new Exception(
216
                'link create failed for '.$linkFile.' -> '.$usedTarget,
217
                static::EXCEP_LINK_CREATION_FAILED
218 1
            );
219 1
        }
220 1
    }
221 1
    
222
    /**
223
     * Remove a symlink
224
     *
225 1
     * @param string $linkFile The symlink file path
226 1
     *
227 1
     * @return void
228 1
     */
229 1
    public function removeSymLink(string $linkFile)
230
    {
231
        $this->sendMsgInLogger(
232 1
            'FileManager - Remove symlink',
233
            ['linkFile' => $linkFile]
234
        );
235
        
236
        if (file_exists($linkFile) === false) {
237
            throw new Exception(
238
                'link file '.$linkFile.' not found.',
239
                static::EXCEP_FILE_NOT_EXIST
240
            );
241
        }
242
        
243 1
        $status = unlink($linkFile);
244 1
        if ($status === false) {
245 1
            throw new Exception(
246
                'link remove failed for '.$linkFile,
247
                static::EXCEP_LINK_REMOVE_FAILED
248 1
            );
249 1
        }
250 1
    }
251 1
    
252
    /**
253
     * Create a new directory
254
     *
255 1
     * @param string $dirPath The directory path
256 1
     *
257 1
     * @return void
258 1
     */
259 1
    public function createDirectory(string $dirPath)
260
    {
261
        $this->sendMsgInLogger(
262 1
            'FileManager - Create directory',
263
            ['path' => $dirPath]
264
        );
265
        
266
        if (file_exists($dirPath) === true) {
267
            throw new Exception(
268
                'Directory '.$dirPath.' already exist.',
269
                static::EXCEP_DIRECTORY_EXIST
270
            );
271
        }
272
        
273
        $status = mkdir($dirPath, 0755);
274 1
        if ($status === false) {
275 1
            throw new Exception(
276
                'Directory '.$dirPath.' creation failed.',
277 1
                static::EXCEP_DIRECTORY_CREATION_FAILED
278 1
            );
279
        }
280
    }
281
    
282 1
    /**
283 1
     * Copy a file
284 1
     *
285 1
     * @param string $source The source file path
286
     * @param string $target The destination file path
287
     *
288
     * @return void
289 1
     */
290 1
    public function copyFile(string $source, string $target)
291 1
    {
292 1
        $this->sendMsgInLogger(
293
            'FileManager - Copy file',
294
            [
295
                'source' => $source,
296 1
                'target' => $target
297 1
            ]
298 1
        );
299 1
        
300 1
        if (file_exists($target)) {
301
            throw new Exception(
302
                'target file '.$target.' already exist.',
303 1
                static::EXCEP_FILE_EXIST
304
            );
305
        }
306
        
307
        if (file_exists($source) === false) {
308
            throw new Exception(
309
                'copy source '.$source.' not found.',
310
                static::EXCEP_COPY_SOURCE_NOT_FOUND
311
            );
312
        }
313
        
314
        $status = copy($source, $target);
315
        if ($status === false) {
316 1
            throw new Exception(
317 1
                'copy failed for '.$source.' -> '.$target,
318 1
                static::EXCEP_COPY_FAILED
319
            );
320
        }
321 1
    }
322
    
323 1
    /**
324 1
     * Remove folders recursively
325
     *
326 1
     * @see http://php.net/manual/fr/function.rmdir.php#110489
327 1
     *
328
     * @param string $dirPath Path to directory to remove
329 1
     *
330
     * @return boolean
331
     */
332
    public function removeRecursiveDirectory(string $dirPath)
333 1
    {
334 1
        $this->sendMsgInLogger(
335 1
            'FileManager - Remove files and directories',
336 1
            ['path' => $dirPath]
337 1
        );
338
        
339
        $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

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