PluginProbe
Packeta / trunk
Packeta vtrunk
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
packeta / deps / nette / http / src / Http / FileUpload.php

FileUpload.php in Packeta trunk, at deps/nette/http/src/Http/FileUpload.php

234 lines 8.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * This file is part of the Nette Framework (https://nette.org)
5 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6 */
7 declare (strict_types=1);
8 namespace Packetery\Nette\Http;
9
10 use Packetery\Nette;
11 /**
12 * Provides access to individual files that have been uploaded by a client.
13 *
14 * @property-read string $name
15 * @property-read string $sanitizedName
16 * @property-read string $untrustedFullPath
17 * @property-read string|null $contentType
18 * @property-read int $size
19 * @property-read string $temporaryFile
20 * @property-read int $error
21 * @property-read bool $ok
22 * @property-read string|null $contents
23 */
24 final class FileUpload
25 {
26 use \Packetery\Nette\SmartObject;
27 /** @deprecated */
28 public const IMAGE_MIME_TYPES = ['image/gif', 'image/png', 'image/jpeg', 'image/webp'];
29 /** @var string */
30 private $name;
31 /** @var string|null */
32 private $fullPath;
33 /** @var string|false|null */
34 private $type;
35 /** @var string|false|null */
36 private $extension;
37 /** @var int */
38 private $size;
39 /** @var string */
40 private $tmpName;
41 /** @var int */
42 private $error;
43 public function __construct(?array $value)
44 {
45 foreach (['name', 'size', 'tmp_name', 'error'] as $key) {
46 if (!isset($value[$key]) || !\is_scalar($value[$key])) {
47 $this->error = \UPLOAD_ERR_NO_FILE;
48 return;
49 // or throw exception?
50 }
51 }
52 $this->name = $value['name'];
53 $this->fullPath = $value['full_path'] ?? null;
54 $this->size = $value['size'];
55 $this->tmpName = $value['tmp_name'];
56 $this->error = $value['error'];
57 }
58 /**
59 * @deprecated use getUntrustedName()
60 */
61 public function getName() : string
62 {
63 return $this->name;
64 }
65 /**
66 * Returns the original file name as submitted by the browser. Do not trust the value returned by this method.
67 * A client could send a malicious filename with the intention to corrupt or hack your application.
68 */
69 public function getUntrustedName() : string
70 {
71 return $this->name;
72 }
73 /**
74 * Returns the sanitized file name. The resulting name contains only ASCII characters [a-zA-Z0-9.-].
75 * If the name does not contain such characters, it returns 'unknown'. If the file is JPEG, PNG, GIF, or WebP image,
76 * it returns the correct file extension. Do not blindly trust the value returned by this method.
77 */
78 public function getSanitizedName() : string
79 {
80 $name = \Packetery\Nette\Utils\Strings::webalize($this->name, '.', \false);
81 $name = \str_replace(['-.', '.-'], '.', $name);
82 $name = \trim($name, '.-');
83 $name = $name === '' ? 'unknown' : $name;
84 if ($ext = $this->getSuggestedExtension()) {
85 $name = \preg_replace('#\\.[^.]+$#D', '', $name);
86 $name .= '.' . $ext;
87 }
88 return $name;
89 }
90 /**
91 * Returns the original full path as submitted by the browser during directory upload. Do not trust the value
92 * returned by this method. A client could send a malicious directory structure with the intention to corrupt
93 * or hack your application.
94 *
95 * The full path is only available in PHP 8.1 and above. In previous versions, this method returns the file name.
96 */
97 public function getUntrustedFullPath() : string
98 {
99 return $this->fullPath ?? $this->name;
100 }
101 /**
102 * Detects the MIME content type of the uploaded file based on its signature. Requires PHP extension fileinfo.
103 * If the upload was not successful or the detection failed, it returns null.
104 */
105 public function getContentType() : ?string
106 {
107 if ($this->isOk() && $this->type === null) {
108 $this->type = \finfo_file(\finfo_open(\FILEINFO_MIME_TYPE), $this->tmpName);
109 }
110 return $this->type ?: null;
111 }
112 /**
113 * Returns the appropriate file extension (without the period) corresponding to the detected MIME type. Requires the PHP extension fileinfo.
114 */
115 public function getSuggestedExtension() : ?string
116 {
117 if ($this->isOk() && $this->extension === null) {
118 $exts = \finfo_file(\finfo_open(\FILEINFO_EXTENSION), $this->tmpName);
119 if ($exts && $exts !== '???') {
120 return $this->extension = \preg_replace('~[/,].*~', '', $exts);
121 }
122 [, , $type] = @\getimagesize($this->tmpName);
123 // @ - files smaller than 12 bytes causes read error
124 if ($type) {
125 return $this->extension = \image_type_to_extension($type, \false);
126 }
127 $this->extension = \false;
128 }
129 return $this->extension ?: null;
130 }
131 /**
132 * Returns the size of the uploaded file in bytes.
133 */
134 public function getSize() : int
135 {
136 return $this->size;
137 }
138 /**
139 * Returns the path of the temporary location of the uploaded file.
140 */
141 public function getTemporaryFile() : string
142 {
143 return $this->tmpName;
144 }
145 /**
146 * Returns the path of the temporary location of the uploaded file.
147 */
148 public function __toString() : string
149 {
150 return $this->tmpName;
151 }
152 /**
153 * Returns the error code. It is be one of UPLOAD_ERR_XXX constants.
154 * @see http://php.net/manual/en/features.file-upload.errors.php
155 */
156 public function getError() : int
157 {
158 return $this->error;
159 }
160 /**
161 * Returns true if the file was uploaded successfully.
162 */
163 public function isOk() : bool
164 {
165 return $this->error === \UPLOAD_ERR_OK;
166 }
167 /**
168 * Returns true if the user has uploaded a file.
169 */
170 public function hasFile() : bool
171 {
172 return $this->error !== \UPLOAD_ERR_NO_FILE;
173 }
174 /**
175 * Moves an uploaded file to a new location. If the destination file already exists, it will be overwritten.
176 * @return static
177 */
178 public function move(string $dest)
179 {
180 $dir = \dirname($dest);
181 \Packetery\Nette\Utils\FileSystem::createDir($dir);
182 @\unlink($dest);
183 // @ - file may not exists
184 \Packetery\Nette\Utils\Callback::invokeSafe(\is_uploaded_file($this->tmpName) ? 'move_uploaded_file' : 'rename', [$this->tmpName, $dest], function (string $message) use($dest) : void {
185 throw new \Packetery\Nette\InvalidStateException("Unable to move uploaded file '{$this->tmpName}' to '{$dest}'. {$message}");
186 });
187 @\chmod($dest, 0666);
188 // @ - possible low permission to chmod
189 $this->tmpName = $dest;
190 return $this;
191 }
192 /**
193 * Returns true if the uploaded file is an image and the format is supported by PHP, so it can be loaded using the toImage() method.
194 * Detection is based on its signature, the integrity of the file is not checked. Requires PHP extensions fileinfo & gd.
195 */
196 public function isImage() : bool
197 {
198 $flag = \imagetypes();
199 $types = \array_filter([$flag & \IMG_GIF ? 'image/gif' : null, $flag & \IMG_JPG ? 'image/jpeg' : null, $flag & \IMG_PNG ? 'image/png' : null, $flag & \IMG_WEBP ? 'image/webp' : null, $flag & 256 ? 'image/avif' : null]);
200 return \in_array($this->getContentType(), $types, \true);
201 }
202 /**
203 * Converts uploaded image to \Packetery\Nette\Utils\Image object.
204 * @throws \Packetery\Nette\Utils\ImageException If the upload was not successful or is not a valid image
205 */
206 public function toImage() : \Packetery\Nette\Utils\Image
207 {
208 return \Packetery\Nette\Utils\Image::fromFile($this->tmpName);
209 }
210 /**
211 * Returns a pair of [width, height] with dimensions of the uploaded image.
212 */
213 public function getImageSize() : ?array
214 {
215 return $this->isImage() ? \array_intersect_key(\getimagesize($this->tmpName), [0, 1]) : null;
216 }
217 /**
218 * Returns image file extension based on detected content type (without dot).
219 * @deprecated use getSuggestedExtension()
220 */
221 public function getImageFileExtension() : ?string
222 {
223 return $this->getSuggestedExtension();
224 }
225 /**
226 * Returns the contents of the uploaded file. If the upload was not successful, it returns null.
227 */
228 public function getContents() : ?string
229 {
230 // future implementation can try to work around safe_mode and open_basedir limitations
231 return $this->isOk() ? \file_get_contents($this->tmpName) : null;
232 }
233 }
234