PluginProbe
ووسلام – همگام سازی ووکامرس و باسلام / 1.10.19
ووسلام – همگام سازی ووکامرس و باسلام v1.10.19
1.10.19 1.10.20 1.10.18 1.10.17 1.10.15 1.10.14 1.10.13 1.10.12 1.10.10 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.2 1.9.1 1.9.0 1.8.8 1.8.5 1.8.6 All 53 releases
sync-basalam / includes / Services / Api / FileUploadApiService.php

FileUploadApiService.php in ووسلام – همگام سازی ووکامرس و باسلام 1.10.19, at includes/Services/Api/FileUploadApiService.php

279 lines 9.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace SyncBasalam\Services\Api;
4
5 use SyncBasalam\Logger\Logger;
6 use SyncBasalam\Services\MediaMimeType;
7
8 defined('ABSPATH') || exit;
9
10 class FileUploadApiService
11 {
12 public function upload(string $url, string $filePath, array $data = [], array $headers = [], array $options = []): array
13 {
14 $fileValidation = $this->validateFile($filePath, $options);
15 if (!$fileValidation['valid']) {
16 return [
17 'body' => null,
18 'status_code' => 400,
19 'error' => $fileValidation['message']
20 ];
21 }
22
23 if (function_exists('curl_init') && class_exists('CURLFile')) {
24 return $this->uploadWithCurl($url, $filePath, $data, $headers, $options);
25 }
26
27 $fileSize = filesize($filePath);
28 if ($fileSize !== false && $fileSize > 10 * 1024 * 1024) {
29 return [
30 'body' => null,
31 'status_code' => 500,
32 'error' => 'برای آپلود فایل‌های بزرگ، افزونه cURL در PHP باید فعال باشد.',
33 ];
34 }
35
36 $boundary = wp_generate_password(24);
37 $payload = $this->makePayloadupload($data, $filePath, $boundary);
38 $headers = array_merge($headers, ['content-type' => 'multipart/form-data; boundary=' . $boundary]);
39 $timeout = isset($options['timeout']) ? (int) $options['timeout'] : 120;
40
41 $response = wp_remote_post($url, [
42 'headers' => $headers,
43 'body' => $payload,
44 'timeout' => max(10, $timeout),
45 ]);
46
47 return $this->handleResponse($response, $url);
48 }
49
50 private function uploadWithCurl(string $url, string $filePath, array $data, array $headers, array $options): array
51 {
52 $mimeType = MediaMimeType::detect($filePath);
53 $fields = $data;
54 $fields['file'] = new \CURLFile($filePath, $mimeType, basename($filePath));
55 $headerLines = [];
56
57 foreach ($headers as $name => $value) {
58 if (strtolower((string) $name) === 'content-type') continue;
59 $headerLines[] = $name . ': ' . $value;
60 }
61
62 $timeout = isset($options['timeout']) ? (int) $options['timeout'] : 120;
63 $curl = curl_init($url);
64
65 curl_setopt_array($curl, [
66 CURLOPT_POST => true,
67 CURLOPT_POSTFIELDS => $fields,
68 CURLOPT_HTTPHEADER => $headerLines,
69 CURLOPT_RETURNTRANSFER => true,
70 CURLOPT_CONNECTTIMEOUT => 20,
71 CURLOPT_TIMEOUT => max(10, $timeout),
72 CURLOPT_FOLLOWLOCATION => false,
73 CURLOPT_SSL_VERIFYPEER => true,
74 CURLOPT_SSL_VERIFYHOST => 2,
75 ]);
76
77 $body = curl_exec($curl);
78 $statusCode = (int) curl_getinfo($curl, CURLINFO_HTTP_CODE);
79
80 if ($body === false) {
81 $message = curl_error($curl);
82 curl_close($curl);
83
84 Logger::error('خطا در ارسال فایل با cURL: ' . $message, ['url' => $url]);
85
86 return [
87 'body' => null,
88 'status_code' => 500,
89 'error' => $message ?: 'خطای نا�
90 شخص در ارسال فایل',
91 ];
92 }
93
94 curl_close($curl);
95 RequestStatusTracker::recordHttpStatus($statusCode, $url);
96 $decodedBody = json_decode($body, true);
97
98 if ($statusCode >= 400) {
99 $reason = RequestStatusTracker::describeHttpStatusFa($statusCode);
100 Logger::error('آپلود فایل به باسلا�
101 نا�
102 وفق بود. ' . $reason, [
103 'url' => $url,
104 'status_code' => $statusCode,
105 'reason' => $reason,
106 'response_body' => $decodedBody ?? $body,
107 ]);
108 }
109
110 return [
111 'body' => $decodedBody,
112 'status_code' => $statusCode,
113 'error' => $statusCode >= 400 ? $this->extractErrorMessage($decodedBody, $body) : null,
114 ];
115 }
116
117 private function validateFile(string $filePath, array $options = []): array
118 {
119 if (!file_exists($filePath)) {
120 return ['valid' => false, 'message' => 'فایل در دسترس نیست' . $filePath];
121 }
122
123 $maxSize = isset($options['max_size']) ? (int) $options['max_size'] : 5 * 1024 * 1024;
124
125 $fileSize = filesize($filePath);
126 if ($fileSize > $maxSize) {
127 return ['valid' => false, 'message' => 'حج�
128 فایل بیش از حد �
129 جاز است : ' . $filePath];
130 }
131
132 $allowedExtensions = isset($options['allowed_extensions']) && is_array($options['allowed_extensions'])
133 ? $options['allowed_extensions']
134 : ['jpg', 'png', 'webp', 'bmp', 'jfif', 'jpeg', 'avif'];
135
136 $extension = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
137
138 if (!in_array($extension, $allowedExtensions, true)) {
139 return ['valid' => false, 'message' => 'فر�
140 ت فایل �
141 عتبر نیست ، فر�
142 ت های �
143 عتبر : ' . implode(', ', $allowedExtensions)];
144 }
145
146 return ['valid' => true, 'message' => 'فایل �
147 عتبر است'];
148 }
149
150 private function handleResponse($response, string $url = ''): array
151 {
152 if (is_wp_error($response)) {
153 $category = RequestStatusTracker::recordWpError($response, $url);
154 $errorData = $response->get_error_data();
155 $errorCodes = $response->get_error_codes();
156 $errorMessages = [];
157
158 foreach ($errorCodes as $code) {
159 $messages = $response->get_error_messages($code);
160 if (!empty($messages)) $errorMessages[$code] = $messages;
161 }
162
163 Logger::error('خطا در فرایند آپلود فایل در باسلا�
164 . ' . RequestStatusTracker::describeCategoryFa($category), [
165 'url' => $url,
166 'category' => $category,
167 'reason' => RequestStatusTracker::describeCategoryFa($category),
168 'error_codes' => $errorCodes,
169 'error_messages' => $errorMessages,
170 'error_data' => $errorData,
171 ]);
172
173 return [
174 'body' => null,
175 'status_code' => $this->resolveWpErrorStatusCode($response),
176 'error' => $this->resolveWpErrorMessage($response)
177 ];
178 }
179
180 $body = wp_remote_retrieve_body($response);
181 $statusCode = wp_remote_retrieve_response_code($response);
182 RequestStatusTracker::recordHttpStatus((int) $statusCode, $url);
183
184 $decodedBody = json_decode($body, true);
185
186 if ((int) $statusCode >= 400) {
187 $reason = RequestStatusTracker::describeHttpStatusFa((int) $statusCode);
188 Logger::error('آپلود فایل به باسلا�
189 نا�
190 وفق بود. ' . $reason, [
191 'url' => $url,
192 'status_code' => (int) $statusCode,
193 'reason' => $reason,
194 'response_body' => $decodedBody ?? $body,
195 ]);
196 }
197
198 return [
199 'body' => $decodedBody,
200 'status_code' => $statusCode,
201 'error' => (int) $statusCode >= 400 ? $this->extractErrorMessage($decodedBody, $body) : null
202 ];
203 }
204
205 private function makePayloadupload(array $data, string $localFile, string $boundary): string
206 {
207 $payload = '';
208 $eol = "\r\n";
209
210 foreach ($data as $name => $value) {
211 $payload .= '--' . $boundary . $eol;
212 $payload .= 'Content-Disposition: form-data; name="' . $name . '"' . $eol . $eol;
213 $payload .= $value . $eol;
214 }
215
216 if ($localFile) {
217 $filename = basename($localFile);
218 $fileContent = file_get_contents($localFile);
219
220 if ($fileContent === false) {
221 Logger::error('خطا در خواندن فایل با استفاده از file_get_contents: ' . $localFile);
222 return '';
223 }
224
225 $mimeType = MediaMimeType::detect($localFile);
226
227 $payload .= '--' . $boundary . $eol;
228 $payload .= 'Content-Disposition: form-data; name="file"; filename="' . $filename . '"' . $eol;
229 $payload .= 'Content-Type: ' . $mimeType . $eol;
230 $payload .= 'Content-Transfer-Encoding: binary' . $eol . $eol;
231 $payload .= $fileContent . $eol;
232 }
233
234 $payload .= '--' . $boundary . '--' . $eol;
235
236 return $payload;
237 }
238
239 private function resolveWpErrorMessage(\WP_Error $response): string
240 {
241 $errorData = $response->get_error_data();
242 $host = is_array($errorData) ? ($errorData['host'] ?? '') : '';
243 $message = $response->get_error_message();
244
245 if ($response->get_error_code() === 'http_request_blocked') {
246 return 'درخواست آپلود تصویر به باسلا�
247
248 سدود شد' . ($host ? ': ' . $host : '');
249 }
250
251 return $message ?: 'خطای نا�
252 شخص در آپلود فایل';
253 }
254
255 private function resolveWpErrorStatusCode(\WP_Error $response): int
256 {
257 $errorData = $response->get_error_data();
258
259 if (is_array($errorData) && isset($errorData['status']) && is_numeric($errorData['status'])) {
260 return (int) $errorData['status'];
261 }
262
263 return 500;
264 }
265
266 private function extractErrorMessage($decodedBody, string $rawBody): string
267 {
268 if (is_array($decodedBody)) {
269 if (!empty($decodedBody['messages'][0]['message'])) return (string) $decodedBody['messages'][0]['message'];
270 if (!empty($decodedBody['errors'][0]['message'])) return (string) $decodedBody['errors'][0]['message'];
271 if (!empty($decodedBody['error'])) return (string) $decodedBody['error'];
272 if (!empty($decodedBody['message'])) return (string) $decodedBody['message'];
273 }
274
275 return $rawBody !== '' ? $rawBody : 'خطای نا�
276 شخص در آپلود فایل';
277 }
278 }
279