PHP, cURL, and HTTP POST example?

1,433,039

Solution 1

<?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 { ... }
?>

Solution 2

Procedural

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

Object oriented

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

Usage

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

Side note here: it would be best to create some kind of interface called AdapterInterface for example with getResponse() method and let the class above implement it. Then you can always swap this implementation with another adapter of your like, without any side effects to your application.

Using HTTPS / encrypting traffic

Usually there's a problem with cURL in PHP under the Windows operating system. While trying to connect to a https protected endpoint, you will get an error telling you that certificate verify failed.

What most people do here is to tell the cURL library to simply ignore certificate errors and continue (curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);). As this will make your code work, you introduce huge security hole and enable malicious users to perform various attacks on your app like Man In The Middle attack or such.

Never, ever do that. Instead, you simply need to modify your php.ini and tell PHP where your CA Certificate file is to let it verify certificates correctly:

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

The latest cacert.pem can be downloaded from the Internet or extracted from your favorite browser. When changing any php.ini related settings remember to restart your webserver.

Solution 3

A live example of using php curl_exec to do an HTTP post:

Put this in a file called 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);
?>

Then run it with the command php foobar.php, it dumps this kind of output to screen:

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

So you did a PHP POST to www.google.com and sent it some data.

Had the server been programmed to read in the post variables, it could decide to do something different based upon that.

Solution 4

It's can be easily reached with:

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

Solution 5

Curl Post + Error Handling + Set Headers [thanks to @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',
]);
Share:
1,433,039
mysqllearner
Author by

mysqllearner

Updated on July 08, 2022

