Comment puis-je faire une requête asynchrone GET en PHP?

je souhaite faire une simple requête GET à un autre script sur un serveur différent. Comment dois-je faire?

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 du texte.

$output = make_request('http://www.externalsite.com/script2.php?variable=45');
echo $output; //string output

pour être honnête, je ne veux pas déconner avec CURL car ce n'est pas vraiment le travail de CURL. Je ne veux pas non plus utiliser http_get comme Je n'ai pas les extensions PECL.

est-ce que fsockopen marcherait? Si oui, comment dois-je procéder sans lire le contenu du fichier? Il n'y a pas d'autre moyen?

merci à tous

mise à Jour

je dois d'ajouter, dans le premier cas, je ne veux pas attendre pour que le script retourne rien. Comme je comprends file_get_contents () attendra que la page se charge complètement etc?

87
demandé sur Peter O. 2009-06-08 01:53:04

22 réponses

file_get_contents à faire ce que vous voulez

$output = file_get_contents('http://www.example.com/');
echo $output;

Edit: Une façon de déclencher une requête GET et de revenir 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);
}

ce que cela fait est d'ouvrir une socket, de tirer sur une demande get, et immédiatement fermer la socket et revenir.

50
répondu Marquis Wang 2009-06-07 23:53:47

Voici comment faire fonctionner 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);
  }
33
répondu catgofire 2010-10-15 19:31:38

concernant votre mise à jour, à propos de ne pas vouloir attendre que la page se charge - je pense qu'une requête HTTP HEAD est ce que vous recherchez..

get_headers devrait le faire - je pense qu'il demande uniquement les en-têtes et ne pourront donc pas être envoyé à la page de contenu.

"PHP / Curl: TÊTE de Demande prend beaucoup de temps sur certains sites explique comment faire un HEAD demande à l'aide de PHP/Curl

si vous voulez déclencher la requête, et ne pas tenir le script du tout, il y a quelques façons, de complexité variable..

  • exécuter la requête HTTP comme un processus de fond, php exécuter un processus de fond - fondamentalement, vous exécuteriez quelque chose comme "wget -O /dev/null $carefully_escaped_url" - ce sera spécifique à la plate-forme, et vous devez être really prudent sur les paramètres d'échappement à la commande
  • exécution D'un script PHP en arrière - plan - essentiellement la même que la méthode UNIX process, mais l'exécution d'un script PHP plutôt qu'une commande shell
  • ont une" file d'attente d'emploi", en utilisant une base de données (ou quelque chose comme beanstalkd qui est probablement exagérée). Vous ajoutez une URL à la file d'attente, et un processus de fond ou cron-job vérifie régulièrement les nouveaux travaux et exécute des requêtes sur L'URL
14
répondu dbr 2017-05-23 12:34:40

, Non. Tandis que PHP offre beaucoup de façons d'appeler une URL, il n'offre pas de support hors de la boîte pour faire n'importe quel type de traitement asynchrone/fileté par cycle de demande/exécution. Toute méthode d'envoi d'une requête pour une URL (ou une déclaration SQL, ou un etc.) va attendre certains genre de réponse. Vous aurez besoin d'une sorte de système secondaire fonctionnant sur la machine locale pour y parvenir (google around Pour "php job queue")

6
répondu Alan Storm 2009-06-07 23:28:04

je vous recommande bibliothèque PHP bien testé: 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
répondu stil 2012-11-24 21:00:07
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);
}
4
répondu amez 2015-07-17 18:15:23

problème intéressant. Je suppose que vous voulez juste déclencher un processus ou une action sur l'autre serveur, mais ne vous souciez pas des résultats et voulez que votre script continue. Il y a probablement quelque chose dans cURL qui peut faire que cela arrive, mais vous pouvez envisager d'utiliser exec() pour exécuter un autre script sur le serveur qui fait l'appel si cURL ne peut pas le faire. (Typiquement les gens veulent les résultats de l'appel de script donc je ne suis pas sûr si PHP a la capacité de juste déclencher le processus.) Avec exec() vous pouvez lancer un wget ou même un autre script PHP qui fait la demande avec file_get_conents() .

3
répondu Darryl Hein 2009-06-07 22:52:40

vous feriez mieux d'envisager d'utiliser des files d'attente de messages plutôt que des méthodes conseillées. Je suis sûr que ce sera une meilleure solution, bien qu'elle nécessite un peu plus de travail que simplement envoyer une demande.

2
répondu mra214 2011-02-03 10:10:52

permettez-moi de vous montrer ma façon :)

nécessite l'installation de nodejs sur le serveur

