Passed
Push — master ( 1d699e...0f9ddd )
by Thierry
04:26 queued 02:06
created

UploadManager::getUploadedFile()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 13
nc 3
nop 3
dl 0
loc 22
rs 9.8333
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A UploadManager::getUploadTempDir() 0 9 3
1
<?php
2
3
/**
4
 * UploadManager.php - This class processes uploaded files.
5
 *
6
 * @package jaxon-core
0 ignored issues
show
Coding Style introduced by
Package name "jaxon-core" is not valid; consider "Jaxoncore" instead
Loading history...
7
 * @author Thierry Feuzeu <[email protected]>
8
 * @license https://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
9
 * @link https://github.com/jaxon-php/jaxon-core
10
 */
0 ignored issues
show
Coding Style introduced by
Missing @category tag in file comment
Loading history...
Coding Style introduced by
PHP version not specified
Loading history...
11
12
namespace Jaxon\Request\Upload;
13
14
use Jaxon\Config\ConfigManager;
15
use Jaxon\Request\Validator;
16
use Jaxon\Utils\Translation\Translator;
17
use Jaxon\Exception\RequestException;
18
use Nyholm\Psr7\UploadedFile;
19
use Psr\Http\Message\ServerRequestInterface;
20
21
use Closure;
22
use Exception;
23
24
use function bin2hex;
25
use function file_exists;
26
use function file_get_contents;
27
use function file_put_contents;
28
use function is_array;
29
use function is_dir;
30
use function is_readable;
31
use function is_string;
32
use function is_writable;
33
use function json_decode;
34
use function json_encode;
35
use function mkdir;
36
use function random_bytes;
37
use function rtrim;
38
use function str_shuffle;
39
use function substr;
40
use function trim;
41
use function unlink;
42
43
class UploadManager
0 ignored issues
show
Coding Style introduced by
Missing doc comment for class UploadManager
Loading history...
44
{
45
    /**
46
     * @var ConfigManager
47
     */
48
    protected $xConfigManager;
0 ignored issues
show
Coding Style introduced by
Expected 1 blank line(s) before first member var; 0 found
Loading history...
49
50
    /**
51
     * The request data validator
52
     *
53
     * @var Validator
54
     */
55
    protected $xValidator;
56
57
    /**
58
     * @var Translator
59
     */
60
    protected $xTranslator;
61
62
    /**
63
     * The subdir where uploaded files are stored
64
     *
65
     * @var string
66
     */
67
    protected $sUploadSubdir = '';
68
69
    /**
70
     * The constructor
71
     *
72
     * @param ConfigManager $xConfigManager
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
73
     * @param Validator $xValidator
0 ignored issues
show
Coding Style introduced by
Expected 5 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Missing parameter comment
Loading history...
74
     * @param Translator $xTranslator
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 4 spaces after parameter type; 1 found
Loading history...
75
     */
76
    public function __construct(ConfigManager $xConfigManager, Validator $xValidator, Translator $xTranslator)
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines before function; 1 found
Loading history...
77
    {
78
        $this->xConfigManager = $xConfigManager;
79
        $this->xValidator = $xValidator;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
80
        $this->xTranslator = $xTranslator;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
81
        $this->sUploadSubdir = $this->randomName() . DIRECTORY_SEPARATOR;
82
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
83
84
    /**
85
     * Generate a random name
86
     *
87
     * @return string
88
     */
89
    protected function randomName(): string
90
    {
91
        try
92
        {
93
            return bin2hex(random_bytes(7));
94
        }
95
        catch(Exception $e){}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
96
        // Generate the name
97
        $sChars = '0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz';
98
        return substr(str_shuffle($sChars), 0, 14);
99
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
100
101
    /**
102
     * Filter uploaded file name
103
     *
104
     * @param Closure $cNameSanitizer    The closure which filters filenames
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
105
     *
106
     * @return void
107
     */
108
    public function setNameSanitizer(Closure $cNameSanitizer)
109
    {
110
        File::setNameSanitizer($cNameSanitizer);
111
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
112
113
    /**
114
     * Make sure the upload dir exists and is writable
115
     *
116
     * @param string $sUploadDir    The filename
117
     * @param string $sUploadSubDir    The filename
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
118
     *
119
     * @return string
120
     * @throws RequestException
121
     */
122
    private function _makeUploadDir(string $sUploadDir, string $sUploadSubDir): string
123
    {
124
        $sUploadDir = rtrim(trim($sUploadDir), '/\\') . DIRECTORY_SEPARATOR;
125
        // Verify that the upload dir exists and is writable
126
        if(!is_writable($sUploadDir))
127
        {
128
            throw new RequestException($this->xTranslator->trans('errors.upload.access'));
129
        }
130
        $sUploadDir .= $sUploadSubDir;
131
        if(!file_exists($sUploadDir) && !@mkdir($sUploadDir))
132
        {
133
            throw new RequestException($this->xTranslator->trans('errors.upload.access'));
134
        }
135
        return $sUploadDir;
136
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
137
138
    /**
139
     * Get the path to the upload dir
140
     *
141
     * @param string $sFieldId    The filename
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
142
     *
143
     * @return string
144
     * @throws RequestException
145
     */
146
    protected function getUploadDir(string $sFieldId): string
147
    {
148
        // Default upload dir
149
        $sDefaultUploadDir = $this->xConfigManager->getOption('upload.default.dir');
150
        $sUploadDir = $this->xConfigManager->getOption('upload.files.' . $sFieldId . '.dir', $sDefaultUploadDir);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 8 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
151
        if(!is_string($sUploadDir) || !is_dir($sUploadDir))
152
        {
153
            throw new RequestException($this->xTranslator->trans('errors.upload.access'));
154
        }
155
        return $this->_makeUploadDir($sUploadDir, $this->sUploadSubdir);
156
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
157
158
    /**
159
     * Get the path to the upload temp dir
160
     *
161
     * @return string
162
     * @throws RequestException
163
     */
164
    protected function getUploadTempDir(): string
165
    {
166
        // Default upload dir
167
        $sUploadDir = $this->xConfigManager->getOption('upload.default.dir');
168
        if(!is_string($sUploadDir) || !is_dir($sUploadDir))
169
        {
170
            throw new RequestException($this->xTranslator->trans('errors.upload.access'));
171
        }
172
        return $this->_makeUploadDir($sUploadDir, 'tmp' . DIRECTORY_SEPARATOR);
173
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
174
175
    /**
176
     * Get the path to the upload temp file
177
     *
178
     * @param string $sTempFile
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
179
     *
180
     * @return string
181
     * @throws RequestException
182
     */
183
    protected function getUploadTempFile(string $sTempFile): string
184
    {
185
        // Verify file name validity
186
        if(!$this->xValidator->validateTempFileName($sTempFile))
187
        {
188
            throw new RequestException($this->xTranslator->trans('errors.upload.invalid'));
189
        }
190
        $sUploadDir = $this->xConfigManager->getOption('upload.default.dir', '');
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
191
        $sUploadDir = rtrim(trim($sUploadDir), '/\\') . DIRECTORY_SEPARATOR;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
192
        $sUploadDir .= 'tmp' . DIRECTORY_SEPARATOR;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
193
        $sUploadTempFile = $sUploadDir . $sTempFile . '.json';
194
        if(!is_readable($sUploadTempFile))
195
        {
196
            throw new RequestException($this->xTranslator->trans('errors.upload.access'));
197
        }
198
        return $sUploadTempFile;
199
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
200
201
    /**
202
     * Check uploaded files
203
     *
204
     * @param string $sVarName
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 7 spaces after parameter type; 1 found
Loading history...
205
     * @param UploadedFile $xHttpFile    The uploaded file
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
206
     * @param File $xFile    The uploaded file
0 ignored issues
show
Coding Style introduced by
Expected 5 spaces after parameter name; 4 found
Loading history...
Coding Style introduced by
Expected 9 spaces after parameter type; 1 found
Loading history...
207
     *
208
     * @return void
209
     * @throws RequestException
210
     */
211
    private function checkFile(string $sVarName, UploadedFile $xHttpFile, File $xFile)
212
    {
213
        // Verify upload result
214
        if($xHttpFile->getError())
215
        {
216
            throw new RequestException($this->xTranslator->trans('errors.upload.failed',
217
                ['name' => $xHttpFile->getClientFilename()]));
218
        }
219
        // Verify file validity (format, size)
220
        if(!$this->xValidator->validateUploadedFile($sVarName, $xFile))
221
        {
222
            throw new RequestException($this->xValidator->getErrorMessage());
223
        }
224
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
225
226
    /**
227
     * Read uploaded files info from HTTP request data
228
     *
229
     * @param ServerRequestInterface $xRequest
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
230
     *
231
     * @return array
232
     * @throws RequestException
233
     */
234
    public function readFromHttpData(ServerRequestInterface $xRequest): array
235
    {
236
        // Get the uploaded files
237
        $aTempFiles = $xRequest->getUploadedFiles();
238
239
        $aUserFiles = [];
240
        $aAllFiles = []; // A flat list of all uploaded files
241
        foreach($aTempFiles as $sVarName => $aFiles)
242
        {
243
            $aUserFiles[$sVarName] = [];
244
            // Get the path to the upload dir
245
            $sUploadDir = $this->getUploadDir($sVarName);
246
247
            if(!is_array($aFiles))
248
            {
249
                $aFiles = [$aFiles];
250
            }
251
            foreach($aFiles as $xFile)
252
            {
253
                // Set the user file data
254
                $xUploadedFile = File::fromHttpFile($sVarName, $sUploadDir, $xFile);
255
                // Check the uploaded file validity
256
                $this->checkFile($sVarName, $xFile, $xUploadedFile);
257
                // All's right, save the file for copy.
258
                $aAllFiles[] = ['temp' => $xFile, 'user' => $xUploadedFile];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 13 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
259
                $aUserFiles[$sVarName][] = $xUploadedFile;
260
            }
261
        }
262
        // Copy the uploaded files from the temp dir to the user dir
263
        foreach($aAllFiles as $aFiles)
264
        {
265
            $aFiles['temp']->moveTo($aFiles['user']->path());
266
        }
267
        return $aUserFiles;
268
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
269
270
    /**
271
     * Save uploaded files info to a temp file
272
     *
273
     * @param array $aUserFiles
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
274
     *
275
     * @return string
276
     * @throws RequestException
277
     */
278
    public function saveToTempFile(array $aUserFiles): string
279
    {
280
        // Convert uploaded file to an array
281
        $aFiles = [];
282
        foreach($aUserFiles as $sVarName => $aVarFiles)
283
        {
284
            $aFiles[$sVarName] = [];
285
            foreach($aVarFiles as $aVarFile)
286
            {
287
                $aFiles[$sVarName][] = $aVarFile->toTempData();
288
            }
289
        }
290
        // Save upload data in a temp file
291
        $sUploadDir = $this->getUploadTempDir();
292
        $sTempFile = $this->randomName();
293
        file_put_contents($sUploadDir . $sTempFile . '.json', json_encode($aFiles));
294
        return $sTempFile;
295
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
296
297
    /**
298
     * Read uploaded files info from a temp file
299
     *
300
     * @param string $sTempFile
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
301
     *
302
     * @return array
303
     * @throws RequestException
304
     */
305
    public function readFromTempFile(string $sTempFile): array
306
    {
307
        // Upload temp file
308
        $sUploadTempFile = $this->getUploadTempFile($sTempFile);
309
        $aFiles = json_decode(file_get_contents($sUploadTempFile), true);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 10 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
310
        $aUserFiles = [];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
311
        foreach($aFiles as $sVarName => $aVarFiles)
312
        {
313
            $aUserFiles[$sVarName] = [];
314
            foreach($aVarFiles as $aVarFile)
315
            {
316
                $aUserFiles[$sVarName][] = File::fromTempFile($aVarFile);
317
            }
318
        }
319
        @unlink($sUploadTempFile);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

319
        /** @scrutinizer ignore-unhandled */ @unlink($sUploadTempFile);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
320
        return $aUserFiles;
321
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 0 found
Loading history...
322
}
323