Comments

  • mysqllearner
    mysqllearner almost 2 years

    Can anyone show me how to do a PHP cURL with an HTTP POST?

    I want to send data like this:

    username=user1, password=passuser1, gender=1
    

    To www.example.com

    I expect the cURL to return a response like result=OK. Are there any examples?

  • Manwal
    Manwal almost 10 years
    $postvars .= $key . $value; should $postvars .= $key . $value ."&"; or not?
  • Admin
    Admin over 9 years
    Looking again at this answer, you can also replace your custom query string converter implementation with http_build_query, just give it the $fields array and it'll output a query string.
  • wtf8_decode
    wtf8_decode over 9 years
    Be aware that you should encode your data in order for it to be submitted safely.
  • oriadam
    oriadam over 8 years
    Oh no don't try to build the post string yourself! use this: curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($fields));
  • Eric Seastrand
    Eric Seastrand about 8 years
    -1 because you are not escaping your post vars. The OP's example is sending user-submitted usernames and passwords for authentication. With your solution, a user with an & in their password will never be able to log in. oriadam's comment is correct, but you can leave out http_build_query like: curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
  • Eric Seastrand
    Eric Seastrand about 8 years
    This should really be the accepted answer, because best-practice would be to let the HTTP library handle the encoding of your variables.
  • César
    César about 8 years
    This is not always the case. I've seen web servers that expect POST variables to be encoded in a certain way, causing them to fail otherwise. It seems to me that http_build_query() is actually more reliable than cURL for this.
  • emix
    emix about 8 years
    HTTP spec is pretty straightforward on how the POST parameters should look like. The webserver software should comply to standards anyway.
  • Raptor
    Raptor about 8 years
    no need to use http_build_query() to handle parameters; just pass the array to CURLOPT_POSTFIELDS is enough.
  • Oleg Popov
    Oleg Popov about 8 years
    @Raptor providing array directly to CURLOPT_POSTFIELDS actually curl makes slightly different type of POST. (Expect: 100-continue)
  • Oleg Popov
    Oleg Popov about 8 years
    By using this way you will force cURL to use slightly different type of POST. (Expect: 100-continue). Check this article: support.urbanairship.com/entries/…
  • MarkHu
    MarkHu about 8 years
    If you want to stick with the stock lib, just try adding curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  • Chloe
    Chloe almost 8 years
    Also if value of CURLOPT_POSTFIELDS is an array, the Content-Type header will be set to multipart/form-data instead of application/x-www-form-urlencoded. php.net/manual/en/function.curl-setopt.php
  • Roel Vermeulen
    Roel Vermeulen over 7 years
    $options = array_merge($options, array(CURLOPT_RETURNTRANSFER => true)) is NOT correct. It should be $options = $options + array(CURLOPT_RETURNTRANSFER => true) as array_merge reorders the indexes and will throw an "invalid curl configuration option" error.
  • Jake Z
    Jake Z over 7 years
    Expanding on @César's comment, the PHP documentation explicitly notes the following: "Passing an array to CURLOPT_POSTFIELDS will encode the data as multipart/form-data, while passing a URL-encoded string will encode the data as application/x-www-form-urlencoded.". I recently spent an inordinate amount of time trying to troubleshoot why a cURL call was failing on a third-party endpoint only to eventually realize that they did not support multipart/form-data.
  • Ashith
    Ashith over 7 years
    The only downside is that you still have to deal with setting a cookie jar and other potential issues (like whether to follow redirects, how to deal with non HTTP-based authentication, etc). 6 years later, I would recommend the more generic concept of a "headless-browser" instead of that specific library (or anything on sourceforge, how dated, right?) And while I generally just deal with curl options directly, I would still advise looking at a headless-browser library that is PSR-7 compatible (Guzzle is the only one I know off-hand) to avoid headaches.
  • bnp887
    bnp887 over 7 years
    Using CURLOPT_RETURNTRANSFER means that curl_exec will return the response as a string rather than outputting it.
  • Hydde87
    Hydde87 over 6 years
    This isn't necessarily the most valid answer. Some servers might simply expect a POST to contain urlencoded data, and x-www-form-urlencoded posts are compliant with the HTTP specs as well. Most frameworks do abstract the post format though so in a lot of cases you're better of just passing an array to CURLOPT_POSTFIELDS like in this example. But saying the other ones do unnecessary things is misleading because for some cases http_build_query() might be necessary to make the API call work.
  • FluorescentGreen5
    FluorescentGreen5 over 6 years
    I suggest using true instead of 1 for CURLOPT_POST.
  • emix
    emix over 5 years
    Sure, but OP asked specifically how to do this with cURL so I provided the easiest way to achieve that. You could even send urlencoded data using the GET request, it's just a verb inside the request. How the application handles the request though is out of the scope here.
  • emix
    emix over 5 years
    Your code won't close the handle and free resources, because you curl_close after throwing an exception. You should curl_close inside a finally block.
  • emix
    emix over 5 years
    Your code won't close the handle and free resources, because you curl_close after throwing an exception. You should curl_close inside a finally block.
  • Miquel Canal
    Miquel Canal over 3 years
    Much more cleaner code than the accepted answer, works like a charm.
  • hanshenrik
    hanshenrik almost 3 years
    actually it's supposed to be $postvars .= urlencode($key) . "=" . urlencode($value) . "&"; , but an even better idea is to just use http_build_query() to build it for you. how do you think your original foreach loop would handle $value="AT&T"; ? answer is that it wouldn't, the value would be encoded as AT and generate a separate key named T
  • Lukas
    Lukas over 2 years
    This is a really great answer. However, it works for me only when I remove the content-type header part.
  • cloudxix
    cloudxix over 2 years
    @lukas, the content-type header was added by an SO editor, not me...
  • Sophie
    Sophie about 2 years
    Hello @cloudxix, how you going? I saw you good with this.. so could you take a look in my similar question? stackoverflow.com/questions/72122223/…
  • Sophie
    Sophie about 2 years
    Hello @Pejman, how you going? I saw you good with this.. so could you take a look in my similar question? stackoverflow.com/questions/72122223/…
  • Sophie
    Sophie about 2 years
    Hello @emix , how you going? I saw you good with this.. so could you take a look in my similar question? stackoverflow.com/questions/72122223/…