1: | <?php declare(strict_types=1); |
2: | |
3: | namespace Salient\Http; |
4: | |
5: | use Psr\Http\Message\StreamInterface; |
6: | use Psr\Http\Message\UploadedFileInterface; |
7: | use Salient\Http\Exception\UploadedFileException; |
8: | use Salient\Utility\Exception\InvalidArgumentTypeException; |
9: | use Salient\Utility\File; |
10: | |
11: | |
12: | |
13: | |
14: | |
15: | |
16: | class HttpServerRequestUpload implements UploadedFileInterface |
17: | { |
18: | protected const ERROR_MESSAGE = [ |
19: | \UPLOAD_ERR_OK => 'There is no error, the file uploaded with success', |
20: | \UPLOAD_ERR_INI_SIZE => 'The uploaded file exceeds the upload_max_filesize directive in php.ini', |
21: | \UPLOAD_ERR_FORM_SIZE => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form', |
22: | \UPLOAD_ERR_PARTIAL => 'The uploaded file was only partially uploaded', |
23: | \UPLOAD_ERR_NO_FILE => 'No file was uploaded', |
24: | \UPLOAD_ERR_NO_TMP_DIR => 'Missing a temporary folder', |
25: | \UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk', |
26: | \UPLOAD_ERR_EXTENSION => 'A PHP extension stopped the file upload', |
27: | ]; |
28: | |
29: | protected StreamInterface $Stream; |
30: | protected string $File; |
31: | protected ?int $Size; |
32: | protected int $Error; |
33: | protected ?string $ClientFilename; |
34: | protected ?string $ClientMediaType; |
35: | private bool $IsMoved = false; |
36: | |
37: | |
38: | |
39: | |
40: | |
41: | |
42: | public function __construct( |
43: | $resource, |
44: | ?int $size = null, |
45: | int $error = \UPLOAD_ERR_OK, |
46: | ?string $clientFilename = null, |
47: | ?string $clientMediaType = null |
48: | ) { |
49: | $this->Size = $size; |
50: | $this->Error = $error; |
51: | $this->ClientFilename = $clientFilename; |
52: | $this->ClientMediaType = $clientMediaType; |
53: | |
54: | if ($this->Error !== \UPLOAD_ERR_OK) { |
55: | return; |
56: | } |
57: | |
58: | if ($resource instanceof StreamInterface) { |
59: | $this->Stream = $resource; |
60: | } elseif (File::isStream($resource)) { |
61: | $this->Stream = new HttpStream($resource); |
62: | } elseif (is_string($resource)) { |
63: | $this->File = $resource; |
64: | } else { |
65: | throw new InvalidArgumentTypeException( |
66: | 1, |
67: | 'resource', |
68: | 'StreamInterface|resource|string', |
69: | $resource |
70: | ); |
71: | } |
72: | } |
73: | |
74: | |
75: | |
76: | |
77: | public function getStream(): StreamInterface |
78: | { |
79: | $this->assertIsValid(); |
80: | |
81: | return $this->Stream ?? new HttpStream(File::open($this->File, 'r')); |
82: | } |
83: | |
84: | |
85: | |
86: | |
87: | public function moveTo(string $targetPath): void |
88: | { |
89: | $this->assertIsValid(); |
90: | |
91: | if (isset($this->File)) { |
92: | if (\PHP_SAPI === 'cli') { |
93: | $result = @rename($this->File, $targetPath); |
94: | } else { |
95: | $result = @move_uploaded_file($this->File, $targetPath); |
96: | } |
97: | if ($result === false) { |
98: | $error = error_get_last(); |
99: | throw new UploadedFileException($error['message'] ?? sprintf( |
100: | 'Error moving uploaded file %s to %s', |
101: | $this->File, |
102: | $targetPath, |
103: | )); |
104: | } |
105: | } else { |
106: | $target = new HttpStream(File::open($targetPath, 'w')); |
107: | HttpStream::copyToStream($this->Stream, $target); |
108: | } |
109: | |
110: | $this->IsMoved = true; |
111: | } |
112: | |
113: | |
114: | |
115: | |
116: | public function getSize(): ?int |
117: | { |
118: | return $this->Size; |
119: | } |
120: | |
121: | |
122: | |
123: | |
124: | public function getError(): int |
125: | { |
126: | return $this->Error; |
127: | } |
128: | |
129: | |
130: | |
131: | |
132: | public function getClientFilename(): ?string |
133: | { |
134: | return $this->ClientFilename; |
135: | } |
136: | |
137: | |
138: | |
139: | |
140: | public function getClientMediaType(): ?string |
141: | { |
142: | return $this->ClientMediaType; |
143: | } |
144: | |
145: | private function assertIsValid(): void |
146: | { |
147: | if ($this->Error !== \UPLOAD_ERR_OK) { |
148: | throw new UploadedFileException(sprintf( |
149: | 'Upload failed (%d: %s)', |
150: | $this->Error, |
151: | static::ERROR_MESSAGE[$this->Error] ?? '', |
152: | )); |
153: | } |
154: | |
155: | if ($this->IsMoved) { |
156: | throw new UploadedFileException('Uploaded file already moved'); |
157: | } |
158: | } |
159: | } |
160: | |