PHP + curl, código de muestra HTTP POST?

491

¿Alguien puede mostrarme cómo hacer un php curl con un HTTP POST?

Quiero enviar datos como este:

username=user1, password=passuser1, gender=1

A www.domain.com

Espero que el rizo devuelva una respuesta como result=OK. ¿Hay algún ejemplo?

mysqllearner
fuente

Respuestas:

841
<?php
//
// A very simple PHP example that sends a HTTP POST to a remote site
//

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL,"http://www.example.com/tester.phtml");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,
            "postvar1=value1&postvar2=value2&postvar3=value3");

// In real life you should use something like:
// curl_setopt($ch, CURLOPT_POSTFIELDS, 
//          http_build_query(array('postvar1' => 'value1')));

// Receive server response ...
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$server_output = curl_exec($ch);

curl_close ($ch);

// Further processing ...
if ($server_output == "OK") { ... } else { ... }
?>
miku
fuente
47
no es necesario usar http_build_query()para manejar parámetros; solo pasa la matriz a CURLOPT_POSTFIELDSes suficiente.
Raptor
8
@Raptor proporciona una matriz directamente a CURLOPT_POSTFIELDS curl realmente hace un tipo de POST ligeramente diferente. (Esperar: 100-continuar)
Oleg Popov
22
Además, si el valor de CURLOPT_POSTFIELDSes una matriz, el Content-Typeencabezado se establecerá en multipart/form-datalugar de application/x-www-form-urlencoded. php.net/manual/en/function.curl-setopt.php
Chloe
2
Usar CURLOPT_RETURNTRANSFER significa que curl_exec devolverá la respuesta como una cadena en lugar de generarla.
bnp887
2
Sugiero usar en truelugar de 1para CURLOPT_POST.
FluorescentGreen5
261

Procesal

// set post fields
$post = [
    'username' => 'user1',
    'password' => 'passuser1',
    'gender'   => 1,
];

$ch = curl_init('http://www.example.com');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);

// execute!
$response = curl_exec($ch);

// close the connection, release resources used
curl_close($ch);

// do anything you want with your response
var_dump($response);

Orientado a objetos

<?php

// mutatis mutandis
namespace MyApp\Http;

class CurlPost
{
    private $url;
    private $options;

    /**
     * @param string $url     Request URL
     * @param array  $options cURL options
     */
    public function __construct($url, array $options = [])
    {
        $this->url = $url;
        $this->options = $options;
    }

    /**
     * Get the response
     * @return string
     * @throws \RuntimeException On cURL error
     */
    public function __invoke(array $post)
    {
        $ch = curl_init($this->url);

        foreach ($this->options as $key => $val) {
            curl_setopt($ch, $key, $val);
        }

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post);

        $response = curl_exec($ch);
        $error    = curl_error($ch);
        $errno    = curl_errno($ch);

        if (is_resource($ch)) {
            curl_close($ch);
        }

        if (0 !== $errno) {
            throw new \RuntimeException($error, $errno);
        }

        return $response;
    }
}

Uso

// create curl object
$curl = new \MyApp\Http\CurlPost('http://www.example.com');

try {
    // execute the request
    echo $curl([
        'username' => 'user1',
        'password' => 'passuser1',
        'gender'   => 1,
    ]);
} catch (\RuntimeException $ex) {
    // catch errors
    die(sprintf('Http error %s with code %d', $ex->getMessage(), $ex->getCode()));
}

Nota al margen aquí: sería mejor crear algún tipo de interfaz llamada, AdapterInterfacepor ejemplo, con getResponse()método y dejar que la clase anterior lo implemente. Luego, siempre puede intercambiar esta implementación con otro adaptador de su gusto, sin efectos secundarios para su aplicación.

Usando HTTPS / cifrando el tráfico

Por lo general, hay un problema con cURL en PHP bajo el sistema operativo Windows. Al intentar conectarse a un punto final protegido https, recibirá un error que le indicará eso certificate verify failed.

Lo que la mayoría de la gente hace aquí es decirle a la biblioteca cURL que simplemente ignore los errores de certificado y continúe ( curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);). Como esto hará que su código funcione, introduce un gran agujero de seguridad y permite que los usuarios malintencionados realicen varios ataques en su aplicación, como el ataque Man In The Middle o similar .

Nunca, nunca hagas eso. En cambio, simplemente necesita modificar su php.iniy decirle a PHP dónde está su CA Certificatearchivo para que pueda verificar los certificados correctamente:

; modify the absolute path to the cacert.pem file
curl.cainfo=c:\php\cacert.pem

