non file_get_contents async php http curl asynchronous

file_get_contents - ¿Cómo hago una solicitud GET asíncrona en PHP?



non blocking curl php (21)

Aquí está mi propia función PHP cuando hago POST a una URL específica de cualquier página ....

Muestra: * uso de mi función ...

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

Deseo hacer una simple solicitud GET a otra secuencia de comandos en un servidor diferente. ¿Cómo hago esto?

En un caso, solo necesito solicitar un script externo sin la necesidad de ningún resultado.

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

En el segundo caso, necesito obtener el texto de salida.

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

Para ser honesto, no quiero perder el tiempo con CURL ya que este no es realmente el trabajo de CURL. Tampoco quiero utilizar http_get ya que no tengo las extensiones PECL.

¿Funcionaría fsockopen? Si es así, ¿cómo hago esto sin leer el contenido del archivo? ¿No hay otra manera?

Gracias a todos

Actualizar

Debo agregar, en el primer caso, no quiero esperar a que el script devuelva nada. Como entiendo, file_get_contents () esperará a que la página se cargue por completo, etc.


Aquí hay una adaptación de la respuesta aceptada para realizar una simple solicitud GET.

Una cosa a tener en cuenta si el servidor hace alguna reescritura de URL, esto no funcionará. Tendrá que usar un cliente http con más funciones.

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


Así es cómo hacer que la respuesta de Marquis funcione con las solicitudes POST y 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); }


Basado en este hilo, hice esto para mi proyecto codeigniter. Funciona bien. Puede procesar cualquier función en segundo plano.

Un controlador que acepta las llamadas asincrónicas.

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

Y una biblioteca que hace las llamadas asincrónicas

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

Este método se puede llamar para procesar cualquier modelo :: método () en el ''fondo''. Utiliza argumentos variables

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


En cuanto a su actualización, sobre no querer esperar a que se cargue la página completa, creo que una solicitud HEAD HTTP es lo que está buscando ...

get_headers debería hacer esto: creo que solo solicita los encabezados, por lo que no se enviará el contenido de la página completa.

"PHP / Curl: solicitud HEAD lleva mucho tiempo en algunos sitios" describe cómo hacer una solicitud HEAD utilizando PHP / Curl

Si desea activar la solicitud, y no mantener el script en absoluto, hay algunas formas, de complejidad variable.

  • Ejecute la solicitud HTTP como un proceso en segundo plano, php ejecute un proceso en segundo plano - básicamente ejecutaría algo como "wget -O /dev/null $carefully_escaped_url" - esto será específico de la plataforma, y ​​debe tener mucho cuidado con los parámetros de escape El comando
  • Ejecutar un script PHP en segundo plano , básicamente el mismo que el método de proceso de UNIX, pero ejecutando un script PHP en lugar de un comando de shell
  • Tenga una "cola de trabajos", usando una base de datos (o algo así como beanstalkd que probablemente sea demasiado). Agrega una URL a la cola y un proceso en segundo plano o un trabajo cron rutinariamente busca nuevos trabajos y realiza solicitudes en la URL

Encontré este enlace interesante para hacer un procesamiento asíncrono (obtener solicitud).

askapache

Además, podría hacer un procesamiento asíncrono utilizando una cola de mensajes como, por ejemplo, beanstalkd.


Es mejor que considere usar Message Queues en lugar de los métodos recomendados. Estoy seguro de que esta será la mejor solución, aunque requiere un poco más de trabajo que simplemente enviar una solicitud.


Esto funciona bien para mí, lamentablemente no puedes recuperar la respuesta de tu solicitud:

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

Funciona muy rápido, sin necesidad de enchufes tcp sin procesar :)


Para PHP5.5 +, mpyw/co es la solución definitiva. Funciona como si fuera tj/co en JavaScript.

Ejemplo

