Commit 6a4ab2ed305 for php.net
commit 6a4ab2ed305aaba30f76bcd3a531c33972546304
Author: Sjoerd Langkemper <sjoerd-github@linuxonly.nl>
Date: Thu Aug 6 13:10:54 2026 +0200
ext/curl: show curl option name in error message (#22908)
diff --git a/NEWS b/NEWS
index 96895dc4a4b..fd4d58dd3c2 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,8 @@ PHP NEWS
possible. (NickSdot)
- Curl:
+ . Improved cURL option validation errors to include the option name.
+ (Sjoerd Langkemper)
. Raise a value error when the callback registered with CURLOPT_READFUNCTION
returns an unexpected long. (Sjoerd Langkemper)
diff --git a/ext/curl/interface.c b/ext/curl/interface.c
index 6df7cf66fbe..8c6d6a0c1f2 100644
--- a/ext/curl/interface.c
+++ b/ext/curl/interface.c
@@ -62,10 +62,39 @@ ZEND_DECLARE_MODULE_GLOBALS(curl)
# define php_curl_ret(__ret) RETVAL_FALSE; return;
#endif
+// php_curl_option_get_name(CURLOPT_HTTPHEADER) -> "HTTPHEADER"
+static const char * php_curl_option_get_name(zend_long option) {
+
+#if LIBCURL_VERSION_NUM >= 0x074900
+ const struct curl_easyoption * opt = curl_easy_option_by_id(option);
+ if (EXPECTED(opt != NULL)) {
+ return opt->name;
+ }
+#endif
+
+ const char prefix[] = "CURLOPT_";
+ const size_t prefix_len = sizeof(prefix) - 1;
+ zend_string *key;
+ zend_constant *constant;
+
+ ZEND_HASH_FOREACH_STR_KEY_PTR(EG(zend_constants), key, constant) {
+ if (!key
+ || Z_TYPE(constant->value) != IS_LONG
+ || strncmp(ZSTR_VAL(key), prefix, prefix_len) != 0) {
+ continue;
+ }
+
+ if (Z_LVAL(constant->value) == option) {
+ return ZSTR_VAL(key) + prefix_len;
+ }
+ } ZEND_HASH_FOREACH_END();
+ return "UNKNOWN_OPTION";
+}
+
static zend_result php_curl_option_str(php_curl *ch, zend_long option, const char *str, const size_t len)
{
if (zend_char_has_nul_byte(str, len)) {
- zend_value_error("%s(): cURL option must not contain any null bytes", get_active_function_name());
+ zend_value_error("%s(): cURL option CURLOPT_%s must not contain any null bytes", get_active_function_name(), php_curl_option_get_name(option));
return FAILURE;
}
@@ -2017,7 +2046,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
ch->handlers.write->method = PHP_CURL_FILE;
ZVAL_COPY(&ch->handlers.write->stream, zvalue);
} else {
- zend_value_error("%s(): The provided file handle must be writable", get_active_function_name());
+ zend_value_error("%s(): The file handle provided for CURLOPT_FILE must be writable", get_active_function_name());
return FAILURE;
}
break;
@@ -2035,7 +2064,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
ch->handlers.write_header->method = PHP_CURL_FILE;
ZVAL_COPY(&ch->handlers.write_header->stream, zvalue);
} else {
- zend_value_error("%s(): The provided file handle must be writable", get_active_function_name());
+ zend_value_error("%s(): The file handle provided for CURLOPT_WRITEHEADER must be writable", get_active_function_name());
return FAILURE;
}
break;
@@ -2064,7 +2093,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
zval_ptr_dtor(&ch->handlers.std_err);
ZVAL_COPY(&ch->handlers.std_err, zvalue);
} else {
- zend_value_error("%s(): The provided file handle must be writable", get_active_function_name());
+ zend_value_error("%s(): The file handle provided for CURLOPT_STDERR must be writable", get_active_function_name());
return FAILURE;
}
ZEND_FALLTHROUGH;
@@ -2091,43 +2120,9 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
HashTable *ph;
zend_string *val, *tmp_val;
struct curl_slist *slist = NULL;
- const char *name = NULL;
-
- switch (option) {
- case CURLOPT_HTTPHEADER:
- name = "CURLOPT_HTTPHEADER";
- break;
- case CURLOPT_QUOTE:
- name = "CURLOPT_QUOTE";
- break;
- case CURLOPT_HTTP200ALIASES:
- name = "CURLOPT_HTTP200ALIASES";
- break;
- case CURLOPT_POSTQUOTE:
- name = "CURLOPT_POSTQUOTE";
- break;
- case CURLOPT_PREQUOTE:
- name = "CURLOPT_PREQUOTE";
- break;
- case CURLOPT_TELNETOPTIONS:
- name = "CURLOPT_TELNETOPTIONS";
- break;
- case CURLOPT_MAIL_RCPT:
- name = "CURLOPT_MAIL_RCPT";
- break;
- case CURLOPT_RESOLVE:
- name = "CURLOPT_RESOLVE";
- break;
- case CURLOPT_PROXYHEADER:
- name = "CURLOPT_PROXYHEADER";
- break;
- case CURLOPT_CONNECT_TO:
- name = "CURLOPT_CONNECT_TO";
- break;
- }
if (Z_TYPE_P(zvalue) != IS_ARRAY) {
- zend_type_error("%s(): The %s option must have an array value", get_active_function_name(), name);
+ zend_type_error("%s(): The CURLOPT_%s option must have an array value", get_active_function_name(), php_curl_option_get_name(option));
return FAILURE;
}
@@ -2139,7 +2134,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
if (zend_str_has_nul_byte(val)) {
curl_slist_free_all(slist);
zend_tmp_string_release(tmp_val);
- zend_value_error("%s(): cURL option %s must not contain any null bytes", get_active_function_name(), name);
+ zend_value_error("%s(): cURL option CURLOPT_%s must not contain any null bytes", get_active_function_name(), php_curl_option_get_name(option));
return FAILURE;
}
diff --git a/ext/curl/tests/bug48207.phpt b/ext/curl/tests/bug48207.phpt
index 086f949ff63..6487076e9ee 100644
--- a/ext/curl/tests/bug48207.phpt
+++ b/ext/curl/tests/bug48207.phpt
@@ -36,10 +36,17 @@
$fp = fopen($tempfile, "r"); // Opening 'fubar' with the incorrect readonly flag
$ch = curl_init($url);
-try {
- curl_setopt($ch, CURLOPT_FILE, $fp);
-} catch (ValueError $exception) {
- echo $exception->getMessage() . "\n";
+
+foreach ([
+ CURLOPT_FILE,
+ CURLOPT_WRITEHEADER,
+ CURLOPT_STDERR,
+] as $option) {
+ try {
+ curl_setopt($ch, $option, $fp);
+ } catch (ValueError $exception) {
+ echo $exception->getMessage(), "\n";
+ }
}
curl_exec($ch);
@@ -47,6 +54,8 @@
isset($tempname) and is_file($tempname) and @unlink($tempname);
?>
--EXPECT--
-curl_setopt(): The provided file handle must be writable
+curl_setopt(): The file handle provided for CURLOPT_FILE must be writable
+curl_setopt(): The file handle provided for CURLOPT_WRITEHEADER must be writable
+curl_setopt(): The file handle provided for CURLOPT_STDERR must be writable
Hello World!
Hello World!
diff --git a/ext/curl/tests/bug68089.phpt b/ext/curl/tests/bug68089.phpt
index b8733c5066b..c175df5248f 100644
--- a/ext/curl/tests/bug68089.phpt
+++ b/ext/curl/tests/bug68089.phpt
@@ -16,5 +16,5 @@
?>
Done
--EXPECT--
-curl_setopt(): cURL option must not contain any null bytes
+curl_setopt(): cURL option CURLOPT_URL must not contain any null bytes
Done