Subversion Repositories php-qbpwcf

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 liveuser 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 $test;
35
    private $originalName;
36
    private $mimeType;
37
    private $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
     * @return string The original name
80
     */
81
    public function getClientOriginalName()
82
    {
83
        return $this->originalName;
84
    }
85
 
86
    /**
87
     * Returns the original file extension.
88
     *
89
     * It is extracted from the original file name that was uploaded.
90
     * Then it should not be considered as a safe value.
91
     *
92
     * @return string The extension
93
     */
94
    public function getClientOriginalExtension()
95
    {
96
        return pathinfo($this->originalName, \PATHINFO_EXTENSION);
97
    }
98
 
99
    /**
100
     * Returns the file mime type.
101
     *
102
     * The client mime type is extracted from the request from which the file
103
     * was uploaded, so it should not be considered as a safe value.
104
     *
105
     * For a trusted mime type, use getMimeType() instead (which guesses the mime
106
     * type based on the file content).
107
     *
108
     * @return string The mime type
109
     *
110
     * @see getMimeType()
111
     */
112
    public function getClientMimeType()
113
    {
114
        return $this->mimeType;
115
    }
116
 
117
    /**
118
     * Returns the extension based on the client mime type.
119
     *
120
     * If the mime type is unknown, returns null.
121
     *
122
     * This method uses the mime type as guessed by getClientMimeType()
123
     * to guess the file extension. As such, the extension returned
124
     * by this method cannot be trusted.
125
     *
126
     * For a trusted extension, use guessExtension() instead (which guesses
127
     * the extension based on the guessed mime type for the file).
128
     *
129
     * @return string|null The guessed extension or null if it cannot be guessed
130
     *
131
     * @see guessExtension()
132
     * @see getClientMimeType()
133
     */
134
    public function guessClientExtension()
135
    {
136
        if (!class_exists(MimeTypes::class)) {
137
            throw new \LogicException('You cannot guess the extension as the Mime component is not installed. Try running "composer require symfony/mime".');
138
        }
139
 
140
        return MimeTypes::getDefault()->getExtensions($this->getClientMimeType())[0] ?? null;
141
    }
142
 
143
    /**
144
     * Returns the upload error.
145
     *
146
     * If the upload was successful, the constant UPLOAD_ERR_OK is returned.
147
     * Otherwise one of the other UPLOAD_ERR_XXX constants is returned.
148
     *
149
     * @return int The upload error
150
     */
151
    public function getError()
152
    {
153
        return $this->error;
154
    }
155
 
156
    /**
157
     * Returns whether the file was uploaded successfully.
158
     *
159
     * @return bool True if the file has been uploaded with HTTP and no error occurred
160
     */
161
    public function isValid()
162
    {
163
        $isOk = \UPLOAD_ERR_OK === $this->error;
164
 
165
        return $this->test ? $isOk : $isOk && is_uploaded_file($this->getPathname());
166
    }
167
 
168
    /**
169
     * Moves the file to a new location.
170
     *
171
     * @return File A File object representing the new file
172
     *
173
     * @throws FileException if, for any reason, the file could not have been moved
174
     */
175
    public function move(string $directory, string $name = null)
176
    {
177
        if ($this->isValid()) {
178
            if ($this->test) {
179
                return parent::move($directory, $name);
180
            }
181
 
182
            $target = $this->getTargetFile($directory, $name);
183
 
184
            set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
185
            $moved = move_uploaded_file($this->getPathname(), $target);
186
            restore_error_handler();
187
            if (!$moved) {
188
                throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s).', $this->getPathname(), $target, strip_tags($error)));
189
            }
190
 
191
            @chmod($target, 0666 & ~umask());
192
 
193
            return $target;
194
        }
195
 
196
        switch ($this->error) {
197
            case \UPLOAD_ERR_INI_SIZE:
198
                throw new IniSizeFileException($this->getErrorMessage());
199
            case \UPLOAD_ERR_FORM_SIZE:
200
                throw new FormSizeFileException($this->getErrorMessage());
201
            case \UPLOAD_ERR_PARTIAL:
202
                throw new PartialFileException($this->getErrorMessage());
203
            case \UPLOAD_ERR_NO_FILE:
204
                throw new NoFileException($this->getErrorMessage());
205
            case \UPLOAD_ERR_CANT_WRITE:
206
                throw new CannotWriteFileException($this->getErrorMessage());
207
            case \UPLOAD_ERR_NO_TMP_DIR:
208
                throw new NoTmpDirFileException($this->getErrorMessage());
209
            case \UPLOAD_ERR_EXTENSION:
210
                throw new ExtensionFileException($this->getErrorMessage());
211
        }
212
 
213
        throw new FileException($this->getErrorMessage());
214
    }
215
 
216
    /**
217
     * Returns the maximum size of an uploaded file as configured in php.ini.
218
     *
219
     * @return int The maximum size of an uploaded file in bytes
220
     */
221
    public static function getMaxFilesize()
222
    {
223
        $sizePostMax = self::parseFilesize(ini_get('post_max_size'));
224
        $sizeUploadMax = self::parseFilesize(ini_get('upload_max_filesize'));
225
 
226
        return min($sizePostMax ?: \PHP_INT_MAX, $sizeUploadMax ?: \PHP_INT_MAX);
227
    }
228
 
229
    /**
230
     * Returns the given size from an ini value in bytes.
231
     */
232
    private static function parseFilesize($size): int
233
    {
234
        if ('' === $size) {
235
            return 0;
236
        }
237
 
238
        $size = strtolower($size);
239
 
240
        $max = ltrim($size, '+');
241
        if (0 === strpos($max, '0x')) {
242
            $max = \intval($max, 16);
243
        } elseif (0 === strpos($max, '0')) {
244
            $max = \intval($max, 8);
245
        } else {
246
            $max = (int) $max;
247
        }
248
 
249
        switch (substr($size, -1)) {
250
            case 't': $max *= 1024;
251
            // no break
252
            case 'g': $max *= 1024;
253
            // no break
254
            case 'm': $max *= 1024;
255
            // no break
256
            case 'k': $max *= 1024;
257
        }
258
 
259
        return $max;
260
    }
261
 
262
    /**
263
     * Returns an informative upload error message.
264
     *
265
     * @return string The error message regarding the specified error code
266
     */
267
    public function getErrorMessage()
268
    {
269
        static $errors = [
270
            \UPLOAD_ERR_INI_SIZE => 'The file "%s" exceeds your upload_max_filesize ini directive (limit is %d KiB).',
271
            \UPLOAD_ERR_FORM_SIZE => 'The file "%s" exceeds the upload limit defined in your form.',
272
            \UPLOAD_ERR_PARTIAL => 'The file "%s" was only partially uploaded.',
273
            \UPLOAD_ERR_NO_FILE => 'No file was uploaded.',
274
            \UPLOAD_ERR_CANT_WRITE => 'The file "%s" could not be written on disk.',
275
            \UPLOAD_ERR_NO_TMP_DIR => 'File could not be uploaded: missing temporary directory.',
276
            \UPLOAD_ERR_EXTENSION => 'File upload was stopped by a PHP extension.',
277
        ];
278
 
279
        $errorCode = $this->error;
280
        $maxFilesize = \UPLOAD_ERR_INI_SIZE === $errorCode ? self::getMaxFilesize() / 1024 : 0;
281
        $message = isset($errors[$errorCode]) ? $errors[$errorCode] : 'The file "%s" was not uploaded due to an unknown error.';
282
 
283
        return sprintf($message, $this->getClientOriginalName(), $maxFilesize);
284
    }
285
}