(mon serveur envoie 1000 https demande ne prend que 2 secondes)

de l'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);
}
2
répondu user1031143 2012-02-08 19:04:48

si vous utilisez L'environnement Linux, vous pouvez utiliser la commande exec de PHP pour invoquer la boucle linux. Voici un exemple de code, qui fera un post 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 n'a pas besoin de libs PHP supplémentaires et il peut compléter le post http en moins de 10 millisecondes.

2
répondu Stranger 2014-05-21 14:30:07

pour moi la question au sujet de la requête asynchrone GET est apparue en raison de j'ai rencontré la situation où j'ai besoin de faire des centaines de requêtes , obtenir et traiter données de résultat sur chaque requête et chaque requête prend millisecondes significatives d'exécution qui conduit à des minutes (!) d'exécution totale avec simple file_get_contents .

dans ce cas, il était très utile commentaire de w_haigh à php.net sur la fonction http://php.net/manual/en/function.curl-multi-init.php

donc, voici ma version améliorée et nettoyée de faire beaucoup de demandes simultanément. Pour mon cas, c'est l'équivalent de "asynchrone". Peut-être cela aide 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 des requêtes POST ou d'autres types de requêtes HTTP(s) ou n'importe quelle combinaison d'entre elles. Et la prise en charge des cookies, redirections, http-auth, etc.

2
répondu FlameStorm 2016-04-21 21:01:16

, Essayez:

//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.

1
répondu LM. 2009-06-08 02:19:07

j'ai trouvé ce lien intéressant pour faire le traitement asynchrone(get request).

askapache

en outre, vous pouvez faire un traitement asynchrone en utilisant une file d'attente de messages comme par exemple beanstalkd.

1
répondu Alfred 2009-06-08 02:44:54

voici une adaptation de la réponse acceptée pour effectuer une simple requête GET.

une chose à noter si le serveur réécrit une url, cela ne fonctionnera pas. Vous aurez besoin d'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);
  }
1
répondu blak3r 2013-01-06 00:21:47

personne ne semble mentionner Guzzle , qui est un client HTTP PHP qui rend facile d'envoyer des requêtes HTTP. Il peut fonctionner avec ou sans Curl . Il peut envoyer les demandes 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();
    }
);
1
répondu zstate 2018-06-18 21:11:04

basé sur ce fil j'ai fait ceci pour mon projet codeigniter. Il fonctionne très bien. Vous pouvez faire traiter n'importe quelle fonction 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 à traiter n'importe quel modèle::method() dans le 'background'. Il utilise des arguments variables.

$this->load->library('daemon');
$this->daemon->execute_background( 'model', 'method', $arg1, $arg2, ... );
0
répondu Patrick Savalle 2013-01-25 22:26:44

Suggestion: formater une page HTML qui contient, disons, 9 images à l'intérieur. Chaque image aura une "instance" différente de votre myapp.php page. Il y aura 9 threads différents tournant sur le serveur Web, en parallèle.

0
répondu newbie_dude 2013-08-16 04:04:27

quelques corrections sur les scripts affiché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);
    }
0
répondu A23 2014-05-26 00:54:41

pour PHP5.5+, mpyw / co est la solution ultime. Cela fonctionne comme si c'était tj/co en JavaScript.

exemple

supposons que vous voulez télécharger les avatars de plusieurs utilisateurs de GitHub. Les étapes suivantes sont requises pour chaque utilisateur.

  1. Obtenir le contenu de http://github.com/mpyw (GET HTML)
  2. Trouver <img class="avatar" src="..."> et à la demande (OBTENIR de l'IMAGE)

--- : en Attente de ma réponse

... : Attend d'autre réponse en parallèle des flux

beaucoup de scripts célèbres curl_multi basés 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 inutiles ... ?

        /-----------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 dépôt.

0
répondu mpyw 2016-07-21 07:26:08

voici ma propre fonction PHP quand je poste à une URL spécifique de n'importe quelle page....

Exemple: * utilisation de ma Fonction...

<?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>
-1
répondu i am ArbZ 2014-05-14 00:25:42

essayez ce 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);

s'il vous Plaît n'oubliez pas d'activer l'extension php CURL.

-2
répondu Mukesh 2012-02-16 14:08:01

cela fonctionne très bien pour moi, malheureusement vous ne pouvez pas récupérer la réponse de votre demande:

<?php
header("http://mahwebsite.net/myapp.php?var=dsafs");
?>

il fonctionne très vite, pas besoin de prises tcp brutes:)

-5
répondu D4zk1tty 2013-07-12 14:57:48