PluginProbe
Packeta / 1.6.1
Packeta v1.6.1
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 1.6.1, at deps/nette/http/src/Http/FileUpload.php

211 lines 7.2 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 public const ImageMimeTypes = ['image/gif', 'image/png', 'image/jpeg', 'image/webp'];
28 /** @deprecated use FileUpload::ImageMimeTypes */
29 public const IMAGE_MIME_TYPES = self::ImageMimeTypes;
30 /** @var string */
31 private $name;
32 /** @var string|null */
33 private $fullPath;
34 /** @var string|false|null */
35 private $type;
36 /** @var int */
37 private $size;
38 /** @var string */
39 private $tmpName;
40 /** @var int */
41 private $error;
42 public function __construct(?array $value)
43 {
44 foreach (['name', 'size', 'tmp_name', 'error'] as $key) {
45 if (!isset($value[$key]) || !\is_scalar($value[$key])) {
46 $this->error = \UPLOAD_ERR_NO_FILE;
47 return;
48 // or throw exception?
49 }
50 }
51 $this->name = $value['name'];
52 $this->fullPath = $value['full_path'] ?? null;
53 $this->size = $value['size'];
54 $this->tmpName = $value['tmp_name'];
55 $this->error = $value['error'];
56 }
57 /**
58 * @deprecated use getUntrustedName()
59 */
60 public function getName() : string
61 {
62 return $this->name;
63 }
64 /**
65 * Returns the original file name as submitted by the browser. Do not trust the value returned by this method.
66 * A client could send a malicious filename with the intention to corrupt or hack your application.
67 */
68 public function getUntrustedName() : string
69 {
70 return $this->name;
71 }
72 /**
73 * Returns the sanitized file name. The resulting name contains only ASCII characters [a-zA-Z0-9.-].
74 * If the name does not contain such characters, it returns 'unknown'. If the file is JPEG, PNG, GIF, or WebP image,
75 * it returns the correct file extension. Do not blindly trust the value returned by this method.
76 */
77 public function getSanitizedName() : string
78 {
79 $name = \Packetery\Nette\Utils\Strings::webalize($this->name, '.', \false);
80 $name = \str_replace(['-.', '.-'], '.', $name);
81 $name = \trim($name, '.-');
82 $name = $name === '' ? 'unknown' : $name;
83 if ($this->isImage()) {
84 $name = \preg_replace('#\\.[^.]+$#D', '', $name);
85 $name .= '.' . ($this->getImageFileExtension() ?? 'unknown');
86 }
87 return $name;
88 }
89 /**
90 * Returns the original full path as submitted by the browser during directory upload. Do not trust the value
91 * returned by this method. A client could send a malicious directory structure with the intention to corrupt
92 * or hack your application.
93 *
94 * The full path is only available in PHP 8.1 and above. In previous versions, this method returns the file name.
95 */
96 public function getUntrustedFullPath() : string
97 {
98 return $this->fullPath ?? $this->name;
99 }
100 /**
101 * Detects the MIME content type of the uploaded file based on its signature. Requires PHP extension fileinfo.
102 * If the upload was not successful or the detection failed, it returns null.
103 */
104 public function getContentType() : ?string
105 {
106 if ($this->isOk() && $this->type === null) {
107 $this->type = \finfo_file(\finfo_open(\FILEINFO_MIME_TYPE), $this->tmpName);
108 }
109 return $this->type ?: null;
110 }
111 /**
112 * Returns the path of the temporary location of the uploaded file.
113 */
114 public function getSize() : int
115 {
116 return $this->size;
117 }
118 /**
119 * Returns the path of the temporary location of the uploaded file.
120 */
121 public function getTemporaryFile() : string
122 {
123 return $this->tmpName;
124 }
125 /**
126 * Returns the path of the temporary location of the uploaded file.
127 */
128 public function __toString() : string
129 {
130 return $this->tmpName;
131 }
132 /**
133 * Returns the error code. It is be one of UPLOAD_ERR_XXX constants.
134 * @see http://php.net/manual/en/features.file-upload.errors.php
135 */
136 public function getError() : int
137 {
138 return $this->error;
139 }
140 /**
141 * Returns true if the file was uploaded successfully.
142 */
143 public function isOk() : bool
144 {
145 return $this->error === \UPLOAD_ERR_OK;
146 }
147 /**
148 * Returns true if the user has uploaded a file.
149 */
150 public function hasFile() : bool
151 {
152 return $this->error !== \UPLOAD_ERR_NO_FILE;
153 }
154 /**
155 * Moves an uploaded file to a new location. If the destination file already exists, it will be overwritten.
156 * @return static
157 */
158 public function move(string $dest)
159 {
160 $dir = \dirname($dest);
161 \Packetery\Nette\Utils\FileSystem::createDir($dir);
162 @\unlink($dest);
163 // @ - file may not exists
164 \Packetery\Nette\Utils\Callback::invokeSafe(\is_uploaded_file($this->tmpName) ? 'move_uploaded_file' : 'rename', [$this->tmpName, $dest], function (string $message) use($dest) : void {
165 throw new \Packetery\Nette\InvalidStateException("Unable to move uploaded file '{$this->tmpName}' to '{$dest}'. {$message}");
166 });
167 @\chmod($dest, 0666);
168 // @ - possible low permission to chmod
169 $this->tmpName = $dest;
170 return $this;
171 }
172 /**
173 * Returns true if the uploaded file is a JPEG, PNG, GIF, or WebP image.
174 * Detection is based on its signature, the integrity of the file is not checked. Requires PHP extension fileinfo.
175 */
176 public function isImage() : bool
177 {
178 return \in_array($this->getContentType(), self::ImageMimeTypes, \true);
179 }
180 /**
181 * Loads an image.
182 * @throws \Packetery\Nette\Utils\ImageException If the upload was not successful or is not a valid image
183 */
184 public function toImage() : \Packetery\Nette\Utils\Image
185 {
186 return \Packetery\Nette\Utils\Image::fromFile($this->tmpName);
187 }
188 /**
189 * Returns a pair of [width, height] with dimensions of the uploaded image.
190 */
191 public function getImageSize() : ?array
192 {
193 return $this->isImage() ? \array_intersect_key(\getimagesize($this->tmpName), [0, 1]) : null;
194 }
195 /**
196 * Returns image file extension based on detected content type (without dot).
197 */
198 public function getImageFileExtension() : ?string
199 {
200 return $this->isImage() ? \explode('/', $this->getContentType())[1] : null;
201 }
202 /**
203 * Returns the contents of the uploaded file. If the upload was not successful, it returns null.
204 */
205 public function getContents() : ?string
206 {
207 // future implementation can try to work around safe_mode and open_basedir limitations
208 return $this->isOk() ? \file_get_contents($this->tmpName) : null;
209 }
210 }
211