Hello Internals,
I'd like to formally propose a restart of the original object-oriented
curl API RFC (https://wiki.php.net/rfc/curl-oop):
https://wiki.php.net/rfc/curl_oop_v2
The prior RFC seemed to get positive feedback, with a small consensus
around wanting enum(s) for curl options. I've taken that feedback and
incorporated it into a new RFC, as I am not the original author, but I
am interested in making another potential improvement to the curl
extension.
In a nutshell, this version of the RFC:
- uses enumerations for curl options and other curl constants
- introduces a new Curl namespace
I have not yet created an implementation PR. I realize that is
somewhat discouraged, but I believe that this should be relatively
straightforward to implement (there's also the previous RFC's PR to
build on top of). The implementation of this RFC as it is now will
likely be tedious, however, so I'd like to get feedback on the
enumeration idea before committing to the work.
I've outlined one open question in the RFC, which relates to the above:
- Should we organize the curl option enumerations by value type? Or
have a single enumeration for all curl_setopt options and another for
all curl_multi_setopt options?
If others (including the original RFC author) are interested in
working with me on this, I'm more than open to that, so please let me
know.
Thanks,
Eric
Hello Internals,
I'd like to formally propose a restart of the original object-oriented
curl API RFC (https://wiki.php.net/rfc/curl-oop):https://wiki.php.net/rfc/curl_oop_v2
The prior RFC seemed to get positive feedback, with a small consensus
around wanting enum(s) for curl options. I've taken that feedback and
incorporated it into a new RFC, as I am not the original author, but I
am interested in making another potential improvement to the curl
extension.In a nutshell, this version of the RFC:
- uses enumerations for curl options and other curl constants
- introduces a new Curl namespace
I have not yet created an implementation PR. I realize that is
somewhat discouraged, but I believe that this should be relatively
straightforward to implement (there's also the previous RFC's PR to
build on top of). The implementation of this RFC as it is now will
likely be tedious, however, so I'd like to get feedback on the
enumeration idea before committing to the work.I've outlined one open question in the RFC, which relates to the above:
- Should we organize the curl option enumerations by value type? Or
have a single enumeration for all curl_setopt options and another for
all curl_multi_setopt options?If others (including the original RFC author) are interested in
working with me on this, I'm more than open to that, so please let me
know.Thanks,
Eric
I still support this effort.
-
I don't think the Curl\Option namespace is necessary. They can just be in the main Curl namespace.
-
Please don't name the exception "Exception". It needs some slightly more useful name, to avoid confusion in a file that also uses \Exception.
-
I realize
Handle
is the name from the procedural API, but it's not very self-descriptive. Without knowing Curl, it's non-obvious that it's a self-executing request object. Is there a better name we could find while we're at it? -
Love the use of aviz. :-)
-
Now here's the big one: Using enums rather than a bunch of constants is a good change. However, I feel like it doesn't go far enough.
For instance,
$ch = new Curl\Handle("https://example.com")
->setOptionInt(Curl\Option\IntOpt::ConnectTimeout, 30)
->setOptionBool(Curl\Option\BoolOpt::FollowLocation, true);
Would be 10x easier to use were it:
$ch = new Curl\Handle("https://example.com")
->setConnectionTimeout(30)
->setFollowLocation((true);
Or for that matter, properties would now allow for those to even just be set directly on typed properties. (Though it wouldn't be chainable.)
I realize that would be considerably more work to define all those methods (I don't even know how many Curl has, as I rarely use it directly). But it would result in a vastly easier to use API than "set some random integer-based field(EnumName, 30)". The latter is highly non-obvious. Especially when getting into POST requests with different body formats (something the RFC should include examples of), IMO, good ergonomics is more important than looking like the old, horribly obtuse API.
As a worst case, perhaps the top-20 options or so could be converted to methods/properties, and the rest left to be ugly flags?
--Larry Garfield
Thanks Larry!
- I don't think the Curl\Option namespace is necessary. They can just be in the main Curl namespace.
I don't feel strongly here, but I thought it would be nice to group
the enums (if we go the multiple enum route) for discoverability.
Thoughts?
- Please don't name the exception "Exception". It needs some slightly more useful name, to avoid confusion in a file that also uses \Exception.
Fair point! I could use the previous RFC's HandleException class name,
barring your next point...
- I realize
Handle
is the name from the procedural API, but it's not very self-descriptive. Without knowing Curl, it's non-obvious that it's a self-executing request object. Is there a better name we could find while we're at it?
I'm open to this, but I don't have a suggestion myself.
- Love the use of aviz. :-)
Thanks :) I was happy to be able to use it here.
- Now here's the big one: Using enums rather than a bunch of constants is a good change. However, I feel like it doesn't go far enough.
...
As a worst case, perhaps the top-20 options or so could be converted to methods/properties, and the rest left to be ugly flags?
I think my hesitation here is related to Ben's sibling response: I
want to thread the needle between making small improvements to the
low-level API while translating them to object-oriented equivalents,
and making a high-level API. The latter, I think, would invite a lot
more discussion, and would be potentially hard to get right.
That said, maybe your suggestion does thread that needle; having a
method / property for the N most-used options is still relatively
low-level since it's not expressing an opinion on how they should be
used. I'm not sure.
Now here's the big one: Using enums rather than a bunch of constants is a good change. However, I feel like it doesn't go far enough.
100% this.
Writing "$ch->setOptionInt(Curl\Option\IntOpt::ConnectTimeout, 30);"
instead of "curl_setopt(CURLOPT_CONNECTTIMEOUT, 30);" is mostly
rearranging the punctuation deck-chairs on the ugly code Titanic.
Particularly when you can also
write "$ch->setOptionInt(Curl\Option\IntOpt::ConnectTimeoutMs, 30_000);"
with the same effect.
I realize that would be considerably more work to define all those methods (I don't even know how many Curl has, as I rarely use it directly).
I believe there are 272 "CURLOPT_" constants currently documented in the
PHP manual: https://www.php.net/manual/en/curl.constants.php
So, I totally agree that we need a "set raw option" method to support
some of the more niche features.
But that long list is also exactly why we badly need helpers for common
use cases - the manual page for curl_setopt has been at the top of the
charts for number of user comments for years, because nobody wants to
read the descriptions for two hundred options they'll never need.
I know that in the prior discussion, Rowan Tommins had a vision for a
high-level API (https://externals.io/message/122371#122424)
I think calling it a "vision for a high-level API" is making it sound
far more grandiose than what I suggested. What I suggested, and would
still like to see, is a small number of additional methods, for setting
really common options in a more user-friendly way.
Looking at my earlier message, my finger-in-the-air number was even
smaller than Larry's - a set of 10 methods, each covering a handful of
overlapping or closely related options.
A single "setHttpMethod" method would bring immediate value, instead of
choosing between CURLOPT_HTTPGET, CURLOPT_POST, CURLOPT_PUT,
CURLOPT_NOBODY
(for HEAD) and CURLOPT_CUSTOMREQUEST.
Adding more helpers in later versions is entirely trivial, but we could
set the precedent with a first batch on day one.
The only other "high-level" API I see discussed in the previous thread
is splitting the execute() method, for exactly the same reason as
splitting setOpt(): type safety.
public function executeAndReturn(): string
public function executeAndOutput(): void
public function executeToFile(Stream $fileHandle): void
public function executeWithCallback(callable $wrIteFunction): void
The CURLOPT_RETURNTRANSFER, CURLOPT_FILE, and CURLOPT_WRITEFUNCTION
would then not exist in the option enum(s), because there would be no
way to make use of them.
Unlike the helper methods, that's one we have to decide in advance - it
would be a mess to have those as well as a universal "execute(): ?string".
--
Rowan Tommins
[IMSoP]
On 26/06/2025 17:53, Larry Garfield wrote:
Now here's the big one: Using enums rather than a bunch of constants is a good change. However, I feel like it doesn't go far enough.
100% this.
Writing "$ch->setOptionInt(Curl\Option\IntOpt::ConnectTimeout, 30);" instead of "curl_setopt(CURLOPT_CONNECTTIMEOUT, 30);" is mostly rearranging the punctuation deck-chairs on the ugly code Titanic.
Particularly when you can also write "$ch->setOptionInt(Curl\Option\IntOpt::ConnectTimeoutMs, 30_000);" with the same effect.
I realize that would be considerably more work to define all those methods (I don't even know how many Curl has, as I rarely use it directly).
I believe there are 272 "CURLOPT_" constants currently documented in the PHP manual: https://www.php.net/manual/en/curl.constants.php
So, I totally agree that we need a "set raw option" method to support some of the more niche features.
But that long list is also exactly why we badly need helpers for common use cases - the manual page for curl_setopt has been at the top of the charts for number of user comments for years, because nobody wants to read the descriptions for two hundred options they'll never need.
I know that in the prior discussion, Rowan Tommins had a vision for a
high-level API (https://externals.io/message/122371#122424)I think calling it a "vision for a high-level API" is making it sound far more grandiose than what I suggested. What I suggested, and would still like to see, is a small number of additional methods, for setting really common options in a more user-friendly way.
Looking at my earlier message, my finger-in-the-air number was even smaller than Larry's - a set of 10 methods, each covering a handful of overlapping or closely related options.
A single "setHttpMethod" method would bring immediate value, instead of choosing between CURLOPT_HTTPGET, CURLOPT_POST, CURLOPT_PUT,
CURLOPT_NOBODY
(for HEAD) and CURLOPT_CUSTOMREQUEST.Adding more helpers in later versions is entirely trivial, but we could set the precedent with a first batch on day one.
The only other "high-level" API I see discussed in the previous thread is splitting the execute() method, for exactly the same reason as splitting setOpt(): type safety.
public function executeAndReturn(): string
public function executeAndOutput(): void
public function executeToFile(Stream $fileHandle): void
public function executeWithCallback(callable $wrIteFunction): voidThe CURLOPT_RETURNTRANSFER, CURLOPT_FILE, and
CURLOPT_WRITEFUNCTION
would then not exist in the option enum(s), because there would be no way to make use of them.Unlike the helper methods, that's one we have to decide in advance - it would be a mess to have those as well as a universal "execute(): ?string".
--
Rowan Tommins
[IMSoP]
I’m excited at just the discussion of a possibility for PHP to be able to perform basic HTTP requests in a friendly way. Last month I worked on a solution that involved an SQS Worker that do some work and then sends the output back to the origin via a HTTP request that is passed as part of the message; basically a callback over the wire. However, running it on Serverless as a plugin that makes no assumption about the project state, I couldn’t use Guzzle or any composer library for that matter. I had to stick with what’s available on Amazon Linux and Bref, which cURL is, but it’s such an unpleasant experience to have to write HTTP requests like it’s 2003.
I really hope some minor helpers can be added to this OOP version of cURL and I agree that it doesn’t need to be hashed out, perfect or even cover 270 cases. Just a simple GET/POST/PUT/PATCH/DELETE with a string body and some headers manipulation would go 99% of the way already.
Thanks Rowan!
I think calling it a "vision for a high-level API" is making it sound
far more grandiose than what I suggested. What I suggested, and would
still like to see, is a small number of additional methods, for setting
really common options in a more user-friendly way.
I apologize for misrepresenting your suggestion. I was mainly
responding to your "the eventual aim should be that users don't need a
userland wrapper" comment, which I worry sounds closer to what Ben was
(I believe) objecting to - a higher-level wrapper. Upon re-reading
though, I think that's potentially an unfair take - you did go on to
say "...just to make a simple request in a readable way".
Looking at my earlier message, my finger-in-the-air number was even
smaller than Larry's - a set of 10 methods, each covering a handful of
overlapping or closely related options.A single "setHttpMethod" method would bring immediate value, instead of
choosing between CURLOPT_HTTPGET, CURLOPT_POST, CURLOPT_PUT,
CURLOPT_NOBODY
(for HEAD) and CURLOPT_CUSTOMREQUEST.Adding more helpers in later versions is entirely trivial, but we could
set the precedent with a first batch on day one.
I'm not opposed to this, but I am - as previously stated - nervous
about how and where we draw this line, since I expect there may be a
lot of opinions here. That said, if the general consensus is that we
want direct methods or properties for the N most common use-cases, I'm
happy to make that change to the RFC.
I can try to take a look at the curl options and do some github
searches to see if I can identify common patterns. I agree that
setting the HTTP method and timeout are good contenders. If someone
else wants to propose a list as well, feel free!
The only other "high-level" API I see discussed in the previous thread
is splitting the execute() method, for exactly the same reason as
splitting setOpt(): type safety.public function executeAndReturn(): string
public function executeAndOutput(): void
public function executeToFile(Stream $fileHandle): void
public function executeWithCallback(callable $wrIteFunction): voidThe CURLOPT_RETURNTRANSFER, CURLOPT_FILE, and
CURLOPT_WRITEFUNCTION
would then not exist in the option enum(s), because there would be no
way to make use of them.Unlike the helper methods, that's one we have to decide in advance - it
would be a mess to have those as well as a universal "execute(): ?string".
If we were to go this route, I would suggest:
public function fetch(): string
public function execute(resource|callable $out): void
that is, one method to just return the contents of the URL, and
another to either write output to a file (including STDOUT, if the
user desires), or to send output to a write callback.
Adding more helpers in later versions is entirely trivial, but we could
set the precedent with a first batch on day one.I'm not opposed to this, but I am - as previously stated - nervous
about how and where we draw this line, since I expect there may be a
lot of opinions here. That said, if the general consensus is that we
want direct methods or properties for the N most common use-cases, I'm
happy to make that change to the RFC.I can try to take a look at the curl options and do some github
searches to see if I can identify common patterns. I agree that
setting the HTTP method and timeout are good contenders. If someone
else wants to propose a list as well, feel free!
My gut sense is that the following use cases would cover the 80% case:
GET http://www.example.com/foo?bar=baz
(Send a GET request with a complex URL. Probably needs both transparent redirecting and not options.)
POST http://www.example.com/foo
with a JSON body
(This means setting a body string/array, and a content-type header.)
POST http://www.example.com/foo
with a multipart/form-data body
(This means making it easy to set the body, and corresponding content-type header.)
And those should also allow setting an Accept header. Which means probably any header should be easy to set, with maybe one or two extra-promoted ones. (Like, when setting the body you can also set the content type?)
I don't know if specifying an HTTP version is relevant/useful. I have never done so myself, but that proves little.
On the return side, I almost always check the status code, at least. That's probably the only non-body thing I check. :-) So making that trivial is also important. Whether that's via a mini-response object instead of a string or something else is open for discussion, I think.
Other questions:
- We have these shiny new URL/URI objects. Should those be supported directly?
--Larry Garfield
I can try to take a look at the curl options and do some github
searches to see if I can identify common patterns. I agree that
setting the HTTP method and timeout are good contenders. If someone
else wants to propose a list as well, feel free!
For what it's worth, I grepped my work code base for "CURLOPT_", and
grouped the results into the categories below. Some of these are
definitely more "advanced" than others, and a lot could just be one
helper covering the whole category.
Really basic (already in the proposed constructor):
CURLOPT_URL
Setting HTTP method:
CURLOPT_HTTPGET
CURLOPT_POST
CURLOPT_CUSTOMREQUEST
Request body (despite name, handles both form data and raw string bodies):
CURLOPT_POSTFIELDS
Request headers:
CURLOPT_HTTPHEADER
CURLOPT_USERAGENT
CURLOPT_ENCODING
(deprecated in favour of CURLOPT_ACCEPT_ENCODING)
Response body handling:
CURLOPT_FILE
CURLOPT_RETURNTRANSFER
Response header handling (extremely fiddly; it would be AMAZING to have
something like getLastResponseHeaders() returning an array):
CURLOPT_HEADER
CURLOPT_HEADERFUNCTION
Auth:
CURLOPT_HTTPAUTH
CURLOPT_USERPWD
CURLOPT_SSLCERT
(there's also a CURLOPT_SSLCERT_BLOB)
CURLOPT_SSLKEY
Error and redirect handling:
CURLOPT_FOLLOWLOCATION
CURLOPT_FAILONERROR
Timeouts:
CURLOPT_CONNECTTIMEOUT
CURLOPT_TIMEOUT
CURLOPT_TIMEOUT_MS
Cookie handling:
CURLOPT_COOKIE
CURLOPT_COOKIEFILE
CURLOPT_COOKIEJAR
Connection parameter tuning:
CURLOPT_HTTP_VERSION
CURLOPT_SSLVERSION
CURLOPT_SSL_VERIFYPEER
(common, but shouldn't be!)
Debug output (like response headers, this is very unintuitive for PHP
users; $ch->enableVerboseLogs() and $ch->getVerboseLogs() would be very
handy):
CURLOPT_VERBOSE
CURLOPT_CAINFO
CURLOPT_STDERR
If we were to go this route, I would suggest:
public function fetch(): string
public function execute(resource|callable $out): voidthat is, one method to just return the contents of the URL, and
another to either write output to a file (including STDOUT, if the
user desires), or to send output to a write callback.
That makes a lot of sense! I've never been very good at keeping names
and sentences short :)
--
Rowan Tommins
[IMSoP]
Hi all,
I know that in the prior discussion, Rowan Tommins had a vision for a
high-level API (https://externals.io/message/122371#122424)I think calling it a "vision for a high-level API" is making it sound far more grandiose than what I suggested. What I suggested, and would still like to see, is a small number of additional methods, for setting really common options in a more user-friendly way.
To avoid drifting too much from existing expectations about how such a cURL interface ought to look (as vs what happened with the URL RFC) it would be wise to examine existing userland work earlier rather than later.
A brief search at https://packagist.org/?query=curl reveals https://packagist.org/packages/php-curl-class/php-curl-class and https://packagist.org/packages/curl/curl right away, each with millions of downloads already.
These and other userland offerings should help inform the first steps, in any case.
-- pmj
Hi
Am 2025-06-26 18:53, schrieb Larry Garfield:
- Please don't name the exception "Exception". It needs some slightly
more useful name, to avoid confusion in a file that also uses
\Exception.
More specifically, since it does introduce an entirely new namespace it
should be considered a “new extension” and must therefore follow the
Exception policy outlined in:
https://github.com/php/policies/blob/main/coding-standards-and-naming.rst#throwables
Best regards
Tim Düsterhus
More specifically, since it does introduce an entirely new namespace it
should be considered a “new extension” and must therefore follow the
Exception policy outlined in:
https://github.com/php/policies/blob/main/coding-standards-and-naming.rst#throwables
Embarrassingly, I had read your RFC adding that section - it's kind of
what got me thinking about adding a curl namespace in the first place!
- but I guess I didn't put it into long-term memory. Thanks for the
catch, I'll update that when I can.
Hello Internals,
I'd like to formally propose a restart of the original object-oriented
curl API RFC (https://wiki.php.net/rfc/curl-oop):https://wiki.php.net/rfc/curl_oop_v2
The prior RFC seemed to get positive feedback, with a small consensus
around wanting enum(s) for curl options. I've taken that feedback and
incorporated it into a new RFC, as I am not the original author, but I
am interested in making another potential improvement to the curl
extension.In a nutshell, this version of the RFC:
- uses enumerations for curl options and other curl constants
- introduces a new Curl namespace
I have not yet created an implementation PR. I realize that is
somewhat discouraged, but I believe that this should be relatively
straightforward to implement (there's also the previous RFC's PR to
build on top of). The implementation of this RFC as it is now will
likely be tedious, however, so I'd like to get feedback on the
enumeration idea before committing to the work.I've outlined one open question in the RFC, which relates to the above:
- Should we organize the curl option enumerations by value type? Or
have a single enumeration for all curl_setopt options and another for
all curl_multi_setopt options?If others (including the original RFC author) are interested in
working with me on this, I'm more than open to that, so please let me
know.Thanks,
Eric
IMO, this sounds like something that would be great to start as a userland
OOP wrapper for cURL, where it can be iterated on and the interface can be
tested and changed much quicker. Then, maybe proceed to an external
extension that can be migrated into core later, once its interface is
stable.
cURL is massive, and there are a lot of moving parts. I wouldn’t expect to
get the API correct on the first try. :-)
Cheers,
Ben
IMO, this sounds like something that would be great to start as a userland OOP wrapper for cURL, where it can be iterated on and the interface can be tested and changed much quicker. Then, maybe proceed to an external extension that can be migrated into core later, once its interface is stable.
cURL is massive, and there are a lot of moving parts. I wouldn’t expect to get the API correct on the first try. :-)
Cheers,
Ben
(Apologies for the double-post Ben, I'm a little rusty and forgot to reply-all)
I see, thanks for your feedback! To clarify, I am not aiming to
produce a high-level API for curl. I would expect that most users
would still reach for libraries like Guzzle, etc.
I know that in the prior discussion, Rowan Tommins had a vision for a
high-level API (https://externals.io/message/122371#122424), but I
share your sentiment that this would be difficult to get right in
core. I'd prefer to aim for a (mostly) direct translation of the
low-level API, which I think still has benefits on its own.
Ben Ramsey ramsey@php.net hat am 26.06.2025 18:54 CEST geschrieben:
Hello Internals,
I'd like to formally propose a restart of the original object-oriented
curl API RFC (https://wiki.php.net/rfc/curl-oop):https://wiki.php.net/rfc/curl_oop_v2
The prior RFC seemed to get positive feedback, with a small consensus
around wanting enum(s) for curl options. I've taken that feedback and
incorporated it into a new RFC, as I am not the original author, but I
am interested in making another potential improvement to the curl
extension.In a nutshell, this version of the RFC:
- uses enumerations for curl options and other curl constants
- introduces a new Curl namespace
I have not yet created an implementation PR. I realize that is
somewhat discouraged, but I believe that this should be relatively
straightforward to implement (there's also the previous RFC's PR to
build on top of). The implementation of this RFC as it is now will
likely be tedious, however, so I'd like to get feedback on the
enumeration idea before committing to the work.I've outlined one open question in the RFC, which relates to the above:
- Should we organize the curl option enumerations by value type? Or
have a single enumeration for all curl_setopt options and another for
all curl_multi_setopt options?If others (including the original RFC author) are interested in
working with me on this, I'm more than open to that, so please let me
know.Thanks,
EricIMO, this sounds like something that would be great to start as a userland OOP wrapper for cURL, where it can be iterated on and the interface can be tested and changed much quicker. Then, maybe proceed to an external extension that can be migrated into core later, once its interface is stable.
cURL is massive, and there are a lot of moving parts. I wouldn’t expect to get the API correct on the first try. :-)
Cheers,
Ben
- uses enumerations for curl options and other curl constants
Overall I think the RFC is a good opportunity to clean up a few legacy
oddities in the curl API, but I need to throw in my 2c about enum usage
here, as someone that has actually implemented some complexish
curl-based client [1].
Currently the API is:
curl_setopt($h, CURLOPT_SMTH, 3);
Now moving this into multiple enums to me brings one major issue, that I
first have to think about which type the option has to then know on
which enum I find it.
I understand this brings type safety on the value side if you have
multiple setOptionInt etc, but that does not seem worth the trade-off to
me. Type safety can already be done in static analyzers as they can see
which option you pass in, they know which value should be allowed.
Then on top of that, assuming we'd put all options in one enum.. I am
still wondering what the added value is compared to simply having the
global constants (they could be namespaced if anything...). It's longer
to type, and does not provide any better autocompletion.
\Curl\Option::ConnectTimeout vs CURLOPT_CONNECT_TIMEOUT sounds ok to me
if people feel strongly pro-enum, but I do really hope it is then a
single enum + setOption() call.
Best,
Jordi
[1]
https://github.com/composer/composer/blob/main/src/Composer/Util/Http/CurlDownloader.php
Jordi Boggiano
@seldaek - https://seld.be
- uses enumerations for curl options and other curl constants
Overall I think the RFC is a good opportunity to clean up a few legacy
oddities in the curl API, but I need to throw in my 2c about enum usage
here, as someone that has actually implemented some complexish
curl-based client [1].Currently the API is:
curl_setopt($h, CURLOPT_SMTH, 3);
Now moving this into multiple enums to me brings one major issue, that I
first have to think about which type the option has to then know on
which enum I find it.I understand this brings type safety on the value side if you have
multiple setOptionInt etc, but that does not seem worth the trade-off to
me. Type safety can already be done in static analyzers as they can see
which option you pass in, they know which value should be allowed.Then on top of that, assuming we'd put all options in one enum.. I am
still wondering what the added value is compared to simply having the
global constants (they could be namespaced if anything...). It's longer
to type, and does not provide any better autocompletion.
\Curl\Option::ConnectTimeout vs CURLOPT_CONNECT_TIMEOUT sounds ok to me
if people feel strongly pro-enum, but I do really hope it is then a
single enum + setOption() call.Best,
Jordi[1]
https://github.com/composer/composer/blob/main/src/Composer/Util/Http/CurlDownloader.php
Jordi Boggiano
@seldaek - https://seld.be
Reading this I can't help but feel like there's some cognitive bias
because you have written a Curl class yourself. The author of a PHP Class
that wraps Curl needs to know about every option and how to translate them
back-and-forth, which is really the insight I take from your message. As a
PHP developer making HTTP requests, I would never have guessed 270 options
for Curl configurations and having them split into multiple Enums gives me
smaller "boxes" that are easier to mentally parse individually. With that
said, splitting enumerations by type rather than context does weaken the
argument of split enums. I wouldn't be instinctively looking for "what enum
do I need that is an int?" or "what enum do I need that is a string?"
unless I already know the implementation details by heart. The Info and
Pause enumerations seem more useful in that regard as they reduce the scope
in which I need to understand, process and decide.
With that said, for me this also threads into the bikeshedding area that
could spiral into a failed RFC. Be it a single Enum for everything,
constants, context-based enums or type-based enums, I would much rather
have this RFC than not have it. PHP is one of the most important Web
applications in the world and it severely lacks the ability to simplify
Http. We could take inspiration from Python Requests [1] or Node Fetch [2]
as a really simple and straightforward API that covers the vast majority of
cases. I take the point that we don't need to go all the way and invent a
RequestClient library in PHP and this RFC is threading on the small step of
converting Curl procedural API into OOP, which is where Larry and Rowans
recommendations about some minor helper methods go a really really long way
compared to where we are today.
[1] https://www.w3schools.com/PYTHON/ref_requests_post.asp
[2] https://nodejs.org/en/learn/getting-started/fetch#basic-post-usage
--
Marco Deleu
- uses enumerations for curl options and other curl constants
Overall I think the RFC is a good opportunity to clean up a few legacy
oddities in the curl API, but I need to throw in my 2c about enum usage
here, as someone that has actually implemented some complexish
curl-based client [1].Currently the API is:
curl_setopt($h, CURLOPT_SMTH, 3);
Now moving this into multiple enums to me brings one major issue, that I
first have to think about which type the option has to then know on
which enum I find it.I understand this brings type safety on the value side if you have
multiple setOptionInt etc, but that does not seem worth the trade-off to
me. Type safety can already be done in static analyzers as they can see
which option you pass in, they know which value should be allowed.Then on top of that, assuming we'd put all options in one enum.. I am
still wondering what the added value is compared to simply having the
global constants (they could be namespaced if anything...). It's longer
to type, and does not provide any better autocompletion.
\Curl\Option::ConnectTimeout vs CURLOPT_CONNECT_TIMEOUT sounds ok to me
if people feel strongly pro-enum, but I do really hope it is then a
single enum + setOption() call.Best,
Jordi[1]
https://github.com/composer/composer/blob/main/src/Composer/Util/Http/CurlDownloader.phpJordi Boggiano
@seldaek - https://seld.beReading this I can't help but feel like there's some cognitive bias
because you have written a Curl class yourself. The author of a PHP
Class that wraps Curl needs to know about every option and how to
translate them back-and-forth, which is really the insight I take from
your message. As a PHP developer making HTTP requests, I would never
have guessed 270 options for Curl configurations and having them split
into multiple Enums gives me smaller "boxes" that are easier to
mentally parse individually. With that said, splitting enumerations by
type rather than context does weaken the argument of split enums. I
wouldn't be instinctively looking for "what enum do I need that is an
int?" or "what enum do I need that is a string?" unless I already know
the implementation details by heart. The Info and Pause enumerations
seem more useful in that regard as they reduce the scope in which I
need to understand, process and decide.
I will agree here. Splitting enums by type doesn't add much value. Splitting them by topic might, if the topics are sufficiently distinct that a separate method makes sense. I don't know Curl's API well enough to make specific recommendations there.
--Larry Garfield
With that said, for me this also threads into the bikeshedding area
that could spiral into a failed RFC. Be it a single Enum for
everything, constants, context-based enums or type-based enums, I
would much rather have this RFC than not have it. PHP is one of the
most important Web applications in the world and it severely lacks
the ability to simplify Http. We could take inspiration from Python
Requests [1] or Node Fetch [2] as a really simple and
straightforward API that covers the vast majority of cases.
Python Requests and Node Fetch are simple and straightforward for users
because they're not simple and straightforward to develop. A lot of
care and detailed decisions have gone into those APIs to make them feel
simple and straightforward when used.
Python Requests^1 is a massive third-party library that users install
separately from Python. It also depends on urllib3^2, another massive
third-party library that serves as its underlying HTTP client.
Node Fetch uses the WHATWG Fetch Living Standard^3, which, again, is
neither simple nor straightforward. It just feels that way when using it
because they did a damn good job defining it.
As I said earlier,^4 this level of detail should live in userland.
Arguably, as in the case of Fetch, something like this could live in
core. There was an RFC over 10 years ago that proposed moving pecl_http
into core.^5 However, if we decide to go that route, I think it needs
to be a much bigger undertaking, perhaps with a working group formed to
hash out the details and design the API. I think it's better to let
userland handle something like this, so core can focus on optimizations
that reduce the "if it's in C, it'll be faster" arguments.
I take the point that we don't need to go all the way and invent a
RequestClient library in PHP and this RFC is threading on the small
step of converting Curl procedural API into OOP, which is where
Larry and Rowans recommendations about some minor helper methods go
a really really long way compared to where we are today.
I'm fully on board with making the curl extension API easier to use.
Let's clearly state the problem and use-case we're attempting to solve
for and focus on a solution targeting that and nothing more (without
limiting ourselves for future expansion, of course). Maybe after stating
the problem clearly, we'll find it easier to build consensus on a solution.
The RFC hints at type safety and converting the curl procedural API into
OOP as motivating factors. I don't think these clearly state the problem
we're trying to solve, so if they continue to be the motivating factors,
this discussion is bound to continue into bikeshedding territory.
Here are some of the more concrete things I've seen mentioned that hint
at the problem:
From Rowan:
But that long list is also exactly why we badly need helpers for
common use cases - the manual page for curl_setopt has been at the
top of the charts for number of user comments for years, because
nobody wants to read the descriptions for two hundred options
they'll never need.
From Marco:
perform basic HTTP requests in a friendly way
These are great starts. Solving for the documentation problem might not
even require changes to the extension. We probably need to further
define what is meant by "friendly," though. Otherwise, we'll find
ourselves back at the bike shed.
So, what's the problem (from a user's perspective) this RFC solves?
Cheers,
Ben
-----Original Message-----
From: Eric Norris eric.t.norris@gmail.com
Sent: Thursday, June 26, 2025 7:25 PM
To: PHP internals internals@lists.php.net
Subject: [PHP-DEV] [RFC][DISCUSSION] Object-oriented curl API v2
I'd like to formally propose a restart of the original object-oriented curl API RFC
Cool. Calling functions with object as an argument to modify it's state feels very C-ish and wildly different from how contemporary PHP is usually written. So even a 1:1 translation into a more familiar API is welcome.
It still feels a bit unfamiliar to me that part of the response (the body) is returned from execute()
while the other stuff (like the status code) is stored in the state of the Handle instance. Is this statefulness suggested by libcurl and reflects it's object? Or is it a choice on the PHP side? I'm just wondering, this is probably not something that should be changed on the Handle class but maybe on some new layer.
The enums that you propose are backed. It seems appropriate in the sense of continuity, but is there a need for that? I'd argue that option names like AbstractUnixSocket or AutoReferer are values per se and there is no usable meaning for those integer values behind them. Maybe the translation to numeric constants (assuming it's necessary) can be done internally (in the Handle) via a match
or switch
statement and let's have pure enums?
I see that the option setters return \Curl\Handle
, but it is a bit unclear whether it will actually return static
, self
or $this
. I assume the latter but it would be nice to document that we're mutating the same object.
Should we organize the curl option enumerations by value type? Or have a single enumeration for all curl_setopt options and another for all curl_multi_setopt options?
In fact I would expect a single ->setOption()
instead of the type specific ones. Having to specify the argument type in the setter name and/or the enum name doesn't sound very enjoyable to me.
BR,
Juris
Hello Internals,
I'd like to formally propose a restart of the original object-oriented
curl API RFC (https://wiki.php.net/rfc/curl-oop):https://wiki.php.net/rfc/curl_oop_v2
The prior RFC seemed to get positive feedback, with a small consensus
around wanting enum(s) for curl options. I've taken that feedback and
incorporated it into a new RFC, as I am not the original author, but I
am interested in making another potential improvement to the curl
extension.In a nutshell, this version of the RFC:
- uses enumerations for curl options and other curl constants
- introduces a new Curl namespace
I have not yet created an implementation PR. I realize that is
somewhat discouraged, but I believe that this should be relatively
straightforward to implement (there's also the previous RFC's PR to
build on top of). The implementation of this RFC as it is now will
likely be tedious, however, so I'd like to get feedback on the
enumeration idea before committing to the work.I've outlined one open question in the RFC, which relates to the above:
- Should we organize the curl option enumerations by value type? Or
have a single enumeration for all curl_setopt options and another for
all curl_multi_setopt options?If others (including the original RFC author) are interested in
working with me on this, I'm more than open to that, so please let me
know.Thanks,
Eric
Thank you for taking the initiative to work on this. It's long
overdue, and the pattern we have now — that a CurlHandle
object is
passed to functions — is not as intuitive as calling a method on that
object.
However, I softly oppose this RFC in its current state and the way it
seems to be going.
I have pushed Curl and libcurl to some uncommon cases such as HTTP/3,
DoH, the new debug callback (which I authored the PR for), IMAP, and a
few other obscure tweaks. For all of them, the current curl_setopt
worked, and the more I used it, the more I understood that having just
a few Curl functions and a sea of Curl options is the least
"presumptive way", regardless of the protocol and the options Curl
provides.
The extension is named Curl
, because it's supposed to provide Curl
functionality into PHP. It provides low-level functionality, but we
should leave it to the PHP users to build the libraries that have
fluent APIs.
This way, we can have excellent purpose-built HTTP clients, IMAP
clients, Tor proxies, etc, all built using the low-level functionality
the extension offers.The HTTP client authors know the Curl options
really well to build a secure and fast HTTP client, and someone else
building an IMAP client can provide an intuitive API but still use the
same underlying Curl functionality. I understand that your RFC does
not propose a high-level API, and I agree with you that a high-level
API will need a lot of discussion.
There's a ton of Curl constants, and you are right that it could
really use some organizing. The upstream Curl project has not done it,
most likely because the options behave differently depending on the
protocol, and simply because it's a lot of work and BC trouble to do
so. I don’t think we can realistically have a meaningful discourse on
how to semantically group the constants into meaningful Enums. I'd
also argue that if libcurl is OK with having a sea of Curl options, we
should not be the one to group them.
Grouping them by the expected value type is one way to do it, but as
mentioned elsewhere in replies, now, the consumers of the API will
have to figure out whether that one Curl option they always used
belongs to IntOpt
or a BoolOpt
. It will help with
CURLOPT_SSL_VERIFYHOST
(which should be set to 2
, not true
), but
I don't think this level of type safety is all that useful for the
trouble.
To bring some numbers, we currently have:
- 271 CURLOPT_* consts (https://php.watch/codex?search=CURLOPT_)
- 78 CURLINFO_* consts (https://php.watch/codex?search=CURLINFO_)
- 71 CURLE_* consts (https://php.watch/codex?search=CURLE_) + I sent
a PR to add 41 missing consts
(https://github.com/php/php-src/pull/13340/files) - 150+ constants for options such as protocols, HTTP versions, URL
follow rules, etc.
I think a more light-weight approach would be to:
- Move all of them to the
\Curl
namespace. - Rename Curl options to
\Curl\Option
namespace, and rename them,
so thatCURLOPT_SSL_VERIFYHOST
becomesCurl\Option\SSL_VERIFYHOST
- Rename Curl error codes to similar
\Curl\Error
constants. - Have the
CurlHandle
object accept options, e.g.
$ch->setOption(Curl\Option\SSL_VERIFYHOST, 2)
. libcurl Easy handlers
do not have a way to retrieve the option once it's set, so there will
be nogetOption
either. - Make Curl throw exceptions, and never
false
on\Curl\execute()
,
with the Exception's error code and message mapped to the Curl error
code and message. We will not need to bring overcurl_error
or
curl_errno
functions.
Realistically, I don't think we can deprecate and remove the \curl_*
functions any time soon, so this will actually add more maintenance
work for php-src at the end too.
Thank you.
Ayesh.
Le sam. 28 juin 2025 à 00:01, Ayesh Karunaratne ayesh@php.watch a écrit :
Hello Internals,
I'd like to formally propose a restart of the original object-oriented
curl API RFC (https://wiki.php.net/rfc/curl-oop):https://wiki.php.net/rfc/curl_oop_v2
The prior RFC seemed to get positive feedback, with a small consensus
around wanting enum(s) for curl options. I've taken that feedback and
incorporated it into a new RFC, as I am not the original author, but I
am interested in making another potential improvement to the curl
extension.In a nutshell, this version of the RFC:
- uses enumerations for curl options and other curl constants
- introduces a new Curl namespace
I have not yet created an implementation PR. I realize that is
somewhat discouraged, but I believe that this should be relatively
straightforward to implement (there's also the previous RFC's PR to
build on top of). The implementation of this RFC as it is now will
likely be tedious, however, so I'd like to get feedback on the
enumeration idea before committing to the work.I've outlined one open question in the RFC, which relates to the above:
- Should we organize the curl option enumerations by value type? Or
have a single enumeration for all curl_setopt options and another for
all curl_multi_setopt options?If others (including the original RFC author) are interested in
working with me on this, I'm more than open to that, so please let me
know.Thanks,
EricThank you for taking the initiative to work on this. It's long
overdue, and the pattern we have now — that aCurlHandle
object is
passed to functions — is not as intuitive as calling a method on that
object.However, I softly oppose this RFC in its current state and the way it
seems to be going.I have pushed Curl and libcurl to some uncommon cases such as HTTP/3,
DoH, the new debug callback (which I authored the PR for), IMAP, and a
few other obscure tweaks. For all of them, the currentcurl_setopt
worked, and the more I used it, the more I understood that having just
a few Curl functions and a sea of Curl options is the least
"presumptive way", regardless of the protocol and the options Curl
provides.The extension is named
Curl
, because it's supposed to provide Curl
functionality into PHP. It provides low-level functionality, but we
should leave it to the PHP users to build the libraries that have
fluent APIs.This way, we can have excellent purpose-built HTTP clients, IMAP
clients, Tor proxies, etc, all built using the low-level functionality
the extension offers.The HTTP client authors know the Curl options
really well to build a secure and fast HTTP client, and someone else
building an IMAP client can provide an intuitive API but still use the
same underlying Curl functionality. I understand that your RFC does
not propose a high-level API, and I agree with you that a high-level
API will need a lot of discussion.There's a ton of Curl constants, and you are right that it could
really use some organizing. The upstream Curl project has not done it,
most likely because the options behave differently depending on the
protocol, and simply because it's a lot of work and BC trouble to do
so. I don’t think we can realistically have a meaningful discourse on
how to semantically group the constants into meaningful Enums. I'd
also argue that if libcurl is OK with having a sea of Curl options, we
should not be the one to group them.Grouping them by the expected value type is one way to do it, but as
mentioned elsewhere in replies, now, the consumers of the API will
have to figure out whether that one Curl option they always used
belongs toIntOpt
or aBoolOpt
. It will help with
CURLOPT_SSL_VERIFYHOST
(which should be set to2
, nottrue
), but
I don't think this level of type safety is all that useful for the
trouble.To bring some numbers, we currently have:
- 271 CURLOPT_* consts (https://php.watch/codex?search=CURLOPT_)
- 78 CURLINFO_* consts (https://php.watch/codex?search=CURLINFO_)
- 71 CURLE_* consts (https://php.watch/codex?search=CURLE_) + I sent
a PR to add 41 missing consts
(https://github.com/php/php-src/pull/13340/files)- 150+ constants for options such as protocols, HTTP versions, URL
follow rules, etc.I think a more light-weight approach would be to:
- Move all of them to the
\Curl
namespace.- Rename Curl options to
\Curl\Option
namespace, and rename them,
so thatCURLOPT_SSL_VERIFYHOST
becomesCurl\Option\SSL_VERIFYHOST
- Rename Curl error codes to similar
\Curl\Error
constants.- Have the
CurlHandle
object accept options, e.g.
$ch->setOption(Curl\Option\SSL_VERIFYHOST, 2)
. libcurl Easy handlers
do not have a way to retrieve the option once it's set, so there will
be nogetOption
either.- Make Curl throw exceptions, and never
false
on\Curl\execute()
,
with the Exception's error code and message mapped to the Curl error
code and message. We will not need to bring overcurl_error
or
curl_errno
functions.Realistically, I don't think we can deprecate and remove the
\curl_*
functions any time soon, so this will actually add more maintenance
work for php-src at the end too.Thank you.
Ayesh.
I'm not even sure it's a good idea to add those namespaced options: using
CURLOPT_SSL_VERIFYHOST
is perfect to find the corresponding curl
documentation with your favorite search engine. php's doc is awesome, but
it cannot compete with the details provided by curl's doc on the topic.
Nicolas
I'm not even sure it's a good idea to add those namespaced options: using
CURLOPT_SSL_VERIFYHOST
is perfect to find the corresponding curl documentation with your favorite search engine. php's doc is awesome, but it cannot compete with the details provided by curl's doc on the topic.Nicolas
Hi Nicolas,
You are right, if we were to rename those constants, we would lose the
"grep-ability" in both libcurl source/docs and many years of existing
open source projects and discussions.
So while keeping the existing option consts, info consts, we do not
have a lot of room to improve it. Perhaps the CurlHandle
objects can
accept options similar to how we have date_*
functions alongside
DateTime
methods. I personally continue to use the functions, but I
won't mind having an OOP API. We can leave it to the library authors
(such as you, Nicolas 💟, Symfony HTTP client is just awesome) to
design more intuitive APIs.
Ayesh.
I'm not even sure it's a good idea to add those namespaced options: using
CURLOPT_SSL_VERIFYHOST
is perfect to find the corresponding curl documentation with your favorite search engine. php's doc is awesome, but it cannot compete with the details provided by curl's doc on the topic.
That's a good point, but I do feel like there is value in having these
under a namespace; a part of the reason I am interested in this
proposal is that I'd like to see more of PHP core living under
namespaces, since I think it will present a more consistent vision of
the "standard library". I wonder if there is a way we could preserve
the link, at least? In my proposal, the enum values are equivalent to
their curl constant equivalents, which I thought would help with
discoverability in the documentation.
For the record - and I'll say this elsewhere - based on the discussion
so far I am planning on withdrawing the proposal for separate enums.
I'm still considering using a single enum to group all of these
options, however, instead of constants.
However, I softly oppose this RFC in its current state and the way it
seems to be going.
So I think we've identified a key disagreement about not just the goal, but the intent. To what extent should PHP core ship with a usable HTTP client?
Right now it ships with Curl, which... in its current form is not usable. It's a low-level tool with an inscrutable API we inherited from C. It's not viable as a user-facing tool. It's a tool that's only useful to people writing client libraries like Guzzle or CurlClass or Symfony HTTP or such.
On the one hand, Marco is correct that for a web-centric language to not ship with a non-sucky way to send web requests is... kinda embarrassing. Even if the use cases where you can't install a 3rd party library are few, they are non-zero. And that also doesn't help new users figure out what to use. (Eg, the person who wrote most of the code for our main application at work, learning PHP as he went, is sending lots of requests using... an ungodly mess of curl that can't even understand. Because he didn't know that things like Guzzle even existed.)
On the other hand, Ben is correct that an HTTP client is a not-small task, with a very deep rabbit hole.
So there's two closely related but distinct asks here:
- Make working with curl suck less (giving it an OOP interface is part of that but not all)
- Ship a useful first-party HTTP client that can handle the 80% case, even if it's not full featured.
Beefing up Curl's interface until it fulfills part 2 is one approach, but not the only.
At one extreme would be the "do nothing, status quo is fine" position. Ayesh seems to be close to that position, maybe with a little polish for funsies. The other extreme would essentially be "Guzzle in core," which I don't think anyone is advocating. Where between those extremes we should land is debatable.
Personally I'm of the mind that a simple, basic-features HTTP client in core would be a good thing; that's central enough that it should not be left to userland. It doesn't need to offer every possible feature; no need for async multiplexing, for example. But sending GET and POST requests with straightforward bodies should be table-stakes for a web language, and right now, that's a second class citizen. If it's written in such a way that it can be extended easily in user-space, so much the better.
Whether that basic-features client is Curl itself or a bundled wrapper that uses Curl, I have no strong preference. The challenge of making it separate from Curl is, shocker, that it's bikeshed bait. Does that imply using the new URL/URI classes? Does it imply we need request/response objects? The rabbit hole indeed gets deep fast.
So the first question, I think, is what is the consensus between these three coarse-grained positions:
- Status quo is fine. PHP core not having a user-friendly way to send HTTP requests is acceptable. Maybe make Curl a little nicer, but only to make life easier for Guzzle et al.
- We should develop the Curl API until it's usable for basic HTTP behavior, but no further.
- We should bundle an HTTP client that wraps Curl (with or without minor improvements to Curl), exact scope TBD.
Personally, I'm open to either 2 or 3. 3 is more bikesheddable, but possibly the better end result.
Where does everyone else stand?
--Larry Garfield
However, I softly oppose this RFC in its current state and the way it
seems to be going.So I think we've identified a key disagreement about not just the goal, but the intent. To what extent should PHP core ship with a usable HTTP client?
Right now it ships with Curl, which... in its current form is not usable. It's a low-level tool with an inscrutable API we inherited from C. It's not viable as a user-facing tool. It's a tool that's only useful to people writing client libraries like Guzzle or CurlClass or Symfony HTTP or such.
On the one hand, Marco is correct that for a web-centric language to not ship with a non-sucky way to send web requests is... kinda embarrassing. Even if the use cases where you can't install a 3rd party library are few, they are non-zero. And that also doesn't help new users figure out what to use. (Eg, the person who wrote most of the code for our main application at work, learning PHP as he went, is sending lots of requests using... an ungodly mess of curl that can't even understand. Because he didn't know that things like Guzzle even existed.)
On the other hand, Ben is correct that an HTTP client is a not-small task, with a very deep rabbit hole.
So there's two closely related but distinct asks here:
- Make working with curl suck less (giving it an OOP interface is part of that but not all)
- Ship a useful first-party HTTP client that can handle the 80% case, even if it's not full featured.
Beefing up Curl's interface until it fulfills part 2 is one approach, but not the only.
At one extreme would be the "do nothing, status quo is fine" position. Ayesh seems to be close to that position, maybe with a little polish for funsies. The other extreme would essentially be "Guzzle in core," which I don't think anyone is advocating. Where between those extremes we should land is debatable.
Personally I'm of the mind that a simple, basic-features HTTP client in core would be a good thing; that's central enough that it should not be left to userland. It doesn't need to offer every possible feature; no need for async multiplexing, for example. But sending GET and POST requests with straightforward bodies should be table-stakes for a web language, and right now, that's a second class citizen. If it's written in such a way that it can be extended easily in user-space, so much the better.
Whether that basic-features client is Curl itself or a bundled wrapper that uses Curl, I have no strong preference. The challenge of making it separate from Curl is, shocker, that it's bikeshed bait. Does that imply using the new URL/URI classes? Does it imply we need request/response objects? The rabbit hole indeed gets deep fast.
So the first question, I think, is what is the consensus between these three coarse-grained positions:
- Status quo is fine. PHP core not having a user-friendly way to send HTTP requests is acceptable. Maybe make Curl a little nicer, but only to make life easier for Guzzle et al.
- We should develop the Curl API until it's usable for basic HTTP behavior, but no further.
- We should bundle an HTTP client that wraps Curl (with or without minor improvements to Curl), exact scope TBD.
Personally, I'm open to either 2 or 3. 3 is more bikesheddable, but possibly the better end result.
Where does everyone else stand?
--Larry Garfield
I’m also open to 2 or 3, but 3 sounds more like something that I think should live in userland, so I lean much more towards 2.
Cheers,
Ben
However, I softly oppose this RFC in its current state and the way it
seems to be going.So I think we've identified a key disagreement about not just the goal,
but the intent. To what extent should PHP core ship with a usable HTTP
client?Right now it ships with Curl, which... in its current form is not usable.
It's a low-level tool with an inscrutable API we inherited from C. It's
not viable as a user-facing tool. It's a tool that's only useful to people
writing client libraries like Guzzle or CurlClass or Symfony HTTP or such.On the one hand, Marco is correct that for a web-centric language to not
ship with a non-sucky way to send web requests is... kinda embarrassing.
Even if the use cases where you can't install a 3rd party library are few,
they are non-zero. And that also doesn't help new users figure out what to
use. (Eg, the person who wrote most of the code for our main application
at work, learning PHP as he went, is sending lots of requests using... an
ungodly mess of curl that can't even understand. Because he didn't know
that things like Guzzle even existed.)On the other hand, Ben is correct that an HTTP client is a not-small task,
with a very deep rabbit hole.So there's two closely related but distinct asks here:
- Make working with curl suck less (giving it an OOP interface is part of
that but not all)- Ship a useful first-party HTTP client that can handle the 80% case,
even if it's not full featured.Beefing up Curl's interface until it fulfills part 2 is one approach, but
not the only.At one extreme would be the "do nothing, status quo is fine" position.
Ayesh seems to be close to that position, maybe with a little polish for
funsies. The other extreme would essentially be "Guzzle in core," which I
don't think anyone is advocating. Where between those extremes we should
land is debatable.Personally I'm of the mind that a simple, basic-features HTTP client in
core would be a good thing; that's central enough that it should not be
left to userland. It doesn't need to offer every possible feature; no need
for async multiplexing, for example. But sending GET and POST requests
with straightforward bodies should be table-stakes for a web language, and
right now, that's a second class citizen. If it's written in such a way
that it can be extended easily in user-space, so much the better.Whether that basic-features client is Curl itself or a bundled wrapper
that uses Curl, I have no strong preference. The challenge of making it
separate from Curl is, shocker, that it's bikeshed bait. Does that imply
using the new URL/URI classes? Does it imply we need request/response
objects? The rabbit hole indeed gets deep fast.So the first question, I think, is what is the consensus between these
three coarse-grained positions:
- Status quo is fine. PHP core not having a user-friendly way to send
HTTP requests is acceptable. Maybe make Curl a little nicer, but only to
make life easier for Guzzle et al.- We should develop the Curl API until it's usable for basic HTTP
behavior, but no further.- We should bundle an HTTP client that wraps Curl (with or without minor
improvements to Curl), exact scope TBD.Personally, I'm open to either 2 or 3. 3 is more bikesheddable, but
possibly the better end result.Where does everyone else stand?
--Larry Garfield
I think #2 is the way to go. I personally just use Symfony's HTTP Client
99.9% of the time - there is one exception in recent years where I had to
go low-level cURL and craft an extremely specific HTTP request. Yes, it is
a SOAP service for a government system, because of course it is.
I'm not even sure it's a good idea to add those namespaced options: using
CURLOPT_SSL_VERIFYHOST
is perfect to find the corresponding curl
documentation with your favorite search engine. php's doc is awesome, but
it cannot compete with the details provided by curl's doc on the topic.
Valid. This cannot be avoided if any kind of RFC is to be accepted, so I
think the next best solution is to put effort into documentation and ensure
that things are referenced (like the new Enum options having very obvious
links to the original constants and to the curl documentation), making
discoverability easy.
The good thing is that OOP API is purely new, so there is not going to be
tons of old content about it to make things messy.
Arvīds Godjuks
+371 26 851 664
arvids.godjuks@gmail.com
Telegram: @psihius https://t.me/psihius
Thanks for your thoughtful response, Larry! I agree with your summary.
- Status quo is fine. PHP core not having a user-friendly way to send HTTP requests is acceptable. Maybe make Curl a little nicer, but only to make life easier for Guzzle et al.
- We should develop the Curl API until it's usable for basic HTTP behavior, but no further.
- We should bundle an HTTP client that wraps Curl (with or without minor improvements to Curl), exact scope TBD.
Personally, I'm open to either 2 or 3. 3 is more bikesheddable, but possibly the better end result.
Where does everyone else stand?
Based on the feedback so far (I do plan on waiting for more responses
to your email), and on my own preferences, I wonder if there is a
hybrid option I could propose. Perhaps the RFC could offer both a
\Curl\Handle (tentative name) to address position 1, and a
\Curl\BasicHttpHandle (also tentative name) that addresses position 2?
If people were amenable, I'd even make the BasicHttpHandle a separate
vote.
I agree that 3 is both more bikesheddable and also possibly ideal, but
I feel my above suggestion maybe strikes the right balance between the
"status quo is fine, I don't want to see random HTTP-related methods
on my low(est)-level curl object" and the "I'd like to do basic HTTP
stuff with curl, without a library" crowds.
Barring that, my preference would be 2, but I'd accept 1 just to have
it pass - like I mentioned elsewhere, I think there is value in
introducing namespaces and object-oriented APIs for "modernization"
and language consistency reasons.
Thanks for your response Ayesh! I hope to see this RFC on php.watch :)
However, I softly oppose this RFC in its current state and the way it
seems to be going.I have pushed Curl and libcurl to some uncommon cases such as HTTP/3,
DoH, the new debug callback (which I authored the PR for), IMAP, and a
few other obscure tweaks. For all of them, the currentcurl_setopt
worked, and the more I used it, the more I understood that having just
a few Curl functions and a sea of Curl options is the least
"presumptive way", regardless of the protocol and the options Curl
provides.The extension is named
Curl
, because it's supposed to provide Curl
functionality into PHP. It provides low-level functionality, but we
should leave it to the PHP users to build the libraries that have
fluent APIs.
It would be helpful for me to understand what specifically you are
objecting to in the current discussion, putting aside the suggested
enumeration changes. Do you object to any HTTP-oriented methods at
all, like something to set the HTTP method? Would you object to
methods that apply to all, or nearly all, transport types, like a
setTimeout() method?
I think a more light-weight approach would be to:
- Move all of them to the
\Curl
namespace.- Rename Curl options to
\Curl\Option
namespace, and rename them,
so thatCURLOPT_SSL_VERIFYHOST
becomesCurl\Option\SSL_VERIFYHOST
- Rename Curl error codes to similar
\Curl\Error
constants.- Have the
CurlHandle
object accept options, e.g.
$ch->setOption(Curl\Option\SSL_VERIFYHOST, 2)
. libcurl Easy handlers
do not have a way to retrieve the option once it's set, so there will
be nogetOption
either.- Make Curl throw exceptions, and never
false
on\Curl\execute()
,
with the Exception's error code and message mapped to the Curl error
code and message. We will not need to bring overcurl_error
or
curl_errno
functions.
Acknowledged, thank you. The RFC does propose what you're suggesting
with never returning false and instead throwing exceptions, but it
feels like it might still be useful to have properties on the actual
object. Do you have opinions on that?
Realistically, I don't think we can deprecate and remove the
\curl_*
functions any time soon, so this will actually add more maintenance
work for php-src at the end too.
Agreed, I do not plan on suggesting this, not even under Future Scope.