web-dev-qa-db-ja.com

PHPで非同期GETリクエストを行うにはどうすればよいですか?

別のサーバー上の別のスクリプトに単純なGETリクエストを送信したい。どうすればいいですか?

ある場合には、出力を必要とせずに外部スクリプトを要求するだけです。

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

2番目の場合、テキスト出力を取得する必要があります。

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

正直に言うと、これは実際にはCURLの仕事ではないので、CURLをいじりたくありません。また、PECL拡張機能がないため、http_getを使用したくありません。

Fsockopenは動作しますか?その場合、ファイルの内容を読み取らずにこれを行うにはどうすればよいですか?他に方法はありませんか?

皆さんありがとう

更新

最初のケースでは、スクリプトが何かを返すのを待ちたくありません。 file_get_contents()はページが完全にロードされるのを待つなどと理解していますか?

94
Abs

file_get_contentsはあなたが望むことをします

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

編集:GET要求を実行してすぐに戻る1つの方法。

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

これは、ソケットを開いて、getリクエストを実行し、すぐにソケットを閉じて戻ることです。

49
Marquis Wang

これは、POSTとGETリクエストの両方でMarquisの回答を機能させる方法です。

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

更新に関して、ページ全体がロードされるのを待たないことについて-HTTP HEADリクエストがあなたが探しているものだと思います。

get_headers これを行う必要があります-ヘッダーのみを要求するため、ページ全体のコンテンツは送信されません。

"PHP/Curl:HEAD一部のサイトではリクエストに時間がかかる" PHP/Curlを使用してHEADリクエストを行う方法を説明しています

リクエストをトリガーし、スクリプトをまったく保留しない場合は、複雑さを変えるいくつかの方法があります。

  • HTTPリクエストをバックグラウンドプロセスとして実行します phpはバックグラウンドプロセスを実行します -基本的には"wget -O /dev/null $carefully_escaped_url"-これはプラットフォーム固有であり、コマンドにパラメーターをエスケープする場合は本当に注意する必要があります
  • バックグラウンドでPHPスクリプトを実行 -基本的にUNIXプロセスメソッドと同じですが、PHPスクリプトではなく、シェルコマンド
  • データベース(または beanstalkd のようなもの)を使用して、「ジョブキュー」を用意します。 URLをキューに追加すると、バックグラウンドプロセスまたはcronジョブが定期的に新しいジョブをチェックし、URLで要求を実行します
13
dbr

十分にテストすることをお勧めします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
stil

あなたはしません。 PHPはURLを呼び出すための多くの方法を提供しますが、リクエスト/実行サイクルごとに非同期/スレッド処理を行うためのすぐに使えるサポートは提供しません。 URL(またはSQLステートメントなど)のリクエストはsome種類の応答を待ちます。これを実現するには、ローカルマシンで実行されている何らかの種類のセカンダリシステムが必要です( 「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

興味深い問題。他のサーバーで何らかのプロセスやアクションをトリガーしたいだけなのに、結果がどうであるかは気にせず、スクリプトを続行させたいと思います。おそらくcURLにこれを実現できるものがありますが、exec()を使用して、cURLが実行できない場合に呼び出しを行うサーバーで別のスクリプトを実行することを検討してください。 (通常、人々はスクリプト呼び出しの結果を望んでいるので、PHPにプロセスをトリガーするだけの能力があるかどうかはわかりません。)exec()を使用すると、wgetまたは別のPHP file_get_conents()でリクエストを行うスクリプト。

3
Darryl Hein

Linux環境を使用している場合は、PHPのexecコマンドを使用してlinux curlを呼び出すことができます。非同期HTTPポストを作成するサンプルコードを次に示します。

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

このコードは、余分なPHP libsを必要とせず、http投稿を10ミリ秒未満で完了することができます。

3
Stranger

推奨される方法ではなく、メッセージキューの使用を検討することをお勧めします。これは、単にリクエストを送信するよりも少し多くの仕事が必要ですが、これがより良い解決策になると確信しています。

2
mra214

私のやり方をお見せしましょう:)

サーバーにnodejsをインストールする必要があります

(私のサーバーは1000のhttps get要求を送信するのに2秒しかかかりません)

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

非同期GETリクエストに関する質問が表示されるのは、何百ものリクエストを実行し、結果データを取得して処理する必要がある状況に遭遇したためです。 request そして、すべてのリクエストには重要なミリ秒の実行が必要です。これは、単純なfile_get_contentsで合計実行の分(!)につながります。

この場合、関数のphp.netで w_haigh の非常に役立つコメントでした http://php.net/manual/en/function.curl-multi-init .php

したがって、ここに、アップグレードしてクリーンにしたバージョンで、同時に多くのリクエストを作成するバージョンを示します。私の場合、これは「非同期」の方法と同等です。誰かに役立つかもしれません!

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

POSTまたは他のタイプのHTTP(S)リクエストまたはそれらの任意の組み合わせを処理するようにこれを書き換えることは簡単です。Cookieサポート、リダイレクト、http-authなど。

2
FlameStorm

上記のスクリプトに関するいくつかの修正。次は私のために働いています

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

誰も言及していないようです Guzzle 、これはPHP HTTPリクエストを簡単に送信できるHTTPクライアントです。Curlの有無にかかわらず動作します。同期要求と非同期要求の両方を送信できます。

$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

単純なGETリクエストを実行するために受け入れられた回答を適応させます。

サーバーがURLの書き換えを行う場合、これは機能しません。より完全な機能を備えたhttpクライアントを使用する必要があります。

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

試してください:

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

これはApacheモジュールとしては機能しません。CGIを使用する必要があります。

1
LM.

非同期処理(要求の取得)を行うためのこの興味深いリンクを見つけました。

skapache

さらに、beanstalkdなどのメッセージキューを使用して非同期処理を行うこともできます。

1
Alfred

提案:たとえば、内部に9つのフレームを含むFRAMESET HTMLページをフォーマットします。各フレームは、myapp.phpページの異なる「インスタンス」を取得します。 Webサーバーでは、9つの異なるスレッドが並行して実行されます。

0
newbie_dude

このスレッドに基づいて、codeigniterプロジェクト用に作成しました。それはうまく機能します。バックグラウンドで処理される関数を使用できます。

非同期呼び出しを受け入れるコントローラー。

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

そして、非同期呼び出しを行うライブラリ

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

このメソッドは、「バックグラウンド」でmodel :: method()を処理するために呼び出すことができます。可変引数を使用します。

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

PHP5.5 +の場合、mpyw/coが究極のソリューションです。 JavaScriptの tj/co のように機能します。

指定した複数のGitHubユーザーのアバターをダウンロードするとします。各ユーザーには次の手順が必要です。

  1. http://github.com/mpyw (GET HTML)のコンテンツを取得します
  2. 見つける <img class="avatar" src="...">およびリクエスト(GET IMAGE)

---:応答を待っています
...:並列フローで他の応答を待機しています

多くの有名なcurl_multiベースのスクリプトは、すでに次のフローを提供しています。

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

ただし、これは十分に効率的ではありません。無駄な待ち時間を減らしたいですか...

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

はい、mpyw/coを使用すると非常に簡単です。詳細については、リポジトリのページをご覧ください。

0
mpyw