PSR for HTTP clients

430 views
Skip to first unread message

Tobias Nyholm

unread,
Jul 6, 2017, 12:08:00 PM7/6/17
to PHP Framework Interoperability Group
Hey. 

Im a co-creator of HTTPlug and a maintainer of a number of API clients. I would really like to see a PSR for synchronous HTTP clients. The HTTPlug has created a "meta-standard" which has been stable for 18 months now. We believe it is a really good interface and would like it (or something similar) to be an official PSR. 

I would ask the Fig for an entrance vote.

Here is a first idea: 

Workgroup:
These people has been contacted and said they are interested in participating in a workgroup. 
  • Tobias Nyholm
  • Sara Golemon (cc)
  • Matthew O'Phinney (cc) (Maybe)
  • Mark Sagi-Kazar 
  • Jermey Lindstrom
  • David Buchmann
  • Joel Wurtz
  • Simon Asika
  • Christian Lück
  • David De Boer

Cees-Jan Kiewiet

unread,
Jul 6, 2017, 4:04:15 PM7/6/17
to php...@googlegroups.com
Hey,

This looks interesting, would have to study this in detail but I do like the simplicity from a quick read through.

Cees-Jan

--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+unsubscribe@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/0db70a2c-d528-44df-b7f5-62387eb67872%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Tuomas Koski

unread,
Jul 7, 2017, 4:47:20 AM7/7/17
to php...@googlegroups.com
Hi,

+1 for first creating working stuff and later doing effort to make it a good standard. Way better approach than the other way round :)

Great work.

Cheers and great weekends!
--
Tuomas



For more options, visit https://groups.google.com/d/optout.



--
Cheers,
--
Tuomas

Humap Software Ltd
http://humapsoftware.com
Twitter: @humapsoftware
LinkedIn: https://www.linkedin.com/company/humap-software-ltd
Facebook: http://facebook.com/humapsoftware/
Instagram: @humapsoftware

Larry Garfield

unread,
Jul 7, 2017, 12:58:36 PM7/7/17
to php...@googlegroups.com
The key for me is if the various HTTP clients have buy-in to implement the spec once completed.  Is that the case?

I would also ask why this is just for sync HTTP clients, not async.  There's plenty of use cases for the latter.  That may be scope creep, but I feel it's worth asking.

--Larry Garfield
To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+u...@googlegroups.com.

To post to this group, send email to php...@googlegroups.com.

Tobias Nyholm

unread,
Jul 7, 2017, 1:20:41 PM7/7/17
to PHP Framework Interoperability Group
Excellent questions. Thank you. 

The buy-in would be the same for every PSR, wouldn't it? Libraries do not want to depend on an implementation or provide an implementation themselves. They want the user to decide if a highly flexible client (Guzzle) or a lightweight/fast client (php-http/curl-client). (Source: https://github.com/joelwurtz/http-client-benchmark). Also, as you probably noticed, there are representatives from all PSR7 compliant HTTP clients in the workgroup. 

The simplest answer of async or not is: There is not PSR for Promises yet. I do not think the PSR for HTTP clients should define what the industry standard for a Promise should be. I do also not think we should wait with a PSR for HTTP clients until (if ever) a PSR for promises has been released. 

We, php-http team, have followed discussions in https://github.com/async-interop that sadly has been put on hold. 
Cees-Jan


To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+u...@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.
--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+u...@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.



--
Cheers,
--
Tuomas

Humap Software Ltd
http://humapsoftware.com
Twitter: @humapsoftware
LinkedIn: https://www.linkedin.com/company/humap-software-ltd
Facebook: http://facebook.com/humapsoftware/
Instagram: @humapsoftware

Larry Garfield

unread,
Jul 9, 2017, 6:38:32 PM7/9/17
to php...@googlegroups.com
On 07/07/2017 12:20 PM, Tobias Nyholm wrote:
Excellent questions. Thank you. 

