I was curious and decided to attempt the xhr.abort()
suggestion from my comment.
Below is just a basic example of accomplishing a long running PHP AJAX request, without the client waiting for the response from the server or rather early termination of the XHR and PHP continuing script execution.
This approach can be be adapted to run a local script that issues a cURL or SOAP request to an external host as well.
https://first.domain.com/send.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Send It</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<form method="post" action="https://second.domain.com/recievingScript.php">
<p><input type="text" name="name"/></p>
<p>
<button type="submit">Send</button>
</p>
</form>
<script type="text/javascript">
const writeLog = function(msg) {
let date = new Date();
window.console.log(date.toISOString() + ' ' + msg);
};
jQuery(function($) {
let f = $('form');
let xhrOptions = {
url: f.attr('action'),
type: f.attr('method'),
data: {},
cache: false,
xhr: function() {
let xhr = $.ajaxSettings.xhr();
if ('undefined' === typeof xhr.upload.onload || null === xhr.upload.onload) {
//override the upload.onload event, only if it has not already been
xhr.upload.onload = function() {
//onload is triggered immediately after the POST headers are sent to the recipient
writeLog('Upload Completed - Ending Request');
xhr.abort();
};
}
return xhr;
},
};
f.on('submit', function(e) {
e.preventDefault();
let formData = f.serialize();
writeLog(formData);
$.ajax($.extend(true, xhrOptions, {
data: formData
})).done(function(responseText) {
//this is never triggered since the request is aborted
writeLog('Success');
writeLog(responseText);
}).fail(function(jqxhr) {
writeLog('Request ' + (jqxhr.readyState !== 4 ? 'Aborted' : 'Failed'));
});
return false;
});
});
</script>
</body>
</html>
Response:
send.html:18 2019-01-15T21:19:11.445Z name=Hello%20Dagroa
send.html:18 2019-01-15T21:19:11.558Z Upload Completed - Ending Request
send.html:18 2019-01-15T21:19:11.558Z Request Aborted
https://second.domain.com/recievingScript.php
\date_default_timezone_set('UTC');
\ignore_user_abort(true);
\header('Access-Control-Allow-Origin: https://first.domain.com');
if (!empty($_POST)) {
$dateStart = new \DateTimeImmutable();
//create a placeholder file
if ($file = \tempnam(__DIR__, 'tmp_')) {
$i = 1;
//PHP >= 7 required for random_int - PHP < 7 use mt_rand() instead
$rand = \random_int(5, 30);
//add the number of seconds to create a stop date
$dateStop = $dateStart->add(new \DateInterval(\sprintf('PT' . $rand . 'S')));
while (new \DateTime() < $dateStop) {
//loop until the stop date is reached
$i++;
}
$dateEnd = new \DateTime();
$dateDiff = $dateEnd->diff($dateStart);
//write result to the temporary file
\file_put_contents($file, \json_encode([
'file' => \basename($file),
'scriptFile' => \basename($_SERVER['SCRIPT_FILENAME']),
'iterations' => $i,
'start' => $dateStart->format(\DATE_RFC3339_EXTENDED),
'end' => $dateEnd->format(\DATE_RFC3339_EXTENDED),
'stop' => $dateStop->format(\DATE_RFC3339_EXTENDED),
'elapsed' => $dateDiff->format('%i minutes, %s seconds, %f microseconds'),
'slept' => $rand,
'post' => $_POST,
], \JSON_PRETTY_PRINT));
}
}
Response:
{
"file": "tmp_hjLk4y",
"scriptFile": "recievingScript.php",
"iterations": 9653192,
"start": "2019-01-15T21:19:12.171+00:00",
"end": "2019-01-15T21:19:36.171+00:00",
"stop": "2019-01-15T21:19:36.171+00:00",
"elapsed": "0 minutes, 24 seconds, 3 microseconds",
"slept": 24,
"post": {
"name": "Hello Dagroa"
}
}
Notes: I use php-fpm 7.2 as an fcgi proxy using Apache 2.4, which
should not matter other than with the random_int
function call and DATE_RFC3339_EXTENDED
.
Additionally
the const
and let
usage in the JavaScript is an ECMAScript 6 specification, which is supported by all of the current major browser versions.
PHP output buffering should be disabled, otherwise you may need to force it to be flushed using flush()
and/or ob_end_flush()
. Certain other conditions from your web server configuration may also affect output buffering, such as gzip
encoding.
jQuery will issue an OPTIONS
request method just prior to the POST
request method. Ensure to check that $_POST
is not empty or check $_SERVER['REQUEST_METHOD']
for the desired type, to prevent double execution.