Suponga que desea descargar los avatares de varios usuarios de GitHub especificados. Los siguientes pasos son necesarios para cada usuario.

  1. Obtenga contenido de http://github.com/mpyw (GET HTML)
  2. Encuentra <img class="avatar" src="..."> y solicítalo (GET IMAGE)

--- : Esperando mi respuesta
... : Esperando otra respuesta en flujos paralelos

Muchos scripts famosos basados ​​en curl_multi ya nos proporcionan los siguientes flujos.

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

Sin embargo, esto no es lo suficientemente eficiente. ¿Quieres reducir los tiempos de espera sin valor ... ?

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

Sí, es muy fácil con mpyw / co. Para más detalles, visite la página del repositorio.


Para mí, la pregunta sobre la solicitud GET asíncrona aparece porque me encontré con una situación en la que necesito hacer cientos de solicitudes , obtener y tratar los datos de los resultados en cada solicitud y cada solicitud lleva milisegundos significativos de ejecución que lleva a minutos (!) De ejecución total con simple file_get_contents .

En este caso fue un comentario muy útil de w_haigh en php.net en la función http://php.net/manual/en/function.curl-multi-init.php

Por lo tanto, aquí está mi versión actualizada y limpia de hacer muchas solicitudes simultáneamente. Para mi caso, es equivalente a la forma "asíncrona". ¡Puede ser que ayude para alguien!

// 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 fácil reescribir esto para manejar POST u otros tipos de solicitudes HTTP (S) o cualquier combinación de ellas. Y soporte de cookies, redirecciones, http-auth, etc.


Problema interesante. Supongo que solo desea activar algún proceso o acción en el otro servidor, pero no importa cuáles sean los resultados y desea que el script continúe. Probablemente hay algo en cURL que puede hacer que esto suceda, pero es posible que desee considerar el uso de exec() para ejecutar otro script en el servidor que realiza la llamada si cURL no puede hacerlo. (Por lo general, la gente quiere los resultados de la llamada al script, así que no estoy seguro de si PHP tiene la capacidad de activar el proceso). Con exec() podría ejecutar un wget o incluso otro script PHP que realice la solicitud con file_get_conents() .


Prueba este código ...

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

Por favor, no te olvides de habilitar la extensión CURL php.


Si está utilizando un entorno Linux, puede usar el comando exec de PHP para invocar el rizo de Linux. Aquí hay un código de muestra, que hará una publicación HTTP asíncrona.

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

Este código no necesita ninguna biblioteca adicional de PHP y puede completar la publicación http en menos de 10 milisegundos.


Solo unas pocas correcciones en las secuencias de comandos publicadas anteriormente. Lo siguiente está funcionando para mí

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


Sugerencia: formatee una página HTML de FRAMESET que contenga, digamos, 9 marcos en su interior. Cada cuadro OBTENDRÁ una "instancia" diferente de su página myapp.php. Habrá 9 hilos diferentes ejecutándose en el servidor web, en paralelo.


Te recomendaría una biblioteca PHP bien probada: 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 }


Tratar:

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

Esto NO funcionará como un módulo de apache, debe usar CGI.


Tu no Si bien PHP ofrece muchas formas de llamar a una URL, no ofrece soporte inmediato para realizar ningún tipo de procesamiento asincrónico / por proceso de solicitud / ejecución. Cualquier método para enviar una solicitud de una URL (o una declaración SQL, etc.) va a esperar algún tipo de respuesta. Necesitarás algún tipo de sistema secundario ejecutándose en la máquina local para lograr esto (busca la "cola de trabajos php" en google).


déjame mostrarte mi camino :)

necesita nodejs instalado en el servidor

(mi servidor envía 1000 https obtener solicitud tarda solo 2 segundos)

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


file_get_contents hará lo que quieras

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

Editar: Una forma de despedir una solicitud GET y regresar inmediatamente.

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

Lo que hace es abrir un socket, iniciar una solicitud de get e inmediatamente cerrar el socket y regresar.


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