Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <fabien@symfony.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
 
12
namespace Symfony\Component\HttpFoundation\File;
13
 
14
use Symfony\Component\HttpFoundation\File\Exception\CannotWriteFileException;
15
use Symfony\Component\HttpFoundation\File\Exception\ExtensionFileException;
16
use Symfony\Component\HttpFoundation\File\Exception\FileException;
17
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
18
use Symfony\Component\HttpFoundation\File\Exception\FormSizeFileException;
19
use Symfony\Component\HttpFoundation\File\Exception\IniSizeFileException;
20
use Symfony\Component\HttpFoundation\File\Exception\NoFileException;
21
use Symfony\Component\HttpFoundation\File\Exception\NoTmpDirFileException;
22
use Symfony\Component\HttpFoundation\File\Exception\PartialFileException;
23
use Symfony\Component\Mime\MimeTypes;
24
 
25
/**
26
 * A file uploaded through a form.
27
 *
28
 * @author Bernhard Schussek <bschussek@gmail.com>
29
 * @author Florian Eckerstorfer <florian@eckerstorfer.org>
30
 * @author Fabien Potencier <fabien@symfony.com>
31
 */
32
class UploadedFile extends File
33
{
34
    private bool $test;
35
    private string $originalName;
36
    private string $mimeType;
37
    private int $error;
38
 
39
    /**
40
     * Accepts the information of the uploaded file as provided by the PHP global $_FILES.
41
     *
42
     * The file object is only created when the uploaded file is valid (i.e. when the
43
     * isValid() method returns true). Otherwise the only methods that could be called
44
     * on an UploadedFile instance are:
45
     *
46
     *   * getClientOriginalName,
47
     *   * getClientMimeType,
48
     *   * isValid,
49
     *   * getError.
50
     *
51
     * Calling any other method on an non-valid instance will cause an unpredictable result.
52
     *
53
     * @param string      $path         The full temporary path to the file
54
     * @param string      $originalName The original file name of the uploaded file
55
     * @param string|null $mimeType     The type of the file as provided by PHP; null defaults to application/octet-stream
56
     * @param int|null    $error        The error constant of the upload (one of PHP's UPLOAD_ERR_XXX constants); null defaults to UPLOAD_ERR_OK
57
     * @param bool        $test         Whether the test mode is active
58
     *                                  Local files are used in test mode hence the code should not enforce HTTP uploads
59
     *
60
     * @throws FileException         If file_uploads is disabled
61
     * @throws FileNotFoundException If the file does not exist
62
     */
63
    public function __construct(string $path, string $originalName, string $mimeType = null, int $error = null, bool $test = false)
64
    {
65
        $this->originalName = $this->getName($originalName);
66
        $this->mimeType = $mimeType ?: 'application/octet-stream';
67
        $this->error = $error ?: \UPLOAD_ERR_OK;
68
        $this->test = $test;
69
 
70
        parent::__construct($path, \UPLOAD_ERR_OK === $this->error);
71
    }
72
 
73
    /**
74
     * Returns the original file name.
75
     *
76
     * It is extracted from the request from which the file has been uploaded.
77
     * Then it should not be considered as a safe value.
78
     */
79
    public function getClientOriginalName(): string
80
    {
81
        return $this->originalName;
82
    }
83
 
84
    /**
85
     * Returns the original file extension.
86
     *
87
     * It is extracted from the original file name that was uploaded.
88
     * Then it should not be considered as a safe value.
89
     */
90
    public function getClientOriginalExtension(): string
91
    {
92
        return pathinfo($this->originalName, \PATHINFO_EXTENSION);
93
    }
94
 
95
    /**
96
     * Returns the file mime type.
97
     *
98
     * The client mime type is extracted from the request from which the file
99
     * was uploaded, so it should not be considered as a safe value.
100
     *
101
     * For a trusted mime type, use getMimeType() instead (which guesses the mime
102
     * type based on the file content).
103
     *
104
     * @see getMimeType()
105
     */
106
    public function getClientMimeType(): string
107
    {
108
        return $this->mimeType;
109
    }
110
 
111
    /**
112
     * Returns the extension based on the client mime type.
113
     *
114
     * If the mime type is unknown, returns null.
115
     *
116
     * This method uses the mime type as guessed by getClientMimeType()
117
     * to guess the file extension. As such, the extension returned
118
     * by this method cannot be trusted.
119
     *
120
     * For a trusted extension, use guessExtension() instead (which guesses
121
     * the extension based on the guessed mime type for the file).
122
     *
123
     * @see guessExtension()
124
     * @see getClientMimeType()
125
     */
126
    public function guessClientExtension(): ?string
127
    {
128
        if (!class_exists(MimeTypes::class)) {
129
            throw new \LogicException('You cannot guess the extension as the Mime component is not installed. Try running "composer require symfony/mime".');
130
        }
131
 
132
        return MimeTypes::getDefault()->getExtensions($this->getClientMimeType())[0] ?? null;
133
    }
134
 
135
    /**
136
     * Returns the upload error.
137
     *
138
     * If the upload was successful, the constant UPLOAD_ERR_OK is returned.
139
     * Otherwise one of the other UPLOAD_ERR_XXX constants is returned.
140
     */
141
    public function getError(): int
142
    {
143
        return $this->error;
144
    }
145
 
146
    /**
147
     * Returns whether the file has been uploaded with HTTP and no error occurred.
148
     */
149
    public function isValid(): bool
150
    {
151
        $isOk = \UPLOAD_ERR_OK === $this->error;
152
 
153
        return $this->test ? $isOk : $isOk && is_uploaded_file($this->getPathname());
154
    }
155
 
156
    /**
157
     * Moves the file to a new location.
158
     *
159
     * @throws FileException if, for any reason, the file could not have been moved
160
     */
161
    public function move(string $directory, string $name = null): File
162
    {
163
        if ($this->isValid()) {
164
            if ($this->test) {
165
                return parent::move($directory, $name);
166
            }
167
 
168
            $target = $this->getTargetFile($directory, $name);
169
 
170
            set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
171
            try {
172
                $moved = move_uploaded_file($this->getPathname(), $target);
173
            } finally {
174
                restore_error_handler();
175
            }
176
            if (!$moved) {
177
                throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s).', $this->getPathname(), $target, strip_tags($error)));
178
            }
179
 
180
            @chmod($target, 0666 & ~umask());
181
 
182
            return $target;
183
        }
184
 
185
        switch ($this->error) {
186
            case \UPLOAD_ERR_INI_SIZE:
187
                throw new IniSizeFileException($this->getErrorMessage());
188
            case \UPLOAD_ERR_FORM_SIZE:
189
                throw new FormSizeFileException($this->getErrorMessage());
190
            case \UPLOAD_ERR_PARTIAL:
191
                throw new PartialFileException($this->getErrorMessage());
192
            case \UPLOAD_ERR_NO_FILE:
193
                throw new NoFileException($this->getErrorMessage());
194
            case \UPLOAD_ERR_CANT_WRITE:
195
                throw new CannotWriteFileException($this->getErrorMessage());
196
            case \UPLOAD_ERR_NO_TMP_DIR:
197
                throw new NoTmpDirFileException($this->getErrorMessage());
198
            case \UPLOAD_ERR_EXTENSION:
199
                throw new ExtensionFileException($this->getErrorMessage());
200
        }
201
 
202
        throw new FileException($this->getErrorMessage());
203
    }
