可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I wish to make a simple GET request to another script on a different server. How do I do this?
In one case, I just need to request an external script without the need for any output.
make_request(\'http://www.externalsite.com/script1.php?variable=45\'); //example usage
In the second case, I need to get the text output.
$output = make_request(\'http://www.externalsite.com/script2.php?variable=45\');
echo $output; //string output
To be honest, I do not want to mess around with CURL as this isn\'t really the job of CURL. I also do not want to make use of http_get as I do not have the PECL extensions.
Would fsockopen work? If so, how do I do this without reading in the contents of the file? Is there no other way?
Thanks all
Update
I should of added, in the first case, I do not want to wait for the script to return anything. As I understand file_get_contents() will wait for the page to load fully etc?
回答1:
file_get_contents
will do what you want
$output = file_get_contents(\'http://www.example.com/\');
echo $output;
Edit: One way to fire off a GET request and return immediately.
Quoted from http://petewarden.typepad.com/searchbrowser/2008/06/how-to-post-an.html
function curl_post_async($url, $params)
{
foreach ($params as $key => &$val) {
if (is_array($val)) $val = implode(\',\', $val);
$post_params[] = $key.\'=\'.urlencode($val);
}
$post_string = implode(\'&\', $post_params);
$parts=parse_url($url);
$fp = fsockopen($parts[\'host\'],
isset($parts[\'port\'])?$parts[\'port\']:80,
$errno, $errstr, 30);
$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\";
$out.= \"Content-Length: \".strlen($post_string).\"\\r\\n\";
$out.= \"Connection: Close\\r\\n\\r\\n\";
if (isset($post_string)) $out.= $post_string;
fwrite($fp, $out);
fclose($fp);
}
What this does is open a socket, fire off a get request, and immediately close the socket and return.
回答2:
This is how to make Marquis\' answer work with both POST and GET requests:
// $type must equal \'GET\' or \'POST\'
function curl_request_async($url, $params, $type=\'POST\')
{
foreach ($params as $key => &$val) {
if (is_array($val)) $val = implode(\',\', $val);
$post_params[] = $key.\'=\'.urlencode($val);
}
$post_string = implode(\'&\', $post_params);
$parts=parse_url($url);
$fp = fsockopen($parts[\'host\'],
isset($parts[\'port\'])?$parts[\'port\']:80,
$errno, $errstr, 30);
// Data goes in the path for a GET request
if(\'GET\' == $type) $parts[\'path\'] .= \'?\'.$post_string;
$out = \"$type \".$parts[\'path\'].\" HTTP/1.1\\r\\n\";
$out.= \"Host: \".$parts[\'host\'].\"\\r\\n\";
$out.= \"Content-Type: application/x-www-form-urlencoded\\r\\n\";
$out.= \"Content-Length: \".strlen($post_string).\"\\r\\n\";
$out.= \"Connection: Close\\r\\n\\r\\n\";
// Data goes in the request body for a POST request
if (\'POST\' == $type && isset($post_string)) $out.= $post_string;
fwrite($fp, $out);
fclose($fp);
}
回答3:
Regarding your update, about not wanting to wait for the full page to load - I think a HTTP HEAD
request is what you\'re looking for..
get_headers should do this - I think it only requests the headers, so will not be sent the full page content.
\"PHP / Curl: HEAD Request takes a long time on some sites\" describes how to do a HEAD
request using PHP/Curl
If you want to trigger the request, and not hold up the script at all, there are a few ways, of varying complexities..
- Execute the HTTP request as a background process, php execute a background process - basically you would execute something like
\"wget -O /dev/null $carefully_escaped_url\"
- this will be platform specific, and you have to be really careful about escaping parameters to the command
- Executing a PHP script in the background - basically the same as the UNIX process method, but executing a PHP script rather than a shell command
- Have a \"job queue\", using a database (or something like beanstalkd which is likely overkill). You add a URL to the queue, and a background process or cron-job routinely checks for new jobs and performs requests on the URL
回答4:
You don\'t. While PHP offers lots of ways to call a URL, it doesn\'t offer out of the box support for doing any kind of asynchronous/threaded processing per request/execution cycle. Any method of sending a request for a URL (or a SQL statement, or a etc.) is going to wait for some kind of response. You\'ll need some kind of secondary system running on the local machine to achieve this (google around for \"php job queue\")
回答5:
I would recommend you well tested PHP library: curl-easy
<?php
$request = new cURL\\Request(\'http://www.externalsite.com/script2.php?variable=45\');
$request->getOptions()
->set(CURLOPT_TIMEOUT, 5)
->set(CURLOPT_RETURNTRANSFER, true);
// add callback when the request will be completed
$request->addListener(\'complete\', function (cURL\\Event $event) {
$response = $event->response;
$content = $response->getContent();
echo $content;
});
while ($request->socketPerform()) {
// do anything else when the request is processed
}
回答6:
function make_request($url, $waitResult=true){
$cmi = curl_multi_init();
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_multi_add_handle($cmi, $curl);
$running = null;
do {
curl_multi_exec($cmi, $running);
sleep(.1);
if(!$waitResult)
break;
} while ($running > 0);
curl_multi_remove_handle($cmi, $curl);
if($waitResult){
$curlInfos = curl_getinfo($curl);
if((int) $curlInfos[\'http_code\'] == 200){
curl_multi_close($cmi);
return curl_multi_getcontent($curl);
}
}
curl_multi_close($cmi);
}
回答7:
Interesting problem. I\'m guessing you just want to trigger some process or action on the other server, but don\'t care what the results are and want your script to continue. There is probably something in cURL that can make this happen, but you may want to consider using exec()
to run another script on the server that does the call if cURL can\'t do it. (Typically people want the results of the script call so I\'m not sure if PHP has the ability to just trigger the process.) With exec()
you could run a wget
or even another PHP script that makes the request with file_get_conents()
.
回答8:
You\'d better consider using Message Queues instead of advised methods.
I\'m sure this will be better solution, although it requires a little more job than just sending a request.
回答9:
let me show you my way :)
needs nodejs installed on the server
(my server sends 1000 https get request takes only 2 seconds)
url.php :
<?
$urls = array_fill(0, 100, \'http://google.com/blank.html\');
function execinbackground($cmd) {
if (substr(php_uname(), 0, 7) == \"Windows\"){
pclose(popen(\"start /B \". $cmd, \"r\"));
}
else {
exec($cmd . \" > /dev/null &\");
}
}
fwite(fopen(\"urls.txt\",\"w\"),implode(\"\\n\",$urls);
execinbackground(\"nodejs urlscript.js urls.txt\");
// { do your work while get requests being executed.. }
?>
urlscript.js >
var https = require(\'https\');
var url = require(\'url\');
var http = require(\'http\');
var fs = require(\'fs\');
var dosya = process.argv[2];
var logdosya = \'log.txt\';
var count=0;
http.globalAgent.maxSockets = 300;
https.globalAgent.maxSockets = 300;
setTimeout(timeout,100000); // maximum execution time (in ms)
function trim(string) {
return string.replace(/^\\s*|\\s*$/g, \'\')
}
fs.readFile(process.argv[2], \'utf8\', function (err, data) {
if (err) {
throw err;
}
parcala(data);
});
function parcala(data) {
var data = data.split(\"\\n\");
count=\'\'+data.length+\'-\'+data[1];
data.forEach(function (d) {
req(trim(d));
});
/*
fs.unlink(dosya, function d() {
console.log(\'<%s> file deleted\', dosya);
});
*/
}
function req(link) {
var linkinfo = url.parse(link);
if (linkinfo.protocol == \'https:\') {
var options = {
host: linkinfo.host,
port: 443,
path: linkinfo.path,
method: \'GET\'
};
https.get(options, function(res) {res.on(\'data\', function(d) {});}).on(\'error\', function(e) {console.error(e);});
} else {
var options = {
host: linkinfo.host,
port: 80,
path: linkinfo.path,
method: \'GET\'
};
http.get(options, function(res) {res.on(\'data\', function(d) {});}).on(\'error\', function(e) {console.error(e);});
}
}
process.on(\'exit\', onExit);
function onExit() {
log();
}
function timeout()
{
console.log(\"i am too far gone\");process.exit();
}
function log()
{
var fd = fs.openSync(logdosya, \'a+\');
fs.writeSync(fd, dosya + \'-\'+count+\'\\n\');
fs.closeSync(fd);
}
回答10:
If you are using Linux environment then you can use the PHP\'s exec command to invoke the linux curl. Here is a sample code, which will make a Asynchronous HTTP post.
function _async_http_post($url, $json_string) {
$run = \"curl -X POST -H \'Content-Type: application/json\'\";
$run.= \" -d \'\" .$json_string. \"\' \" . \"\'\" . $url . \"\'\";
$run.= \" > /dev/null 2>&1 &\";
exec($run, $output, $exit);
return $exit == 0;
}
This code does not need any extra PHP libs and it can complete the http post in less than 10 milliseconds.
回答11:
For me the question about asynchronous GET request is appeared because of I met with situation when I need to do hundreds of requests, get and deal with result data on every request and every request takes significant milliseconds of executing that leads to minutes(!) of total executing with simple file_get_contents
.
In this case it was very helpful comment of w_haigh at php.net on function http://php.net/manual/en/function.curl-multi-init.php
So, here is my upgraded and cleaned version of making lot of requests simultaneously.
For my case it\'s equivalent to \"asynchronous\" way. May be it helps for someone!
// Build the multi-curl handle, adding both $ch
$mh = curl_multi_init();
// Build the individual requests, but do not execute them
$chs = [];
$chs[\'ID0001\'] = curl_init(\'http://webservice.example.com/?method=say&word=Hello\');
$chs[\'ID0002\'] = curl_init(\'http://webservice.example.com/?method=say&word=World\');
// $chs[] = ...
foreach ($chs as $ch) {
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true, // Return requested content as string
CURLOPT_HEADER => false, // Don\'t save returned headers to result
CURLOPT_CONNECTTIMEOUT => 10, // Max seconds wait for connect
CURLOPT_TIMEOUT => 20, // Max seconds on all of request
CURLOPT_USERAGENT => \'Robot YetAnotherRobo 1.0\',
]);
// Well, with a little more of code you can use POST queries too
// Also, useful options above can be CURLOPT_SSL_VERIFYHOST => 0
// and CURLOPT_SSL_VERIFYPEER => false ...
// Add every $ch to the multi-curl handle
curl_multi_add_handle($mh, $ch);
}
// Execute all of queries simultaneously, and continue when ALL OF THEM are complete
$running = null;
do {
curl_multi_exec($mh, $running);
} while ($running);
// Close the handles
foreach ($chs as $ch) {
curl_multi_remove_handle($mh, $ch);
}
curl_multi_close($mh);
// All of our requests are done, we can now access the results
// With a help of ids we can understand what response was given
// on every concrete our request
$responses = [];
foreach ($chs as $id => $ch) {
$responses[$id] = curl_multi_getcontent($ch);
curl_close($ch);
}
unset($chs); // Finita, no more need any curls :-)
print_r($responses); // output results
It\'s easy to rewrite this to handle POST or other types of HTTP(S) requests or any combinations of them. And Cookie support, redirects, http-auth, etc.
回答12:
Try:
//Your Code here
$pid = pcntl_fork();
if ($pid == -1) {
die(\'could not fork\');
}
else if ($pid)
{
echo(\"Bye\")
}
else
{
//Do Post Processing
}
This will NOT work as an apache module, you need to be using CGI.
回答13:
I found this interesting link to do asynchronous processing(get request).
askapache
Furthermore you could do asynchronous processing by using a message queue like for instance beanstalkd.
回答14:
Here\'s an adaptation of the accepted answer for performing a simple GET request.
One thing to note if the server does any url rewriting, this will not work. You\'ll need to use a more full featured http client.
/**
* Performs an async get request (doesn\'t wait for response)
* Note: One limitation of this approach is it will not work if server does any URL rewriting
*/
function async_get($url)
{
$parts=parse_url($url);
$fp = fsockopen($parts[\'host\'],
isset($parts[\'port\'])?$parts[\'port\']:80,
$errno, $errstr, 30);
$out = \"GET \".$parts[\'path\'].\" HTTP/1.1\\r\\n\";
$out.= \"Host: \".$parts[\'host\'].\"\\r\\n\";
$out.= \"Connection: Close\\r\\n\\r\\n\";
fwrite($fp, $out);
fclose($fp);
}
回答15:
Nobody seems to mention Guzzle, which is a PHP HTTP client that makes it easy to send HTTP requests. It can work with or without Curl
. It can send both synchronous and asynchronous requests.
$client = new GuzzleHttp\\Client();
$promise = $client->requestAsync(\'GET\', \'http://httpbin.org/get\');
$promise->then(
function (ResponseInterface $res) {
echo $res->getStatusCode() . \"\\n\";
},
function (RequestException $e) {
echo $e->getMessage() . \"\\n\";
echo $e->getRequest()->getMethod();
}
);
回答16:
Based on this thread I made this for my codeigniter project. It works just fine. You can have any function processed in the background.
A controller that accepts the async calls.
class Daemon extends CI_Controller
{
// Remember to disable CI\'s csrf-checks for this controller
function index( )
{
ignore_user_abort( 1 );
try
{
if ( strcmp( $_SERVER[\'REMOTE_ADDR\'], $_SERVER[\'SERVER_ADDR\'] ) != 0 && !in_array( $_SERVER[\'REMOTE_ADDR\'], $this->config->item( \'proxy_ips\' ) ) )
{
log_message( \"error\", \"Daemon called from untrusted IP-address: \" . $_SERVER[\'REMOTE_ADDR\'] );
show_404( \'/daemon\' );
return;
}
$this->load->library( \'encrypt\' );
$params = unserialize( urldecode( $this->encrypt->decode( $_POST[\'data\'] ) ) );
unset( $_POST );
$model = array_shift( $params );
$method = array_shift( $params );
$this->load->model( $model );
if ( call_user_func_array( array( $this->$model, $method ), $params ) === FALSE )
{
log_message( \"error\", \"Daemon could not call: \" . $model . \"::\" . $method . \"()\" );
}
}
catch(Exception $e)
{
log_message( \"error\", \"Daemon has error: \" . $e->getMessage( ) . $e->getFile( ) . $e->getLine( ) );
}
}
}
And a library that does the async calls
class Daemon
{
public function execute_background( /* model, method, params */ )
{
$ci = &get_instance( );
// The callback URL (its ourselves)
$parts = parse_url( $ci->config->item( \'base_url\' ) . \"/daemon\" );
if ( strcmp( $parts[\'scheme\'], \'https\' ) == 0 )
{
$port = 443;
$host = \"ssl://\" . $parts[\'host\'];
}
else
{
$port = 80;
$host = $parts[\'host\'];
}
if ( ( $fp = fsockopen( $host, isset( $parts[\'port\'] ) ? $parts[\'port\'] : $port, $errno, $errstr, 30 ) ) === FALSE )
{
throw new Exception( \"Internal server error: background process could not be started\" );
}
$ci->load->library( \'encrypt\' );
$post_string = \"data=\" . urlencode( $ci->encrypt->encode( serialize( func_get_args( ) ) ) );
$out = \"POST \" . $parts[\'path\'] . \" HTTP/1.1\\r\\n\";
$out .= \"Host: \" . $host . \"\\r\\n\";
$out .= \"Content-Type: application/x-www-form-urlencoded\\r\\n\";
$out .= \"Content-Length: \" . strlen( $post_string ) . \"\\r\\n\";
$out .= \"Connection: Close\\r\\n\\r\\n\";
$out .= $post_string;
fwrite( $fp, $out );
fclose( $fp );
}
}
This method can be called to process any model::method() in the \'background\'. It uses variable arguments.
$this->load->library(\'daemon\');
$this->daemon->execute_background( \'model\', \'method\', $arg1, $arg2, ... );
回答17:
Suggestion: format a FRAMESET HTML page which contains, let´s say, 9 frames inside. Each frame will GET a different \"instance\" of your myapp.php page. There will be 9 different threads running on the Web server, in parallel.
回答18:
Just a few corrections on scripts posted above. The following is working for me
function curl_request_async($url, $params, $type=\'GET\')
{
$post_params = array();
foreach ($params as $key => &$val) {
if (is_array($val)) $val = implode(\',\', $val);
$post_params[] = $key.\'=\'.urlencode($val);
}
$post_string = implode(\'&\', $post_params);
$parts=parse_url($url);
echo print_r($parts, TRUE);
$fp = fsockopen($parts[\'host\'],
(isset($parts[\'scheme\']) && $parts[\'scheme\'] == \'https\')? 443 : 80,
$errno, $errstr, 30);
$out = \"$type \".$parts[\'path\'] . (isset($parts[\'query\']) ? \'?\'.$parts[\'query\'] : \'\') .\" HTTP/1.1\\r\\n\";
$out.= \"Host: \".$parts[\'host\'].\"\\r\\n\";
$out.= \"Content-Type: application/x-www-form-urlencoded\\r\\n\";
$out.= \"Content-Length: \".strlen($post_string).\"\\r\\n\";
$out.= \"Connection: Close\\r\\n\\r\\n\";
// Data goes in the request body for a POST request
if (\'POST\' == $type && isset($post_string)) $out.= $post_string;
fwrite($fp, $out);
fclose($fp);
}
回答19:
For PHP5.5+, mpyw/co is the ultimate solution. It works as if it is tj/co in JavaScript.
Example
Assume that you want to download specified multiple GitHub users\' avatars. The following steps are required for each user.
- Get content of http://github.com/mpyw (GET HTML)
- Find
<img class=\"avatar\" src=\"...\">
and request it (GET IMAGE)
---
: Waiting my response
...
: Waiting other response in parallel flows
Many famous curl_multi
based scripts already provide us the following flows.
/-----------GET HTML\\ /--GET IMAGE.........\\
/ \\/ \\
[Start] GET HTML..............----------------GET IMAGE [Finish]
\\ /\\ /
\\-----GET HTML....../ \\-----GET IMAGE....../
However, this is not efficient enough. Do you want to reduce worthless waiting times ...
?
/-----------GET HTML--GET IMAGE\\
/ \\
[Start] GET HTML----------------GET IMAGE [Finish]
\\ /
\\-----GET HTML-----GET IMAGE.../
Yes, it\'s very easy with mpyw/co. For more details, visit the repository page.
回答20:
Here is my own PHP function when I do POST to a specific URL of any page....
Sample: * usage of my Function...
<?php
parse_str(\"email=myemail@ehehehahaha.com&subject=this is just a test\");
$_POST[\'email\']=$email;
$_POST[\'subject\']=$subject;
echo HTTP_Post(\"http://example.com/mail.php\",$_POST);***
exit;
?>
<?php
/*********HTTP POST using FSOCKOPEN **************/
// by ArbZ
function HTTP_Post($URL,$data, $referrer=\"\") {
// parsing the given URL
$URL_Info=parse_url($URL);
// Building referrer
if($referrer==\"\") // if not given use this script as referrer
$referrer=$_SERVER[\"SCRIPT_URI\"];
// making string from $data
foreach($data as $key=>$value)
$values[]=\"$key=\".urlencode($value);
$data_string=implode(\"&\",$values);
// Find out which port is needed - if not given use standard (=80)
if(!isset($URL_Info[\"port\"]))
$URL_Info[\"port\"]=80;
// building POST-request: HTTP_HEADERs
$request.=\"POST \".$URL_Info[\"path\"].\" HTTP/1.1\\n\";
$request.=\"Host: \".$URL_Info[\"host\"].\"\\n\";
$request.=\"Referer: $referer\\n\";
$request.=\"Content-type: application/x-www-form-urlencoded\\n\";
$request.=\"Content-length: \".strlen($data_string).\"\\n\";
$request.=\"Connection: close\\n\";
$request.=\"\\n\";
$request.=$data_string.\"\\n\";
$fp = fsockopen($URL_Info[\"host\"],$URL_Info[\"port\"]);
fputs($fp, $request);
while(!feof($fp)) {
$result .= fgets($fp, 128);
}
fclose($fp); //$eco = nl2br();
function getTextBetweenTags($string, $tagname) {
$pattern = \"/<$tagname ?.*>(.*)<\\/$tagname>/\";
preg_match($pattern, $string, $matches);
return $matches[1]; }
//STORE THE FETCHED CONTENTS to a VARIABLE, because its way better and fast...
$str = $result;
$txt = getTextBetweenTags($str, \"span\"); $eco = $txt; $result = explode(\"&\",$result);
return $result[1];
<span style=background-color:LightYellow;color:blue>\".trim($_GET[\'em\']).\"</span>
</pre> \";
}
</pre>
回答21:
Try this code....
$chu = curl_init();
curl_setopt($chu, CURLOPT_URL, \'http://www.myapp.com/test.php?someprm=xyz\');
curl_setopt($chu, CURLOPT_FRESH_CONNECT, true);
curl_setopt($chu, CURLOPT_TIMEOUT, 1);
curl_exec($chu);
curl_close($chu);
Please dont forget to enable CURL php extension.
回答22:
This works fine for me, sadly you cannot retrieve the response from your request:
<?php
header(\"http://mahwebsite.net/myapp.php?var=dsafs\");
?>
It works very fast, no need for raw tcp sockets :)