Как сделать асинхронный запрос GET в PHP?

Я хочу сделать простой запрос GET другому script на другом сервере. Как это сделать?

В одном случае мне просто нужно запросить внешний script без необходимости вывода.

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

Во втором случае мне нужно получить текст.

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

Честно говоря, я не хочу вмешиваться в CURL, так как это не действительно работа CURL. Я также не хочу использовать http_get, поскольку у меня нет расширений PECL.

Будет ли fsockopen работать? Если да, то как это сделать, не читая содержимое файла? Нет другого пути?

Спасибо всем

Update

Я должен добавить, в первом случае, я не хочу ждать, пока script что-нибудь вернет. Насколько я понимаю, file_get_contents() будет ждать полной загрузки страницы и т.д.?

Ответ 1

file_get_contents сделает то, что вы хотите

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

Изменить: один из способов запустить GET-запрос и немедленно вернуться.

Цитата из 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);
}

Что это значит, это открыть сокет, отключить запрос на получение и сразу закрыть сокет и вернуться.

Ответ 2

Вот как заставить ответ Marquis работать с запросами POST и 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);
  }

Ответ 3

Что касается вашего обновления, о том, что вы не хотите ждать полной загрузки страницы - я думаю, что запрос HTTP HEAD - это то, что вы ищете.

get_headers должен сделать это - я думаю, что он запрашивает только заголовки, поэтому не будет отправлено полное содержимое страницы.

"PHP/Curl: HEAD Request занимает много времени на некоторых сайтах" описывает, как выполнить запрос HEAD, используя PHP/Curl

Если вы хотите вызвать запрос и не задерживать script вообще, существует несколько способов различной сложности.

  • Выполнять HTTP-запрос как фоновый процесс, php выполнять фоновый процесс - в основном вы выполняете что-то вроде "wget -O /dev/null $carefully_escaped_url" - это будет специфично для платформы, и вы должны быть очень осторожны в том, чтобы сбрасывать параметры в команду
  • Выполнение PHP script в фоновом режиме - в основном то же, что и метод процесса UNIX, но выполнение PHP script чем команда оболочки
  • У вас есть "очередь заданий", используя базу данных (или что-то вроде beanstalkd, что, вероятно, будет излишним). Вы добавляете URL-адрес в очередь, а фоновый процесс или cron-job регулярно проверяет наличие новых заданий и выполняет запросы по URL-адресу.

Ответ 4

Нет. Хотя PHP предлагает множество способов вызова URL-адреса, он не предлагает бесплатную поддержку для любой обработки асинхронной/потоковой обработки на каждый запрос/цикл выполнения. Любой способ отправки запроса на URL-адрес (или SQL-запрос или т.д.) Ожидает ответа some. Для этого вам понадобится какая-то вторичная система, работающая на локальной машине (google around для "очереди заданий php" )

Ответ 5

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

Ответ 6

Интересная проблема. Я предполагаю, что вы просто хотите вызвать какой-то процесс или действие на другом сервере, но все равно, что результаты и хотите, чтобы ваш script продолжался. Возможно, что-то в cURL может произойти, но вы можете захотеть использовать exec() для запуска другого script на сервере, который выполняет вызов, если cURL не может этого сделать. (Обычно люди хотят получить результаты вызова script, поэтому я не уверен, что PHP имеет возможность просто запускать процесс.) С помощью exec() вы можете запустить wget или даже другой PHP скрипт, что делает запрос с file_get_conents().

Ответ 7

Я бы порекомендовал вам хорошо протестированную библиотеку 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
}

Ответ 8

Лучше подумать об использовании очередей сообщений вместо рекомендуемых методов. Я уверен, что это будет лучшее решение, хотя для этого требуется немного больше работы, чем просто отправка запроса.

Ответ 9

позвольте мне показать вам мой путь:)

требуется nodejs, установленный на сервере

(мой сервер отправляет 1000 https получить запрос занимает всего 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);
}

Ответ 10

Если вы используете среду Linux, вы можете использовать команду exec PHP для вызова зависания linux. Вот пример кода, который сделает сообщение Asynchronous 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-библиотеках, и он может завершить HTTP-сообщение менее чем за 10 миллисекунд.

Ответ 11

Для меня возникает вопрос об асинхронном запросе GET из-за того, что я столкнулся с ситуацией, когда мне нужно выполнить сотни запросов, получить и обработать данные результата по каждому запросу и каждый запрос занимает значительную миллисекунду выполнения, что приводит к минутному (!) общему выполнению с помощью простого file_get_contents.

В этом случае это был очень полезный комментарий w_haigh на php.net по функции http://php.net/manual/en/function.curl-multi-init.php p >

Итак, вот моя обновленная и очищенная версия, позволяющая делать много запросов одновременно. Для моего случая это эквивалентно "асинхронному" способу. Может быть, это помогает кому-то!

// 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 и т.д.

Ответ 12

Try:

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

Это НЕ будет работать как модуль apache, вам нужно использовать CGI.

Ответ 13

Я нашел эту интересную ссылку для асинхронной обработки (получить запрос).

askapache

Кроме того, вы можете выполнять асинхронную обработку, используя очередь сообщений, например, beanstalkd.

Ответ 14

Здесь адаптация принятого ответа для выполнения простого запроса 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);
  }

Ответ 15

Основываясь на этой теме, я сделал это для моего проекта codeigniter. Он работает отлично. Вы можете использовать любую функцию в фоновом режиме.

Контроллер, который принимает асинхронные вызовы.

class Daemon extends CI_Controller
{
    // Remember to disable CI 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 );
    }
}

Этот метод можно вызвать для обработки любой модели:: method() в "background". Он использует переменные аргументы.

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

Ответ 16

Предложение: отформатируйте HTML-страницу FRAMESET, которая содержит, скажем, 9 кадров внутри. Каждый кадр будет получать другой "экземпляр" вашей страницы myapp.php. Параллельно будет выполняться 9 различных потоков на веб-сервере.

Ответ 17

Всего несколько исправлений в сценариях, опубликованных выше. Для меня работает

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

Ответ 18

Для PHP5.5 +, mpyw/co - это окончательное решение. Он работает так, как будто tj/co в JavaScript.

Пример

Предположим, что вы хотите загрузить указанные несколько аватаров пользователей GitHub. Для каждого пользователя требуются следующие шаги.

  • Получить контент http://github.com/mpyw (GET HTML)
  • Найти <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. Подробнее см. на странице репозитория.

Ответ 19

Вот моя собственная функция PHP, когда я отправляю POST на определенный URL любой страницы....

Пример: * использование моей функции...

<?php
    parse_str("[email protected]&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>

Ответ 20

Попробуйте этот код....

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

Пожалуйста, не забудьте включить расширение CURL php.

Ответ 21

Это отлично работает для меня, к сожалению, вы не можете получить ответ от вашего запроса:

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

Он работает очень быстро, нет необходимости в сырых сокетах tcp:)