From 3ebc94a1cd5526e19b072ad49c023e0b3fe984f0 Mon Sep 17 00:00:00 2001 From: Gennady Kovshenin Date: Sun, 11 Feb 2018 23:37:32 +0500 Subject: [PATCH 1/6] Initial cURL file upload support Inspired by #289 --- library/Requests.php | 9 +++++---- library/Requests/Transport/cURL.php | 25 +++++++++++++++++++----- library/Requests/Transport/fsockopen.php | 3 ++- tests/Transport/Base.php | 9 +++++++++ 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/library/Requests.php b/library/Requests.php index bb266189c..86c786acc 100644 --- a/library/Requests.php +++ b/library/Requests.php @@ -259,13 +259,14 @@ public static function trace($url, $headers = array(), $options = array()) { * @param array $headers * @param array $data * @param array $options + * @param array $files * @return Requests_Response */ /** * Send a POST request */ - public static function post($url, $headers = array(), $data = array(), $options = array()) { - return self::request($url, $headers, $data, self::POST, $options); + public static function post($url, $headers = array(), $data = array(), $options = array(), $files = array()) { + return self::request($url, $headers, $data, self::POST, $options, $files); } /** * Send a PUT request @@ -354,7 +355,7 @@ public static function patch($url, $headers, $data = array(), $options = array() * @param array $options Options for the request (see description for more information) * @return Requests_Response */ - public static function request($url, $headers = array(), $data = array(), $type = self::GET, $options = array()) { + public static function request($url, $headers = array(), $data = array(), $type = self::GET, $options = array(), $files = array()) { if (empty($options['type'])) { $options['type'] = $type; } @@ -376,7 +377,7 @@ public static function request($url, $headers = array(), $data = array(), $type $capabilities = array('ssl' => $need_ssl); $transport = self::get_transport($capabilities); } - $response = $transport->request($url, $headers, $data, $options); + $response = $transport->request($url, $headers, $data, $options, $files); $options['hooks']->dispatch('requests.before_parse', array(&$response, $url, $headers, $data, $type, $options)); diff --git a/library/Requests/Transport/cURL.php b/library/Requests/Transport/cURL.php index 4429edb64..7e0339d83 100644 --- a/library/Requests/Transport/cURL.php +++ b/library/Requests/Transport/cURL.php @@ -125,12 +125,13 @@ public function __destruct() { * @param array $headers Associative array of request headers * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD * @param array $options Request options, see {@see Requests::response()} for documentation + * @param array $files File uploads * @return string Raw HTTP result */ - public function request($url, $headers = array(), $data = array(), $options = array()) { + public function request($url, $headers = array(), $data = array(), $options = array(), $files = array()) { $this->hooks = $options['hooks']; - $this->setup_handle($url, $headers, $data, $options); + $this->setup_handle($url, $headers, $data, $options, $files); $options['hooks']->dispatch('curl.before_send', array(&$this->handle)); @@ -305,8 +306,9 @@ public function &get_subrequest_handle($url, $headers, $data, $options) { * @param array $headers Associative array of request headers * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD * @param array $options Request options, see {@see Requests::response()} for documentation + * @param array $files File uploads */ - protected function setup_handle($url, $headers, $data, $options) { + protected function setup_handle($url, $headers, $data, $options, $files = array()) { $options['hooks']->dispatch('curl.before_request', array(&$this->handle)); // Force closing the connection for old versions of cURL (<7.22). @@ -321,13 +323,26 @@ protected function setup_handle($url, $headers, $data, $options) { if ($data_format === 'query') { $url = self::format_get($url, $data); - $data = ''; + $data = array(); } - elseif (!is_string($data)) { + + if (!is_string($data) && empty($files)) { $data = http_build_query($data, null, '&'); } } + if (!empty($files)) { + if (function_exists('curl_file_create')) { + foreach($files as $key => $path) { + $data[$key] = curl_file_create($path); + } + } else { + foreach($files as $key => $path) { + $data[$key] = "@$path"; + } + } + } + switch ($options['type']) { case Requests::POST: curl_setopt($this->handle, CURLOPT_POST, true); diff --git a/library/Requests/Transport/fsockopen.php b/library/Requests/Transport/fsockopen.php index 21cb56d5e..a4fb2cb33 100644 --- a/library/Requests/Transport/fsockopen.php +++ b/library/Requests/Transport/fsockopen.php @@ -53,9 +53,10 @@ class Requests_Transport_fsockopen implements Requests_Transport { * @param array $headers Associative array of request headers * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD * @param array $options Request options, see {@see Requests::response()} for documentation + * @param array $files File uploads * @return string Raw HTTP result */ - public function request($url, $headers = array(), $data = array(), $options = array()) { + public function request($url, $headers = array(), $data = array(), $options = array(), $files = array()) { $options['hooks']->dispatch('fsockopen.before_request'); $url_parts = parse_url($url); diff --git a/tests/Transport/Base.php b/tests/Transport/Base.php index 566e09fad..9d1ffc016 100644 --- a/tests/Transport/Base.php +++ b/tests/Transport/Base.php @@ -845,4 +845,13 @@ public function testBodyDataFormat() { $this->assertEquals(httpbin('/post'), $result['url']); $this->assertEquals(array('test' => 'true', 'test2' => 'test'), $result['form']); } + + public function testFileUploads() { + file_put_contents($tmpfile = tempnam(sys_get_temp_dir(), 'requests'), 'some secret bytes, yo'); + $request = Requests::post('http://httpbin.org/post', array(), array('foo' => 'bar'), $this->getOptions(), array('file1' => $tmpfile)); + + $result = json_decode($request->body, true); + $this->assertEquals($result['files']['file1'], 'some secret bytes, yo'); + $this->assertEquals($result['form']['foo'], 'bar'); + } } From 1b60f1436f3d591a837272eade31346854f354b0 Mon Sep 17 00:00:00 2001 From: Gennady Kovshenin Date: Mon, 12 Feb 2018 00:54:38 +0500 Subject: [PATCH 2/6] Initial fsockopen transport upload support Next up: test multiple files, request_multi See #289 --- library/Requests/Transport/fsockopen.php | 32 ++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/library/Requests/Transport/fsockopen.php b/library/Requests/Transport/fsockopen.php index a4fb2cb33..985b3471a 100644 --- a/library/Requests/Transport/fsockopen.php +++ b/library/Requests/Transport/fsockopen.php @@ -154,14 +154,14 @@ public function request($url, $headers = array(), $data = array(), $options = ar $out = sprintf("%s %s HTTP/%.1f\r\n", $options['type'], $path, $options['protocol_version']); if ($options['type'] !== Requests::TRACE) { - if (is_array($data)) { + if (is_array($data) && empty($files)) { $request_body = http_build_query($data, null, '&'); } else { $request_body = $data; } - if (!empty($data)) { + if (!empty($data) && empty($files)) { if (!isset($case_insensitive_headers['Content-Length'])) { $headers['Content-Length'] = strlen($request_body); } @@ -170,6 +170,34 @@ public function request($url, $headers = array(), $data = array(), $options = ar $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'; } } + + if (!empty($files)) { + $boundary = sha1(time()); + $headers['Content-Type'] = "multipart/form-data; boundary=$boundary"; + + $request_body = ''; + + if (!empty($data)) { + foreach ($data as $key => $value) { + $request_body .= "--$boundary\r\n"; + $request_body .= "Content-Disposition: form-data; name=\"$key\""; + $request_body .= "\r\n\r\n" . $value . "\r\n"; + } + } + + foreach ($files as $key => $path) { + $filename = basename($path); + $request_body .= "--$boundary\r\n"; + $request_body .= "Content-Disposition: form-data; name=\"$key\"; filename=\"$filename\""; + // @todo Compression, encoding (base64), etc. + // @todo Large files can hit PHP memory limits quite quickly + $request_body .= "\r\n\r\n" . file_get_contents($path) . "\r\n"; + } + + $request_body .= "--$boundary--\r\n\r\n"; + + $headers['Content-Length'] = strlen($request_body); + } } if (!isset($case_insensitive_headers['Host'])) { From a47689dfdfd6d74b175403b01efe7811347a6be4 Mon Sep 17 00:00:00 2001 From: Gennady Kovshenin Date: Thu, 15 Feb 2018 22:39:44 +0500 Subject: [PATCH 3/6] Introduce Requests_File for simple file uploads --- library/Requests.php | 9 ++- library/Requests/Exception/File.php | 5 ++ library/Requests/File.php | 70 ++++++++++++++++++++++++ library/Requests/Transport/cURL.php | 33 ++++++----- library/Requests/Transport/fsockopen.php | 34 +++++++----- tests/File.php | 39 +++++++++++++ tests/Transport/Base.php | 2 +- tests/phpunit.xml.dist | 1 + 8 files changed, 160 insertions(+), 33 deletions(-) create mode 100644 library/Requests/Exception/File.php create mode 100644 library/Requests/File.php create mode 100644 tests/File.php diff --git a/library/Requests.php b/library/Requests.php index 86c786acc..bb266189c 100644 --- a/library/Requests.php +++ b/library/Requests.php @@ -259,14 +259,13 @@ public static function trace($url, $headers = array(), $options = array()) { * @param array $headers * @param array $data * @param array $options - * @param array $files * @return Requests_Response */ /** * Send a POST request */ - public static function post($url, $headers = array(), $data = array(), $options = array(), $files = array()) { - return self::request($url, $headers, $data, self::POST, $options, $files); + public static function post($url, $headers = array(), $data = array(), $options = array()) { + return self::request($url, $headers, $data, self::POST, $options); } /** * Send a PUT request @@ -355,7 +354,7 @@ public static function patch($url, $headers, $data = array(), $options = array() * @param array $options Options for the request (see description for more information) * @return Requests_Response */ - public static function request($url, $headers = array(), $data = array(), $type = self::GET, $options = array(), $files = array()) { + public static function request($url, $headers = array(), $data = array(), $type = self::GET, $options = array()) { if (empty($options['type'])) { $options['type'] = $type; } @@ -377,7 +376,7 @@ public static function request($url, $headers = array(), $data = array(), $type $capabilities = array('ssl' => $need_ssl); $transport = self::get_transport($capabilities); } - $response = $transport->request($url, $headers, $data, $options, $files); + $response = $transport->request($url, $headers, $data, $options); $options['hooks']->dispatch('requests.before_parse', array(&$response, $url, $headers, $data, $type, $options)); diff --git a/library/Requests/Exception/File.php b/library/Requests/Exception/File.php new file mode 100644 index 000000000..e4a62e6b8 --- /dev/null +++ b/library/Requests/Exception/File.php @@ -0,0 +1,5 @@ +path = $path; + $this->type = $type ? $type : mime_content_type($path); + $this->name = $name ? $name : basename($path); + } + + /** + * Retrieve the contents into a string. + * + * Caution: large files will fill up the RAM. + * + * @return string The contents. + */ + public function get_contents() { + return file_get_contents($this->path); + } +} diff --git a/library/Requests/Transport/cURL.php b/library/Requests/Transport/cURL.php index 7e0339d83..945f0efbf 100644 --- a/library/Requests/Transport/cURL.php +++ b/library/Requests/Transport/cURL.php @@ -125,13 +125,12 @@ public function __destruct() { * @param array $headers Associative array of request headers * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD * @param array $options Request options, see {@see Requests::response()} for documentation - * @param array $files File uploads * @return string Raw HTTP result */ - public function request($url, $headers = array(), $data = array(), $options = array(), $files = array()) { + public function request($url, $headers = array(), $data = array(), $options = array()) { $this->hooks = $options['hooks']; - $this->setup_handle($url, $headers, $data, $options, $files); + $this->setup_handle($url, $headers, $data, $options); $options['hooks']->dispatch('curl.before_send', array(&$this->handle)); @@ -306,9 +305,8 @@ public function &get_subrequest_handle($url, $headers, $data, $options) { * @param array $headers Associative array of request headers * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD * @param array $options Request options, see {@see Requests::response()} for documentation - * @param array $files File uploads */ - protected function setup_handle($url, $headers, $data, $options, $files = array()) { + protected function setup_handle($url, $headers, $data, $options) { $options['hooks']->dispatch('curl.before_request', array(&$this->handle)); // Force closing the connection for old versions of cURL (<7.22). @@ -318,27 +316,34 @@ protected function setup_handle($url, $headers, $data, $options, $files = array( $headers = Requests::flatten($headers); + $files = array(); + if (!empty($data)) { $data_format = $options['data_format']; + if (is_array($data)) { + foreach($data as $key => $value) { + if ($value instanceof Requests_File) { + $files[$key] = $value; + } + } + } + if ($data_format === 'query') { $url = self::format_get($url, $data); $data = array(); } - - if (!is_string($data) && empty($files)) { - $data = http_build_query($data, null, '&'); - } } if (!empty($files)) { if (function_exists('curl_file_create')) { - foreach($files as $key => $path) { - $data[$key] = curl_file_create($path); + foreach($files as $key => $file) { + $data[$key] = curl_file_create($file->path, $file->type, $file->name); } - } else { - foreach($files as $key => $path) { - $data[$key] = "@$path"; + } + else { + foreach($files as $key => $file) { + $data[$key] = "@{$file->path}"; } } } diff --git a/library/Requests/Transport/fsockopen.php b/library/Requests/Transport/fsockopen.php index 985b3471a..02b6e9239 100644 --- a/library/Requests/Transport/fsockopen.php +++ b/library/Requests/Transport/fsockopen.php @@ -53,10 +53,9 @@ class Requests_Transport_fsockopen implements Requests_Transport { * @param array $headers Associative array of request headers * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD * @param array $options Request options, see {@see Requests::response()} for documentation - * @param array $files File uploads * @return string Raw HTTP result */ - public function request($url, $headers = array(), $data = array(), $options = array(), $files = array()) { + public function request($url, $headers = array(), $data = array(), $options = array()) { $options['hooks']->dispatch('fsockopen.before_request'); $url_parts = parse_url($url); @@ -154,6 +153,16 @@ public function request($url, $headers = array(), $data = array(), $options = ar $out = sprintf("%s %s HTTP/%.1f\r\n", $options['type'], $path, $options['protocol_version']); if ($options['type'] !== Requests::TRACE) { + $files = array(); + + if (is_array($data)) { + foreach($data as $key => $value) { + if ($value instanceof Requests_File) { + $files[$key] = $value; + } + } + } + if (is_array($data) && empty($files)) { $request_body = http_build_query($data, null, '&'); } @@ -180,18 +189,17 @@ public function request($url, $headers = array(), $data = array(), $options = ar if (!empty($data)) { foreach ($data as $key => $value) { $request_body .= "--$boundary\r\n"; - $request_body .= "Content-Disposition: form-data; name=\"$key\""; - $request_body .= "\r\n\r\n" . $value . "\r\n"; - } - } - foreach ($files as $key => $path) { - $filename = basename($path); - $request_body .= "--$boundary\r\n"; - $request_body .= "Content-Disposition: form-data; name=\"$key\"; filename=\"$filename\""; - // @todo Compression, encoding (base64), etc. - // @todo Large files can hit PHP memory limits quite quickly - $request_body .= "\r\n\r\n" . file_get_contents($path) . "\r\n"; + if ($value instanceof Requests_File) { + $request_body .= "Content-Disposition: form-data; name=\"$key\"; filename=\"$value->name\"\r\n"; + $request_body .= "Content-Type: $value->type"; + $request_body .= "\r\n\r\n" . $value->get_contents() . "\r\n"; + } + else { + $request_body .= "Content-Disposition: form-data; name=\"$key\""; + $request_body .= "\r\n\r\n" . $value . "\r\n"; + } + } } $request_body .= "--$boundary--\r\n\r\n"; diff --git a/tests/File.php b/tests/File.php new file mode 100644 index 000000000..d2ea89ccf --- /dev/null +++ b/tests/File.php @@ -0,0 +1,39 @@ +assertEquals($file->path, $tmpfile); + $this->assertEquals($file->type, 'text/plain'); + $this->assertEquals($file->name, 'readme.txt'); + + file_put_contents($tmpfile = tempnam(sys_get_temp_dir(), 'requests'), 'hello'); + $file = new Requests_File($tmpfile); + $this->assertEquals($file->name, basename($tmpfile)); + + $this->assertEquals($file->get_contents(), 'hello'); + } + + public function testMime() { + file_put_contents($tmpfile = tempnam(sys_get_temp_dir(), 'requests'), 'hello'); + $file = new Requests_File($tmpfile); + $this->assertEquals($file->type, 'text/plain'); + + file_put_contents($tmpfile = tempnam(sys_get_temp_dir(), 'requests'), "\xff\xd8\xff"); + $file = new Requests_File($tmpfile); + $this->assertEquals($file->type, 'image/jpeg'); + + file_put_contents($tmpfile = tempnam(sys_get_temp_dir(), 'requests'), "\x78\x01"); + $file = new Requests_File($tmpfile); + $this->assertEquals($file->type, 'application/octet-stream'); + } +} diff --git a/tests/Transport/Base.php b/tests/Transport/Base.php index 9d1ffc016..f3438f1f4 100644 --- a/tests/Transport/Base.php +++ b/tests/Transport/Base.php @@ -848,7 +848,7 @@ public function testBodyDataFormat() { public function testFileUploads() { file_put_contents($tmpfile = tempnam(sys_get_temp_dir(), 'requests'), 'some secret bytes, yo'); - $request = Requests::post('http://httpbin.org/post', array(), array('foo' => 'bar'), $this->getOptions(), array('file1' => $tmpfile)); + $request = Requests::post('http://httpbin.org/post', array(), array('foo' => 'bar', 'file1' => new Requests_File($tmpfile)), $this->getOptions()); $result = json_decode($request->body, true); $this->assertEquals($result['files']['file1'], 'some secret bytes, yo'); diff --git a/tests/phpunit.xml.dist b/tests/phpunit.xml.dist index 64e0b8260..1e3602332 100644 --- a/tests/phpunit.xml.dist +++ b/tests/phpunit.xml.dist @@ -13,6 +13,7 @@ ChunkedEncoding.php Cookies.php + File.php IDNAEncoder.php IRI.php Requests.php From 6d92c08390f0d3b116943971cfd637565cdd7db7 Mon Sep 17 00:00:00 2001 From: pbearne Date: Tue, 28 Sep 2021 16:56:59 -0400 Subject: [PATCH 4/6] fixed test/code Added helper function Requests::add_files_to_body() to add files to body --- src/Exception/File.php | 5 - src/Exception/RequestsExceptionFile.php | 6 ++ src/Requests.php | 27 +++++ src/{File.php => Utility/RequestsFile.php} | 12 ++- tests/File.php | 39 -------- tests/utils/FileTest.php | 111 +++++++++++++++++++++ 6 files changed, 152 insertions(+), 48 deletions(-) delete mode 100644 src/Exception/File.php create mode 100644 src/Exception/RequestsExceptionFile.php rename src/{File.php => Utility/RequestsFile.php} (81%) delete mode 100644 tests/File.php create mode 100644 tests/utils/FileTest.php diff --git a/src/Exception/File.php b/src/Exception/File.php deleted file mode 100644 index e4a62e6b8..000000000 --- a/src/Exception/File.php +++ /dev/null @@ -1,5 +0,0 @@ - $file_path ){ + $body[ $key ] = $file_path; + } + } elseif( is_scalar( $file_key ) && ! empty( $key_files ) ) { + $body[ $file_key ] = $key_files; + } + + return $body; + } + /**#@+ * @see \WpOrg\Requests\Requests::request() * @param string $url diff --git a/src/File.php b/src/Utility/RequestsFile.php similarity index 81% rename from src/File.php rename to src/Utility/RequestsFile.php index 39f66e595..66b43d304 100644 --- a/src/File.php +++ b/src/Utility/RequestsFile.php @@ -6,6 +6,10 @@ * @subpackage Utilities */ +namespace WpOrg\Requests\Utility; + + +use WpOrg\Requests\Exception\RequestsExceptionFile; /** * A file object describing an upload. * @@ -14,7 +18,7 @@ * @package Requests * @subpackage Utilities */ -class Requests_File { +class RequestsFile { /** * The path to the file. * @@ -43,13 +47,13 @@ class Requests_File { * @param string $mimetype The mimetype override. Will try to guess if not given. * @param string $filename The upload file name. * - * @throws Requests_Exception_File If file is not readable or does not exist. + * @return RequestsFile + *@throws RequestsExceptionFile If file is not readable or does not exist. * - * @return Requests_File */ public function __construct($path, $type = null, $name = null) { if (!file_exists($path) || !is_readable($path)) { - throw new Requests_Exception_File('File is not readable', null, $path); + throw new RequestsExceptionFile('File is not readable', null, $path); } $this->path = $path; diff --git a/tests/File.php b/tests/File.php deleted file mode 100644 index d2ea89ccf..000000000 --- a/tests/File.php +++ /dev/null @@ -1,39 +0,0 @@ -assertEquals($file->path, $tmpfile); - $this->assertEquals($file->type, 'text/plain'); - $this->assertEquals($file->name, 'readme.txt'); - - file_put_contents($tmpfile = tempnam(sys_get_temp_dir(), 'requests'), 'hello'); - $file = new Requests_File($tmpfile); - $this->assertEquals($file->name, basename($tmpfile)); - - $this->assertEquals($file->get_contents(), 'hello'); - } - - public function testMime() { - file_put_contents($tmpfile = tempnam(sys_get_temp_dir(), 'requests'), 'hello'); - $file = new Requests_File($tmpfile); - $this->assertEquals($file->type, 'text/plain'); - - file_put_contents($tmpfile = tempnam(sys_get_temp_dir(), 'requests'), "\xff\xd8\xff"); - $file = new Requests_File($tmpfile); - $this->assertEquals($file->type, 'image/jpeg'); - - file_put_contents($tmpfile = tempnam(sys_get_temp_dir(), 'requests'), "\x78\x01"); - $file = new Requests_File($tmpfile); - $this->assertEquals($file->type, 'application/octet-stream'); - } -} diff --git a/tests/utils/FileTest.php b/tests/utils/FileTest.php new file mode 100644 index 000000000..5b67a95b8 --- /dev/null +++ b/tests/utils/FileTest.php @@ -0,0 +1,111 @@ +expectException( RequestsExceptionFile::class ); + new RequestsFile( sys_get_temp_dir() . 'null.exe'); + } + + /** + * @throws RequestsExceptionFile + * + * @covers \WpOrg\Requests\Utility\RequestsFile + */ + public function testBasic() { + file_put_contents( $tmpfile = tempnam( sys_get_temp_dir(), 'requests' ), ''); + $file = new RequestsFile( $tmpfile, 'text/plain', 'readme.txt' ); + + $this->assertEquals( $file->path, $tmpfile); + $this->assertEquals( $file->type, 'text/plain' ); + $this->assertEquals( $file->name, 'readme.txt' ); + + file_put_contents( $tmpfile = tempnam(sys_get_temp_dir(), 'requests' ), 'hello'); + $file = new RequestsFile($tmpfile); + $this->assertEquals($file->name, basename( $tmpfile ) ); + + $this->assertEquals( $file->get_contents(), 'hello' ); + } + + /** + * @throws RequestsExceptionFile + * + * @covers \WpOrg\Requests\Utility\RequestsFile + */ + public function testMime() { + file_put_contents( $tmpfile = tempnam( sys_get_temp_dir(), 'requests' ), 'hello'); + $file = new RequestsFile( $tmpfile ); + $this->assertEquals( $file->type, 'text/plain' ); + + file_put_contents( $tmpfile = tempnam(sys_get_temp_dir(), 'requests' ), "\xff\xd8\xff"); + $file = new RequestsFile($tmpfile); + $this->assertEquals( $file->type, 'image/jpeg' ); + + file_put_contents( $tmpfile = tempnam(sys_get_temp_dir(), 'requests' ), "\x78\x01"); + $file = new RequestsFile($tmpfile); + $this->assertEquals( $file->type, 'application/octet-stream' ); + } + + /** + * @covers \WpOrg\Requests\Requests::add_files_to_body + */ + public function test_add_file_to_empty_body(){ + $body = ''; + file_put_contents( $tmpfile = tempnam( sys_get_temp_dir(), 'requests' ), 'hello'); + $body = Requests::add_files_to_body( $body, $tmpfile, 'filename' ); + $this->assertEquals( $body, array( 'filename' => $tmpfile ) ); + } + + /** + * @covers \WpOrg\Requests\Requests::add_files_to_body + */ + public function test_add_file_to_body_with_string(){ + $body = 'dd'; + file_put_contents( $tmpfile = tempnam( sys_get_temp_dir(), 'requests' ), 'hello'); + $body = Requests::add_files_to_body( $body, $tmpfile, 'filename' ); + $this->assertEquals( $body, array( 0 => 'dd','filename' => $tmpfile ) ); + } + + /** + * @covers \WpOrg\Requests\Requests::add_files_to_body + */ + public function test_add_file_to_body_with_array(){ + $body = array( 'dd' => 'fff' ) ; + file_put_contents( $tmpfile = tempnam( sys_get_temp_dir(), 'requests' ), 'hello' ); + $body = Requests::add_files_to_body( $body, $tmpfile, 'filename' ); + $this->assertEquals( $body, array( 'dd' => 'fff','filename' => $tmpfile ) ); + } + /** + * @covers \WpOrg\Requests\Requests::add_files_to_body + */ + public function test_add_files_to_body_with_array(){ + $body = array( 'dd' => 'fff' ) ; + file_put_contents( $tmpfile1 = tempnam( sys_get_temp_dir(), 'requests' ), 'hello' ); + file_put_contents( $tmpfile2 = tempnam( sys_get_temp_dir(), 'requests' ), 'hello2' ); + $files = array( $tmpfile1, $tmpfile2 ); + $body = Requests::add_files_to_body( $body, $files ); + $this->assertEquals( $body, array( 'dd' => 'fff',0 => $tmpfile1, 1 => $tmpfile2 ) ); + } + /** + * @covers \WpOrg\Requests\Requests::add_files_to_body + */ + public function test_add_files_to_body_with_keyed_array(){ + $body = array( 'dd' => 'fff' ) ; + file_put_contents( $tmpfile1 = tempnam( sys_get_temp_dir(), 'requests' ), 'hello' ); + file_put_contents( $tmpfile2 = tempnam( sys_get_temp_dir(), 'requests' ), 'hello2' ); + $files = array( 'tmpfile1' => $tmpfile1,'tmpfile2' => $tmpfile2 ); + $body = Requests::add_files_to_body( $body, $files ); + $this->assertEquals( $body, array( 'dd' => 'fff','tmpfile1' => $tmpfile1, 'tmpfile2' => $tmpfile2 ) ); + } +} From 099139043cdf760ba7f9d689c37582549efa3bc3 Mon Sep 17 00:00:00 2001 From: pbearne Date: Tue, 28 Sep 2021 17:13:03 -0400 Subject: [PATCH 5/6] fixed test/code Added helper function Requests::add_files_to_body() to add files to body --- src/Requests.php | 22 +++++++++++++--------- tests/utils/FileTest.php | 19 +++++++++++++------ 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/Requests.php b/src/Requests.php index e49241b81..90b497065 100644 --- a/src/Requests.php +++ b/src/Requests.php @@ -21,6 +21,7 @@ use WpOrg\Requests\Response; use WpOrg\Requests\Transport\Curl; use WpOrg\Requests\Transport\Fsockopen; +use WpOrg\Requests\Utility\RequestsFile; /** * Requests for PHP @@ -266,10 +267,10 @@ public static function add_files_to_body( $body, $key_files, $file_key = '' ) { } if ( is_array( $key_files ) ) { foreach ($key_files as $key => $file_path ){ - $body[ $key ] = $file_path; + $body[ $key ] = new RequestsFile( $file_path ); } } elseif( is_scalar( $file_key ) && ! empty( $key_files ) ) { - $body[ $file_key ] = $key_files; + $body[ $file_key ] = new RequestsFile( $key_files ); } return $body; @@ -589,15 +590,16 @@ public static function set_certificate_path($path) { self::$certificate_path = $path; } + /** - * Set the default values + * @param $url + * @param $headers + * @param $data + * @param $type + * @param $options * - * @param string $url URL to request - * @param array $headers Extra headers to send with the request - * @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests - * @param string $type HTTP request type - * @param array $options Options for the request - * @return array $options + * @return mixed + * @throws \WpOrg\Requests\Exception */ protected static function set_defaults(&$url, &$headers, &$data, &$type, &$options) { if (!preg_match('/^http(s)?:\/\//i', $url, $matches)) { @@ -649,6 +651,8 @@ protected static function set_defaults(&$url, &$headers, &$data, &$type, &$optio $options['data_format'] = 'body'; } } + + return $options; } /** diff --git a/tests/utils/FileTest.php b/tests/utils/FileTest.php index 5b67a95b8..d92829159 100644 --- a/tests/utils/FileTest.php +++ b/tests/utils/FileTest.php @@ -6,7 +6,7 @@ use WpOrg\Requests\Exception\RequestsExceptionFile; use WpOrg\Requests\Utility\RequestsFile; -class RequestsTest_File extends TestCase { +class FileTest extends TestCase { /** * @throws RequestsExceptionFile @@ -63,8 +63,9 @@ public function testMime() { public function test_add_file_to_empty_body(){ $body = ''; file_put_contents( $tmpfile = tempnam( sys_get_temp_dir(), 'requests' ), 'hello'); + $file = new RequestsFile( $tmpfile ); $body = Requests::add_files_to_body( $body, $tmpfile, 'filename' ); - $this->assertEquals( $body, array( 'filename' => $tmpfile ) ); + $this->assertEquals( $body, array( 'filename' => $file ) ); } /** @@ -73,8 +74,9 @@ public function test_add_file_to_empty_body(){ public function test_add_file_to_body_with_string(){ $body = 'dd'; file_put_contents( $tmpfile = tempnam( sys_get_temp_dir(), 'requests' ), 'hello'); + $file = new RequestsFile( $tmpfile ); $body = Requests::add_files_to_body( $body, $tmpfile, 'filename' ); - $this->assertEquals( $body, array( 0 => 'dd','filename' => $tmpfile ) ); + $this->assertEquals( $body, array( 0 => 'dd','filename' => $file ) ); } /** @@ -83,8 +85,9 @@ public function test_add_file_to_body_with_string(){ public function test_add_file_to_body_with_array(){ $body = array( 'dd' => 'fff' ) ; file_put_contents( $tmpfile = tempnam( sys_get_temp_dir(), 'requests' ), 'hello' ); + $file = new RequestsFile( $tmpfile ); $body = Requests::add_files_to_body( $body, $tmpfile, 'filename' ); - $this->assertEquals( $body, array( 'dd' => 'fff','filename' => $tmpfile ) ); + $this->assertEquals( $body, array( 'dd' => 'fff','filename' => $file ) ); } /** * @covers \WpOrg\Requests\Requests::add_files_to_body @@ -92,10 +95,12 @@ public function test_add_file_to_body_with_array(){ public function test_add_files_to_body_with_array(){ $body = array( 'dd' => 'fff' ) ; file_put_contents( $tmpfile1 = tempnam( sys_get_temp_dir(), 'requests' ), 'hello' ); + $file1 = new RequestsFile( $tmpfile1 ); file_put_contents( $tmpfile2 = tempnam( sys_get_temp_dir(), 'requests' ), 'hello2' ); + $file2 = new RequestsFile( $tmpfile2 ); $files = array( $tmpfile1, $tmpfile2 ); $body = Requests::add_files_to_body( $body, $files ); - $this->assertEquals( $body, array( 'dd' => 'fff',0 => $tmpfile1, 1 => $tmpfile2 ) ); + $this->assertEquals( $body, array( 'dd' => 'fff',0 => $file1, 1 => $file2 ) ); } /** * @covers \WpOrg\Requests\Requests::add_files_to_body @@ -103,9 +108,11 @@ public function test_add_files_to_body_with_array(){ public function test_add_files_to_body_with_keyed_array(){ $body = array( 'dd' => 'fff' ) ; file_put_contents( $tmpfile1 = tempnam( sys_get_temp_dir(), 'requests' ), 'hello' ); + $file1 = new RequestsFile( $tmpfile1 ); file_put_contents( $tmpfile2 = tempnam( sys_get_temp_dir(), 'requests' ), 'hello2' ); + $file2 = new RequestsFile( $tmpfile2 ); $files = array( 'tmpfile1' => $tmpfile1,'tmpfile2' => $tmpfile2 ); $body = Requests::add_files_to_body( $body, $files ); - $this->assertEquals( $body, array( 'dd' => 'fff','tmpfile1' => $tmpfile1, 'tmpfile2' => $tmpfile2 ) ); + $this->assertEquals( $body, array( 'dd' => 'fff','tmpfile1' => $file1, 'tmpfile2' => $file2 ) ); } } From 7cac25030cfcf2f035a3fa06a37fe5d77d06b9ed Mon Sep 17 00:00:00 2001 From: pbearne Date: Tue, 28 Sep 2021 17:22:23 -0400 Subject: [PATCH 6/6] Added example and docs --- docs/usage.md | 8 ++++++++ examples/post.php | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/docs/usage.md b/docs/usage.md index 453ec9b59..def3e5685 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -96,6 +96,14 @@ $data = array('some' => 'data'); $response = WpOrg\Requests\Requests::post($url, $headers, json_encode($data)); ``` +To send a file as part of a post body: + +```php +$url = 'https://api.github.com/some/endpoint'; +$body = \WpOrg\Requests\Requests::add_files_to_body( array('mydata' => 'something'), $file_path, 'file1' ); +$response = WpOrg\Requests\Requests::post( $url, $headers, $body ); +``` + Note that if you don't manually specify a Content-Type header, Requests has undefined behaviour for the header. It may be set to various values depending on the internal execution path, so it's recommended to set this explicitly if diff --git a/examples/post.php b/examples/post.php index 47abd674d..51ce40267 100644 --- a/examples/post.php +++ b/examples/post.php @@ -11,3 +11,12 @@ // Check what we received var_dump($request); +// create temp file for example +file_put_contents( $tmpfile = tempnam(sys_get_temp_dir(), 'requests' ), 'hello'); + +// Now let's make a request! +$body = \WpOrg\Requests\Requests::add_files_to_body( array('mydata' => 'something'), $tmpfile, 'file1' ); +$request = WpOrg\Requests\Requests::post('http://httpbin.org/post', array(), $body ); + +// Check what we received +var_dump($request);