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


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

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

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.

Открылся бы работать? Если да, то как мне это сделать, не читая содержимое файла? Неужели нет другого выхода?

Спасибо всем

Обновление

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

Author: Peter O., 2009-06-08

22 answers

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

Что это делает, так это открывает сокет, запускает запрос get, немедленно закрывает сокет и возвращается.

 50
Author: Marquis Wang, 2009-06-07 23:53:47

Вот как заставить ответ Маркиза работать как с запросами 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);
  }
 33
Author: catgofire, 2010-10-15 19:31:38

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

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

" PHP/Curl: Запрос ЗАГОЛОВКА занимает много времени на некоторых сайтах" описывает, как выполнить запрос HEAD с помощью PHP/Curl

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

  • Выполнить HTTP-запрос в качестве фонового процесса, php выполнить фоновый процесс - в основном вы бы выполнили что-то вроде "wget -O /dev/null $carefully_escaped_url" - это будет зависеть от платформы, и вы должны быть действительно осторожны с экранированием параметров в команду
  • Выполнение PHP-скрипта в фоновом режиме - в основном то же самое, что и метод обработки UNIX, но выполнение PHP-скрипта, а не оболочки команда
  • Есть "очередь заданий", использующая базу данных (или что-то вроде beanstalkd, что, вероятно, излишне). Вы добавляете URL-адрес в очередь, и фоновый процесс или задание cron регулярно проверяет наличие новых заданий и выполняет запросы по URL-адресу
 14
Author: dbr, 2017-05-23 12:34:40

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

 6
Author: Alan Storm, 2009-06-07 23:28:04

Я бы рекомендовал вам хорошо протестированную библиотеку PHP: curl-легко

<?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
Author: 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
Author: amez, 2015-07-17 18:15:23

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

 3
Author: Darryl Hein, 2009-06-07 22:52:40

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

 2
Author: mra214, 2011-02-03 10:10:52

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

Требуется, чтобы на сервере были установлены 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);
}
 2
Author: user1031143, 2012-02-08 19:04:48

Если вы используете среду 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, и он может завершить http-сообщение менее чем за 10 миллисекунд.

 2
Author: Stranger, 2014-05-21 14:30:07

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

В данном случае это был очень полезный комментарий w_haigh на php.net по функции 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-запросов или любых их комбинаций. И поддержка файлов cookie, перенаправления, http-аутентификация и т.д.

 2
Author: FlameStorm, 2016-04-21 21:01:16

Попробуйте:

//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
Author: LM., 2009-06-08 02:19:07

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

Аскапаче

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

 1
Author: Alfred, 2009-06-08 02:44:54

Вот адаптация принятого ответа для выполнения простого запроса 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
Author: blak3r, 2013-01-06 00:21:47

Кажется, никто не упоминает Guzzle, который является HTTP-клиентом PHP, который упрощает отправку 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
Author: zstate, 2018-06-18 21:11:04

Основываясь на этой теме, я сделал это для своего проекта 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 );
    }
}

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

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

Предложение: отформатируйте HTML-страницу НАБОРА ФРЕЙМОВ, которая содержит, скажем, 9 фреймов внутри. Каждый кадр получит отдельный "экземпляр" вашего myapp.php страница. На веб-сервере будет параллельно запущено 9 различных потоков.

 0
Author: newbie_dude, 2013-08-16 04:04:27

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

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
Author: A23, 2014-05-26 00:54:41

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

Пример

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

  1. Получить содержимое http://github.com/mpyw (ПОЛУЧИТЬ HTML)
  2. Найдите <img class="avatar" src="..."> и запросите его (ПОЛУЧИТЕ ИЗОБРАЖЕНИЕ)

---: Жду моего ответа
...: Ожидание другого отклик в параллельных потоках

Многие известные сценарии на основе 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
Author: mpyw, 2016-07-21 07:26:08

Вот моя собственная функция PHP, когда я отправляю сообщение на определенный 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>
 -1
Author: i am ArbZ, 2014-05-14 00:25:42

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

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

 -2
Author: Mukesh, 2012-02-16 14:08:01

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

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

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

 -5
Author: D4zk1tty, 2013-07-12 14:57:48