FileHelper   A
last analyzed

Complexity

Total Complexity 42

Size/Duplication

Total Lines 517
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 170
dl 0
loc 517
rs 9.0399
c 1
b 0
f 0
wmc 42

22 Methods

Rating   Name   Duplication   Size   Complexity  
A isUploaded() 0 7 2
A __construct() 0 5 1
A exists() 0 16 3
A getAttachmentMimeRules() 0 3 1
A existImage() 0 10 1
A isImageExists() 0 20 3
A uploadImage() 0 10 1
A getRootPathId() 0 3 1
A doUploadImage() 0 32 3
A getAttachmentExtensionRules() 0 3 1
A uploadAttachment() 0 57 5
A getRootPath() 0 25 3
A deletePublicImage() 0 10 1
A uploadPublicImage() 0 10 1
A deleteImage() 0 10 1
A doUpload() 0 37 3
A existPublicImage() 0 10 1
A deleteFile() 0 5 2
A getImageMaxSize() 0 3 1
A getAttachmentMaxSize() 0 3 1
A delete() 0 23 3
A handleDeleteImage() 0 21 3

How to fix   Complexity   

Complex Class

Complex classes like FileHelper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FileHelper, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Platine Framework
5
 *
6
 * Platine Framework is a lightweight, high-performance, simple and elegant
7
 * PHP Web framework
8
 *
9
 * This content is released under the MIT License (MIT)
10
 *
11
 * Copyright (c) 2020 Platine Framework
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a copy
14
 * of this software and associated documentation files (the "Software"), to deal
15
 * in the Software without restriction, including without limitation the rights
16
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
 * copies of the Software, and to permit persons to whom the Software is
18
 * furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all
21
 * copies or substantial portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
 * SOFTWARE.
30
 */
31
32
declare(strict_types=1);
33
34
namespace Platine\Framework\Helper;
35
36
use Platine\Config\Config;
37
use Platine\Filesystem\Filesystem;
38
use Platine\Http\UploadedFile;
39
use Platine\Lang\Lang;
40
use Platine\Upload\Exception\UploadException;
41
use Platine\Upload\File\UploadFileInfo;
42
use Platine\Upload\Storage\FileSystem as UploadFileSystem;
43
use Platine\Upload\Upload;
44
use Platine\Upload\Validator\Rule\Extension;
45
use Platine\Upload\Validator\Rule\MimeType;
46
use Platine\Upload\Validator\Rule\Required;
47
use Platine\Upload\Validator\Rule\Size;
48
use Platine\Upload\Validator\RuleInterface;
49
50
/**
51
 * @class FileHelper
52
 * @package Platine\Framework\Helper
53
 * @template T
54
 */
