it-swarm.com.de

Wie mache ich eine asynchrone GET-Anfrage in PHP?

Ich möchte eine einfache GET-Anforderung an ein anderes Skript auf einem anderen Server senden. Wie mache ich das?

In einem Fall muss ich nur ein externes Skript anfordern, ohne dass eine Ausgabe erforderlich ist.

make_request('http://www.externalsite.com/script1.php?variable=45'); //example usage

Im zweiten Fall muss der Text ausgegeben werden.

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

Um ehrlich zu sein, ich möchte mich nicht mit CURL herumspielen, da dies nicht die Aufgabe von CURL ist. Ich möchte auch nicht von http_get Gebrauch machen, da ich keine PECL-Erweiterungen habe.

Würde fsockopen funktionieren? Wenn ja, wie gehe ich vor, ohne den Inhalt der Datei einzulesen? Gibt es keinen anderen Weg?

Danke euch allen

Aktualisieren

Ich sollte hinzufügen, im ersten Fall möchte ich nicht warten, bis das Skript etwas zurückgibt. Wie ich verstehe, wird file_get_contents () warten, bis die Seite vollständig geladen ist usw.?

88
Abs

file_get_contents macht, was Sie wollen

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

Bearbeiten: Eine Möglichkeit, eine GET-Anfrage abzufeuern und sofort zurückzukehren.

Zitat aus http://petewarden.typepad.com/searchbrowser/2008/06/wie-nach-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);
}

Was dies tut, ist eine Steckdose zu öffnen, eine Get-Anfrage abzufeuern und sofort die Steckdose zu schließen und zurückzukehren.

50
Marquis Wang

So können Sie die Antwort von Marquis sowohl mit POST als auch mit GET-Anforderungen arbeiten lassen:

  // $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);
  }
32
catgofire

Was das Update betrifft, möchten Sie nicht warten, bis die gesamte Seite geladen ist. Ich denke, eine HTTP HEAD-Anforderung ist das, wonach Sie suchen.

get_headers sollte dies tun - ich denke, es werden nur die Header angefordert, daher wird nicht der gesamte Seiteninhalt gesendet.

"PHP/Curl: HEAD -Anforderung dauert auf einigen Sites sehr lange" beschreibt, wie eine HEAD-Anfrage mit PHP/Curl ausgeführt wird

Wenn Sie die Anforderung auslösen möchten und das Skript überhaupt nicht aufhalten möchten, gibt es einige Möglichkeiten, die sich in ihrer Komplexität unterscheiden.

  • Führen Sie die HTTP-Anfrage als Hintergrundprozess aus. php führt einen Hintergrundprozess aus - Im Grunde würden Sie etwas wie "wget -O /dev/null $carefully_escaped_url" ausführen - dies wird plattformspezifisch sein, und Sie müssen wirklich vorsichtig sein, um Parameter an den Befehl zu übergeben
  • Das Ausführen eines PHP - Skripts im Hintergrund - entspricht im Wesentlichen der UNIX-Prozessmethode, jedoch das Ausführen eines PHP - Skripts anstelle eines Shell-Befehls
  • Verwenden Sie eine "Jobqueue", die eine Datenbank verwendet (oder etwas wie beanstalkd , das wahrscheinlich übertrieben ist). Sie fügen der Warteschlange eine URL hinzu, und ein Hintergrundprozess oder ein Cron-Job sucht routinemäßig nach neuen Jobs und führt Anforderungen an die URL aus
13
dbr

Ich würde Sie gut getestet empfehlen PHP Bibliothek: 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
stil

Du nicht Während PHP viele Möglichkeiten zum Aufrufen einer URL bietet, bietet sie keine Unterstützung für die Ausführung von asynchroner/Thread-Verarbeitung pro Anfrage/Ausführungszyklus. Jede Methode zum Senden einer Anfrage nach einer URL (oder einer SQL-Anweisung oder einer usw.) wartet auf die Antwort " some ". Um dies zu erreichen, benötigen Sie ein sekundäres System, das auf dem lokalen Computer ausgeführt wird (google around für "php job queue").

6
Alan Storm
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
amez

Interessantes Problem. Ich schätze, Sie wollen nur einen Prozess oder eine Aktion auf dem anderen Server auslösen, aber kümmern Sie sich nicht um die Ergebnisse und möchten, dass Ihr Skript fortgesetzt wird. Es gibt wahrscheinlich etwas in cURL, das dies bewirken kann. Sie können jedoch erwägen, exec() zu verwenden, um ein anderes Skript auf dem Server auszuführen, das den Aufruf ausführt, wenn cURL dies nicht kann. (Normalerweise möchten die Leute die Ergebnisse des Skriptaufrufs, daher bin ich mir nicht sicher, ob PHP die Möglichkeit hat, den Prozess einfach auszulösen.) Mit exec() können Sie ein wget oder sogar ein anderes PHP -Skript ausführen die Anfrage mit file_get_conents().

3
Darryl Hein

Sie sollten Message Queues lieber als empfohlene Methoden verwenden. Ich bin sicher, dass dies eine bessere Lösung ist, obwohl es etwas mehr Arbeit erfordert, als nur eine Anfrage zu senden.

2
mra214

lass mich dir meinen Weg zeigen :)

benötigt nodejs, die auf dem Server installiert sind