Lo último cacert.pemse puede descargar de Internet o extraer de su navegador favorito . Al cambiar cualquier php.iniconfiguración relacionada, recuerde reiniciar su servidor web.

emix
fuente
44
Esta realmente debería ser la respuesta aceptada, porque la mejor práctica sería dejar que la biblioteca HTTP maneje la codificación de sus variables.
Eric Seastrand
44
Este no es siempre el caso. He visto servidores web que esperan que las variables POST se codifiquen de cierta manera, lo que hace que fallen de lo contrario. Me parece que http_build_query () es en realidad más confiable que cURL para esto.
César
44
La especificación HTTP es bastante sencilla sobre cómo deberían ser los parámetros POST. El software del servidor web debe cumplir con los estándares de todos modos.
emix
1
Al usarlo de esta manera, obligará a cURL a usar un tipo de POST ligeramente diferente. (Esperar: 100-continuar). Consulte este artículo: support.urbanairship.com/entries/…
Oleg Popov el
55
Ampliando el comentario de @ César, la documentación de PHP señala explícitamente lo siguiente: "Pasar una matriz a CURLOPT_POSTFIELDS codificará los datos como multipart / form-data , mientras que pasar una cadena codificada por URL codificará los datos como application / x-www-form "codificado ". Recientemente, pasé una cantidad excesiva de tiempo tratando de solucionar por qué una llamada cURL fallaba en un punto final de un tercero solo para finalmente darme cuenta de que no admitían datos multiparte / formulario.
Jake Z
31

Un ejemplo en vivo del uso de php curl_exec para hacer una publicación HTTP:

Pon esto en un archivo llamado foobar.php:

<?php
  $ch = curl_init();
  $skipper = "luxury assault recreational vehicle";
  $fields = array( 'penguins'=>$skipper, 'bestpony'=>'rainbowdash');
  $postvars = '';
  foreach($fields as $key=>$value) {
    $postvars .= $key . "=" . $value . "&";
  }
  $url = "http://www.google.com";
  curl_setopt($ch,CURLOPT_URL,$url);
  curl_setopt($ch,CURLOPT_POST, 1);                //0 for a get request
  curl_setopt($ch,CURLOPT_POSTFIELDS,$postvars);
  curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch,CURLOPT_CONNECTTIMEOUT ,3);
  curl_setopt($ch,CURLOPT_TIMEOUT, 20);
  $response = curl_exec($ch);
  print "curl response is:" . $response;
  curl_close ($ch);
?>

Luego ejecútelo con el comando php foobar.php, volcará este tipo de salida en la pantalla:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" 
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Title</title>

<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<body>
  A mountain of content...
</body>
</html>

Entonces hiciste una POST PHP a www.google.com y le enviaste algunos datos.

Si el servidor hubiera sido programado para leer las variables de publicación, podría decidir hacer algo diferente en función de eso.

Eric Leschinski
fuente
$postvars .= $key . $value;debería $postvars .= $key . $value ."&";o no?
Manwal
Mirando nuevamente esta respuesta, también puede reemplazar su implementación de convertidor de cadena de consulta personalizada con http_build_query , solo dele la $fieldsmatriz y generará una cadena de consulta.
Tenga en cuenta que debe codificar sus datos para que se envíen de forma segura.
wtf8_decode
3
¡Oh, no, no intentes construir la cadena de publicaciones tú mismo! use esto:curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($fields));
oriadam
3
-1 porque no escapas de tus publicaciones. El ejemplo del OP es enviar nombres de usuario y contraseñas enviados por el usuario para la autenticación. Con su solución, un usuario con una & en su contraseña nunca podrá iniciar sesión. El comentario de oriadam es correcto, pero puede http_build_querycurl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
omitirlo
26

Se puede llegar fácilmente con:

<?php

