Issues (257)

src/Services/Attachments/AttachmentManager.php (1 issue)

Labels
Severity
1
<?php
2
/**
3
 * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
4
 *
5
 * Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as published
9
 * by the Free Software Foundation, either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License
18
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
 */
20
21
declare(strict_types=1);
22
23
namespace App\Services\Attachments;
24
25
use App\Entity\Attachments\Attachment;
26
use SplFileInfo;
27
use function strlen;
28
29
/**
30
 * This service contains basic commonly used functions to work with attachments.
31
 * Especially this services gives you important infos about attachments, that can not be retrieved via the entities
32
 * (like filesize or if a file is existing).
33
 *
34
 * Special operations like getting attachment urls or handling file uploading/downloading are in their own services.
35
 */
36
class AttachmentManager
37
{
38
    protected AttachmentPathResolver $pathResolver;
39
40
    public function __construct(AttachmentPathResolver $pathResolver)
41
    {
42
        $this->pathResolver = $pathResolver;
43
    }
44
45
    /**
46
     * Gets an SPLFileInfo object representing the file associated with the attachment.
47
     *
48
     * @param Attachment $attachment The attachment for which the file should be generated
49
     *
50
     * @return SplFileInfo|null The fileinfo for the attachment file. Null, if the attachment is external or has
51
     *                          invalid file.
52
     */
53
    public function attachmentToFile(Attachment $attachment): ?SplFileInfo
54
    {
55
        if ($attachment->isExternal() || !$this->isFileExisting($attachment)) {
56
            return null;
57
        }
58
59
        return new SplFileInfo($this->toAbsoluteFilePath($attachment));
60
    }
61
62
    /**
63
     * Returns the absolute filepath of the attachment. Null is returned, if the attachment is externally saved,
64
     * or is not existing.
65
     *
66
     * @param Attachment $attachment The attachment for which the filepath should be determined
67
     */
68
    public function toAbsoluteFilePath(Attachment $attachment): ?string
69
    {
70
        if (empty($attachment->getPath())) {
71
            return null;
72
        }
73
74
        if ($attachment->isExternal()) {
75
            return null;
76
        }
77
78
        $path = $this->pathResolver->placeholderToRealPath($attachment->getPath());
79
80
        //realpath does not work with null as argument
81
        if (null === $path) {
82
            return null;
83
        }
84
85
        $tmp = realpath($path);
86
        //If path is not existing realpath returns false.
87
        if (false === $tmp) {
88
            return null;
89
        }
90
91
        return $tmp;
92
    }
93
94
    /**
95
     * Checks if the file in this attachement is existing. This works for files on the HDD, and for URLs
96
     * (it's not checked if the ressource behind the URL is really existing, so for every external attachment true is returned).
97
     *
98
     * @param Attachment $attachment The attachment for which the existence should be checked
99
     *
100
     * @return bool true if the file is existing
101
     */
102
    public function isFileExisting(Attachment $attachment): bool
103
    {
104
        if (empty($attachment->getPath())) {
105
            return false;
106
        }
107
108
        if ($attachment->isExternal()) {
109
            return true;
110
        }
111
112
        $absolute_path = $this->toAbsoluteFilePath($attachment);
113
114
        if (null === $absolute_path) {
115
            return false;
116
        }
117
118
        return file_exists($absolute_path);
119
    }
120
121
    /**
122
     * Returns the filesize of the attachments in bytes.
123
     * For external attachments or not existing attachments, null is returned.
124
     *
125
     * @param Attachment $attachment the filesize for which the filesize should be calculated
126
     */
127
    public function getFileSize(Attachment $attachment): ?int
128
    {
129
        if ($attachment->isExternal()) {
130
            return null;
131
        }
132
133
        if (!$this->isFileExisting($attachment)) {
134
            return null;
135
        }
136
137
        $tmp = filesize($this->toAbsoluteFilePath($attachment));
0 ignored issues
show
It seems like $this->toAbsoluteFilePath($attachment) can also be of type null; however, parameter $filename of filesize() does only seem to accept string, 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

137
        $tmp = filesize(/** @scrutinizer ignore-type */ $this->toAbsoluteFilePath($attachment));
Loading history...
138
139
        return  false !== $tmp ? $tmp : null;
140
    }
141
142
    /**
143
     * Returns a human readable version of the attachment file size.
144
     * For external attachments, null is returned.
145
     *
146
     * @param  int  $decimals The number of decimals numbers that should be printed
147
     *
148
     * @return string|null A string like 1.3M
149
     */
150
    public function getHumanFileSize(Attachment $attachment, int $decimals = 2): ?string
151
    {
152
        $bytes = $this->getFileSize($attachment);
153
154
        if (null === $bytes) {
155
            return null;
156
        }
157
158
        //Format filesize for human reading
159
        //Taken from: https://www.php.net/manual/de/function.filesize.php#106569 and slightly modified
160
161
        $sz = 'BKMGTP';
162
        $factor = (int) floor((strlen((string) $bytes) - 1) / 3);
163
164
        return sprintf("%.{$decimals}f", $bytes / 1024 ** $factor).@$sz[$factor];
165
    }
166
}
167