(mein Server sendet 1000 https-Get-Anfrage dauert nur 2 Sekunden)

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
user1031143

Wenn Sie eine Linux-Umgebung verwenden, können Sie mit dem Befehl exec den PHP-Befehl curl aufrufen. Hier ist ein Beispielcode, der einen asynchronen HTTP-Beitrag erstellt. 

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;
}

Dieser Code benötigt keine zusätzlichen PHP - Bibliotheken und kann den http-Beitrag in weniger als 10 Millisekunden abschließen. 

2
Stranger

Für mich ist die Frage nach einer asynchronen GET-Anfrage aufgetaucht, weil ich mit der Situation zusammengetroffen bin, in der ich Hunderte von Anfragen ausführen und Ergebnisdaten bei jeder Anfrage abrufen und bearbeiten muss, und dass jede Anfrage dauert signifikante Millisekunden der Ausführung von, die zu Minuten (!) der gesamten Ausführung mit einfachem file_get_contents führt.

In diesem Fall war es ein sehr hilfreicher Kommentar von w_haigh auf php.net zur Funktion http://php.net/manual/de/function.curl-multi-init.php

Hier ist also meine aktualisierte und bereinigte Version, bei der viele Anfragen gleichzeitig gestellt werden .. Für meinen Fall entspricht dies "asynchron". Vielleicht hilft es jemandem!

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

Es ist leicht, dies umzuschreiben, um POST oder andere Arten von HTTP (S) -Anforderungen oder Kombinationen davon zu verarbeiten. Und Cookie-Unterstützung, Weiterleitungen, http-auth usw.

2
FlameStorm

Niemand scheint Guzzle zu erwähnen, einen PHP HTTP-Client, der das Senden von HTTP-Anforderungen vereinfacht. Es kann mit oder ohne Curl arbeiten. Es kann sowohl synchrone als auch asynchrone Anforderungen senden.

$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
zstate

Hier ist eine Anpassung der akzeptierten Antwort zum Durchführen einer einfachen GET-Anforderung.

Eine Sache zu beachten, wenn der Server URL neu schreibt, funktioniert dies nicht. Sie müssen einen umfassenderen http-Client verwenden. 

  /**
   * 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
blak3r

Versuchen:

//Your Code here
$pid = pcntl_fork();
if ($pid == -1) {
     die('could not fork');
}
else if ($pid)
{
echo("Bye")  
}
else
{
     //Do Post Processing
}

Dies wird NICHT als Apache-Modul funktionieren.

1
LM.

Ich fand diesen interessanten Link für die asynchrone Verarbeitung (Anforderung anfordern).

askapache

Darüber hinaus können Sie eine asynchrone Verarbeitung durchführen, indem Sie eine Nachrichtenwarteschlange verwenden, wie zum Beispiel beanstalkd.

1
Alfred

Vorschlag: Formatieren Sie eine FRAMESET-HTML-Seite, die beispielsweise 9 Frames enthält. Jeder Frame erhält eine andere "Instanz" Ihrer myapp.php-Seite. Auf dem Webserver laufen parallel 9 verschiedene Threads.

0
newbie_dude

Basierend auf diesem Thread habe ich dies für mein Codeigniter-Projekt gemacht. Es funktioniert gut. Sie können jede Funktion im Hintergrund bearbeiten lassen.

Ein Controller, der die asynchronen Anrufe entgegennimmt.

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( ) );
        }
    }
}

Und eine Bibliothek, die asynchrone Aufrufe durchführt

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 );
    }
}

Diese Methode kann aufgerufen werden, um alle model :: method () im 'background' zu verarbeiten. Es verwendet variable Argumente.

$this->load->library('daemon');
$this->daemon->execute_background( 'model', 'method', $arg1, $arg2, ... );
0
Patrick Savalle

Nur ein paar Korrekturen an den oben genannten Skripten. Folgendes arbeitet für mich

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
A23

Für PHP5.5 + ist MPYW/CO die ultimative Lösung. Es funktioniert so, als ob es in JavaScript tj/co wäre.

Beispiel

Angenommen, Sie möchten die angegebenen Avatare mehrerer GitHub-Benutzer herunterladen. Die folgenden Schritte sind für jeden Benutzer erforderlich.

  1. Holen Sie sich Inhalte von http://github.com/mpyw (GET HTML)
  2. <img class="avatar" src="..."> suchen und anfordern (GET IMAGE)

---: Warten auf meine Antwort
...: Warten auf andere Antwort in parallelen Flüssen

Viele bekannte curl_multi-basierte Skripts liefern bereits die folgenden Abläufe.

        /-----------GET HTML\  /--GET IMAGE.........\
       /                     \/                      \ 
[Start] GET HTML..............----------------GET IMAGE [Finish]
       \                     /\                      /
        \-----GET HTML....../  \-----GET IMAGE....../

Dies ist jedoch nicht effizient genug. Möchten Sie wertlose Wartezeiten ... reduzieren?

        /-----------GET HTML--GET IMAGE\
       /                                \            
[Start] GET HTML----------------GET IMAGE [Finish]
       \                                /
        \-----GET HTML-----GET IMAGE.../

Ja, mit mpyw/co ist das sehr einfach. Weitere Informationen finden Sie auf der Repository-Seite.

0
mpyw