$post = [
    'username' => 'user1',
    'password' => 'passuser1',
    'gender'   => 1,
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://www.domain.com');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post));
$response = curl_exec($ch);
var_export($response);
V. Kovpak
fuente
13

Curl Post + Error Handling + Set Headers [gracias a @ mantas-d]:

function curlPost($url, $data=NULL, $headers = NULL) {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    if(!empty($data)){
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    }

    if (!empty($headers)) {
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    }

    $response = curl_exec($ch);

    if (curl_error($ch)) {
        trigger_error('Curl Error:' . curl_error($ch));
    }

    curl_close($ch);
    return $response;
}


curlPost('google.com', [
    'username' => 'admin',
    'password' => '12345',
]);
MSS
fuente
Su código no cerrará el identificador y los recursos libres, porque curl_close después de lanzar una excepción. Deberías curl_close dentro de un bloque finalmente.
emix
7
curlPost('google.com', [
    'username' => 'admin',
    'password' => '12345',
]);


function curlPost($url, $data) {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    $response = curl_exec($ch);
    $error = curl_error($ch);
    curl_close($ch);
    if ($error !== '') {
        throw new \Exception($error);
    }

    return $response;
}
Mantas D
fuente
1
Su código no cerrará el identificador y los recursos libres, porque curl_close después de lanzar una excepción. Deberías curl_closedentro de un finallybloque.
emix
6

Si el formulario usa redireccionamientos, autenticación, cookies, SSL (https) o cualquier otra cosa que no sea un script totalmente abierto que espera variables POST, comenzará a rechinar los dientes muy rápido. Eche un vistazo a Snoopy , que hace exactamente lo que tiene en mente al tiempo que elimina la necesidad de configurar gran parte de la sobrecarga.

Antonio
fuente
Si desea seguir con el stock lib, intente agregarcurl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
MarkHu
El único inconveniente es que aún tiene que lidiar con la configuración de un tarro de cookies y otros problemas potenciales (como seguir redirecciones, cómo lidiar con la autenticación no basada en HTTP, etc.). 6 años después, recomendaría el concepto más genérico de un "navegador sin cabeza" en lugar de esa biblioteca específica (o cualquier cosa en sourceforge, ¿qué fecha tiene, verdad?) Y aunque generalmente solo trato directamente con las opciones de curl, aún así recomendaría mirando una biblioteca de navegador sin cabeza que sea compatible con PSR-7 (Guzzle es el único que conozco de forma remota) para evitar dolores de cabeza.
Anthony
3

Una respuesta más simple SI está pasando información a su propio sitio web es usar una variable SESSION. Comience la página php con:

session_start();

Si en algún momento hay información que desea generar en PHP y pasar a la siguiente página de la sesión, en lugar de utilizar una variable POST, asígnela a una variable SESSION. Ejemplo:

$_SESSION['message']='www.'.$_GET['school'].'.edu was not found.  Please try again.'

Luego, en la página siguiente, simplemente haga referencia a esta variable SESIÓN. NOTA: después de usarlo, asegúrese de destruirlo para que no persista después de usarlo:

if (isset($_SESSION['message'])) {echo $_SESSION['message']; unset($_SESSION['message']);}
usuario2532795
fuente
3

Aquí hay un código repetitivo para PHP + curl http://www.webbotsspidersscreenscrapers.com/DSP_download.php

incluir en estas bibliotecas simplificará el desarrollo

<?php
# Initialization
include("LIB_http.php");
include("LIB_parse.php");
$product_array=array();
$product_count=0;

# Download the target (store) web page
$target = "http://www.tellmewhenitchanges.com/buyair";
$web_page = http_get($target, "");
    ...
?>
AzizSM
fuente
2

Si intenta iniciar sesión en el sitio con cookies.

Este código:

if ($server_output == "OK") { ... } else { ... }

Es posible que no funcione si intenta iniciar sesión, porque muchos sitios devuelven el estado 200, pero la publicación no es exitosa.

Una manera fácil de verificar si la publicación de inicio de sesión es exitosa es verificar si configura las cookies nuevamente. Si en la salida tiene una cadena Set-Cookies, esto significa que las publicaciones no son exitosas y comienza una nueva sesión.

Además, la publicación puede ser exitosa, pero el estado se puede redirigir en lugar de 200.

Para estar seguro de que la publicación es exitosa, intente esto:

Siga la ubicación después de la publicación, por lo que irá a la página donde la publicación redirige a:

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

Y luego verifique si existen nuevas cookies en la solicitud:

if (!preg_match('/^Set-Cookie:\s*([^;]*)/mi', $server_output)) 

{echo 'post successful'; }

else { echo 'not successful'; }
Atanas Atanasov
fuente
1

Ejemplos de envío de formulario y datos sin procesar :

$curlHandler = curl_init();

curl_setopt_array($curlHandler, [
    CURLOPT_URL => 'https://postman-echo.com/post',
    CURLOPT_RETURNTRANSFER => true,

    /**
     * Specify POST method
     */
    CURLOPT_POST => true,

    /**
     * Specify array of form fields
     */
    CURLOPT_POSTFIELDS => [
        'foo' => 'bar',
        'baz' => 'biz',
    ],
]);

$response = curl_exec($curlHandler);

curl_close($curlHandler);

echo($response);
Serhii Andriichuk
fuente