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