Je souhaite faire une simple requête GET à un autre script sur un serveur différent. Comment puis-je faire cela?
Dans un cas, j'ai juste besoin de demander un script externe sans avoir besoin de sortie.
make_request('http://www.externalsite.com/script1.php?variable=45'); //example usage
Dans le second cas, je dois obtenir la sortie texte.
$output = make_request('http://www.externalsite.com/script2.php?variable=45');
echo $output; //string output
Pour être honnête, je ne veux pas perdre mon temps avec CURL car ce n’est pas vraiment son travail. Je ne veux pas non plus utiliser http_get car je n'ai pas les extensions PECL.
Est-ce que fsockopen fonctionnerait? Si tel est le cas, comment puis-je procéder sans lire le contenu du fichier? N'y a-t-il pas d'autre moyen?
Merci a tous
J'aurais du ajouter, dans le premier cas, je ne veux pas attendre que le script retourne quoi que ce soit. Si je comprends bien, file_get_contents () attendra que la page se charge complètement, etc.?
file_get_contents
fera ce que vous voulez
$output = file_get_contents('http://www.example.com/');
echo $output;
Edit: Une façon de lancer une requête GET et de la retourner immédiatement.
Cité de 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);
}
Cela ouvre une socket, lance une requête get, ferme immédiatement la socket et le renvoie.
Voici comment utiliser la réponse de Marquis avec les requêtes POST et GET:
// $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);
}
En ce qui concerne votre mise à jour, ne pas vouloir attendre que la page complète soit chargée. Je pense qu'une requête HTTP HEAD
est ce que vous recherchez.
get_headers devrait le faire - je pense que seuls les en-têtes sont demandés, le contenu de la page complète ne sera donc pas envoyé.
"PHP/Curl: HEAD La requête prend beaucoup de temps sur certains sites" explique comment effectuer une requête HEAD
à l'aide de PHP/Curl
Si vous souhaitez déclencher la demande sans bloquer le script, vous avez plusieurs possibilités, dont la complexité varie.
"wget -O /dev/null $carefully_escaped_url"
- ce sera spécifique à la plate-forme et vous devrez être vraiment attentif à échapper des paramètres à la commandeJe vous recommanderais bien testé la bibliothèque PHP: 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
}
Vous pas. Bien que PHP offre de nombreuses façons d'appeler une URL, il n'offre pas de support prêt à l'emploi pour effectuer tout type de traitement asynchrone/en mode thread par requête/cycle d'exécution. Toute méthode d’envoi d’une requête pour une URL (ou une instruction SQL, ou une requête, etc.) va attendre la réponse some kind. Pour ce faire, vous aurez besoin d’une sorte de système secondaire fonctionnant sur la machine locale (google around pour "file d’attente php")
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);
}
Problème intéressant. J'imagine que vous voulez simplement déclencher un processus ou une action sur l'autre serveur, mais ne vous souciez pas des résultats et souhaitez que votre script continue. Cela peut probablement être fait dans cURL, mais vous pouvez envisager d’utiliser exec()
pour exécuter un autre script sur le serveur qui effectue l’appel si cURL ne peut le faire. (En général, les utilisateurs veulent les résultats de l'appel du script. Je ne sais donc pas si PHP a la capacité de déclencher le processus.) Avec exec()
, vous pouvez exécuter un wget
ou même un autre script PHP qui permet la demande avec file_get_conents()
.
Vous feriez mieux d’envisager d’utiliser Message Queues au lieu de méthodes conseillées .. Je suis sûr que ce sera une meilleure solution, même si cela nécessite un peu plus de travail que le simple envoi d’une requête.
laisse moi te montrer mon chemin :)
besoin de nodejs installé sur le serveur
(mon serveur envoie 1 000 requêtes https en 2 secondes seulement)
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);
}
Si vous utilisez un environnement Linux, vous pouvez utiliser la commande exec de PHP pour appeler le curl linux. Voici un exemple de code permettant de créer une publication HTTP asynchrone.
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;
}
Ce code ne nécessite aucune bibliothèque supplémentaire PHP et peut compléter la publication http en moins de 10 millisecondes.
Pour moi la question sur les requêtes GET asynchrones est apparue parce que j’ai rencontré une situation où j’ai besoin de faire des centaines de requêtes, d’obtenir et de traiter des données result sur chaque requête et que chaque demande prend significatif millisecondes d’exécution qui conduit à l’exécution totale en minutes (!) avec le simple file_get_contents
.
Dans ce cas, c'était un commentaire très utile de w_haigh sur php.net on function http://php.net/manual/en/function.curl-multi-init.php
Donc, voici ma version mise à jour et nettoyée de faire beaucoup de demandes simultanément . Pour mon cas, cela équivaut à "asynchrone". Peut-être que cela aide pour quelqu'un!
// 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
Il est facile de réécrire ceci pour gérer POST ou d'autres types de demandes HTTP (S) ou toute combinaison de celles-ci. Et le support des cookies, redirections, http-auth, etc.
Personne ne semble mentionner Guzzle , qui est un client HTTP PHP facilitant l'envoi de requêtes HTTP. Cela peut fonctionner avec ou sans Curl
. Il peut envoyer des requêtes synchrones et asynchrones.
$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();
}
);
Voici une adaptation de la réponse acceptée pour effectuer une requête GET simple.
Une chose à noter si le serveur effectue une réécriture d'URL, cela ne fonctionnera pas. Vous devrez utiliser un client http plus complet.
/**
* 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);
}
Essayer:
//Your Code here
$pid = pcntl_fork();
if ($pid == -1) {
die('could not fork');
}
else if ($pid)
{
echo("Bye")
}
else
{
//Do Post Processing
}
Cela ne fonctionnera PAS comme un module Apache, vous devez utiliser CGI.
J'ai trouvé ce lien intéressant pour effectuer un traitement asynchrone (obtenir une demande).
De plus, vous pouvez effectuer un traitement asynchrone en utilisant une file de messages comme par exemple beanstalkd.
Suggestion: formatez une page HTML FRAMESET contenant, disons, 9 cadres. Chaque image obtiendra une "instance" différente de votre page myapp.php. Il y aura 9 threads différents fonctionnant en parallèle sur le serveur Web.
Sur la base de ce fil, j'ai créé ceci pour mon projet codeigniter. Cela fonctionne très bien. Vous pouvez avoir n'importe quelle fonction traitée en arrière-plan.
Un contrôleur qui accepte les appels asynchrones.
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( ) );
}
}
}
Et une bibliothèque qui fait les appels asynchrones
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 );
}
}
Cette méthode peut être appelée pour traiter n'importe quel model :: method () en "arrière-plan". Il utilise des arguments variables.
$this->load->library('daemon');
$this->daemon->execute_background( 'model', 'method', $arg1, $arg2, ... );
Juste quelques corrections sur les scripts postés ci-dessus. Ce qui suit fonctionne pour moi
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);
}
Pour PHP 5.5 +, mpyw/co est la solution ultime. Cela fonctionne comme s'il s'agissait de tj/co en JavaScript.
Supposons que vous souhaitiez télécharger les avatars de plusieurs utilisateurs GitHub spécifiés. Les étapes suivantes sont requises pour chaque utilisateur.
<img class="avatar" src="...">
et demandez-le (OBTENIR UNE IMAGE)---
: En attente de ma réponse...
: Attente d'une autre réponse dans des flux parallèles
De nombreux scripts basés sur curl_multi
nous fournissent déjà les flux suivants.
/-----------GET HTML\ /--GET IMAGE.........\
/ \/ \
[Start] GET HTML..............----------------GET IMAGE [Finish]
\ /\ /
\-----GET HTML....../ \-----GET IMAGE....../
Cependant, ce n'est pas assez efficace. Voulez-vous réduire les temps d'attente sans valeur ...
?
/-----------GET HTML--GET IMAGE\
/ \
[Start] GET HTML----------------GET IMAGE [Finish]
\ /
\-----GET HTML-----GET IMAGE.../
Oui, c'est très facile avec mpyw/co. Pour plus de détails, visitez la page du référentiel.