errorCode = $errorCode; $this->size = $size; $this->localFileName = $localFileName; $this->suggestedFileName = $suggestedFileName; $this->suggestedMediaType = $suggestedMediaType; } /** * Retrieves the PHP file upload error code. * * @return int PHP file upload error code. */ public function getErrorCode(): int { return $this->errorCode; } /** * Retrieves the size of the uploaded file. * * @return int Size of uploaded file. */ public function getSize(): int { return $this->size; } /** * Retrieves the local path to the uploaded file. * * @return ?string Path to file, or null. */ public function getLocalFileName(): ?string { return $this->localFileName; } /** * Retrieves media type of the uploaded file. * * @return ?MediaType Type of file, or null. */ public function getLocalMediaType(): ?MediaType { return MediaType::fromPath($this->localFileName); } /** * Retrieves the suggested name for the uploaded file. * * @return string Suggested name for the file. */ public function getSuggestedFileName(): string { return $this->suggestedFileName; } /** * Retrieves the suggested media type for the uploaded file. * * @return MediaType Suggested type for the file. */ public function getSuggestedMediaType(): MediaType { return $this->suggestedMediaType; } /** * Checks whether the file has been moved to its final destination. * * @return bool true if it has been moved. */ public function hasMoved(): bool { return $this->hasMoved; } /** * Gets a read-only stream to the uploaded file. * * @throws RuntimeException If the file cannot be opened. * @return Stream Read-only stream of the upload file. */ public function getStream(): Stream { if($this->stream === null) { if($this->errorCode !== UPLOAD_ERR_OK) throw new RuntimeException('Can\'t open stream because of an upload error.'); if($this->hasMoved) throw new RuntimeException('Can\'t open stream because file has already been moved.'); $this->stream = FileStream::openRead($this->localFileName); } return $this->stream; } /** * Moves the uploaded file to its final destination. * * @param string $path Path to move the file to. * @throws RuntimeException If the file has already been moved. * @throws RuntimeException If an upload error occurred. * @throws InvalidArgumentException If the provided $path is not valid. * @throws RuntimeException If the file failed to move. */ public function moveTo(string $path): void { if($this->hasMoved) throw new RuntimeException('This uploaded file has already been moved.'); if($this->errorCode !== UPLOAD_ERR_OK) throw new RuntimeException('Can\'t move file because of an upload error.'); if(empty($path)) throw new InvalidArgumentException('$path is not a valid path.'); if($this->stream !== null) $this->stream->close(); $this->hasMoved = PHP_SAPI === 'CLI' ? rename($this->localFileName, $path) : move_uploaded_file($this->localFileName, $path); if(!$this->hasMoved) throw new RuntimeException('Failed to move file to ' . $path); $this->localFileName = $path; } public function close(): void { if($this->stream !== null) $this->stream->close(); } public function __destruct() { $this->close(); } /** * Creates a HttpUploadedFile instance from an entry in the $_FILES superglobal. * * @return HttpUploadedFile Uploaded file info. */ public static function createFromFILE(array $file): self { return new HttpUploadedFile( $file['error'] ?? UPLOAD_ERR_NO_FILE, $file['size'] ?? -1, $file['tmp_name'] ?? '', $file['name'] ?? '', $file['type'] ?? '' ); } /** * Creates a collection of HttpUploadedFile instances from the $_FILES superglobal. * * @return array Uploaded files. */ public static function createFromFILES(array $files): array { if(empty($files)) return []; return self::createObjectInstances(self::normalizeFILES($files)); } private static function traverseFILES(array $files, string $keyName): array { $arr = []; foreach($files as $key => $val) { $key = "_{$key}"; if(is_array($val)) { $arr[$key] = self::traverseFILES($val, $keyName); } else { $arr[$key][$keyName] = $val; } } return $arr; } private static function normalizeFILES(array $files): array { $out = []; foreach($files as $key => $arr) { if(empty($arr)) continue; $key = '_' . $key; if(is_int($arr['error'])) { $out[$key] = $arr; continue; } if(is_array($arr['error'])) { $keys = array_keys($arr); foreach($keys as $keyName) { $out[$key] = array_merge_recursive($out[$key] ?? [], self::traverseFILES($arr[$keyName], $keyName)); } continue; } } return $out; } private static function createObjectInstances(array $files): array { $coll = []; foreach($files as $key => $val) { $key = substr($key, 1); if(isset($val['error'])) { $coll[$key] = self::createFromFILE($val); } else { $coll[$key] = self::createObjectInstances($val); } } return $coll; } }