mirror of
https://github.com/php/php-src.git
synced 2024-11-27 11:53:33 +08:00
cURL: make possible to send file from buffer string
Add CURLStringFile class which works similarly to CURLFile, but uploads a file from a string rather than a file. This avoids the need to create a temporary file, or use of a data:// stream. Basic usage: $file = new CURLStringFile($data, 'filename.txt', 'text/plain'); curl_setopt($curl, CURLOPT_POSTFIELDS, ['file' => $file]); Closes GH-6456.
This commit is contained in:
parent
d16341dabe
commit
e727919b97
@ -100,6 +100,11 @@ PHP 8.1 UPGRADE NOTES
|
||||
|
||||
- Curl:
|
||||
. Added CURLOPT_DOH_URL option.
|
||||
. Added CURLStringFile, which can be used to post a file from a string rather
|
||||
than a file:
|
||||
|
||||
$file = new CURLStringFile($data, 'filename.txt', 'text/plain');
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, ['file' => $file]);
|
||||
|
||||
- hash:
|
||||
. The following functions have changed signatures:
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "curl_file_arginfo.h"
|
||||
|
||||
PHP_CURL_API zend_class_entry *curl_CURLFile_class;
|
||||
PHP_CURL_API zend_class_entry *curl_CURLStringFile_class;
|
||||
|
||||
static void curlfile_ctor(INTERNAL_FUNCTION_PARAMETERS)
|
||||
{
|
||||
@ -120,9 +121,34 @@ ZEND_METHOD(CURLFile, setPostFilename)
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
ZEND_METHOD(CURLStringFile, __construct)
|
||||
{
|
||||
zend_string *data, *postname, *mime = NULL;
|
||||
zval *object;
|
||||
|
||||
object = ZEND_THIS;
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(2,3)
|
||||
Z_PARAM_STR(data)
|
||||
Z_PARAM_STR(postname)
|
||||
Z_PARAM_OPTIONAL
|
||||
Z_PARAM_STR(mime)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
zend_update_property_str(curl_CURLStringFile_class, Z_OBJ_P(object), "data", sizeof("data") - 1, data);
|
||||
zend_update_property_str(curl_CURLStringFile_class, Z_OBJ_P(object), "postname", sizeof("postname")-1, postname);
|
||||
if (mime) {
|
||||
zend_update_property_str(curl_CURLStringFile_class, Z_OBJ_P(object), "mime", sizeof("mime")-1, mime);
|
||||
} else {
|
||||
zend_update_property_string(curl_CURLStringFile_class, Z_OBJ_P(object), "mime", sizeof("mime")-1, "application/octet-stream");
|
||||
}
|
||||
}
|
||||
|
||||
void curlfile_register_class(void)
|
||||
{
|
||||
curl_CURLFile_class = register_class_CURLFile();
|
||||
curl_CURLFile_class->serialize = zend_class_serialize_deny;
|
||||
curl_CURLFile_class->unserialize = zend_class_unserialize_deny;
|
||||
|
||||
curl_CURLStringFile_class = register_class_CURLStringFile();
|
||||
}
|
||||
|
@ -28,3 +28,12 @@ class CURLFile
|
||||
/** @return void */
|
||||
public function setPostFilename(string $posted_filename) {}
|
||||
}
|
||||
|
||||
class CURLStringFile
|
||||
{
|
||||
public string $data;
|
||||
public string $postname;
|
||||
public string $mime;
|
||||
|
||||
public function __construct(string $data, string $postname, string $mime = "application/octet-stream") {}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* This is a generated file, edit the .stub.php file instead.
|
||||
* Stub hash: 2bd78005380fd7f885618c4cb993bb21abe8cea9 */
|
||||
* Stub hash: fdeef1c2a9e835b443d6e4cced23656ce21d8a30 */
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_CURLFile___construct, 0, 0, 1)
|
||||
ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
|
||||
@ -22,6 +22,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_CURLFile_setPostFilename, 0, 0, 1)
|
||||
ZEND_ARG_TYPE_INFO(0, posted_filename, IS_STRING, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_CURLStringFile___construct, 0, 0, 2)
|
||||
ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)
|
||||
ZEND_ARG_TYPE_INFO(0, postname, IS_STRING, 0)
|
||||
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mime, IS_STRING, 0, "\"application/octet-stream\"")
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
|
||||
ZEND_METHOD(CURLFile, __construct);
|
||||
ZEND_METHOD(CURLFile, getFilename);
|
||||
@ -29,6 +35,7 @@ ZEND_METHOD(CURLFile, getMimeType);
|
||||
ZEND_METHOD(CURLFile, getPostFilename);
|
||||
ZEND_METHOD(CURLFile, setMimeType);
|
||||
ZEND_METHOD(CURLFile, setPostFilename);
|
||||
ZEND_METHOD(CURLStringFile, __construct);
|
||||
|
||||
|
||||
static const zend_function_entry class_CURLFile_methods[] = {
|
||||
@ -41,6 +48,12 @@ static const zend_function_entry class_CURLFile_methods[] = {
|
||||
ZEND_FE_END
|
||||
};
|
||||
|
||||
|
||||
static const zend_function_entry class_CURLStringFile_methods[] = {
|
||||
ZEND_ME(CURLStringFile, __construct, arginfo_class_CURLStringFile___construct, ZEND_ACC_PUBLIC)
|
||||
ZEND_FE_END
|
||||
};
|
||||
|
||||
zend_class_entry *register_class_CURLFile()
|
||||
{
|
||||
zend_class_entry ce, *class_entry;
|
||||
@ -69,3 +82,31 @@ zend_class_entry *register_class_CURLFile()
|
||||
return class_entry;
|
||||
}
|
||||
|
||||
zend_class_entry *register_class_CURLStringFile()
|
||||
{
|
||||
zend_class_entry ce, *class_entry;
|
||||
|
||||
INIT_CLASS_ENTRY(ce, "CURLStringFile", class_CURLStringFile_methods);
|
||||
class_entry = zend_register_internal_class_ex(&ce, NULL);
|
||||
|
||||
zval property_data_default_value;
|
||||
ZVAL_UNDEF(&property_data_default_value);
|
||||
zend_string *property_data_name = zend_string_init("data", sizeof("data") - 1, 1);
|
||||
zend_declare_typed_property(class_entry, property_data_name, &property_data_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
|
||||
zend_string_release(property_data_name);
|
||||
|
||||
zval property_postname_default_value;
|
||||
ZVAL_UNDEF(&property_postname_default_value);
|
||||
zend_string *property_postname_name = zend_string_init("postname", sizeof("postname") - 1, 1);
|
||||
zend_declare_typed_property(class_entry, property_postname_name, &property_postname_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
|
||||
zend_string_release(property_postname_name);
|
||||
|
||||
zval property_mime_default_value;
|
||||
ZVAL_UNDEF(&property_mime_default_value);
|
||||
zend_string *property_mime_name = zend_string_init("mime", sizeof("mime") - 1, 1);
|
||||
zend_declare_typed_property(class_entry, property_mime_name, &property_mime_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
|
||||
zend_string_release(property_mime_name);
|
||||
|
||||
return class_entry;
|
||||
}
|
||||
|
||||
|
@ -90,6 +90,9 @@ struct _php_curl_send_headers {
|
||||
struct _php_curl_free {
|
||||
zend_llist post;
|
||||
zend_llist stream;
|
||||
#if LIBCURL_VERSION_NUM < 0x073800 /* 7.56.0 */
|
||||
zend_llist buffers;
|
||||
#endif
|
||||
HashTable *slist;
|
||||
};
|
||||
|
||||
|
@ -1654,6 +1654,15 @@ static void curl_free_cb_arg(void **cb_arg_p)
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
#if LIBCURL_VERSION_NUM < 0x073800 /* 7.56.0 */
|
||||
/* {{{ curl_free_buffers */
|
||||
static void curl_free_buffers(void **buffer)
|
||||
{
|
||||
zend_string_release((zend_string *) *buffer);
|
||||
}
|
||||
/* }}} */
|
||||
#endif
|
||||
|
||||
/* {{{ curl_free_slist */
|
||||
static void curl_free_slist(zval *el)
|
||||
{
|
||||
@ -1744,6 +1753,10 @@ void init_curl_handle(php_curl *ch)
|
||||
zend_llist_init(&ch->to_free->post, sizeof(struct HttpPost *), (llist_dtor_func_t)curl_free_post, 0);
|
||||
zend_llist_init(&ch->to_free->stream, sizeof(struct mime_data_cb_arg *), (llist_dtor_func_t)curl_free_cb_arg, 0);
|
||||
|
||||
#if LIBCURL_VERSION_NUM < 0x073800 /* 7.56.0 */
|
||||
zend_llist_init(&ch->to_free->buffers, sizeof(zend_string *), (llist_dtor_func_t)curl_free_buffers, 0);
|
||||
#endif
|
||||
|
||||
ch->to_free->slist = emalloc(sizeof(HashTable));
|
||||
zend_hash_init(ch->to_free->slist, 4, NULL, curl_free_slist, 0);
|
||||
ZVAL_UNDEF(&ch->postfields);
|
||||
@ -2086,6 +2099,78 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Z_TYPE_P(current) == IS_OBJECT && instanceof_function(Z_OBJCE_P(current), curl_CURLStringFile_class)) {
|
||||
/* new-style file upload from string */
|
||||
zval *prop, rv;
|
||||
char *type = NULL, *filename = NULL;
|
||||
|
||||
prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "postname", sizeof("postname")-1, 0, &rv);
|
||||
if (EG(exception)) {
|
||||
zend_string_release_ex(string_key, 0);
|
||||
return FAILURE;
|
||||
}
|
||||
ZVAL_DEREF(prop);
|
||||
ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);
|
||||
|
||||
filename = Z_STRVAL_P(prop);
|
||||
|
||||
prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "mime", sizeof("mime")-1, 0, &rv);
|
||||
if (EG(exception)) {
|
||||
zend_string_release_ex(string_key, 0);
|
||||
return FAILURE;
|
||||
}
|
||||
ZVAL_DEREF(prop);
|
||||
ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);
|
||||
|
||||
type = Z_STRVAL_P(prop);
|
||||
|
||||
prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "data", sizeof("data")-1, 0, &rv);
|
||||
if (EG(exception)) {
|
||||
zend_string_release_ex(string_key, 0);
|
||||
return FAILURE;
|
||||
}
|
||||
ZVAL_DEREF(prop);
|
||||
ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);
|
||||
|
||||
postval = Z_STR_P(prop);
|
||||
|
||||
#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */
|
||||
zval_ptr_dtor(&ch->postfields);
|
||||
ZVAL_COPY(&ch->postfields, zpostfields);
|
||||
|
||||
part = curl_mime_addpart(mime);
|
||||
if (part == NULL) {
|
||||
zend_string_release_ex(string_key, 0);
|
||||
return FAILURE;
|
||||
}
|
||||
if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK
|
||||
|| (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK
|
||||
|| (form_error = curl_mime_filename(part, filename)) != CURLE_OK
|
||||
|| (form_error = curl_mime_type(part, type)) != CURLE_OK) {
|
||||
error = form_error;
|
||||
}
|
||||
#else
|
||||
postval = zend_string_copy(postval);
|
||||
zend_llist_add_element(&ch->to_free->buffers, &postval);
|
||||
|
||||
form_error = curl_formadd(&first, &last,
|
||||
CURLFORM_COPYNAME, ZSTR_VAL(string_key),
|
||||
CURLFORM_NAMELENGTH, ZSTR_LEN(string_key),
|
||||
CURLFORM_BUFFER, filename,
|
||||
CURLFORM_CONTENTTYPE, type,
|
||||
CURLFORM_BUFFERPTR, ZSTR_VAL(postval),
|
||||
CURLFORM_BUFFERLENGTH, ZSTR_LEN(postval),
|
||||
CURLFORM_END);
|
||||
if (form_error != CURL_FORMADD_OK) {
|
||||
/* Not nice to convert between enums but we only have place for one error type */
|
||||
error = (CURLcode)form_error;
|
||||
}
|
||||
#endif
|
||||
|
||||
zend_string_release_ex(string_key, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
postval = zval_get_tmp_string(current, &tmp_postval);
|
||||
|
||||
#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */
|
||||
@ -3330,6 +3415,11 @@ static void curl_free_obj(zend_object *object)
|
||||
if (--(*ch->clone) == 0) {
|
||||
zend_llist_clean(&ch->to_free->post);
|
||||
zend_llist_clean(&ch->to_free->stream);
|
||||
|
||||
#if LIBCURL_VERSION_NUM < 0x073800 /* 7.56.0 */
|
||||
zend_llist_clean(&ch->to_free->buffers);
|
||||
#endif
|
||||
|
||||
zend_hash_destroy(ch->to_free->slist);
|
||||
efree(ch->to_free->slist);
|
||||
efree(ch->to_free);
|
||||
|
@ -39,5 +39,6 @@ PHP_CURL_API extern zend_class_entry *curl_ce;
|
||||
PHP_CURL_API extern zend_class_entry *curl_share_ce;
|
||||
PHP_CURL_API extern zend_class_entry *curl_multi_ce;
|
||||
PHP_CURL_API extern zend_class_entry *curl_CURLFile_class;
|
||||
PHP_CURL_API extern zend_class_entry *curl_CURLStringFile_class;
|
||||
|
||||
#endif /* _PHP_CURL_H */
|
||||
|
86
ext/curl/tests/curl_string_file_upload.phpt
Normal file
86
ext/curl/tests/curl_string_file_upload.phpt
Normal file
@ -0,0 +1,86 @@
|
||||
--TEST--
|
||||
CURL file uploading from string
|
||||
--SKIPIF--
|
||||
<?php include 'skipif.inc'; ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function testcurl($ch, $postname, $data, $mime = null)
|
||||
{
|
||||
if (is_null($mime)) {
|
||||
// for default mime value
|
||||
$file = new CURLStringFile($data, $postname);
|
||||
} else {
|
||||
$file = new CURLStringFile($data, $postname, $mime);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file));
|
||||
var_dump(curl_exec($ch));
|
||||
}
|
||||
|
||||
include 'server.inc';
|
||||
$host = curl_cli_server_start();
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, "{$host}/get.php?test=string_file");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
|
||||
$data = "test\0test";
|
||||
var_dump(md5($data));
|
||||
testcurl($ch, 'foo.txt', $data);
|
||||
testcurl($ch, 'foo.txt', $data, 'text/plain');
|
||||
testcurl($ch, '', $data);
|
||||
testcurl($ch, 'foo.txt', '');
|
||||
testcurl($ch, "foo.txt\0broken_string", $data, "text/plain\0broken_string");
|
||||
|
||||
// properties
|
||||
$file = new CURLStringFile($data, 'foo.txt');
|
||||
$file->mime = 'text/plain';
|
||||
var_dump($file->mime);
|
||||
var_dump($file->postname);
|
||||
var_dump(md5($file->data));
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file));
|
||||
var_dump(curl_exec($ch));
|
||||
|
||||
// serialization / deserialization
|
||||
$old = new CURLStringFile($data, 'foo.txt', 'text/plain');
|
||||
$serialized = serialize($old);
|
||||
$new = unserialize($serialized);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $new));
|
||||
var_dump(curl_exec($ch));
|
||||
|
||||
// destroy object before send request
|
||||
$file = new CURLStringFile($data, 'foo.txt', 'text/plain');
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file));
|
||||
unset($file);
|
||||
var_dump(curl_exec($ch));
|
||||
|
||||
// clone curl handler
|
||||
$file = new CURLStringFile($data, 'foo.txt', 'text/plain');
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file));
|
||||
$ch2 = clone $ch;
|
||||
var_dump(curl_exec($ch2));
|
||||
|
||||
// properties are references
|
||||
|
||||
$file = new CURLStringFile($data, 'foo.txt', 'text/plain');
|
||||
$data =& $file->data;
|
||||
$postname =& $file->postname;
|
||||
$mime =& $file->mime;
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file));
|
||||
var_dump(curl_exec($ch));
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
string(%d) "62942c05ed0d1b501c4afe6dc1c4db1b"
|
||||
string(%d) "foo.txt|application/octet-stream|62942c05ed0d1b501c4afe6dc1c4db1b"
|
||||
string(%d) "foo.txt|text/plain|62942c05ed0d1b501c4afe6dc1c4db1b"
|
||||
string(%d) "error:4"
|
||||
string(%d) "foo.txt|application/octet-stream|d41d8cd98f00b204e9800998ecf8427e"
|
||||
string(%d) "foo.txt|text/plain|62942c05ed0d1b501c4afe6dc1c4db1b"
|
||||
string(%d) "text/plain"
|
||||
string(%d) "foo.txt"
|
||||
string(%d) "62942c05ed0d1b501c4afe6dc1c4db1b"
|
||||
string(%d) "foo.txt|text/plain|62942c05ed0d1b501c4afe6dc1c4db1b"
|
||||
string(%d) "foo.txt|text/plain|62942c05ed0d1b501c4afe6dc1c4db1b"
|
||||
string(%d) "foo.txt|text/plain|62942c05ed0d1b501c4afe6dc1c4db1b"
|
||||
string(%d) "foo.txt|text/plain|62942c05ed0d1b501c4afe6dc1c4db1b"
|
||||
string(%d) "foo.txt|text/plain|62942c05ed0d1b501c4afe6dc1c4db1b"
|
@ -31,6 +31,15 @@
|
||||
echo $_FILES['file']['name'] . '|' . $_FILES['file']['type'] . '|' . $_FILES['file']['size'];
|
||||
}
|
||||
break;
|
||||
case 'string_file':
|
||||
if (isset($_FILES['file'])) {
|
||||
if ($_FILES['file']['error'] === UPLOAD_ERR_OK) {
|
||||
echo $_FILES['file']['name'] . '|' . $_FILES['file']['type'] . '|' . md5_file($_FILES['file']['tmp_name']);
|
||||
} else {
|
||||
echo 'error:' . $_FILES['file']['error'];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'method':
|
||||
echo $_SERVER['REQUEST_METHOD'];
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user