getMessage()} | URL: {$url} | Body: {$body}"); throw $err; } finally { curl_close($ch); } } /** * Configure cURL HTTP method settings * * Sets the appropriate cURL options based on the HTTP method. * * @param CurlHandle $ch The cURL handle * @param string $method HTTP method in uppercase * * @return void */ function configureHttpMethod(CurlHandle $ch, string $method): void { curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); switch ($method) { case 'POST': curl_setopt($ch, CURLOPT_POST, true); break; case 'PUT': curl_setopt($ch, CURLOPT_PUT, true); break; case 'GET': curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET'); break; case 'DELETE': case 'PATCH': case 'OPTIONS': case 'HEAD': curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); break; } } /** * Process HTTP body based on content type * * Converts the request body to the appropriate format based on the body type. * Supports JSON and form-encoded data. * * @param string $body Raw body content as JSON string * @param string $bodyType Content type ('json' or 'form') * @param string $method HTTP method * @param bool $logEnabled Whether logging is enabled * * @throws JsonException When JSON processing fails * @throws InvalidArgumentException When unsupported body type is provided * * @return string|null Processed body content or null if no body needed */ function processHttpBody(string $body, string $bodyType, string $method, bool $logEnabled): ?string { if (empty($body) || $method === 'GET') { return null; } try { $bodyObject = json_decode($body, true, 256, JSON_THROW_ON_ERROR); return match ($bodyType) { 'json' => json_encode($bodyObject, JSON_THROW_ON_ERROR), 'form' => http_build_query($bodyObject ?? []), default => throw new InvalidArgumentException("Unsupported body type: {$bodyType}") }; } catch (JsonException $err) { logError($logEnabled, "JSON processing error: {$err->getMessage()} | Body: {$body}"); throw $err; } } /** * Configure HTTP headers for the request * * Sets appropriate Content-Type headers if none are provided, * based on the body type. * * @param string[] $headers Array of custom headers * @param string $bodyType Content type ('json' or 'form') * * @return string[] Final array of headers to use */ function configureHeaders(array $headers, string $bodyType): array { if (empty($headers)) { return match ($bodyType) { 'form' => ['Content-Type: application/x-www-form-urlencoded'], 'json' => ['Content-Type: application/json'], default => [] }; } return $headers; } /** * Log the outgoing HTTP request details * * Logs comprehensive information about the request being sent, * including URL, method, body, and headers. * * @param bool $logEnabled Whether logging is enabled * @param string $additionalLog Additional context information * @param string $method HTTP method * @param string $url Target URL * @param string $bodyType Content type * @param string|null $body Processed request body * @param string[] $headers Array of HTTP headers * * @return void */ function logRequest( bool $logEnabled, string $additionalLog, string $method, string $url, string $bodyType, ?string $body, array $headers ): void { if (!$logEnabled) { return; } $cleanUrl = urldecode($url); $cleanBody = $body ? str_replace('\/', '/', $body) : ''; $headersJson = json_encode($headers); $logMessage = trim("{$additionalLog} ♦♦ sendReq ⏩ {$method}: {$cleanUrl} ♦♦ {$bodyType} ♦♦ {$cleanBody} ♦♦ {$headersJson}"); logWarning($logEnabled, $logMessage); } /** * Execute cURL request and handle response * * Executes the configured cURL request and handles both success * and error responses with appropriate logging. * * @param CurlHandle $ch The configured cURL handle * @param bool $logEnabled Whether logging is enabled * * @throws RuntimeException When cURL execution fails * * @return void */ function executeRequest(CurlHandle $ch, bool $logEnabled): void { $response = curl_exec($ch); if (curl_errno($ch)) { $error = curl_error($ch); logError($logEnabled, "cURL error: {$error}"); throw new RuntimeException("cURL error: {$error}"); } $info = curl_getinfo($ch); $httpCode = $info['http_code'] ?? 'unknown'; logWarning($logEnabled, "Response ✅ ({$httpCode}) {$response}"); } /** * Log warning message using FreshRSS logging system * * Safely logs warning messages through the FreshRSS Minz_Log system * with proper class existence checking. * * @param bool $logEnabled Whether logging is enabled * @param mixed $data Data to log (will be converted to string) * * @return void */ function logWarning(bool $logEnabled, $data): void { if ($logEnabled && class_exists('Minz_Log')) { Minz_Log::warning("[WEBHOOK] " . $data); } } /** * Log error message using FreshRSS logging system * * Safely logs error messages through the FreshRSS Minz_Log system * with proper class existence checking. * * @param bool $logEnabled Whether logging is enabled * @param mixed $data Data to log (will be converted to string) * * @return void */ function logError(bool $logEnabled, $data): void { if ($logEnabled && class_exists('Minz_Log')) { Minz_Log::error("[WEBHOOK]❌ " . $data); } } /** * Backward compatibility alias for logWarning function * * @deprecated Use logWarning() instead * @param bool $logEnabled Whether logging is enabled * @param mixed $data Data to log * * @return void */ function LOG_WARN(bool $logEnabled, $data): void { logWarning($logEnabled, $data); } /** * Backward compatibility alias for logError function * * @deprecated Use logError() instead * @param bool $logEnabled Whether logging is enabled * @param mixed $data Data to log * * @return void */ function LOG_ERR(bool $logEnabled, $data): void { logError($logEnabled, $data); }