mirror of
https://github.com/php-curl-class/php-curl-class.git
synced 2026-09-10 10:26:35 +00:00
Fix #345: Allow POSTing multidimensional array with file
This commit is contained in:
+64
-53
@@ -135,40 +135,44 @@ class Curl
|
||||
*/
|
||||
public function buildPostData($data)
|
||||
{
|
||||
$binary_data = false;
|
||||
if (is_array($data)) {
|
||||
// Return JSON-encoded string when the request's content-type is JSON.
|
||||
if (isset($this->headers['Content-Type']) &&
|
||||
preg_match($this->jsonPattern, $this->headers['Content-Type'])) {
|
||||
$json_str = json_encode($data);
|
||||
if (!($json_str === false)) {
|
||||
$data = $json_str;
|
||||
}
|
||||
} else if (self::is_array_multidim($data)) {
|
||||
$data = self::http_build_multi_query($data);
|
||||
} else {
|
||||
$binary_data = false;
|
||||
foreach ($data as $key => $value) {
|
||||
// Fix "Notice: Array to string conversion" when $value in curl_setopt($ch, CURLOPT_POSTFIELDS,
|
||||
// $value) is an array that contains an empty array.
|
||||
if (is_array($value) && empty($value)) {
|
||||
$data[$key] = '';
|
||||
// Fix "curl_setopt(): The usage of the @filename API for file uploading is deprecated. Please use
|
||||
// the CURLFile class instead". Ignore non-file values prefixed with the @ character.
|
||||
} elseif (is_string($value) && strpos($value, '@') === 0 && is_file(substr($value, 1))) {
|
||||
$binary_data = true;
|
||||
if (class_exists('CURLFile')) {
|
||||
$data[$key] = new \CURLFile(substr($value, 1));
|
||||
}
|
||||
} elseif ($value instanceof \CURLFile) {
|
||||
$binary_data = true;
|
||||
}
|
||||
// Manually build a single-dimensional array from a multi-dimensional array as using curl_setopt($ch,
|
||||
// CURLOPT_POSTFIELDS, $data) doesn't correctly handle multi-dimensional arrays when files are
|
||||
// referenced.
|
||||
if (self::is_array_multidim($data)) {
|
||||
$data = self::array_flatten_multidim($data);
|
||||
}
|
||||
|
||||
if (!$binary_data) {
|
||||
$data = http_build_query($data, '', '&');
|
||||
// Modify array values to ensure any referenced files are properly handled depending on the support of
|
||||
// the @filename API or CURLFile usage. This also fixes the warning "curl_setopt(): The usage of the
|
||||
// @filename API for file uploading is deprecated. Please use the CURLFile class instead". Ignore
|
||||
// non-file values prefixed with the @ character.
|
||||
if (class_exists('CURLFile')) {
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_string($value) && strpos($value, '@') === 0 && is_file(substr($value, 1))) {
|
||||
$binary_data = true;
|
||||
$data[$key] = new \CURLFile(substr($value, 1));
|
||||
} else if ($value instanceof \CURLFile) {
|
||||
$binary_data = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$binary_data && (is_array($data) || is_object($data))) {
|
||||
$data = http_build_query($data, '', '&');
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
@@ -579,7 +583,9 @@ class Curl
|
||||
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'PUT');
|
||||
$put_data = $this->buildPostData($data);
|
||||
if (empty($this->options[CURLOPT_INFILE]) && empty($this->options[CURLOPT_INFILESIZE])) {
|
||||
$this->setHeader('Content-Length', strlen($put_data));
|
||||
if (is_string($put_data)) {
|
||||
$this->setHeader('Content-Length', strlen($put_data));
|
||||
}
|
||||
}
|
||||
if (!empty($put_data)) {
|
||||
$this->setOpt(CURLOPT_POSTFIELDS, $put_data);
|
||||
@@ -1153,38 +1159,6 @@ class Curl
|
||||
return $response_headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Http Build Multi Query
|
||||
*
|
||||
* @access public
|
||||
* @param $data
|
||||
* @param $key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function http_build_multi_query($data, $key = null)
|
||||
{
|
||||
$query = array();
|
||||
|
||||
if (empty($data)) {
|
||||
return $key . '=';
|
||||
}
|
||||
|
||||
$is_array_assoc = self::is_array_assoc($data);
|
||||
|
||||
foreach ($data as $k => $value) {
|
||||
if (is_string($value) || is_numeric($value)) {
|
||||
$brackets = $is_array_assoc ? '[' . $k . ']' : '[]';
|
||||
$query[] = urlencode($key === null ? $k : $key . $brackets) . '=' . rawurlencode($value);
|
||||
} elseif (is_array($value)) {
|
||||
$nested = $key === null ? $k : $key . '[' . $k . ']';
|
||||
$query[] = self::http_build_multi_query($value, $nested);
|
||||
}
|
||||
}
|
||||
|
||||
return implode('&', $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is Array Assoc
|
||||
*
|
||||
@@ -1214,4 +1188,41 @@ class Curl
|
||||
|
||||
return (bool)count(array_filter($array, 'is_array'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Array Flatten Multidim
|
||||
*
|
||||
* @access public
|
||||
* @param $array
|
||||
* @param $prefix
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function array_flatten_multidim($array, $prefix = false) {
|
||||
$return = array();
|
||||
if (is_array($array) || is_object($array)) {
|
||||
if (empty($array)) {
|
||||
$return[$prefix] = '';
|
||||
} else {
|
||||
foreach ($array as $key => $value) {
|
||||
if (is_scalar($value)) {
|
||||
if ($prefix) {
|
||||
$return[$prefix . '[' . $key . ']'] = $value;
|
||||
} else {
|
||||
$return[$key] = $value;
|
||||
}
|
||||
} else {
|
||||
if (class_exists('CURLFile') && $value instanceof \CURLFile) {
|
||||
$return[$key] = $value;
|
||||
} else {
|
||||
$return = array_merge($return, self::array_flatten_multidim($value, $prefix ? $prefix . '[' . $key . ']' : $key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ($array === null) {
|
||||
$return[$prefix] = $array;
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,25 +310,21 @@ class CurlTest extends PHPUnit_Framework_TestCase
|
||||
|
||||
public function testPostAssociativeArrayData()
|
||||
{
|
||||
$test = new Test();
|
||||
$this->assertEquals(
|
||||
'username=myusername' .
|
||||
'&password=mypassword' .
|
||||
'&more_data%5Bparam1%5D=something' .
|
||||
'&more_data%5Bparam2%5D=other%20thing' .
|
||||
'&more_data%5Bparam3%5D=123' .
|
||||
'&more_data%5Bparam4%5D=3.14',
|
||||
$test->server('post_multidimensional', 'POST', array(
|
||||
'username' => 'myusername',
|
||||
'password' => 'mypassword',
|
||||
'more_data' => array(
|
||||
'param1' => 'something',
|
||||
'param2' => 'other thing',
|
||||
'param3' => 123,
|
||||
'param4' => 3.14,
|
||||
),
|
||||
))
|
||||
$data = array(
|
||||
'username' => 'myusername',
|
||||
'password' => 'mypassword',
|
||||
'more_data' => array(
|
||||
'param1' => 'something',
|
||||
'param2' => 'other thing',
|
||||
'param3' => 123,
|
||||
'param4' => 3.14,
|
||||
),
|
||||
);
|
||||
|
||||
$test = new Test();
|
||||
$test->curl->setDefaultJsonDecoder(true);
|
||||
$response = $test->server('post_multidimensional', 'POST', $data);
|
||||
$this->assertEquals($data, $response['post']);
|
||||
}
|
||||
|
||||
public function testPostContentLength()
|
||||
@@ -353,18 +349,79 @@ class CurlTest extends PHPUnit_Framework_TestCase
|
||||
|
||||
public function testPostMultidimensionalData()
|
||||
{
|
||||
$test = new Test();
|
||||
$this->assertEquals(
|
||||
'key=file&file%5B%5D=wibble&file%5B%5D=wubble&file%5B%5D=wobble',
|
||||
$test->server('post_multidimensional', 'POST', array(
|
||||
'key' => 'file',
|
||||
'file' => array(
|
||||
'wibble',
|
||||
'wubble',
|
||||
'wobble',
|
||||
),
|
||||
))
|
||||
$data = array(
|
||||
'key' => 'file',
|
||||
'file' => array(
|
||||
'wibble',
|
||||
'wubble',
|
||||
'wobble',
|
||||
),
|
||||
);
|
||||
|
||||
$this->assertEquals('key=file&file[0]=wibble&file[1]=wubble&file[2]=wobble', urldecode(http_build_query($data)));
|
||||
|
||||
$test = new Test();
|
||||
$test->curl->setDefaultJsonDecoder(true);
|
||||
$response = $test->server('post_multidimensional', 'POST', $data);
|
||||
$this->assertEquals($data, $response['post']);
|
||||
}
|
||||
|
||||
public function testPostMultidimensionalDataWithFile()
|
||||
{
|
||||
$tests = array();
|
||||
|
||||
$file_path_1 = Helper\get_png();
|
||||
$tests[] = array(
|
||||
'file_path' => $file_path_1,
|
||||
'post_data_image' => '@' . $file_path_1,
|
||||
);
|
||||
|
||||
if (class_exists('CURLFile')) {
|
||||
$file_path_2 = Helper\get_png();
|
||||
$tests[] = array(
|
||||
'file_path' => $file_path_2,
|
||||
'post_data_image' => new CURLFile($file_path_2),
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($tests as $test_data) {
|
||||
$file_path = $test_data['file_path'];
|
||||
$post_data_image = $test_data['post_data_image'];
|
||||
|
||||
$test = new Test();
|
||||
|
||||
// Return associative for comparison.
|
||||
$assoc = true;
|
||||
$test->curl->setDefaultJsonDecoder($assoc);
|
||||
|
||||
// Keep POST data separate from FILES data for comparison.
|
||||
$post_data_without_file = array(
|
||||
'key' => 'value',
|
||||
'alpha' => array(
|
||||
'a' => '1',
|
||||
'b' => '2',
|
||||
'c' => '3',
|
||||
),
|
||||
);
|
||||
$post_data = $post_data_without_file;
|
||||
$post_data['image'] = $post_data_image;
|
||||
|
||||
$test->server('post_multidimensional_with_file', 'POST', $post_data);
|
||||
|
||||
// Expect "Content-Type: multipart/form-data" in request headers.
|
||||
preg_match('/^multipart\/form-data; boundary=/', $test->curl->requestHeaders['Content-Type'], $content_type);
|
||||
$this->assertTrue(!empty($content_type));
|
||||
|
||||
// Expect received POST data to match POSTed data less the file.
|
||||
$this->assertTrue($test->curl->response['post'] === $post_data_without_file);
|
||||
|
||||
// Expect POSTed files is received as $_FILES.
|
||||
$this->assertTrue(isset($test->curl->response['files']['image']['tmp_name']));
|
||||
$this->assertEquals(0, $test->curl->response['files']['image']['error']);
|
||||
|
||||
unlink($file_path);
|
||||
$this->assertFalse(file_exists($file_path));
|
||||
}
|
||||
}
|
||||
|
||||
public function testPostFilePathUpload()
|
||||
|
||||
@@ -110,8 +110,13 @@ if ($test === 'http_basic_auth') {
|
||||
} elseif ($test === 'patch') {
|
||||
echo $http_raw_post_data;
|
||||
exit;
|
||||
} elseif ($test === 'post_multidimensional') {
|
||||
echo $http_raw_post_data;
|
||||
} elseif ($test === 'post_multidimensional' ||
|
||||
$test === 'post_multidimensional_with_file') {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(array(
|
||||
'post' => $_POST,
|
||||
'files' => $_FILES,
|
||||
), JSON_PRETTY_PRINT);
|
||||
exit;
|
||||
} elseif ($test === 'post_file_path_upload') {
|
||||
echo Helper\mime_type($_FILES[$key]['tmp_name']);
|
||||
|
||||
Reference in New Issue
Block a user