$api_url, CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => "", CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 30, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => $request_method, CURLOPT_HTTPHEADER => $headers, ]); // Set POST/PUT payload if provided if (!empty($post_fields) && in_array(strtoupper($request_method), ['POST', 'PUT', 'PATCH'])) { curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($post_fields)); } $response = curl_exec($curl); $err = curl_error($curl); $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); if ($err) { return "cURL Error: " . $err; } return $response; } public function fireAndForget($url, $payload, $accessToken) { $urlParts = parse_url($url); $host = $urlParts['host']; $path = $urlParts['path']; $fp = fsockopen("ssl://{$host}", 443, $errno, $errstr, 5); if (!$fp) { CakeLog::write(LOG_ERR, "Push notification: fsockopen failed ({$errno}) {$errstr}"); return false; } $body = json_encode($payload); $out = "POST {$path} HTTP/1.1\r\n"; $out .= "Host: {$host}\r\n"; $out .= "Authorization: Bearer {$accessToken}\r\n"; $out .= "Content-Type: application/json\r\n"; $out .= "Content-Length: " . strlen($body) . "\r\n"; $out .= "Connection: Close\r\n\r\n"; $out .= $body; fwrite($fp, $out); // Signal end of write so FCM receives the full request, // then close without waiting for the response. stream_socket_shutdown($fp, STREAM_SHUT_WR); fclose($fp); return true; } }