Completed
Push — master ( ac238c...da4625 )
by Jan
04:59
created

AttachmentHelper::placeholderToRealPath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
c 2
b 0
f 0
nc 1
nop 1
dl 0
loc 13
rs 10

1 Method

Rating   Name   Duplication   Size   Complexity  
A AttachmentHelper::getFileSize() 0 7 2
1
<?php
2
/**
3
 *
4
 * part-db version 0.1
5
 * Copyright (C) 2005 Christoph Lechner
6
 * http://www.cl-projects.de/
7
 *
8
 * part-db version 0.2+
9
 * Copyright (C) 2009 K. Jacobs and others (see authors.php)
10
 * http://code.google.com/p/part-db/
11
 *
12
 * Part-DB Version 0.4+
13
 * Copyright (C) 2016 - 2019 Jan Böhmer
14
 * https://github.com/jbtronics
15
 *
16
 * This program is free software; you can redistribute it and/or
17
 * modify it under the terms of the GNU General Public License
18
 * as published by the Free Software Foundation; either version 2
19
 * of the License, or (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU General Public License
27
 * along with this program; if not, write to the Free Software
28
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
29
 *
30
 */
31
32
namespace App\Services;
33
34
35
use App\Entity\Attachments\Attachment;
36
use App\Entity\Attachments\AttachmentTypeAttachment;
37
use App\Entity\Attachments\CategoryAttachment;
38
use App\Entity\Attachments\CurrencyAttachment;
39
use App\Entity\Attachments\DeviceAttachment;
40
use App\Entity\Attachments\FootprintAttachment;
41
use App\Entity\Attachments\GroupAttachment;
42
use App\Entity\Attachments\ManufacturerAttachment;
43
use App\Entity\Attachments\MeasurementUnitAttachment;
44
use App\Entity\Attachments\PartAttachment;
45
use App\Entity\Attachments\StorelocationAttachment;
46
use App\Entity\Attachments\SupplierAttachment;
47
use App\Entity\Attachments\UserAttachment;
48
use App\Services\Attachments\AttachmentPathResolver;
49
use Symfony\Component\HttpFoundation\File\UploadedFile;
50
51
class AttachmentHelper
52
{
53
54
    protected $pathResolver;
55
56
    public function __construct(AttachmentPathResolver $pathResolver)
57
    {
58
        $this->pathResolver = $pathResolver;
59
    }
60
61
    /**
62
     * Gets an SPLFileInfo object representing the file associated with the attachment.
63
     * @param Attachment $attachment The attachment for which the file should be generated
64
     * @return \SplFileInfo|null The fileinfo for the attachment file. Null, if the attachment is external or has
65
     * invalid file.
66
     */
67
    public function attachmentToFile(Attachment $attachment) : ?\SplFileInfo
68
    {
69
        if ($attachment->isExternal() || !$this->isFileExisting($attachment)) {
70
            return null;
71
        }
72
73
        return new \SplFileInfo($this->toAbsoluteFilePath($attachment));
74
    }
75
76
    /**
77
     * Returns the absolute filepath of the attachment. Null is returned, if the attachment is externally saved.
78
     * @param Attachment $attachment The attachment for which the filepath should be determined
79
     * @return string|null
80
     */
81
    public function toAbsoluteFilePath(Attachment $attachment): ?string
82
    {
83
        if (empty($attachment->getPath())) {
84
            return null;
85
        }
86
87
        if ($attachment->isExternal()) {
88
            return null;
89
        }
90
91
        $path = $attachment->getPath();
92
        $path = $this->pathResolver->placeholderToRealPath($path);
93
        return realpath($path);
94
    }
95
96
    /**
97
     * Checks if the file in this attachement is existing. This works for files on the HDD, and for URLs
98
     * (it's not checked if the ressource behind the URL is really existing, so for every external attachment true is returned).
99
     *
100
     * @param Attachment $attachment The attachment for which the existence should be checked
101
     *
102
     * @return bool True if the file is existing.
103
     */
104
    public function isFileExisting(Attachment $attachment): bool
105
    {
106
        if (empty($attachment->getPath())) {
107
            return false;
108
        }
109
110
        return file_exists($this->toAbsoluteFilePath($attachment)) || $attachment->isExternal();
111
    }
112
113
    /**
114
     * Returns the filesize of the attachments in bytes.
115
     * For external attachments, null is returned.
116
     *
117
     * @param Attachment $attachment The filesize for which the filesize should be calculated.
118
     * @return int|null
119
     */
120
    public function getFileSize(Attachment $attachment): ?int
121
    {
122
        if ($attachment->isExternal()) {
123
            return null;
124
        }
125
126
        return filesize($this->toAbsoluteFilePath($attachment));
127
    }
128
129
    /**
130
     * Returns a human readable version of the attachment file size.
131
     * For external attachments, null is returned.
132
     *
133
     * @param Attachment $attachment
134
     * @param int $decimals The number of decimals numbers that should be printed
135
     * @return string|null A string like 1.3M
136
     */
137
    public function getHumanFileSize(Attachment $attachment, $decimals = 2): ?string
138
    {
139
        $bytes = $this->getFileSize($attachment);
140
141
        if ($bytes == null) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $bytes of type integer|null against null; this is ambiguous if the integer can be zero. Consider using a strict comparison === instead.
Loading history...
142
            return null;
143
        }
144
145
        //Format filesize for human reading
146
        //Taken from: https://www.php.net/manual/de/function.filesize.php#106569 and slightly modified
147
148
        $sz = 'BKMGTP';
149
        $factor = (int) floor((strlen($bytes) - 1) / 3);
150
        return sprintf("%.{$decimals}f", $bytes / 1024 ** $factor) . @$sz[$factor];
151
    }