The buy-in would be the same for every PSR, wouldn't it? Libraries do not want to depend on an implementation or provide an implementation themselves. They want the user to decide if a highly flexible client (Guzzle) or a lightweight/fast client (php-http/curl-client). (Source: https://github.com/joelwurtz/http-client-benchmark). Also, as you probably noticed, there are representatives from all PSR7 compliant HTTP clients in the workgroup.

That last point was what I was getting at, thank you.  By buy-in, I mean "do we have an indication that the major HTTP clients are going to adopt it?"  It sounds like the answer is a loud "yes", which is great to see.


The simplest answer of async or not is: There is not PSR for Promises yet. I do not think the PSR for HTTP clients should define what the industry standard for a Promise should be. I do also not think we should wait with a PSR for HTTP clients until (if ever) a PSR for promises has been released. 

We, php-http team, have followed discussions in https://github.com/async-interop that sadly has been put on hold.

A fair reason, thanks.  It may be worth mentioning in the metadoc, including what a future Async version MIGHT look like if/when a Promises PS happens.  (You don't need to implement it, just show that it could still be implemented.)

--Larry Garfield

Alessandro Lai

unread,
Jul 11, 2017, 7:48:52 AM7/11/17
to PHP Framework Interoperability Group
Very good suggestions.
Also, it's very pleasant to see a PSR pushed this way and backed from the start by a working lib and all the interestend players. Really good job!

Josh Di Fabio

unread,
Jul 11, 2017, 11:46:15 AM7/11/17
to PHP Framework Interoperability Group
This looks pretty good, but I don't like exceptions for 4xx and 5xx responses, which smells of exceptions for flow control. Within the context of HTTP there is nothing exceptional about 4xx and 5xx responses. Rather, whether a certain response status is considered exceptional depends on the context of the caller. For example, in many contexts a 3xx response would be unexpected but a 404 response would not. Instead of throwing, the client should simply return the response.

tobias...@gmail.com

unread,
Jul 11, 2017, 11:51:38 AM7/11/17
to php...@googlegroups.com
I think I've been unclear or you misread something. 
A HTTP client MUST NOT throw exceptions for any response. 

So I very much agree with you. 

You received this message because you are subscribed to a topic in the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/php-fig/iFZF6T9L2zA/unsubscribe.
To unsubscribe from this group and all its topics, send an email to php-fig+u...@googlegroups.com.

To post to this group, send email to php...@googlegroups.com.

Josh Di Fabio

unread,
Jul 11, 2017, 12:12:28 PM7/11/17
to php...@googlegroups.com
Right, I see; I saw the HttpException class but I missed that it MUST NOT be thrown when using the client's default configuration.

Having said that, I think the fact that the behaviour of sendRequest() varies based on some configuration which isn't available via the interface is actually worse than always throwing. Given that most people will be injecting their HttpClient instance into other objects and probably passing one instance around to multiple classes, the code which is calling sendRequest() won't generally have visibility over how the client is configured, so we'll have to be super defensive when calling the client to be sure we handle 4xx and 5xx responses correctly:

try {
    $rep = $client->sendRequest($req);
} catch (HttpException $e) {
    $rep = $e->getResponse();
}

It should either always throw or never throw. The behaviour of redirects should also be consistent or configurable via the interface or else autowiring will be useless for instances of HttpClient.

Larry Garfield

unread,
Jul 11, 2017, 12:33:15 PM7/11/17
to php...@googlegroups.com
I'm inclined to agree here.  Error handling that may change based on some config not available to me in local scope, like PDO does, is a design flaw, as I then have to account for both possible error flows.

I could be argued in favor of exceptions always or exceptions never (I'm leaning exceptions on all 4xx/xx right now, but could be convinced otherwise), but "exceptions sometimes" doesn't seem like a good choice.

That said, that's a detail that doesn't block officially forming the WG; I'm +1 on approving this as a WG to hash out exactly those sorts of details.

--Larry Garfield

Matthieu Napoli

unread,
Jul 11, 2017, 5:18:30 PM7/11/17
to PHP Framework Interoperability Group
I have to agree with Larry, having the behavior of a standard depend on some configuration is risky. If the goal of the PSR is to be used by libraries or frameworks (not end users) then it's fine to force a choice IMO.

I have minor comments about the proposal, maybe it's too early to get into details and create a proper thread but I would suggest making the exceptions clearer:

 * Exception for when a request failed.
 * Examples:
 *      - Request is invalid (eg. method is missing)
 *      - Runtime request errors (like the body stream is not seekable)
interface RequestException extends Exception

How about InvalidRequestException? (I would suggest InvalidRequest but it would be a waste of time :p)
We need to make it clear that the HTTP request did not fail, it's the Request object that was invalid.

 * Thrown when the request cannot be completed because of network issues.
 * There is no response object as this exception is thrown when no response has been received.
 * Example: the target host name can not be resolved or the connection failed.
interface NetworkException extends Exception

NetworkErrorException? OK maybe this one is too much (NetworkError would be better but…)

And as said above HttpException should probably be removed entirely?

Matthieu

Tobias Nyholm

unread,
Jul 13, 2017, 8:17:23 AM7/13/17
to PHP Framework Interoperability Group
Thank you for all your comments and suggestions. I will bring these to the work group if/when it is approved. 

I lean towards NEVER throw exceptions. 

And about the exceptions: I agree, they could maybe need some renaming and updates to be more clear. 

Márk Sági-Kazár

unread,
Aug 4, 2017, 4:25:03 AM8/4/17
to PHP Framework Interoperability Group
Hi all,

Joining the discussion as one of the PHP-HTTP founders.

I am not 100% aware of all major HTTP clients wanting to implement this PSR, but I can speak for Guzzle where the answer is clearly YES. As for other clients: PHP-HTTP has adapters for major HTTP clients, so there is proof that the interface can work together with those clients, but given how adoption looks like now, I have the impression that it's not going to be a problem.

As for exceptions: we had quite a few discussions about throwing exceptions for HTTP errors. We decided that clients should never throw such exceptions, not even with a secret configuration. The reason behind is simply what's already been said above.

There are two common exceptions to this rule:

- In case of adapters (if the underlying client supports this feature) we do not forbid this behavior, but we require that with the default configuration the client does not throw exceptions. When a client implements this PSR, it should stop throwing exceptions.
- There is a PHP-HTTP plugin which converts 4xx/5xx HTTP errors to exceptions, but it has been said a thousand times (I think it's mentioned in the documentation as well) that this plugin should only be used when there is no business logic wrapped around the HTTP workflow or for testing purposes.

Except the two points above, throwing exceptions is already forbidden I think.

Mark

Barry vd. Heuvel

unread,
Aug 25, 2017, 4:10:06 AM8/25/17
to PHP Framework Interoperability Group
So given the unanimous support for the HTTP Client and the current implementations, the only actual point of discussion is the throwing of Exceptions on 4xx/5xx responses, right?

As a library maintainer, I'd like to rely on the behavior of the Client, so either ALWAYS or NEVER, not depending om some hidden config. Otherwise I'd have to check both cases again every time. 
And in that case my preference would be to NOT throw exceptions on 4xx/5xx errors.

Hope this will make it to a real PSR soon :) Good job!

Op vrijdag 4 augustus 2017 10:25:03 UTC+2 schreef Márk Sági-Kazár:

Jason Judge

unread,
Aug 26, 2017, 8:35:28 AM8/26/17
to PHP Framework Interoperability Group
This is my preference too. A 40x or 50x being returned from the remote endpoint is an exception at the remote end. Whether it is an exception in your application is up to your application. The response should still be available in full, which can be difficult or at least a little clunky to get hold of if the client is throwing exceptions on behalf of your application. So IMO the client should get the HTTP response, provide the result, then leave the application to decide for itself whether has now entered an exceptional state that it cannot get out of.

There may be exceptions raised locally, for example if the URL is invalid (if that is even possible, if the URL is an object that must be valid just to be instantiated), but they are local exceptions for which there is not even a response to see. Not sure if timeouts are in this category.

-- Jason

Larry Garfield

unread,
Aug 28, 2017, 4:44:00 PM8/28/17
to php...@googlegroups.com
Proposed guideline for the client:

- If it's possible to return an accurate HTTP response object, do so, whatever it is.  (Even if it's a 500 or 418, it's still a valid response object.)

- If it's not possible to return an accurate HTTP response object, throw an appropriate exception.  Because there's no legit return value to return, an exception is the appropriate alternative.

Does that seem reasonable?

--Larry Garfield

Niels Braczek

unread,
Aug 28, 2017, 5:09:23 PM8/28/17
to php...@googlegroups.com
Am 28.08.2017 um 22:43 schrieb Larry Garfield:

> Proposed guideline for the client:
>
> - If it's possible to return an accurate HTTP response object, do so,
> whatever it is. (Even if it's a 500 or 418, it's still a valid response
> object.)
>
> - If it's not possible to return an accurate HTTP response object, throw
> an appropriate exception. Because there's no legit return value to
> return, an exception is the appropriate alternative.
>
> Does that seem reasonable?

Sounds very reasonable to me (although my voice don't count much).

Regards,
Niels

--
| New Stars on the Horizon: GreenCape · nibralab · laJoom |
| http://www.bsds.de · BSDS Braczek Software- und DatenSysteme |
| Webdesign · Webhosting · e-Commerce · Joomla! Content Management |
------------------------------------------------------------------

Jason Judge

unread,
Aug 28, 2017, 6:35:14 PM8/28/17
to PHP Framework Interoperability Group
Shorter and more eloquently than I put it, but yes, exactly this would be the way to go IMO. If there is a response message available, then I would like to see it.

-- Jason

Jan Schneider

unread,
Aug 29, 2017, 3:45:22 AM8/29/17
to php...@googlegroups.com

+1

Sounds good.

Zitat von Larry Garfield <la...@garfieldtech.com>:


Jan Schneider
The Horde Project
https://www.horde.org/

Sara Golemon

unread,
Sep 3, 2017, 8:13:45 PM9/3/17
to PHP Framework Interoperability Group
On Friday, August 25, 2017 at 4:10:06 AM UTC-4, Barry vd. Heuvel wrote:
So given the unanimous support for the HTTP Client and the current implementations, the only actual point of discussion is the throwing of Exceptions on 4xx/5xx responses, right?

As a library maintainer, I'd like to rely on the behavior of the Client, so either ALWAYS or NEVER, not depending om some hidden config. Otherwise I'd have to check both cases again every time. 
And in that case my preference would be to NOT throw exceptions on 4xx/5xx errors.

Hope this will make it to a real PSR soon :) Good job!

Agreed 100% about having an ALWAYS/NEVER decision on exceptions.  I differ on my preference, but I also defer to those maintaining and using http clients on a regular basis.  Jason made the argument that the exception is on the remote end, but I take the view that the consumer was expecting a response and didn't get one. That's exceptional.  The client gets to decide how it handles that error whether it's a status code or an exception, so that half of the argument doesn't hold.  By the same token, that also means that as a consumer, I don't really care what the library does so long as it's consistent, so I come back to the start: Just define ALWAYS/NEVER and the rest of us will deal with it from there.

Fabien Bourigault

unread,
Sep 4, 2017, 2:23:44 AM9/4/17
to PHP Framework Interoperability Group
I agree that exceptions throwing has to be explicitly defined. Anybody would rely on an execution flow interruption that MAY happen. My preference is to NEVER throw exception on 4xx/5xx as those are not exceptional. Those are just valid HTTP responses like any other responses. I know some API that use 409 (Conflict) as a normal execution flow. As an example, the Gitlab API return this one when you try to add a user to a group he already belongs to.
As a conclusion, I think the only exception a client can throw are NetworkExceptions as anybody expect the network to fail in his application, so it's an exceptional case.

--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+u...@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages