¿Cómo envío una solicitud POST con PHP?

655

En realidad, quiero leer el contenido que viene después de la consulta de búsqueda, cuando está hecho. El problema es que la URL solo acepta POSTmétodos, y no realiza ninguna acción con el GETmétodo ...

Tengo que leer todos los contenidos con la ayuda de domdocumento file_get_contents(). ¿Hay algún método que me permita enviar parámetros con el POSTmétodo y luego leer el contenido a través de PHP?

Fred Tanrikut
fuente

Respuestas:

1259

Método sin CURL con PHP5:

$url = 'http://server.com/path';
$data = array('key1' => 'value1', 'key2' => 'value2');

// use key 'http' even if you send the request to https://...
$options = array(
    'http' => array(
        'header'  => "Content-type: application/x-www-form-urlencoded\r\n",
        'method'  => 'POST',
        'content' => http_build_query($data)
    )
);
$context  = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if ($result === FALSE) { /* Handle error */ }

var_dump($result);

Consulte el manual de PHP para obtener más información sobre el método y cómo agregar encabezados, por ejemplo:

dbau
fuente
64
Vale la pena señalar que si decide usar una matriz para los encabezados, NO termine las claves o valores con '\ r \ n'. stream_context_create () solo llevará el texto hasta el primer '\ r \ n'
raptor
11
Una URL se puede usar como un nombre de archivo file_get_contents()solo si se han habilitado los contenedores fopen. Ver php.net/manual/en/…
Pino
3
@I lovefile_get_contents()
punto muerto
14
¿Hay alguna razón específica para no usar CURL?
jvannistelrooy
37
@jvannistelrooy CURL para PHP es una extensión que puede no existir en todos los entornos, mientras que file_get_contents()es parte del núcleo de PHP. Además, el uso innecesario de una extensión puede ampliar la superficie de ataque de su aplicación. Por ejemplo, Google php curl cve
Pocketsand
139

Podrías usar cURL :

<?php
//The url you wish to send the POST request to
$url = $file_name;

//The data you want to send via POST
$fields = [
    '__VIEWSTATE '      => $state,
    '__EVENTVALIDATION' => $valid,
    'btnSubmit'         => 'Submit'
];

//url-ify the data for the POST
$fields_string = http_build_query($fields);

//open connection
$ch = curl_init();

//set the url, number of POST vars, POST data
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string);

//So that curl_exec returns the contents of the cURL; rather than echoing it
curl_setopt($ch,CURLOPT_RETURNTRANSFER, true); 

//execute post
$result = curl_exec($ch);
echo $result;
?>
Fred Tanrikut
fuente
3
este funcionó para mí porque la página que estoy enviando a una página que no tiene contenido, por lo que la versión file_get_contents no funcionó.
CommentLuv
99
La solución file_get_contents no funciona en configuraciones PHP con allow_url_fopen Off (como en el alojamiento compartido). Esta versión utiliza la biblioteca de rizos y creo que es muy "universal", así que le doy mi voto
Dayron Gallardo
81
No encontró el sitio donde copió este ejemplo de código de: davidwalsh.name/curl-post
efreed
44
Aunque no es muy importante, los datos del parámetro CURLOPT_POSTFIELDS en realidad no necesitan convertirse en una cadena ("urlified"). Cita: "Este parámetro se puede pasar como una cadena codificada como 'para1 = val1 & para2 = val2 & ...' o como una matriz con el nombre del campo como clave y los datos del campo como valor. Si el valor es una matriz, el tipo de contenido el encabezado se establecerá en multipart / form-data ". Enlace: php.net/manual/en/function.curl-setopt.php .
Edward
2
Además, no se ofende por escribirlo de manera diferente, pero no sé por qué el parámetro CURLOPT_POST se especifica como un número aquí, ya que dice establecerlo en un valor booleano en la página del manual. Cita: "CURLOPT_POST: TRUE para hacer una POST HTTP normal". Enlace: php.net/manual/en/function.curl-setopt.php .
Edward
68

Utilizo la siguiente función para publicar datos usando curl. $ data es una matriz de campos para publicar (se codificará correctamente usando http_build_query). Los datos se codifican usando application / x-www-form-urlencoded.

function httpPost($url, $data)
{
    $curl = curl_init($url);
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data));
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($curl);
    curl_close($curl);
    return $response;
}

@Edward menciona que http_build_query puede omitirse ya que curl codificará correctamente la matriz pasada al parámetro CURLOPT_POSTFIELDS, pero tenga en cuenta que en este caso los datos se codificarán usando multipart / form-data.

Utilizo esta función con API que esperan que los datos se codifiquen mediante application / x-www-form-urlencoded. Es por eso que uso http_build_query ().

Dima L.
fuente
Pasar la matriz a CURLOPT_POSTFIELDS hace que los datos se codifiquen utilizando multipart / form-data, lo que puede no ser deseable.
Dima L.
El usuario solicitó file_get_contents, por lo que necesita una solución para cambiar el default_stream_context
Radon8472
Para aclarar: creo que @DimaL. está respondiendo a un comentario que se ha eliminado; http_build_queryconvierte la $datamatriz en una cadena, evitando la salida como datos multiparte / formulario.
ToolmakerSteve
@ Radon8472: ... CURLOPT_RETURNTRANSFER, trueda como resultado $responseel contenido.
ToolmakerSteve
@ToolmakerSteve como dije, la pregunta era para file_get_contents y su solución necesita CURL lo que mucha gente no tiene. entonces su solución tal vez esté funcionando, pero no está respondiendo a la pregunta de cómo hacer esto con las funciones nativas de archivo / secuencia incorporadas.
Radon8472
42

Te recomiendo que uses el paquete de código abierto guzzle que está completamente probado en la unidad y utiliza las últimas prácticas de codificación.

Instalando Guzzle

Vaya a la línea de comando en su carpeta de proyecto y escriba el siguiente comando (suponiendo que ya tenga instalado el gestor de paquetes compositor ). Si necesita ayuda para instalar Composer, debería echar un vistazo aquí .

php composer.phar require guzzlehttp/guzzle

Usando Guzzle para enviar una solicitud POST

El uso de Guzzle es muy sencillo, ya que utiliza una API orientada a objetos liviana:

// Initialize Guzzle client
$client = new GuzzleHttp\Client();

// Create a POST request
$response = $client->request(
    'POST',
    'http://example.org/',
    [
        'form_params' => [
            'key1' => 'value1',
            'key2' => 'value2'
        ]
    ]
);

// Parse the response object, e.g. read the headers, body, etc.
$headers = $response->getHeaders();
$body = $response->getBody();

// Output headers and body for debugging purposes
var_dump($headers, $body);
Andreas
fuente
77
Sería útil saber qué ventajas tiene esto sobre la solución PHP nativa ya publicada, y también sobre la cURL.
artfulrobot
99
@artfulrobot: la solución nativa de PHP tiene muchos problemas (por ejemplo, conectarse con https, verificación de certificados, etc.pp), razón por la cual casi todos los desarrolladores de PHP usan cURL. ¿Y por qué no usar cURL en este caso? Es simple: Guzzle tiene una interfaz sencilla, sencilla y ligera que abstrae todos esos "problemas de manejo de cURL de bajo nivel". Casi todos los que desarrollan PHP moderno usan Composer de todos modos, por lo que usar Guzzle es realmente simple.
Andreas
2
Gracias, sé que guzzle es popular, sin embargo, hay casos de uso en los que el compositor causa dolor (por ejemplo, desarrollar complementos para proyectos de software más grandes que ya podrían usar una (versión diferente) de guzzle u otras dependencias), por lo que es bueno saber esta información para hacer una decisión sobre qué solución será más robusta
artfulrobot
26

Hay otro método CURL si vas por ese camino.

Esto es bastante sencillo una vez que entienda cómo funciona la extensión curl de PHP, combinando varias banderas con llamadas setopt (). En este ejemplo, tengo una variable $ xml que contiene el XML que he preparado para enviar; voy a publicar el contenido de eso en el método de prueba del ejemplo.

$url = 'http://api.example.com/services/xmlrpc/';
$ch = curl_init($url);

curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);
//process $response

Primero inicializamos la conexión, luego configuramos algunas opciones usando setopt (). Estos le dicen a PHP que estamos haciendo una solicitud posterior, y que estamos enviando algunos datos con él, proporcionándonos los datos. El indicador CURLOPT_RETURNTRANSFER le dice a curl que nos dé el resultado como el valor de retorno de curl_exec en lugar de generarlo. Luego hacemos la llamada y cerramos la conexión; el resultado es en $ respuesta.

Josip Ivic
fuente
1
en la llamada tercera curl_setopt (), el primer argumento debe ser $chno $curl, ¿verdad?
jcomeau_ictx
¿Puedes usar este mismo código para PUBLICAR datos JSON? Pero reemplace $ xml con say $ json (donde $ json es probablemente una cadena JSON)
Neal Davis
24

Si por casualidad está utilizando Wordpress para desarrollar su aplicación (en realidad es una forma conveniente de obtener autorización, páginas de información, etc. incluso para cosas muy simples), puede usar el siguiente fragmento:

$response = wp_remote_post( $url, array('body' => $parameters));

if ( is_wp_error( $response ) ) {
    // $response->get_error_message()
} else {
    // $response['body']
}

Utiliza diferentes formas de realizar la solicitud HTTP real, dependiendo de lo que esté disponible en el servidor web. Para más detalles, consulte la documentación de la API HTTP .

Si no desea desarrollar un tema o complemento personalizado para iniciar el motor de Wordpress, puede hacer lo siguiente en un archivo PHP aislado en la raíz de WordPress:

require_once( dirname(__FILE__) . '/wp-load.php' );

// ... your code

No mostrará ningún tema ni generará ningún HTML, ¡simplemente piratee con las API de Wordpress!


fuente
22

Me gustaría agregar algunas ideas sobre la respuesta basada en rizos de Fred Tanrikut. Sé que la mayoría de ellos ya están escritos en las respuestas anteriores, pero creo que es una buena idea mostrar una respuesta que los incluya a todos juntos.

Aquí está la clase que escribí para hacer solicitudes HTTP-GET / POST / PUT / DELETE basadas en curl, relacionadas con el cuerpo de la respuesta:

class HTTPRequester {
    /**
     * @description Make HTTP-GET call
     * @param       $url
     * @param       array $params
     * @return      HTTP-Response body or an empty string if the request fails or is empty
     */
    public static function HTTPGet($url, array $params) {
        $query = http_build_query($params); 
        $ch    = curl_init($url.'?'.$query);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HEADER, false);
        $response = curl_exec($ch);
        curl_close($ch);
        return $response;
    }
    /**
     * @description Make HTTP-POST call
     * @param       $url
     * @param       array $params
     * @return      HTTP-Response body or an empty string if the request fails or is empty
     */
    public static function HTTPPost($url, array $params) {
        $query = http_build_query($params);
        $ch    = curl_init();
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $query);
        $response = curl_exec($ch);
        curl_close($ch);
        return $response;
    }
    /**
     * @description Make HTTP-PUT call
     * @param       $url
     * @param       array $params
     * @return      HTTP-Response body or an empty string if the request fails or is empty
     */
    public static function HTTPPut($url, array $params) {
        $query = \http_build_query($params);
        $ch    = \curl_init();
        \curl_setopt($ch, \CURLOPT_RETURNTRANSFER, true);
        \curl_setopt($ch, \CURLOPT_HEADER, false);
        \curl_setopt($ch, \CURLOPT_URL, $url);
        \curl_setopt($ch, \CURLOPT_CUSTOMREQUEST, 'PUT');
        \curl_setopt($ch, \CURLOPT_POSTFIELDS, $query);
        $response = \curl_exec($ch);
        \curl_close($ch);
        return $response;
    }
    /**
     * @category Make HTTP-DELETE call
     * @param    $url
     * @param    array $params
     * @return   HTTP-Response body or an empty string if the request fails or is empty
     */
    public static function HTTPDelete($url, array $params) {
        $query = \http_build_query($params);
        $ch    = \curl_init();
        \curl_setopt($ch, \CURLOPT_RETURNTRANSFER, true);
        \curl_setopt($ch, \CURLOPT_HEADER, false);
        \curl_setopt($ch, \CURLOPT_URL, $url);
        \curl_setopt($ch, \CURLOPT_CUSTOMREQUEST, 'DELETE');
        \curl_setopt($ch, \CURLOPT_POSTFIELDS, $query);
        $response = \curl_exec($ch);
        \curl_close($ch);
        return $response;
    }
}

Mejoras

  • Usando http_build_query para obtener la cadena de consulta de una matriz de solicitud (también puede usar la matriz en sí misma, por lo tanto, consulte: http://php.net/manual/en/function.curl-setopt.php )
  • Devolver la respuesta en lugar de repetirla. Por cierto, puede evitar el retorno quitando la línea curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, true); . Después de eso, el valor de retorno es un valor booleano (verdadero = la solicitud se realizó correctamente; de ​​lo contrario, se produjo un error) y la respuesta se repite. Ver: http://php.net/en/manual/function.curl-exec.php
  • Limpie el cierre de sesión y la eliminación del controlador de curl utilizando curl_close . Ver: http://php.net/manual/en/function.curl-close.php
  • Usando valores booleanos para curl_setopt función lugar de usar cualquier número (sé que cualquier número que no sea igual a cero también se considera verdadero, pero el uso de verdadero genera un código más legible, pero esa es solo mi opinión)
  • Capacidad para realizar llamadas HTTP-PUT / DELETE (útil para pruebas de servicio RESTful)

Ejemplo de uso

OBTENER

$response = HTTPRequester::HTTPGet("http://localhost/service/foobar.php", array("getParam" => "foobar"));

ENVIAR

$response = HTTPRequester::HTTPPost("http://localhost/service/foobar.php", array("postParam" => "foobar"));

PONER

$response = HTTPRequester::HTTPPut("http://localhost/service/foobar.php", array("putParam" => "foobar"));

ELIMINAR

$response = HTTPRequester::HTTPDelete("http://localhost/service/foobar.php", array("deleteParam" => "foobar"));

Pruebas

También puede realizar algunas pruebas de servicio interesantes utilizando esta clase simple.

class HTTPRequesterCase extends TestCase {
    /**
     * @description test static method HTTPGet
     */
    public function testHTTPGet() {
        $requestArr = array("getLicenses" => 1);
        $url        = "http://localhost/project/req/licenseService.php";
        $this->assertEquals(HTTPRequester::HTTPGet($url, $requestArr), '[{"error":false,"val":["NONE","AGPL","GPLv3"]}]');
    }
    /**
     * @description test static method HTTPPost
     */
    public function testHTTPPost() {
        $requestArr = array("addPerson" => array("foo", "bar"));
        $url        = "http://localhost/project/req/personService.php";
        $this->assertEquals(HTTPRequester::HTTPPost($url, $requestArr), '[{"error":false}]');
    }
    /**
     * @description test static method HTTPPut
     */
    public function testHTTPPut() {
        $requestArr = array("updatePerson" => array("foo", "bar"));
        $url        = "http://localhost/project/req/personService.php";
        $this->assertEquals(HTTPRequester::HTTPPut($url, $requestArr), '[{"error":false}]');
    }
    /**
     * @description test static method HTTPDelete
     */
    public function testHTTPDelete() {
        $requestArr = array("deletePerson" => array("foo", "bar"));
        $url        = "http://localhost/project/req/personService.php";
        $this->assertEquals(HTTPRequester::HTTPDelete($url, $requestArr), '[{"error":false}]');
    }
}
mwatzer
fuente
Para mí, dice "Error no detectado: llamada al método indefinido HTTPRequester :: HTTPost ()" . Simplemente pegué tu clase en mi archivo .php. ¿Algo más que deba hacer?
LinusGeffarth
1
¿Puedes publicar tu código? Es bastante difícil adivinar lo que está mal sin ningún fragmento de código.
mwatzer
Como dije, literalmente copié el tuyo en mi archivo PHP simple y me dio este error.
LinusGeffarth
1
Ok, ahora veo el problema, ¡estaba mal en el ejemplo! Debe llamar a HTTPRequester :: HTTPPost () en lugar de HTTPRequester :: HTTPost ()
mwatzer
1
Ah Ese es fácil de perder. Tuve que leer tu comentario como 5 veces antes de detectar la P adicional . ¡Gracias!
LinusGeffarth
20

Otra alternativa del método anterior sin rizos es usar las funciones de flujo nativas :

  • stream_context_create():

    Crea y devuelve un contexto de transmisión con las opciones proporcionadas en las opciones preestablecidas.

  • stream_get_contents():

    Idéntico a file_get_contents(), excepto que stream_get_contents() opera en un recurso de flujo ya abierto y devuelve el contenido restante en una cadena, hasta bytes de longitud máxima y comenzando en el desplazamiento especificado .

Una función POST con estos puede ser simplemente así:

<?php

function post_request($url, array $params) {
  $query_content = http_build_query($params);
  $fp = fopen($url, 'r', FALSE, // do not use_include_path
    stream_context_create([
    'http' => [
      'header'  => [ // header array does not need '\r\n'
        'Content-type: application/x-www-form-urlencoded',
        'Content-Length: ' . strlen($query_content)
      ],
      'method'  => 'POST',
      'content' => $query_content
    ]
  ]));
  if ($fp === FALSE) {
    return json_encode(['error' => 'Failed to get contents...']);
  }
  $result = stream_get_contents($fp); // no maxlength/offset
  fclose($fp);
  return $result;
}
CPHPython
fuente
1
Este método sin CURL funcionó bien para mí para validar reCAPTCHA de google. Esta respuesta converge con este código de google: github.com/google/recaptcha/blob/master/src/ReCaptcha/…
Xavi Montero
1
No tiene que usar fclose()si $fpes así false. Porque fclose()espera que un recurso sea parámetro.
Floris
1
@Floris lo editó hace un momento y, de hecho, fclose docs menciona "El puntero del archivo debe ser válido". ¡Gracias por notar eso!
CPHPython
8

La mejor forma de enviar GETo POSTsolicitar con PHPes la siguiente:

<?php
    $r = new HttpRequest('http://example.com/form.php', HttpRequest::METH_POST);
    $r->setOptions(array('cookies' => array('lang' => 'de')));
    $r->addPostFields(array('user' => 'mike', 'pass' => 's3c|r3t'));

    try {
        echo $r->send()->getBody();
    } catch (HttpException $ex) {
        echo $ex;
    }
?>

El código está tomado de la documentación oficial aquí http://docs.php.net/manual/da/httprequest.send.php

Imran Zahoor
fuente
1
@akinuri gracias por resaltar, voy a compartir el nuevo.
Imran Zahoor
¿Cómo hacerlo en PHP 5x?
@YumYumYum consulte la respuesta de dbau anterior para 5x que utiliza esta técnica php.net/manual/en/function.stream-context-create.php O siempre puede volver a la solución de rizo estándar.
Imran Zahoor
5

Hay uno más que puedes usar

<?php
$fields = array(
    'name' => 'mike',
    'pass' => 'se_ret'
);
$files = array(
    array(
        'name' => 'uimg',
        'type' => 'image/jpeg',
        'file' => './profile.jpg',
    )
);

$response = http_post_fields("http://www.example.com/", $fields, $files);
?>

Haga clic aquí para más detalles.

Código
fuente
2
Esto se basa en una extensión PECL que la mayoría no habrá instalado. Ni siquiera estoy seguro de que todavía esté disponible, ya que se han eliminado las páginas del manual.
miken32
5

Estaba buscando un problema similar y encontré un mejor enfoque para hacerlo. Así que aquí va.

Simplemente puede poner la siguiente línea en la página de redirección (por ejemplo, page1.php).

header("Location: URL", TRUE, 307); // Replace URL with to be redirected URL, e.g. final.php

Necesito esto para redirigir las solicitudes POST para las llamadas a la API REST . Esta solución puede redirigir con datos de publicación, así como valores de encabezado personalizados.

Aquí está el enlace de referencia .

Arindam Nayak
fuente
1
Esto responde cómo redirigir una solicitud de página, no ¿Cómo envío una solicitud POST con PHP? Claro que esto reenviaría cualquier parámetro POST, pero eso no es lo mismo
Wesley Smith el
@ DelightedD0D, Lo siento, no entiendo la diferencia entre redirect a page request with POST paramvs send POST request. Para mí el propósito de ambos es el mismo, corrígeme si estoy equivocado.
Arindam Nayak
1
¿Hay algún método que me permita enviar parámetros con el método POST y luego leer el contenido a través de PHP? El OP quiere que su script php construya un conjunto de parámetros POST y los envíe a otra página php y que su script reciba el resultado de esa página. Esta solución simplemente aceptaría un conjunto de valores ya PUBLICADOS y los reenviaría a otra página. Son muy diferentes
Wesley Smith el
5

Aquí está usando solo un comando sin cURL. Súper simple

echo file_get_contents('https://www.server.com', false, stream_context_create([
    'http' => [
        'method' => 'POST',
        'header'  => "Content-type: application/x-www-form-urlencoded",
        'content' => http_build_query([
            'key1' => 'Hello world!', 'key2' => 'second value'
        ])
    ]
]));
Liga
fuente
¿Cómo funcionará la Key2? ¿Cuál es el separador entre ellos?
Sayed Muhammad Idrees
@Sayedidrees para agregar key2, puede ingresarlo como un segundo elemento de matriz. 'key1' => 'Hello world!', 'key2' => 'second value'
Liga
Esto funciona muy bien mientras se usa con zapier.
Moxet
3

Pruebe el paquete HTTP_Request2 de PEAR para enviar fácilmente solicitudes POST. Alternativamente, puede usar las funciones curl de PHP o usar un contexto de flujo PHP .

HTTP_Request2 también hace posible simular el servidor , por lo que puede probar su código fácilmente

Cweiske
fuente
77
Me gustaría verte elaborarlo, si es posible.
Gui Imamura