I'm trying to get the response & the response headers from CURL using PHP, specifically for Content-Disposition: attachment; so I can return the filename passed within the header. This doesn't seem to get returned within curl_getinfo.
I've tried using the HeaderFunction to call a function to read the additional headers, however, I am unable to add the contents to an array.
Does anyone have any ideas please?
Below is part of my code which is a Curl wrapper class:
...
curl_setopt($this->_ch, CURLOPT_URL, $this->_url);
curl_setopt($this->_ch, CURLOPT_HEADER, false);
curl_setopt($this->_ch, CURLOPT_POST, 1);
curl_setopt($this->_ch, CURLOPT_POSTFIELDS, $this->_postData);
curl_setopt($this->_ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($this->_ch, CURLOPT_USERAGENT, $this->_userAgent);
curl_setopt($this->_ch, CURLOPT_HEADERFUNCTION, 'readHeader');
$this->_response = curl_exec($this->_ch);
$info = curl_getinfo($this->_ch);
...
function readHeader($ch, $header)
{
array_push($this->_headers, $header);
}
Here, this should do it:
curl_setopt($this->_ch, CURLOPT_URL, $this->_url);
curl_setopt($this->_ch, CURLOPT_HEADER, 1);
curl_setopt($this->_ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($this->_ch);
$info = curl_getinfo($this->_ch);
$headers = get_headers_from_curl_response($response);
function get_headers_from_curl_response($response)
{
$headers = array();
$header_text = substr($response, 0, strpos($response, "\r\n\r\n"));
foreach (explode("\r\n", $header_text) as $i => $line)
if ($i === 0)
$headers['http_code'] = $line;
else
{
list ($key, $value) = explode(': ', $line);
$headers[$key] = $value;
}
return $headers;
}
The anwser from c.hill is great but the code will not handle if the first response is a 301 or 302 - in that case only the first header will be added to the array returned by get_header_from_curl_response().
I've updated the function to return an array with each of the headers.
First I use this lines to create a variable with only the header content
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($a, 0, $header_size);
Than I pass $header in to the new get_headers_from_curl_response()-function:
static function get_headers_from_curl_response($headerContent)
{
$headers = array();
// Split the string on every "double" new line.
$arrRequests = explode("\r\n\r\n", $headerContent);
// Loop of response headers. The "count() -1" is to
//avoid an empty row for the extra line break before the body of the response.
for ($index = 0; $index < count($arrRequests) -1; $index++) {
foreach (explode("\r\n", $arrRequests[$index]) as $i => $line)
{
if ($i === 0)
$headers[$index]['http_code'] = $line;
else
{
list ($key, $value) = explode(': ', $line);
$headers[$index][$key] = $value;
}
}
}
return $headers;
}
This function will take header like this:
HTTP/1.1 302 Found
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/html; charset=utf-8
Expires: -1
Location: http://www.website.com/
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Date: Sun, 08 Sep 2013 10:51:39 GMT
Connection: close
Content-Length: 16313
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Date: Sun, 08 Sep 2013 10:51:39 GMT
Connection: close
Content-Length: 15519
And return an array like this:
(
[0] => Array
(
[http_code] => HTTP/1.1 302 Found
[Cache-Control] => no-cache
[Pragma] => no-cache
[Content-Type] => text/html; charset=utf-8
[Expires] => -1
[Location] => http://www.website.com/
[Server] => Microsoft-IIS/7.5
[X-AspNet-Version] => 4.0.30319
[Date] => Sun, 08 Sep 2013 10:51:39 GMT
[Connection] => close
[Content-Length] => 16313
)
[1] => Array
(
[http_code] => HTTP/1.1 200 OK
[Cache-Control] => private
[Content-Type] => text/html; charset=utf-8
[Server] => Microsoft-IIS/7.5
[X-AspNet-Version] => 4.0.30319
[Date] => Sun, 08 Sep 2013 10:51:39 GMT
[Connection] => close
[Content-Length] => 15519
)
)
Using the array() form for method callbacks should make the original example work:
curl_setopt($this->_ch, CURLOPT_HEADERFUNCTION, array($this, 'readHeader'));
Another my implementation:
function getHeaders($response){
if (!preg_match_all('/([A-Za-z\-]{1,})\:(.*)\\r/', $response, $matches)
|| !isset($matches[1], $matches[2])){
return false;
}
$headers = [];
foreach ($matches[1] as $index => $key){
$headers[$key] = $matches[2][$index];
}
return $headers;
}
Used in case, which request format is:
Host: *
Accept: *
Content-Length: *
and etc ...
Simple and straightforward
$headers = [];
// Get the response body as string
$response = curl_exec($curl);
// Get the response headers as string
$headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
// Get the substring of the headers and explode as an array by \r\n
// Each element of the array will be a string `Header-Key: Header-Value`
// Retrieve this two parts with a simple regex `/(.*?): (.*)/`
foreach(explode("\r\n", trim(substr($response, 0, $headerSize))) as $row) {
if(preg_match('/(.*?): (.*)/', $row, $matches)) {
$headers[$matches[1]] = $matches[2];
}
}
Fixing issues:
Error when content of the header contained ': '(split string)
Multiline-headers were not supported
Duplicate headers (Set-Cookie) were not supported
This is my take on the topic ;-)
list($head, $body)=explode("\r\n\r\n", $content, 2);
$headers=parseHeaders($head);
function parseHeaders($text) {
$headers=array();
foreach (explode("\r\n", $text) as $i => $line) {
// Special HTTP first line
if (!$i && preg_match('#^HTTP/(?<protocol>[0-9.]+)\s+(?<code>\d+)(?:\s+(?<message>.*))?$#', $line, $match)) {
$headers['#status']=$line;
$headers['#code']=$match['code'];
$headers['#protocol']=$match['protocol'];
$headers['#message']=$match['message'];
continue;
}
// Multiline header - join with previous
if ($key && preg_match('/^\s/', $line)) {
$headers[$key].=' '.trim($line);
continue;
}
list ($key, $value) = explode(': ', $line, 2);
$key=strtolower($key);
// Append duplicate headers - namely Set-Cookie header
$headers[$key]=isset($headers[$key]) ? $headers[$key].' ' : $value;
}
return $headers;
}
C.hill's answer is great but breaks when retrieving multiple cookies. Made the change here
public function get_headers_from_curl_response($response) {
$headers = array();
$header_text = substr($response, 0, strpos($response, "\r\n\r\n"));
foreach (explode("\r\n", $header_text) as $i => $line)
if ($i === 0) $headers['http_code'] = $line;
else {
list ($key, $value) = explode(': ', $line); $headers[$key][] = $value;
}
return $headers;
}
You can use http_parse_headers function.
It comes from PECL but you will find fallbacks in this SO thread.
you can do 2 ways
by set curl_setopt($this->_ch, CURLOPT_HEADER, true);
header will come out with response message from curl_exec();
you must search keyword 'Content-Disposition:' from response message.
by use this function get_headers($url) right after you call curl_exec(). $url is url called in curl. the return is array of headers. search for 'Content-Disposition' in array to get what you want.
Related
I'm having a hard time trying to parse response headers. Returned headers contain duplicate keys with the name of set-cookie which I'm trying to get along with the other headers but the problem is it overwrides the set-cookie value to the last one available. I know that we cannot have multiple name of the same key in an associative array, in that case what shuold I use to achieve this result?
[16] => set-cookie: GPS=1; Domain=.youtube.com; Expires=Sat, 14-Aug-2021 15:09:33 GMT; Path=/; Secure; HttpOnly
[17] => set-cookie: YSC=HYwhlVWBBfQ; Domain=.youtube.com; Path=/; Secure; HttpOnly; SameSite=none
[18] => set-cookie: VISITOR_INFO1_LIVE=E2nuslFFVxI; Domain=.youtube.com; Expires=Thu, 10-Feb-2022 14:39:33 GMT; Path=/; Secure; HttpOnly; SameSite=none
The script goes on forever if I include the while statement and if I remove it, it works fine but returning only the last set-cookie which is 18
$url = "https://www.youtube.com";
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HEADER, 1);
curl_setopt($curl, CURLOPT_NOBODY, 1);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$resp = curl_exec($curl);
curl_close($curl);
$h = array_filter(explode("\n", $resp), 'trim');
$headers = [];
foreach ($h as $k => $v) {
$l = explode(": ", $v); // Desired keys values here.
if (strpos($v, 'HTTP') == true) {
$headers['Headers']['status'] = $l[0];
} elseif (strpos($v, 'set-cookie') !== false) {
// $headers['Headers']['Cookies'][]['set-cookie'] = $l[1]; Works but return each cookie as array
while (str_contains($v, 'set-cookie')) {
$headers['Headers']['set-cookie'] = $l[1];
}
} else {
$headers['Headers'][$l[0]] = $l[1];
}
}
The reason you get an array using the code below is that you are exploding the header keys using : which it is also available in the content of cookie values.
You can implode the initial exploded array from index 1 and it should work fine, so the code would be:
$headers['Headers']['Cookies'][]['set-cookie'] = implode(':', array_slice($l, 1));
Another solution is that you replace the set-cookie: with empty string and use it.
$headers['Headers']['Cookies'][]['set-cookie'] = str_replace("set-cookie:",'', $v);
I am calling a REST service using php curl. If an error occurs (for example because I posted invalid data) the REST server returns error code 400 and provides informative application error details in the response header custom field.
However, when error 400 occurs the header is not provided in the result from curl_exec() at it returns FALSE even though setopt as been provided. Headers are seen if code returned is 2xx.
curl_setopt($curl,CURLOPT_HEADER, 1);
Is there any way to get the response headers on errors >= 400?
In the example below, I'm using https://httpstat.us/400 to simulate a HTTP 400 response code.
<?php
// create curl resource
$ch = curl_init();
// set url that responds with HTTP 400 status
curl_setopt($ch, CURLOPT_URL, "https://httpstat.us/400");
//return the transfer as a string
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
//enable headers
curl_setopt($ch, CURLOPT_HEADER, 1);
//get only headers
curl_setopt($ch, CURLOPT_NOBODY, 1);
// $output contains the output string
$output = curl_exec($ch);
// close curl resource to free up system resources
curl_close($ch);
$headers = [];
$output = rtrim($output);
$data = explode("\n",$output);
$headers['status'] = $data[0];
array_shift($data);
foreach($data as $part){
//some headers will contain ":" character (Location for example), and the part after ":" will be lost, Thanks to #Emanuele
$middle = explode(":",$part,2);
//Supress warning message if $middle[1] does not exist, Thanks to #crayons
if ( !isset($middle[1]) ) { $middle[1] = null; }
$headers[trim($middle[0])] = trim($middle[1]);
}
// Print all headers as array
print_r($headers);
This returns
Array
(
[status] => HTTP/1.1 400 Bad Request
[Cache-Control] => private
[Content-Length] => 15
[Content-Type] => text/plain; charset=utf-8
[Server] => Microsoft-IIS/10.0
[X-AspNetMvc-Version] => 5.1
[Access-Control-Allow-Origin] => *
[X-AspNet-Version] => 4.0.30319
[X-Powered-By] => ASP.NET
[Set-Cookie] => ARRAffinity=93fdbab9d364704de8ef77182b4d13811344b7dd1ec45d3a9682bbd6fa154ead;Path=/;HttpOnly;Domain=httpstat.us
[Date] => Wed, 13 Nov 2019 23:31:51 GMT
)
That array with all response headers matches up with what I get when I use curl from my terminal:
curl -v https://httpstat.us/400
returns
< HTTP/1.1 400 Bad Request
< Cache-Control: private
< Content-Length: 15
< Content-Type: text/plain; charset=utf-8
< Server: Microsoft-IIS/10.0
< X-AspNetMvc-Version: 5.1
< Access-Control-Allow-Origin: *
< X-AspNet-Version: 4.0.30319
< X-Powered-By: ASP.NET
< Set-Cookie: ARRAffinity=93fdbab9d364704de8ef77182b4d13811344b7dd1ec45d3a9682bbd6fa154ead;Path=/;HttpOnly;Domain=httpstat.us
< Date: Wed, 13 Nov 2019 23:33:19 GMT
Here's another option using the CURLOPT_HEADERFUNCTION option with a callback function:
<?php
// this holds the response headers from the curl call
$responseHeaders = array();
// this function processes the response headers from the curl call
function curlResponseHeaderCallback($ch, $headerLine) {
global $responseHeaders;
// trim all the whitespace on this line
$trimmed = trim($headerLine);
// only proceed if the string is not empty
if(!empty($trimmed)) {
// headers follow Key: Value format
$split = explode(':', $trimmed);
// only proceed if the value of the header is not empty
if(!empty($split[1])) {
// $split[0] is the Key of the response header
// $split[1] is the Value of the response header, which can also have whitespace
$responseHeaders[$split[0]] = trim($split[1]);
}
}
// who knows why, but you have to return this.
return strlen($headerLine);
}
// get cURL resource
$ch = curl_init();
// set url
curl_setopt($ch, CURLOPT_URL, "https://httpstat.us/400");
curl_setopt($ch, CURLOPT_HEADERFUNCTION, "curlResponseHeaderCallback");
// send the request
curl_exec($ch);
// close the handle
curl_close($ch);
print_r($responseHeaders);
returns
Array
(
[Cache-Control] => private
[Content-Length] => 15
[Content-Type] => text/plain; charset=utf-8
[Server] => Microsoft-IIS/10.0
[X-AspNetMvc-Version] => 5.1
[Access-Control-Allow-Origin] => *
[X-AspNet-Version] => 4.0.30319
[X-Powered-By] => ASP.NET
[Set-Cookie] => ARRAffinity=93fdbab9d364704de8ef77182b4d13811344b7dd1ec45d3a9682bbd6fa154ead;Path=/;HttpOnly;Domain=httpstat.us
[Date] => Wed, 13 Nov 2019 23
)
I've been trying to get json and some header elements using this code:
And I was able to print the details that I need but it seems that I can't actually "get" them.
$url = 'http://192.168.254.211:8080/sampleOnCurl/auth/login';
$initCurl = curl_init($url);
curl_setopt_array($initCurl,
array(
CURLOPT_URL => $url,
CURLOPT_POST => true,
// CURLOPT_NOBODY => true,
CURLOPT_RETURNTRANSFER => true,
// CURLOPT_FOLLOWLOCATION => true,
CURLOPT_POSTFIELDS => $loginData,
CURLOPT_HEADER => true,
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
'jrr: 7ffed684cbe1fc085b7b47dc4e508e99a5effee9',
'slave: 01bdfcc20777907712e97f7bd2faeb978584f317',
'spoil: ' .$timeStamp
)
)
);
$result = curl_exec($initCurl);
print_r($result);
//prints jrr, slave, spoil
$result = json_decode($result);
// $header = curl_getinfo($initCurl);
curl_close($initCurl);
and this is what I get
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
jrr: 2e278657295bdfae2bc49b0bc6ad38363e9b149b
slave: d77985811796708b89471b4d29a904b224d41dde
spoil: 20130916222842701
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 16 Sep 2013 14:28:42 GMT
{"true":null}
I want to get {"true":null} and convert it to array to check if index is true
and get jrr, slave and spoil values .. without using the get method ..
The string that is printed doesn't seem to be in json as well. My guess is that the problem is in my curl options. What could I be missing out?
Here's what I need to do:
get the json {"true":null}
get the Response Header: jrr, slave, spoil
avoid using Get method
thanks
How about something like this:
list($headers, $body) = explode("\r\n\r\n", $result, 2);
function parseSimpleHeaders($headers) {
$lines = explode("\r\n", $headers);
$parsed = array();
foreach($lines as $line) {
$colon = strpos($line, ':');
if($colon !== false) {
$name = trim(substr($line, 0, $colon));
$value = trim(substr($line, $colon + 1));
$parsed[$name] = $value;
}
}
return $parsed;
}
$headers = parseSimpleHeaders($headers);
$json = json_decode($body);
echo $headers['jrr'], "\n"; // 2e278657295bdfae2bc49b0bc6ad38363e9b149b
echo $headers['slave'], "\n"; // d77985811796708b89471b4d29a904b224d41dde
echo $headers['spoil'], "\n"; // 20130916222842701
var_dump($json); // object(stdClass)#1 (1) { ["true"] => NULL }
N.B. There's a http_parse_headers() function in pecl_http but I've included my own simplified version parseSimpleHeaders() in case it's not installed.
You are getting response content and header in variable $result so you can't parse it directly.
To get it working explode $result using two new lines.
$result = explode("\n\n", $result);
and now you can access headers string by $result[0] and response content by $result[1] (and parse it using json_decode($result[1], true).
This is what $result returns :
HTTP/1.1 200 OK
Server: SERVER
Content-Type: text/xml;charset=utf-8
Connection: close
Expires: Tue, 26 Mar 2013 00:28:45 GMT
Cache-Control: max-age=0, no-cache, no-store
Pragma: no-cache
Date: Tue, 26 Mar 2013 00:28:45 GMT
Content-Length: 290
Connection: keep-alive
Set-Cookie: KEY=isbgvigbiwsb124252525252; Domain=www.website.com; Expires=Tue, 26-Mar-13 02:28:44 GMT; Path=/; HttpOnly
Set-Cookie: session=12345566789:abc1231552662626262; Domain=www.website.com; Expires=Thu, 25-Apr-2013 00:28:43 GMT; Path=/
<login>
<success>1</success>
<player>
<id>1234567</id>
<AnotherId>123456</AnotherId>
<email>email#email.com</email>
<accountinformation>
<id>123456</id>
<name>namehere</name>
<number>1234360</number>
</accountinformation>
</player>
</login>
I want to retrieve the KEY cookie from the response. Currently my code is as follows
//a cURL function would be here
$result = curl_exec($ch);
list($body, $split) = explode("\r\n\r\n", $result, 2);
$arr = explode("\r\n", $body);
$start = explode(":", $arr[10]);
$end = explode(";", $start[1]);
$INFO_I_NEED = $end[0];
What would be a simpler way of performing this action ? since it needs to be done 3/4 times for different parsing areas.
It looks like preg_match_all might be what you are looking for. Using this answer as inspiration try:
preg_match_all('/^Set-Cookie:\s*([^;]*)/mi', $result, $m);
You could then write a function like this:
function getCookies($result) {
preg_match_all('/^Set-Cookie:\s*([^;]*)/mi', $result, $m);
return($m)
}
$result = curl_exec($ch);
$cookiesArray = getCookies($result);
The return value of the function will be an array of all the cookie values. So $cookiesArray will hold:
array (
0 => 'KEY=isbgvigbiwsb124252525252',
1 => 'session=12345566789:abc1231552662626262',
)
Put it inside a function so you can reuse when need be:
<?php
//a cURL function would be here
$result = curl_exec($ch);
$INFO_I_NEED = myExplode($result);
function myExplode($data){
list($body, $split) = explode("\r\n\r\n", $result, 2);
$arr = explode("\r\n", $body);
$start = explode(":", $arr[10]);
$end = explode(";", $start[1]);
return($end[0]);
}
?>
I'm calling a service from PHP using cURL, like this:
$response = curl_exec($ch);
and the request/response headers look something like this:
Request:
POST /item/save HTTP/1.1
Host: services.mydomain.com
Accept: */*
Content-Length: 429
Expect: 100-continue
Content-Type: multipart/form-data
Response:
HTTP/1.1 100 Continue
HTTP/1.1 200 OK
Date: Fri, 06 Jul 2012 08:37:01 GMT
Server: Apache
Vary: Accept-Encoding,User-Agent
Content-Length: 256
Content-Type: application/json; charset=utf-8
followed by the body (json encoded data).
The problem is that the common thing is to split headers and body in the response by the first empty line encountered, except in this case, the empty line is after the 100 Continue and therefore everything else gets pushed into the body–and that is not valid json anymore :-)
So my question is this: What's the common way to deal with this?
I have 3 options lined up:
Specify that curl should not expect 100-continue? (How?)
Specify that curl should only send back the headers of the last response? (How?)
Manually check for 100 Continue headers and disregard them and their following empty line? (In that case, are there other similar things that could happen, that I should manually check for?)
Unless I'm missing something obvious, I'm sure people have stumbled upon this and solved it many times!
I will opt for #1.
You can force curl to send empty "Expect" header, by adding:
curl_setopt($ch, CURLOPT_HTTPHEADER,array("Expect:"));
to your code
If you want check it manually, you should define your own header callback and maybe write callback (look for CURLOPT_HEADERFUNCTION and CURLOPT_WRITEFUNCTION in curl_setopt doc), which has simply to ignore all "HTTP/1.1 100 Continue" headers.
Here's another method that uses the approach I described in the comment by parsing the response into header vs. body using CURLINFO_HEADER_SIZE:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://test/curl_test.php");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLINFO_HEADER_OUT, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
// sets multipart/form-data content-type
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
'field1' => 'foo',
'field2' => 'bar'
));
$data = curl_exec($ch);
// if you want the headers sent by CURL
$sentHeaders = curl_getinfo($ch, CURLINFO_HEADER_OUT);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
curl_close($ch);
$header = substr($data, 0, $headerSize);
$body = substr($data, $headerSize);
echo "==Sent Headers==\n$sentHeaders\n==End Sent Headers==\n";
echo "==Response Headers==\n$headers\n==End Response Headers==\n";
echo "==Response Body==\n$body\n==End Body==";
I've tested this, and it results in the following output:
==Sent Headers==
POST /curl_test.php HTTP/1.1
Host: test
Accept: */*
Content-Length: 242
Expect: 100-continue
Content-Type: multipart/form-data; boundary=----------------------------
d86ac263ce1b
==End Sent Headers==
==Response Headers==
HTTP/1.1 100 Continue
HTTP/1.1 200 OK
Date: Fri, 06 Jul 2012 14:21:53 GMT
Server: Apache/2.4.2 (Win32) PHP/5.4.4
X-Powered-By: PHP/5.4.4
Content-Length: 112
Content-Type: text/plain
==End Response Headers==
==Response Body==
**FORM DATA**
array(2) {
["field1"]=>
string(3) "foo"
["field2"]=>
string(3) "bar"
}
**END FORM DATA**
==End Body==
i had the same problem but this solution does note work for me, finaly i have found this methode and all its fine:
we have to prepare data post fields before sending them:
function curl_custom_postfields($curl, array $assoc = array(), array $files = array()) {
/**
* For safe multipart POST request for PHP5.3 ~ PHP 5.4.
* #param resource $ch cURL resource
* #param array $assoc "name => value"
* #param array $files "name => path"
* #return bool
*/
// invalid characters for "name" and "filename"
static $disallow = array("\0", "\"", "\r", "\n");
// build normal parameters
foreach ($assoc as $key => $value) {
$key = str_replace($disallow, "_", $key);
$body[] = implode("\r\n", array(
"Content-Disposition: form-data; name=\"{$key}\"",
"",
filter_var($value),
));
}
// build file parameters
foreach ($files as $key => $value) {
switch (true) {
case false === $value = realpath(filter_var($value)):
case !is_file($value):
case !is_readable($value):
continue; // or return false, throw new InvalidArgumentException
}
$data = file_get_contents($value);
$value = call_user_func("end", explode(DIRECTORY_SEPARATOR, $value));
$key = str_replace($disallow, "_", $key);
$value = str_replace($disallow, "_", $value);
$body[] = implode("\r\n", array(
"Content-Disposition: form-data; name=\"{$key}\"; filename=\"{$value}\"",
"Content-Type: application/octet-stream",
"",
$data,
));
}
// generate safe boundary
do {
$boundary = "---------------------" . md5(mt_rand() . microtime());
} while (preg_grep("/{$boundary}/", $body));
// add boundary for each parameters
array_walk($body, function (&$part) use ($boundary) {
$part = "--{$boundary}\r\n{$part}";
});
// add final boundary
$body[] = "--{$boundary}--";
$body[] = "";
// set options
return #curl_setopt_array($curl, array(
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => implode("\r\n", $body),
CURLOPT_HTTPHEADER => array(
"Expect: 100-continue",
"Content-Type: multipart/form-data; boundary={$boundary}", // change Content-Type
),
));}
you have to prepare two arrays:
1- post field with normal data: (name1 = val1, name2 = val2, ...)
2- post field with file data: (name_file 1, path_file1, name_file2 = path_file2, ..)
and finaly call this function before executing curl like this.
$r = curl_custom_postfields($curl, $post, $postfields_files);
I have come across this with 100s and 302s etc it's annoying but sometimes needed (gdata calls etc) so i would say leave curl returning all headers and extract the body a little differently.
I handle it like this (can't find my actual code but you'll get the idea):
$response = curl_exec($ch);
$headers = array();
$body = array();
foreach(explode("\n\n", $response) as $frag){
if(preg_match('/^HTTP\/[0-9\.]+ [0-9]+/', $frag)){
$headers[] = $frag;
}else{
$body[] = $frag;
}
}
echo implode("\n\n", $headers);
echo implode("\n\n", $body);
I begrudge the longwinded hackish method (would prefer it if curl marked the body content somehow) but it has worked well over the years. let us know how you get on.