55
class FileHelper
56
{
57
    /**
58
     *
59
     * @param Config<T> $config
60
     * @param Filesystem $filesystem
61
     * @param Lang $lang
62
     */
63
    public function __construct(
64
        protected Config $config,
65
        protected Filesystem $filesystem,
66
        protected Lang $lang,
67
    ) {
68
    }
69
70
    /**
71
     * Whether the file is uploaded
72
     * @param string $name
73
     * @return bool
74
     */
75
    public function isUploaded(string $name): bool
76
    {
77
        $uploadedFiles = UploadedFile::createFromGlobals();
78
        $uploadFile = $uploadedFiles[$name] ?? null;
79
80
        return $uploadFile instanceof UploadedFile
81
                && $uploadFile->getClientMediaType() !== null;
82
    }
83
84
    /**
85
     * Delete the given uploaded file in some situation, like after upload error
86
     * or can not save the information in database, etc.
87
     * @param UploadFileInfo $info
88
     * @return void
89
     */
90
    public function deleteFile(UploadFileInfo $info): void
91
    {
92
        $file = $this->filesystem->file($info->getPath());
93
        if ($file->exists()) {
94
            $file->delete();
95
        }
96
    }
97
98
    /**
99
     * Whether the given file exist
100
     * @param string $filename
101
     * @param string|null $folder
102
     * @param bool $useRoot
103
     * @return bool
104
     */
105
    public function exists(string $filename, ?string $folder = null, bool $useRoot = true): bool
106
    {
107
        $configPath = $this->getRootPath(
108
            $this->config->get('platform.data_attachment_path'),
109
            $useRoot
110
        );
111
112
        $path = $configPath;
113
        if ($folder !== null) {
114
            $path .= DIRECTORY_SEPARATOR . $folder;
115
        }
116
117
        $filepath = sprintf('%s/%s', $path, $filename);
118
        $handle = $this->filesystem->get($filepath);
119
120
        return $handle !== null && $handle->exists();
121
    }
122
123
    /**
124
     * Whether the given image file exist
125
     * @param string $filename
126
     * @param string|null $folder
127
     * @param bool $useRoot
128
     * @return bool
129
     */
130
    public function existImage(
131
        string $filename,
132
        ?string $folder = null,
133
        bool $useRoot = true
134
    ): bool {
135
        return $this->isImageExists(
136
            $filename,
137
            'platform.data_image_path',
138
            $folder,
139
            $useRoot
140
        );
141
    }
142
143
    /**
144
     * Whether the given public image file exist
145
     * @param string $filename
146
     * @param string|null $folder
147
     * @param bool $useRoot
148
     * @return bool
149
     */
150
    public function existPublicImage(
151
        string $filename,
152
        ?string $folder = null,
153
        bool $useRoot = true
154
    ): bool {
155
        return $this->isImageExists(
156
            $filename,
157
            'platform.public_image_path',
158
            $folder,
159
            $useRoot
160
        );
161
    }
162
163
    /**
164
     * Delete the given file
165
     * @param string $filename
166
     * @param string|null $folder
167
     * @param bool $useRoot
168
     * @return bool
169
     */
170
    public function delete(
171
        string $filename,
172
        ?string $folder = null,
173
        bool $useRoot = true
174
    ): bool {
175
        $configPath = $this->getRootPath(
176
            $this->config->get('platform.data_attachment_path'),
177
            $useRoot
178
        );
179
        $path = $configPath;
180
        if ($folder !== null) {
181
            $path .= DIRECTORY_SEPARATOR . $folder;
182
        }
183
184
        $filepath = sprintf('%s/%s', $path, $filename);
185
        $handle = $this->filesystem->get($filepath);
186
        if ($handle === null) {
187
            return true; //no need to delete if file does not exist
188
        }
189
190
        $handle->delete();
191
192
        return true;
193
    }
194
195
    /**
196
     * Delete the given upload public image file
197
     * @param string $filename
198
     * @param string|null $folder
199
     * @param bool $useRoot
200
     * @return bool
201
     */
202
    public function deletePublicImage(
203
        string $filename,
204
        ?string $folder = null,
205
        bool $useRoot = true
206
    ): bool {
207
        return $this->handleDeleteImage(
208
            $filename,
209
            'platform.public_image_path',
210
            $folder,
211
            $useRoot
212
        );
213
    }
214
215
    /**
216
     * Delete the given upload image file
217
     * @param string $filename
218
     * @param string|null $folder
219
     * @param bool $useRoot
220
     * @return bool
221
     */
222
    public function deleteImage(
223
        string $filename,
224
        ?string $folder = null,
225
        bool $useRoot = true
226
    ): bool {
227
        return $this->handleDeleteImage(
228
            $filename,
229
            'platform.data_image_path',
230
            $folder,
231
            $useRoot
232
        );
233
    }
234
235
    /**
236
     * Upload an image
237
     * @param string $name
238
     * @param string|null $folder
239
     * @param bool $useRoot
240
     * @return UploadFileInfo
241
     */
242
    public function uploadImage(
243
        string $name,
244
        ?string $folder = null,
245
        bool $useRoot = true
246
    ): UploadFileInfo {
247
        return $this->doUploadImage(
248
            $name,
249
            'platform.data_image_path',
250
            $folder,
251
            $useRoot
252
        );
253
    }
254
255
    /**
256
     * Upload an public image
257
     * @param string $name
258
     * @param string|null $folder
259
     * @param bool $useRoot
260
     * @return UploadFileInfo
261
     */
262
    public function uploadPublicImage(
263
        string $name,
264
        ?string $folder = null,
265
        bool $useRoot = true
266
    ): UploadFileInfo {
267
        return $this->doUploadImage(
268
            $name,
269
            'platform.public_image_path',
270
            $folder,
271
            $useRoot
272
        );
273
    }
274
275
    /**
276
     * Upload an attachment
277
     * @param string $name
278
     * @param null|string $folder
279
     * @param bool $useRoot
280
     * @return UploadFileInfo
281
     */
282
    public function uploadAttachment(
283
        string $name,
284
        ?string $folder = null,
285
        bool $useRoot = true
286
    ): UploadFileInfo {
287
        $configPath = $this->getRootPath(
288
            $this->config->get('platform.data_attachment_path'),
289
            $useRoot
290
        );
291
        $path = $configPath;
292
293
        if ($folder !== null) {
294
            $directory = $this->filesystem->directory($configPath);
295
            $path .= DIRECTORY_SEPARATOR . $folder;
296
            // Create the folder if it does not exist
297
            if ($this->filesystem->directory($path)->exists() === false) {
298
                $directory->create($folder, 0775, true);
299
            }
300
        }
301
302
        $rules = [
303
            new Required(),
304
            new Size($this->getAttachmentMaxSize()),
305
        ];
306
307
        $extensions = $this->getAttachmentExtensionRules();
308
        if (count($extensions) === 0) {
309
            $extensions = [
310
                    'png',
311
                    'gif',
312
                    'jpg',
313
                    'jpeg',
314
                    'csv',
315
                    'txt',
316
                    'docx',
317
                    'doc',
318
                    'pdf',
319
                    'xls',
320
                    'xlsx',
321
                    'pptx',
322
                    'ppt',
323
                    'zip',
324
                    'json',
325
                ];
326
        }
327
        $rules[] = new Extension($extensions);
328
329
        $mimes = $this->getAttachmentMimeRules();
330
        if (count($mimes) > 0) {
331
            $rules[] = new MimeType($mimes);
332
        }
333
334
335
        return $this->doUpload(
336
            $name,
337
            $rules,
338
            $path,
339
        );
340
    }
341
342
    /**
343
     * Return the path with the root suffix
344
     * @param string $configPath
345
     * @param bool $useRoot whether to use root path
346
     * @return string
347
     */
348
    public function getRootPath(string $configPath, bool $useRoot = true): string
349
    {
350
        $rootId = '';
351
        if ($useRoot) {
352
            $rootId = $this->getRootPathId();
353
        }
354
355
        $path = sprintf(
356
            '%s%s%s',
357
            $configPath,
358
            DIRECTORY_SEPARATOR,
359
            $rootId
360
        );
361
362
        $directory = $this->filesystem->directory($configPath);
363
364
        // Create the folder if it does not exist
365
        if ($this->filesystem->directory($path)->exists() === false) {
366
            $directory->create($rootId, 0775, true);
367
368
            // if it's public path add index file to prevent directory listing
369
            $this->filesystem->directory($path)->createFile('index.html', 'Access denied');
370
        }
371
372
        return $path;
373
    }
374
375
    /**
376
     * Whether the given image file exist
377
     * @param string $filename
378
     * @param string $configPathKey
379
     * @param string|null $folder
380
     * @param bool $useRoot
381
     * @return bool
382
     */
383
    protected function isImageExists(
384
        string $filename,
385
        string $configPathKey,
386
        ?string $folder = null,
387
        bool $useRoot = true
388
    ): bool {
389
        $configPath = $this->getRootPath(
390
            $this->config->get($configPathKey),
391
            $useRoot
392
        );
393
394
        $path = $configPath;
395
        if ($folder !== null) {
396
            $path .= DIRECTORY_SEPARATOR . $folder;
397
        }
398
399
        $filepath = sprintf('%s/%s', $path, $filename);
400
        $handle = $this->filesystem->get($filepath);
401
402
        return $handle !== null && $handle->exists();
403
    }
404
405
    /**
406
     * Process the upload
407
     * @param string $name
408
     * @param RuleInterface[] $rules
409
     * @param string $path
410
     *
411
     * @return UploadFileInfo
412
     */
413
    protected function doUpload(
414
        string $name,
415
        array $rules,
416
        string $path,
417
    ): UploadFileInfo {
418
        $uploadedFiles = UploadedFile::createFromGlobals();
419
        /** @var UploadedFile $uploadFile */
420
        $uploadFile = $uploadedFiles[$name] ?? [];
421
422
        $upload = new Upload(
423
            $name,
424
            new UploadFileSystem(
425
                $path,
426
                true
427
            ),
428
            null,
429
            [$name => $uploadFile]
430
        );
431
432
        $upload->setFilename(md5(uniqid() . time()));
433
434
        $upload->addValidations($rules);
435
436
        if ($upload->isUploaded() === false) {
437
            throw new UploadException($this->lang->tr('The file to upload is empty'));
438
        }
439
440
        $isUploaded = $upload->process();
441
        if ($isUploaded === false) {
442
            $errors = $upload->getErrors();
443
            throw new UploadException($errors[0]);
444
        }
445
446
        /** @var UploadFileInfo $info */
447
        $info = $upload->getInfo();
448
449
        return $info;
450
    }
451
452
    /**
453
     * Upload an image
454
     * @param string $name
455
     * @param string $path
456
     * @param string|null $folder
457
     * @param bool $useRoot
458
     *
459
     * @return UploadFileInfo
460
     */
461
    protected function doUploadImage(
462
        string $name,
463
        string $path,
464
        ?string $folder = null,
465
        bool $useRoot = true
466
    ): UploadFileInfo {
467
        $configPath = $this->getRootPath($this->config->get($path), $useRoot);
468
469
        $imagePath = $configPath;
470
471
        if ($folder !== null) {
472
            $directory = $this->filesystem->directory($configPath);
473
            $imagePath .= DIRECTORY_SEPARATOR . $folder;
474
            // Create the folder if it does not exist
475
            if ($this->filesystem->directory($imagePath)->exists() === false) {
476
                $directory->create($folder, 0775, true);
477
            }
478
        }
479
480
        return $this->doUpload(
481
            $name,
482
            [
483
                new Size($this->getImageMaxSize()),
484
                new Extension(['png', 'gif', 'jpg', 'jpeg']),
485
                new MimeType([
486
                    'image/png',
487
                    'image/jpg',
488
                    'image/gif',
489
                    'image/jpeg',
490
                ])
491
            ],
492
            $imagePath
493
        );
494
    }
495
496
    /**
497
     * Delete the given upload image file
498
     * @param string $filename
499
     * @param string $cfgPath
500
     * @param string|null $folder
501
     * @param bool $useRoot
502
     * @return bool
503
     */
504
    protected function handleDeleteImage(
505
        string $filename,
506
        string $cfgPath,
507
        ?string $folder = null,
508
        bool $useRoot = true
509
    ): bool {
510
        $configPath = $this->getRootPath($this->config->get($cfgPath), $useRoot);
511
        $path = $configPath;
512
        if ($folder !== null) {
513
            $path .= DIRECTORY_SEPARATOR . $folder;
514
        }
515
516
        $filepath = sprintf('%s/%s', $path, $filename);
517
        $handle = $this->filesystem->get($filepath);
518
        if ($handle === null) {
519
            return true; //no need to delete if file does not exist
520
        }
521
522
        $handle->delete();
523
524
        return true;
525
    }
526
527
528
    /**
529
     * Return the identifier of root path
530
     * @return string
531
     */
532
    protected function getRootPathId(): string
533
    {
534
        return '';
535
    }
536
537
538
    /**
539
     * Return the validation mime type rules of attachment upload
540
     * @return string[]
541
     */
542
    protected function getAttachmentMimeRules(): array
543
    {
544
        return ['text/plain'];
545
    }
546
547
    /**
548
     * Return the validation extension rules of attachment upload
549
     * @return string[]
550
     */
551
    protected function getAttachmentExtensionRules(): array
552
    {
553
        return [];
554
    }
555
556
    /**
557
     * Return the validation max file size rules of attachment upload
558
     * @return int|string
559
     */
560
    protected function getAttachmentMaxSize(): int|string
561
    {
562
        return '2M';
563
    }
564
565
    /**
566
     * Return the image max file size value
567
     * @return int|string
568
     */
569
    protected function getImageMaxSize(): int|string
570
    {
571
        return '1M';
572
    }
573
}
574