PHP 8.4: Curl: New CURLOPT_PREREQFUNCTION
option
The Curl extension in PHP 8.4 introduces a new option named CURLOPT_PREREQFUNCTION
that allows setting a custom callable
to decide if the request should be continued or aborted. This feature is available if the extension is built with libcurl 7.80.0 or later.
The CURLOPT_PREREQFUNCTION
callback is called after the initial connection is established, and before the request is sent. For example in an HTTPS request, the CURLOPT_PREREQFUNCTION
callback is called after the HTTPS connection (DNS + TCP + TLS) is established, before the actual HTTP request is sent.
This callback can be useful in situations where the application needs to determine if the connection should be processed depending on the IP address and the port of both source and destination.
Some of the use cases include:
- Block making requests to intranet IP addresses
- Block making requests to IP addresses belonging to sanctioned countries or networks
- Only allowing requests to a known list of IP addresses, but when the DNS addresses can vary.
New constants
This feature adds three new constants:
CURLOPT_PREREQFUNCTION
:int
assigned20312
, the Curl option forcurl_setopt
CURL_PREREQFUNC_OK
:int
assigned1
, a possible return value for the callback, to allow the request.CURL_PREREQFUNC_ABORT
:int
assigned0
, a possible return value for the callback, to abort the request.
CURLOPT_PREREQFUNCTION
Curl Option
CURLOPT_PREREQFUNCTION
option accepts a callable
, and it must return CURL_PREREQFUNC_OK
or CURL_PREREQFUNC_ABORT
to allow or abort the request.
The callable
is called with the CurlHandle
object, destination IP address, source (local) IP address, remote port number, and source (local) port number.
If the connection is reused or the request follows an HTTP redirect, it is possible for the callback to get called multiple times.
function check_is_not_local_ip_address(
\CurlHandle $ch,
string $destination_ip,
string $local_ip,
int $destination_port,
int $local_port
): int {
$isGlobalIP = filter_var($destination_ip, FILTER_VALIDATE_IP, FILTER_FLAG_GLOBAL_RANGE) !== false;
return $isGlobalIP
? CURL_PREREQFUNC_OK
: CURL_PREREQFUNC_ABORT;
}
$ch = curl_init('https://php.watch');
curl_setopt($ch, CURLOPT_PREREQFUNCTION, 'check_is_not_local_ip_address');
- If the
CURLOPT_PREREQFUNCTION
callback does not return any value, aTypeError
is thrown. - If the
CURLOPT_PREREQFUNCTION
returns anint
value other thanCURL_PREREQFUNC_OK
orCURL_PREREQFUNC_ABORT
, aValueError
is thrown.
Both of the exceptions carry The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT
as the error message. In both situations, the callback gets aborted.
If a Curl request is aborted by the CURLOPT_PREREQFUNCTION
callable, the Curl request fails with CURLE_ABORTED_BY_CALLBACK
error:
$ch = curl_init('https://php.watch');
curl_setopt($ch, CURLOPT_PREREQFUNCTION, static fn($x): int => CURL_PREREQFUNC_ABORT);
curl_exec($ch);
echo curl_error($ch); // "operation aborted by pre-request callback"
echo curl_errno($ch); // CURLE_ABORTED_BY_CALLBACK (int 42)
Once the callback is set, it can be removed by setting it to null
:
curl_setopt($ch, CURLOPT_PREREQFUNCTION, null);
Backward Compatibility Impact
CURLOPT_PREREQFUNCTION
, CURL_PREREQFUNC_OK
, and CURL_PREREQFUNC_ABORT
are new global PHP constants. Because this feature requires internal handling at the PHP Curl extension level, it is not possible to backport this feature to older PHP versions.