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: | public function __construct( |
41: | $resource, |
42: | ?int $size = null, |
43: | int $error = \UPLOAD_ERR_OK, |
44: | ?string $clientFilename = null, |
45: | ?string $clientMediaType = null |
46: | ) { |
47: | $this->Size = $size; |
48: | $this->Error = $error; |
49: | $this->ClientFilename = $clientFilename; |
50: | $this->ClientMediaType = $clientMediaType; |
51: | |
52: | if ($this->Error !== \UPLOAD_ERR_OK) { |
53: | return; |
54: | } |
55: | |
56: | if ($resource instanceof StreamInterface) { |
57: | $this->Stream = $resource; |
58: | } elseif (File::isStream($resource)) { |
59: | $this->Stream = new HttpStream($resource); |
60: | } elseif (is_string($resource)) { |
61: | $this->File = $resource; |
62: | } else { |
63: | throw new InvalidArgumentTypeException( |
64: | 1, |
65: | 'resource', |
66: | 'StreamInterface|resource|string', |
67: | $resource |
68: | ); |
69: | } |
70: | } |
71: | |
72: | |
73: | |
74: | |
75: | public function getStream(): StreamInterface |
76: | { |
77: | $this->assertIsValid(); |
78: | |
79: | return $this->Stream ?? new HttpStream(File::open($this->File, 'r')); |
80: | } |
81: | |
82: | |
83: | |
84: | |
85: | public function moveTo(string $targetPath): void |
86: | { |
87: | $this->assertIsValid(); |
88: | |
89: | if (isset($this->File)) { |
90: | if (\PHP_SAPI === 'cli') { |
91: | $result = @rename($this->File, $targetPath); |
92: | } else { |
93: | $result = @move_uploaded_file($this->File, $targetPath); |
94: | } |
95: | if ($result === false) { |
96: | $error = error_get_last(); |
97: | throw new UploadedFileException($error['message'] ?? sprintf( |
98: | 'Error moving uploaded file %s to %s', |
99: | $this->File, |
100: | $targetPath, |
101: | )); |
102: | } |
103: | } else { |
104: | $target = new HttpStream(File::open($targetPath, 'w')); |
105: | HttpStream::copyToStream($this->Stream, $target); |
106: | } |
107: | |
108: | $this->IsMoved = true; |
109: | } |
110: | |
111: | |
112: | |
113: | |
114: | public function getSize(): ?int |
115: | { |
116: | return $this->Size; |
117: | } |
118: | |
119: | |
120: | |
121: | |
122: | public function getError(): int |
123: | { |
124: | return $this->Error; |
125: | } |
126: | |
127: | |
128: | |
129: | |
130: | public function getClientFilename(): ?string |
131: | { |
132: | return $this->ClientFilename; |
133: | } |
134: | |
135: | |
136: | |
137: | |
138: | public function getClientMediaType(): ?string |
139: | { |
140: | return $this->ClientMediaType; |
141: | } |
142: | |
143: | private function assertIsValid(): void |
144: | { |
145: | if ($this->Error !== \UPLOAD_ERR_OK) { |
146: | throw new UploadedFileException(sprintf( |
147: | 'Upload failed (%d: %s)', |
148: | $this->Error, |
149: | static::ERROR_MESSAGE[$this->Error] ?? '', |
150: | )); |
151: | } |
152: | |
153: | if ($this->IsMoved) { |
154: | throw new UploadedFileException('Uploaded file already moved'); |
155: | } |
156: | } |
157: | } |
158: | |