546

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.domain.com

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

cn007b
  • 14,506
  • 6
  • 48
  • 62
mysqllearner
  • 11,983
  • 14
  • 41
  • 43

13 Answers13

927
<?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 { ... }
?>
mimarcel
  • 696
  • 7
  • 20
miku
  • 161,705
  • 45
  • 286
  • 300
  • 60
    no need to use `http_build_query()` to handle parameters; just pass the array to `CURLOPT_POSTFIELDS` is enough. – Raptor Apr 05 '16 at 03:13
  • 11
    @Raptor providing array directly to CURLOPT_POSTFIELDS actually curl makes slightly different type of POST. (Expect: 100-continue) – Oleg Popov Apr 14 '16 at 04:49
  • 29
    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`. http://php.net/manual/en/function.curl-setopt.php – Chloe Jul 21 '16 at 02:34
  • 4
    Using CURLOPT_RETURNTRANSFER means that curl_exec will return the response as a string rather than outputting it. – bnp887 Dec 28 '16 at 14:27
  • 10
    I suggest using `true` instead of `1` for `CURLOPT_POST`. – FluorescentGreen5 Aug 19 '17 at 08:53
293

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.

emix
  • 12,863
  • 8
  • 50
  • 72
  • 7
    This should really be the accepted answer, because best-practice would be to let the HTTP library handle the encoding of your variables. – Eric Seastrand Mar 07 '16 at 14:57
  • 5
    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. – César Mar 14 '16 at 15:25
  • 5
    HTTP spec is pretty straightforward on how the POST parameters should look like. The webserver software should comply to standards anyway. – emix Mar 15 '16 at 11:46
  • 1
    By using this way you will force cURL to use slightly different type of POST. (Expect: 100-continue). Check this article: https://support.urbanairship.com/entries/59909909--Expect-100-Continue-Issues-and-Risks – Oleg Popov Apr 14 '16 at 04:52
  • $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. – Roel Vermeulen Aug 27 '16 at 20:39
  • 8
    Expanding on @César's comment, the [PHP documentation](http://php.net/curl_setopt) 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. – Jake Z Sep 16 '16 at 19:31
  • 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. – Hydde87 Aug 11 '17 at 09:49
  • 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 Jan 25 '19 at 10:00
  • Much more cleaner code than the accepted answer, works like a charm. – Miquel Canal Nov 10 '20 at 20:57
37

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.

Eric Leschinski
  • 123,728
  • 82
  • 382
  • 321
  • `$postvars .= $key . $value;` should `$postvars .= $key . $value ."&";` or not? – Manwal Aug 06 '14 at 07:41
  • Looking again at this answer, you can also replace your custom query string converter implementation with [http_build_query](http://php.net/manual/en/function.http-build-query.php), just give it the `$fields` array and it'll output a query string. –  Nov 17 '14 at 11:54
  • Be aware that you should encode your data in order for it to be submitted safely. – wtf8_decode Jan 16 '15 at 17:45
  • 6
    Oh no don't try to build the post string yourself! use this: `curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($fields));` – oriadam Sep 29 '15 at 00:21
  • 5
    -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 Mar 07 '16 at 14:56
31

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);
cn007b
  • 14,506
  • 6
  • 48
  • 62
16

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',
]);
MSS
  • 2,820
  • 20
  • 25
  • 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 Jan 25 '19 at 10:04
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
  • 3,400
  • 3
  • 21
  • 24
  • 1
    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 Jan 25 '19 at 10:03
6

If the form is using redirects, authentication, cookies, SSL (https), or anything else other than a totally open script expecting POST variables, you are going to start gnashing your teeth really quick. Take a look at Snoopy, which does exactly what you have in mind while removing the need to set up a lot of the overhead.

Anthony
  • 34,084
  • 23
  • 90
  • 154
  • If you want to stick with the stock lib, just try adding `curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);` – MarkHu Apr 26 '16 at 21:32
  • 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](http://www.php-fig.org/psr/psr-7/) compatible (Guzzle is the only one I know off-hand) to avoid headaches. – Anthony Sep 19 '16 at 12:53
4

1.Step by step

  • Initialize the cURL session:
$url = "www.domain.com";
$ch = curl_init($url);
  • If your request has headers like bearer token or defining JSON contents you have to set HTTPHEADER options to cURL:
$token = "generated token code";
curl_setopt(
    $ch, 
    CURLOPT_HTTPHEADER, 
    array(
        'Content-Type: application/json', // for define content type that is json
        'bearer: '.$token, // send token in header request
        'Content-length: 100' // content length for example 100 characters (can add by strlen($fields))
    )
);
  • If you want to include the header in the output set CURLOPT_HEADER to true:
curl_setopt($ch, CURLOPT_HEADER, false);
  • Set RETURNTRANSFER option to true to return the transfer as a string instead of outputting it directly:
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  • To check the existence of a common name in the SSL peer certificate can be set to 0(to not check the names), 1(not supported in cURL 7.28.1), 2(default value and for production mode):
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
  • For posting fields as an array by cURL:
$fields = array(
    "username" => "user1",
    "password" => "passuser1",
    "gender" => 1
);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
  • Execute cURL and return the string. depending on your resource this returns output like result=OK:
$result = curl_exec($ch);
  • Close cURL resource, and free up system resources:
curl_close($ch);

2.Use as a class

  • The whole call_cURL class that can be extended:
class class_name_for_call_cURL {
    protected function getUrl() {
        return "www.domain.com";
    }

    public function call_cURL() {
        $token = "generated token code";

        $fields = array(
            "username" => "user1",
            "password" => "passuser1",
            "gender" => 1
        );

        $url = $this->getUrl();
        $output = $this->_execute($fields, $url, $token);
        
        // if you want to get json data
        // $output = json_decode($output);
            
        if ($output == "OK") {
            return true;
        } else {
             return false;
        }
    }

    private function _execute($postData, $url, $token) {
        // for sending data as json type
        $fields = json_encode($postData);

        $ch = curl_init($url);
        curl_setopt(
            $ch, 
            CURLOPT_HTTPHEADER, 
            array(
                'Content-Type: application/json', // if the content type is json
                'bearer: '.$token // if you need token in header
            )
        );
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);

        $result = curl_exec($ch);
        curl_close($ch);

        return $result;
    }
}
  • Using the class and call cURL:
$class = new class_name_for_call_cURL();
var_dump($class->call_cURL()); // output is true/false

3.One function

  • A function for using anywhere that needed:
function get_cURL() {

        $url = "www.domain.com";
        $token = "generated token code";

        $postData = array(
            "username" => "user1",
            "password" => "passuser1",
            "gender" => 1
        );

        // for sending data as json type
        $fields = json_encode($postData);

        $ch = curl_init($url);
        curl_setopt(
            $ch, 
            CURLOPT_HTTPHEADER, 
            array(
                'Content-Type: application/json', // if the content type is json
                'bearer: '.$token // if you need token in header
            )
        );
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);

        $result = curl_exec($ch);
        curl_close($ch);

        return $result;
}
  • This function is usable just by:
var_dump(get_cURL());
Pejman Kheyri
  • 1,389
  • 6
  • 13
  • 22
3

A simpler answer IF you are passing information to your own website is to use a SESSION variable. Begin php page with:

session_start();

If at some point there is information you want to generate in PHP and pass to the next page in the session, instead of using a POST variable, assign it to a SESSION variable. Example:

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

Then on the next page you simply reference this SESSION variable. NOTE: after you use it, be sure you destroy it, so it doesn't persist after it is used:

if (isset($_SESSION['message'])) {echo $_SESSION['message']; unset($_SESSION['message']);}
andrewsi
  • 10,954
  • 132
  • 33
  • 48
3

Here are some boilerplate code for PHP + curl http://www.webbotsspidersscreenscrapers.com/DSP_download.php

include in these library will simplify development

<?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
  • 5,763
  • 4
  • 36
  • 51
2

If you try to login on site with cookies.

This code:

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

It May not works if you try to login, because many sites return status 200, but the post is not successful.

The easy way to check if the login post is successful is to check if it setting cookies again. If in output have a Set-Cookies string, this means the posts are not successful and it starts a new session.

Also, the post can be successful, but the status can redirect instead of 200.

To be sure the post is successful try this:

Follow location after the post, so it will go to the page where the post does redirect to:

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

And than check if new cookies existing in the request:

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

{echo 'post successful'; }

else { echo 'not successful'; }
Pejman Kheyri
  • 1,389
  • 6
  • 13
  • 22
2

Examples of sending form and raw data:

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

I'm surprised nobody suggested file_get_contents:

$url = "http://www.example.com";
$parameters = array('username' => 'user1', 'password' => 'passuser1', 'gender' => '1');
$options = array('http' => array(
    'method'  => 'POST',
    'content' => http_build_query($parameters)
));

$context  = stream_context_create($options);
$result = file_get_contents($url, false, $context);

it's simple, it works; I use it in an environment where I control the code at both ends.

even better, use json_decode (and set up your code to return JSON)

$result = json_decode(file_get_contents($url, false, $context), TRUE);

this approach invokes curl behind the scenes, but you don't jump through as many hoops.

Answer refined from this original answer elsewhere on Stack Overflow: PHP sending variables to file_get_contents()

cloudxix
  • 103
  • 1
  • 1
  • 8