Reputation: 684
I need a PHP cURL configuration so that my script is able to send requests and ignore the answers sent by the API.
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_POST,count($fields));
curl_setopt($ch,CURLOPT_POSTFIELDS,$fields_string);
// curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
//curl_setopt($ch, CURLOPT_TIMEOUT_MS, 100);
$result = curl_exec($ch);
echo $result;
curl_close ($ch);
I tried adding: // curl_setopt($ch, CURLOPT_RETURNTRANSFER, false); //curl_setopt($ch, CURLOPT_TIMEOUT_MS, 100);
But its not working properly and the API webserver is not receiving the requests.
The reason for this is I am sending large amount of requests to the API therefore my script is very slow because it waits for each and every request.
Any help is appreciated.
Upvotes: 33
Views: 62666
Reputation: 1092
Sender file example ./ajax/sender.php
Script sending POST -> it makes full request to host, but it doesn't wait on answer from server : CURLOPT_HEADER(0) we dont needs headers from server) and CURLOPT_RETURNTRANSFER (false) we don't needs data from server.CURLOPT_TIMEOUT - Extra procteted : We waiting after sent only 1ms on respond server, this is extra quaranty to not wait any more ms if server keep us. ### NOTE ### HTTP1.1 has one package max 16kb. HTTP2 has 36kb one pacakge. If POST are more biggest, server will be send with many packages in series = $SIZE%16kb
$url = 'https://127.0.0.1/ajax/received.php';
$curl = curl_init();
$post['test'] = 'examples daata'; // our data todo in received
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt ($curl, CURLOPT_POST, TRUE);
curl_setopt ($curl, CURLOPT_POSTFIELDS, $post);
curl_setopt($curl, CURLOPT_USERAGENT, 'api');
//curl_setopt($curl, CURLOPT_TIMEOUT, 1); //if your connect is longer than 1s it lose data in POST better is finish script in recevie
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, false);
curl_setopt($curl, CURLOPT_FORBID_REUSE, true);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 1);
curl_setopt($curl, CURLOPT_DNS_CACHE_TIMEOUT, 100);
curl_setopt($curl, CURLOPT_FRESH_CONNECT, true);
curl_exec($curl);
curl_close($curl);
Received file example ./ajax/received.php
ignore_user_abort(true); //if connect is close, we continue php script in background up to script will be end
header("Connection: close\r\n");
header("Content-Encoding: none\r\n");
header("Content-Length: 1");
### we just close connect above if webbrowser, or request waiting on answer ( we know we set CURLOP to not wait) ###
ob_end_clean(); //just flush all content if exists to request. If server still waiting on answer.
//HERE all script doing in background: Example
$this->db->query('UPDATE new_hook_memory SET new=new+1 WHERE id=1');
EDIT 2019 if you using fastcgi just finish fastcgi and browser close connection but script still will be working up to end.
How finish script: PHP mod_fcgi with fastcgi_finish_request();
For Apache2:
ob_end_flush();
flush();
For php-fpm
fastcgi_finish_request(); $this->db->query('UPDATE new_hook_memory SET new=new+1 WHERE id=1');
Old version:
Upvotes: 32
Reputation: 875
There was some frustration in finding a solution that actually works, so I ended up building a service based on fsockopen()
that can handle both GET and POST requests, without being blocking.
Below is the service class:
class NonBlockingHttpClientService {
private $method = 'GET';
private $params = [];
private $port = 80;
private $host;
private $path;
private $post_content;
public function isPost(): bool
{
return ($this->method === 'POST');
}
public function setMethodToPost(): NonBlockingHttpClientService
{
$this->method = 'POST';
return $this;
}
public function setPort(int $port): NonBlockingHttpClientService
{
$this->port = $port;
return $this;
}
public function setParams(array $params): NonBlockingHttpClientService
{
$this->params = $params;
return $this;
}
private function handleUrl(string $url): void
{
$url = str_replace(['https://', 'http://'], '', $url);
$url_parts = explode('/', $url);
if(count($url_parts) < 2) {
$this->host = $url_parts[0];
$this->path = '/';
} else {
$this->host = $url_parts[0];
$this->path = str_replace($this->host, '', $url);
}
}
private function handleParams(): void
{
if(empty($this->params)) return;
if($this->isPost()) {
$this->post_content = http_build_query($this->params);
} else {
/*
if you want to specify the params as an array for GET request, they will just be
appended to the path as a query string
*/
if(strpos($this->path, '?') === false) {
$this->path .= '?' . ltrim($this->arrayToQueryString($this->params), '&');
} else {
$this->path .= $this->arrayToQueryString($this->params);
}
}
}
private function arrayToQueryString(array $params): string
{
$string = '';
foreach($params as $name => $value) {
$string .= "&$name=" . urlencode($value);
}
return $string;
}
public function doRequest(string $url): bool
{
$this->handleUrl($url);
$this->handleParams();
$host = $this->host;
$path = $this->path;
$fp = fsockopen($host, $this->port, $errno, $errstr, 1);
if (!$fp) {
$error_message = __CLASS__ . ": cannot open connection to $host$path : $errstr ($errno)";
echo $error_message;
error_log($error_message);
return false;
} else {
fwrite($fp, $this->method . " $path HTTP/1.1\r\n");
fwrite($fp, "Host: $host\r\n");
if($this->isPost()) fwrite($fp, "Content-Type: application/x-www-form-urlencoded\r\n");
if($this->isPost()) fwrite($fp, "Content-Length: " . strlen($this->post_content) . "\r\n");
fwrite($fp, "Connection: close\r\n");
fwrite($fp, "\r\n");
if($this->isPost()) fwrite($fp, $this->post_content);
return true;
}
}
}
It can be used like this:
$req = new NonBlockingHttpClientService();
$req->setMethodToPost(); //default is GET, so just omit this for GET requests
$req->setParams([
'test2' => 'aaaa', //if parameters are specified both with setParams() and in the query string, for GET requests, params specified with setParams() will take precedence
'test3' => 'bbbb',
'time' => date('H:i:s')
]);
$req->doRequest('test.localhost/some_path/slow_api.php?test1=value1&test2=value2');
And the slow_api.php file, can be something like this.
<?php
error_log('start');
sleep(10);
error_log(print_r($_REQUEST, 1) . 'end');
I find it easier to monitor (tail -f) the error log in order to see what is happening.
Upvotes: 3
Reputation: 1894
If you use Linux, you can use shell_exec
in your PHP and send the result of curl to dev/null
. By this method, you don't receive any answer and after sending PHP goes to the next line and Linux executes your command in the background. You should use this method if the answer is not important to you.
This is an example:
shell_exec("curl 'http://domian.com/message?text=$text' > /dev/null 2>/dev/null &")
Note: You can add any curl option like header or post method or proxy as your need.
Upvotes: -2
Reputation: 190
( Of course it has been a long time, but I don't think this question is outdated )
using file_get_contents:
//url of php to be called
$url = "example.php/test?id=1";
//this will set the minimum time to wait before proceed to the next line to 1 second
$ctx = stream_context_create(['http'=> ['timeout' => 1]]);
file_get_contents($url,null,$ctx);
//the php will read this after 1 second
using cURL:
//url of php to be called
$url = "example.php/test?id=1";
$test = curl_init();
//this will set the minimum time to wait before proceed to the next line to 100 milliseconds
curl_setopt_array($test,[CURLOPT_URL=>$url,CURLOPT_TIMEOUT_MS=>100,CURLOPT_RETURNTRANSFER=>TRUE]);
curl_exec($test);
//this line will be executed after 100 milliseconds
curl_close ($test);
in both case the called php must set ignore_user_abort(true)
.
And the result will not be printed in both case, but be careful with the timeout you will set, it needs to be greater than the time that the called php needs to start yielding results.
Upvotes: 11
Reputation: 553
A bit late now but the solution to this for anyone interested is that CURLOPT_RETURNTRANSFER
needs to be set to TRUE
, not false
. That way the curl_exec
function returns a value immediately rather than waiting for the request to complete before returning - i.e. it acts asynchronously rather than synchronously.
Example:
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
Upvotes: -2
Reputation: 1545
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0);
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 1);
curl_exec($ch);
curl_close($ch);
That works well for me.
Tested on PHP 7.1.14 Windows
Upvotes: 1
Reputation: 340
How can you tell if the request succeeded or not? You need to wait for at least the status code from the server to determine that. If latency is the issue, look at the curl multi API to perform multiple requests in parallel. You should be able to set a write callback function to abort reception of returned data once the status code has been returned.
Upvotes: 0