152
153
    /**
154
     * Generate a path to a folder, where this attachment can save its file.
155
     * @param Attachment $attachment The attachment for which the folder should be generated
156
     * @return string The path to the folder (without trailing slash)
157
     */
158
    public function generateFolderForAttachment(Attachment $attachment) : string
159
    {
160
        $mapping = [PartAttachment::class => 'part', AttachmentTypeAttachment::class => 'attachment_type',
161
            CategoryAttachment::class => 'category', CurrencyAttachment::class => 'currency',
162
            DeviceAttachment::class => 'device', FootprintAttachment::class => 'footprint',
163
            GroupAttachment::class => 'group', ManufacturerAttachment::class => 'manufacturer',
164
            MeasurementUnitAttachment::class => 'measurement_unit', StorelocationAttachment::class => 'storelocation',
165
            SupplierAttachment::class => 'supplier', UserAttachment::class => 'user'];
166
167
        $path = $this->base_path . DIRECTORY_SEPARATOR . $mapping[get_class($attachment)] . DIRECTORY_SEPARATOR . $attachment->getElement()->getID();
0 ignored issues
show
Bug Best Practice introduced by
The property base_path does not exist on App\Services\AttachmentHelper. Did you maybe forget to declare it?
Loading history...
168
        return $path;
169
    }
170
171
    /**
172
     * Moves the given uploaded file to a permanent place and saves it into the attachment
173
     * @param Attachment $attachment The attachment in which the file should be saved
174
     * @param UploadedFile|null $file The file which was uploaded
175
     * @return Attachment The attachment with the new filepath
176
     */
177
    public function upload(Attachment $attachment, ?UploadedFile $file) : Attachment
178
    {
179
        //If file is null, do nothing (helpful, so we dont have to check if the file was reuploaded in controller)
180
        if (!$file) {
181
            return $attachment;
182
        }
183
184
        $folder = $this->generateFolderForAttachment($attachment);
185
186
        //Sanatize filename
187
        $originalFilename = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
0 ignored issues
show
Unused Code introduced by
The assignment to $originalFilename is dead and can be removed.
Loading history...
188
        $newFilename = $attachment->getName() . '-' . uniqid('', false) . '.' . $file->guessExtension();
189
190
        //Move our temporay attachment to its final location
191
        $file_path = $file->move($folder, $newFilename)->getRealPath();
192
193
        //Make our file path relative to %BASE%
194
        $file_path = $this->pathResolver->realPathToPlaceholder($file_path);
195
196
        //Save the path to the attachment
197
        $attachment->setPath($file_path);
0 ignored issues
show
Bug introduced by
It seems like $file_path can also be of type null; however, parameter $path of App\Entity\Attachments\Attachment::setPath() 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

197
        $attachment->setPath(/** @scrutinizer ignore-type */ $file_path);
Loading history...
198
        //And save original filename
199
        $attachment->setFilename($file->getClientOriginalName());
200
201
        return $attachment;
202
    }
203
204
}