204
 
205
    /**
206
     * Returns the maximum size of an uploaded file as configured in php.ini.
207
     *
208
     * @return int|float The maximum size of an uploaded file in bytes (returns float if size > PHP_INT_MAX)
209
     */
210
    public static function getMaxFilesize(): int|float
211
    {
212
        $sizePostMax = self::parseFilesize(\ini_get('post_max_size'));
213
        $sizeUploadMax = self::parseFilesize(\ini_get('upload_max_filesize'));
214
 
215
        return min($sizePostMax ?: \PHP_INT_MAX, $sizeUploadMax ?: \PHP_INT_MAX);
216
    }
217
 
218
    private static function parseFilesize(string $size): int|float
219
    {
220
        if ('' === $size) {
221
            return 0;
222
        }
223
 
224
        $size = strtolower($size);
225
 
226
        $max = ltrim($size, '+');
227
        if (str_starts_with($max, '0x')) {
228
            $max = \intval($max, 16);
229
        } elseif (str_starts_with($max, '0')) {
230
            $max = \intval($max, 8);
231
        } else {
232
            $max = (int) $max;
233
        }
234
 
235
        switch (substr($size, -1)) {
236
            case 't': $max *= 1024;
237
                // no break
238
            case 'g': $max *= 1024;
239
                // no break
240
            case 'm': $max *= 1024;
241
                // no break
242
            case 'k': $max *= 1024;
243
        }
244
 
245
        return $max;
246
    }
247
 
248
    /**
249
     * Returns an informative upload error message.
250
     */
251
    public function getErrorMessage(): string
252
    {
253
        static $errors = [
254
            \UPLOAD_ERR_INI_SIZE => 'The file "%s" exceeds your upload_max_filesize ini directive (limit is %d KiB).',
255
            \UPLOAD_ERR_FORM_SIZE => 'The file "%s" exceeds the upload limit defined in your form.',
256
            \UPLOAD_ERR_PARTIAL => 'The file "%s" was only partially uploaded.',
257
            \UPLOAD_ERR_NO_FILE => 'No file was uploaded.',
258
            \UPLOAD_ERR_CANT_WRITE => 'The file "%s" could not be written on disk.',
259
            \UPLOAD_ERR_NO_TMP_DIR => 'File could not be uploaded: missing temporary directory.',
260
            \UPLOAD_ERR_EXTENSION => 'File upload was stopped by a PHP extension.',
261
        ];
262
 
263
        $errorCode = $this->error;
264
        $maxFilesize = \UPLOAD_ERR_INI_SIZE === $errorCode ? self::getMaxFilesize() / 1024 : 0;
265
        $message = $errors[$errorCode] ?? 'The file "%s" was not uploaded due to an unknown error.';
266
 
267
        return sprintf($message, $this->getClientOriginalName(), $maxFilesize);
268
    }
269
}