I recently wrote an API callback script that performed some heavy calculations and took a long time to return. To keep the user from having to wait, I wanted to have the script immediately return cached results and asynchronously process the calculations. There are a few partial solutions on the web but none of them properly deal with sites using HTTPS, so here's my solution:
/** * Posts to a page in the background. * * Specifically what this does is open a connection, write the data, and then * immediately close the connection. * * Example usage: background_post(current_full_url(), array('background' => 1)); * * Pages that expect to be called in the background should usually call * ignore_user_abort(true) as soon as possible to avoid being terminated early * when the connection is closed. * * @param $url * The URL to call in the background. * @param $data * (Optional) An associative array of data to POST. * @param $debug * (Optional) If TRUE, the request is executed synchronously and an array of * relevant information is returned on success (instead of returning TRUE). * * @return * TRUE if the request succeeded or an error string if it failed. */ function background_post($url, $data = array(), $debug = FALSE) { $parts = parse_url($url); $host = $parts['host']; if (isset($parts['port'])) { $port = $parts['port']; } else { $port = $parts['scheme'] == 'https' ? 443 : 80; } if ($port == 443) { $host = 'ssl://' . $host; } $data_params = array(); foreach ($data as $k => $v) { $data_params[] = urlencode($k) . '=' . urlencode($v); } $data_str = implode('&', $data_params); $fp = fsockopen( $host, $port, $errno, $errstr, 3 ); if (!$fp) { return "Error $errno: $errstr"; } else { $out = "POST " . $parts['path'] . " HTTP/1.1\r\n"; $out .= "Host: " . $parts['host'] . "\r\n"; $out .= "Content-Type: application/x-www-form-urlencoded\r\n"; if (!empty($data_str)) { $out .= "Content-Length: " . strlen($data_str) . "\r\n"; } $out .= "Connection: Close\r\n\r\n"; if (!empty($data_str)) { $out .= $data_str; } fwrite($fp, $out); if ($debug) { $contents = ''; while (!feof($fp)) { $contents .= fgets($fp, 128); } } fclose($fp); if (!$debug) { return TRUE; } else { return array( 'url' => $parts, 'hostname' => $host, 'port' => $port, 'request' => $out, 'result' => $contents, ); } } }
Example usage:
ignore_user_abort(TRUE); /** * Your time-consuming operations go here. */ function perform_heavy_calculations() {} /** * Returns the current full URL. * * Example response: https://example.com/index.php */ function current_full_url() { return ( isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on' ? 'https' : 'http' ) . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; } if (empty($_POST['background'])) { echo file_get_contents('cache.html'); background_post(current_full_url(), array('background' => TRUE)); die(); } $results = perform_heavy_calculations(); file_put_contents('cache.html', $results);