A proposal for a HTTP Client

298 views
Skip to first unread message

Benjamin Eberlei

unread,
Mar 24, 2012, 12:06:55 PM3/24/12
to php-st...@googlegroups.com

See https://github.com/php-fig/fig-standards/pull/24

From the introduction:

Many libraries and applications require an HTTP client to talk to other servers. In general all these libraries either ship their own HTTP client library, or use low-level PHP functionality such as file_get_contents, ext/curl or the ext/socket. Depending on the use-cases there are very tricky implementation details to be handled such as proxies, SSL, authentication protocols and many more. Not every small library can afford to implement all the different details. However there are only a few http client libraries that are widely used between different projects, because of NIH or fears of vendor lock-in.

Motivation:

Doctrine has about 4 projects that need an HTTP client. Currently every projects implements them itself, which is annoying. We could abstract this into our "Common" library, however that would mean we would start being a "http client" vendor. Now personally, i don't care about http clients and would rather let others do this, however i also don't want to face vendor lock-in by deciding for any of the many http clients. In a perfect world (with this standard) Doctrine could ship one very simple http client using sockets, and if anyone needs something more awesome, he could just use any other provider of a PSR http client.

Questions:

1. Do the voting members think this is something to standardize?
2. API: I didn't include a Request object as i think its not really necessary. The proposal is expclicitly very simple.
3. How to proceed?
4. Feedback?

greetings,
Benjamin

Benjamin Eberlei

unread,
Mar 24, 2012, 12:15:33 PM3/24/12
to php-st...@googlegroups.com
Ah more questions of course:

* Did i forget something on the request?
* Did i forget something on the response?
* Should the http client be bound to a host? so that $url is not containing a host? Or should the client wiggle this itself?

Hari K T

unread,
Mar 24, 2012, 12:19:09 PM3/24/12
to php-st...@googlegroups.com
Hi Benjamin Eberlei, 

ie really a great proposal looking from my point of view . I would have loved if php itself had one in core :-) .

Hope all will support this in a single voice :-)

Thank you

--
You received this message because you are subscribed to the Google Groups "PHP Standards Working Group" group.
To post to this group, send email to php-st...@googlegroups.com.
To unsubscribe from this group, send email to php-standard...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/php-standards?hl=en.

Kris Wallsmith

unread,
Mar 24, 2012, 12:19:54 PM3/24/12
to php-st...@googlegroups.com
Thanks for proposing this, Benjamin. I think this is an excellent area for standardization. We are all in the business of HTTP, after all.

However, I think we should take a step back and start with interfaces for an HTTP request and an HTTP response. Once those are done, then we can move on to an HTTP client.

Kris (author of Buzz)
--

Jordi Boggiano

unread,
Mar 24, 2012, 12:27:51 PM3/24/12
to php-st...@googlegroups.com
On 24.03.2012 17:19, Kris Wallsmith wrote:
> However, I think we should take a step back and start with interfaces
> for an HTTP request and an HTTP response. Once those are done, then we
> can move on to an HTTP client.

I realize this is a long shot, but in my dream world the Symfony
HttpFoundation classes for request/response would be made standard.
Their use in the framework & then adoption by Drupal, PPI (?) and
perhaps others make them a great candidate.

For those that don't know it:

https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/Request.php
https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/Response.php

The use of public properties for some things make them hard to be turned
into interfaces, but anyone can rely on that component and just use
those classes. I guess Composer adoption isn't broad enough for people
to agree with me that this is ok though.

Cheers

--
Jordi Boggiano
@seldaek - http://nelm.io/jordi

Evert Pot

unread,
Mar 24, 2012, 12:35:16 PM3/24/12
to php-st...@googlegroups.com
On Mar 24, 2012, at 5:06 PM, Benjamin Eberlei wrote:

> See https://github.com/php-fig/fig-standards/pull/24
>
> From the introduction:
>
> Many libraries and applications require an HTTP client to talk to other servers. In general all these libraries either ship their own HTTP client library, or use low-level PHP functionality such as file_get_contents, ext/curl or the ext/socket. Depending on the use-cases there are very tricky implementation details to be handled such as proxies, SSL, authentication protocols and many more. Not every small library can afford to implement all the different details. However there are only a few http client libraries that are widely used between different projects, because of NIH or fears of vendor lock-in.
>
> Motivation:
>
> Doctrine has about 4 projects that need an HTTP client. Currently every projects implements them itself, which is annoying. We could abstract this into our "Common" library, however that would mean we would start being a "http client" vendor. Now personally, i don't care about http clients and would rather let others do this, however i also don't want to face vendor lock-in by deciding for any of the many http clients. In a perfect world (with this standard) Doctrine could ship one very simple http client using sockets, and if anyone needs something more awesome, he could just use any other provider of a PSR http client.
>
> Questions:
>
> 1. Do the voting members think this is something to standardize?

I personally feel that this group should focus on interface, not implementation.
There is definitely something amiss in the PHP world, where a lot of project have trouble depending on external components.

But introducing a new one, doesn't really solve that I feel. What we need is more people on board with composer.phar,
and have solid, minimal components that projects like doctrine feel comfortable with to add as a dependency.


I _do_ think there is a lot of value in a common request and response interface though. Not just for HTTP client, but
also to wrap $_SERVER, php://output, header(), etc..

If these 2 interfaces exist, it would also not be terribly hard to define a very simple interface for making requests.
After all, at that point all that's really required is a method that takes a Request object, and returns a Response object.

At that point all doctrine would have to do is to provide a way for users to inject the 'Client' interface.


I feel the end-result of your proposal is a nobel and useful one, but I have serious doubts this should be done through PSR.

Evert

Lukas Kahwe Smith

unread,
Mar 24, 2012, 12:44:54 PM3/24/12
to php-st...@googlegroups.com

On Mar 24, 2012, at 12:35 , Evert Pot wrote:

> I personally feel that this group should focus on interface, not implementation.
> There is definitely something amiss in the PHP world, where a lot of project have trouble depending on external components.


agreed .. we might elect one implementation as a reference implementation, but more important is that we propose interfaces, a guideline for how to interpret these interfaces and a test suite to check for compliance.

regards,
Lukas Kahwe Smith
m...@pooteeweet.org

Benjamin Eberlei

unread,
Mar 24, 2012, 1:26:17 PM3/24/12
to php-st...@googlegroups.com
Just realized i forgot cookies.

I am not sure if we need interfaces for request+response. These are value objects, and if simple and clean don't need interfaces IMHO.

The client needs.

thom...@gmail.com

unread,
Mar 24, 2012, 1:31:30 PM3/24/12
to php-st...@googlegroups.com
Expand
Sent from my BlackBerry® wireless device

From: Benjamin Eberlei <kon...@beberlei.de>
Date: Sat, 24 Mar 2012 18:26:17 +0100
Subject: Re: A proposal for a HTTP Client

Evert Pot

unread,
Mar 24, 2012, 1:59:20 PM3/24/12
to php-st...@googlegroups.com

On Mar 24, 2012, at 6:26 PM, Benjamin Eberlei wrote:

> Just realized i forgot cookies.

If you're taking this approach, you will find that you probably forgot a lot of HTTP features :)

If you want to keep things simple, I'd strongly suggest focusing on the following:

1. Request message
- http method
- url
- http version
- headers
- stream for body

2. Response message:
- http status code
- human-readable status
- http version
- headers
- stream for body

Keep in mind that multiple headers can be set with the same name, so a key-value list for
headers may not cover all the use cases.


Using these basic components, you can represent (as far as i know?) any http message.
Anything can be implemented with these components. Cookies would not be an issue, for instance.

Utility objects and classes could be used to aid constructing these headers and deliver them.

These guidelines as a basis, this would allow me:

1. To also wrap the request and response object in my current PHP process.
2. Create a custom client that's capable of asynchronous requests through libevent
3. Create a client that gives me multiple Response objects for every hop in a case of redirects, or a situation where HTTP/1.1 100 Continue is returned.

Your simple client may well be good enough for your use-cases, but in order for something like
this to be successful, it'd be important to create something that doesn't require people to
use other tools as soon as they need anything more complex.

Evert

Lukas Kahwe Smith

unread,
Mar 24, 2012, 2:01:48 PM3/24/12
to php-st...@googlegroups.com

On Mar 24, 2012, at 13:59 , Evert Pot wrote:

> Your simple client may well be good enough for your use-cases, but in order for something like
> this to be successful, it'd be important to create something that doesn't require people to
> use other tools as soon as they need anything more complex.


why?

how is not covering the more complex cases going to be a reason to not adopt this for the simple cases?

Evert Pot

unread,
Mar 24, 2012, 2:10:38 PM3/24/12
to php-st...@googlegroups.com

As a user needing a simple HTTP client, I agree.. no reason to use something more complex if you don't need to.
If you are a provider of an HTTP client (such as Symfony), I'm sure you would agree that you wouldn't want to provide an incomplete client.

Evert

Benjamin Eberlei

unread,
Mar 24, 2012, 2:11:32 PM3/24/12
to php-st...@googlegroups.com
hm, maybe it makes more sense to focus on pecl_http getting a sane OOP api before it goes into PHP core and until then live with the HTTP client dilemma we have in userland?

Lukas Kahwe Smith

unread,
Mar 24, 2012, 2:18:20 PM3/24/12
to php-st...@googlegroups.com


incomplete client? implementing in interface does not preclude you from adding other methods. so i dont see you issue at all.
and there can be other higher level standards that build on this simple interface.

Evert Pot

unread,
Mar 24, 2012, 2:25:29 PM3/24/12
to php-st...@googlegroups.com

I'm referring to the proposal on github. My point was that it's too simple, and best to first focus on just the Request and Response messages before attempting to define the client.
The current proposal does not have a Request interface, but just a simple set of arguments for making requests.

Was there a miscommunication?

Evert

Lukas Kahwe Smith

unread,
Mar 24, 2012, 3:13:01 PM3/24/12
to php-st...@googlegroups.com


Apparently so, since i understood you are saying that people wouldnt implement this interface, because its too simple.
Now as for Request/Response, I agree it would be great to also standardize these, but its going to be much harder and the discussion can run in parallel.

As for Benjamin's comment about pecl http: I totally agree that its a good idea to bring Michael into this discussion (actually one of the first things I did after the PR was opened was to ping him on IRC). That being said, we are discussing interfaces here and only implementations in so far as to ensure that the interfaces make sense once they are implemented. Furthermore if possible it is of course also nice to ensure compatibility with existing libs, but as there are already so many, the risk is high that it will just alienate the authors of the given libs if we pick a "favorite".

Michael Dowling

unread,
Mar 25, 2012, 11:57:36 AM3/25/12
to php-st...@googlegroups.com
As the creator of Guzzle (https://github.com/guzzle/guzzle), I'm very interested in this proposal.  I've added several comments to the PR, which I'd be happy to discuss here or on github.  Quick rundown of my comments:
  • Why return an array for headers that have only one value?  Why not return an array only when there are multiple headers of the same name (this is basically what Guzzle does)?
  • HttpException should probably be an interface so that it's easier to plug into existing projects.
  • I'd like to see $headers move before $content in the arguments of HttpClient::request()
  • Should $content in HttpClient::request() accept an array or string?  An array would allow you to easily send form style POST requests.  I'm fine with just expecting a string to be passed to this method, but I want to see what others think.
  • I think that the $options array needs to have a standardized array of options or should be removed from HttpClient::request().  Allowing HTTP client vendors to specify their own custom options would still cause vendor lock-in or abstraction code to be present in consumers of these clients.
  • As I think most clients will offer adapters instead of changing their interfaces, would it make sense to add a method to the HttpClient that would allow access to the more advanced adapted HTTP client (e.g. getAdaptedClient())?  I don't think it's a good idea to change the interfaces of Guzzle to support a much simpler HTTP client, so I'm probably going to offer an adapter.
The impetus for this proposal stems from the fact that several Doctrine projects need an HTTP client.  With the currently proposed interfaces and lack of a Request interface, you will not be able to send HTTP requests in parallel, which is something I'm thinking Doctrine would benefit from greatly.  For example, when working with unit of work pattern, you often need to do several actions when flush is called (delete things, update things, add things).  Without the use of parallel requests, you'll need to do all of these requests serially.  The way that this is handled in Guzzle is by using a client object as a sort of builder that returns requests.  These requests can then be sent one at a time or in parallel by calling the ``send`` method of the client and passing an array of requests (http://guzzlephp.org/tour/http.html#send-http-requests-in-parallel).


hm, maybe it makes more sense to focus on pecl_http getting a sane OOP api before it goes into PHP core and until then live with the HTTP client dilemma we have in userland?

While that would be awesome, I don't think it's feasible to wait on pecl_http to be updated or put into PHP's core for you or others to release new libraries.  It didn't make it into PHP 5.4, so you'd require your users to install the pecl extension or wait on PHP next.

I think this proposal could work as long as we're realistic-- provide only an interface and not an implementation, limit the scope as much as possible, and expect existing libraries to provide adapters if the proposed PSR interface differs from the original client's interface.

Thanks,
Michael


On Saturday, March 24, 2012 1:11:32 PM UTC-5, Benjamin Eberlei wrote:
hm, maybe it makes more sense to focus on pecl_http getting a sane OOP api before it goes into PHP core and until then live with the HTTP client dilemma we have in userland?

On Sat, Mar 24, 2012 at 7:01 PM, Lukas Kahwe Smith <m...@pooteeweet.org> wrote:

On Mar 24, 2012, at 13:59 , Evert Pot wrote:

> Your simple client may well be good enough for your use-cases, but in order for something like
> this to be successful, it'd be important to create something that doesn't require people to
> use other tools as soon as they need anything more complex.


why?

how is not covering the more complex cases going to be a reason to not adopt this for the simple cases?

regards,
Lukas Kahwe Smith
m...@pooteeweet.org



--
You received this message because you are subscribed to the Google Groups "PHP Standards Working Group" group.
To post to this group, send email to php-st...@googlegroups.com​.
To unsubscribe from this group, send email to php-standards+unsubscribe@​googlegroups.com.

Benjamin Eberlei

unread,
Mar 25, 2012, 12:22:01 PM3/25/12
to php-st...@googlegroups.com
i disagree on pecl/http. If it ships with a sane API that does not require wrapping, then developing a PHP clone of this NOW is much better task for all our limited time than trying to find a standard to wrap pecl/http into.

I agree on your points though, the API was a long shot and i already want to remove request in its own object, that way we dont have to argue about order in the request() method.

To view this discussion on the web visit https://groups.google.com/d/msg/php-standards/-/uTPxNJwxz0QJ.

To post to this group, send email to php-st...@googlegroups.com.
To unsubscribe from this group, send email to php-standard...@googlegroups.com.

Michael Dowling

unread,
Mar 25, 2012, 12:37:28 PM3/25/12
to php-st...@googlegroups.com
I think it would be great for pecl/http to be updated, and I'd consider using it in Guzzle instead of curl (as long as it has the same callback support that curl provides).  However, providing a new interface for pecl/http and implementing a PHP only version is just reinventing the wheel on top of many existing wheels (Guzzle, Buzz, Zend HTTP client, PEAR, etc).

I like the concept of introducing a simple set of interfaces for libraries that need a simple HTTP client.  I don't think the PSR group getting into the business of providing implementations is a good idea though (maybe I misunderstood though).

-Michael

Paul M Jones

unread,
Mar 25, 2012, 4:03:35 PM3/25/12
to php-st...@googlegroups.com
Hi all,

To suggest a course of action that appears to have been fruitful with PSR-1: Compile a list of known HTTP request/response/client libraries, examine them for commonalities, publish the results, and go from there.

Guzzle, Buzz, ZF, Solar (and soon Aura), Symfony, Lithium, Cake, PECL HTTP, etc. all have HTTP request/response classes. They'd be the likely targets for research.

That way at least we have an idea of what people are *actually* doing, and not be limited to our own ideas of what we *think* people *ought* to be doing.


--

Paul M. Jones
http://paul-m-jones.com/

Evert Pot

unread,
Mar 25, 2012, 4:12:37 PM3/25/12
to php-st...@googlegroups.com

If we feel it helps, I can ask someone from the httpbis group to provide input too.
We should be a bit further ourselves though :)

Evert

Alexandre Gaigalas

unread,
Mar 25, 2012, 8:25:01 PM3/25/12
to php-st...@googlegroups.com
We should also look how the PHP native HTTP stream wrapper works. After researching a lot of clients I've settled on stream_get_contents() for the body + stream_get_metadata() for the response headers and stream_context_create() for the request declaration, even though they don't have an OOP interface.

ref: http://www.php.net/manual/en/context.http.php

A thin OOP layer based on these functions would make more sense to me than any other. A reference implementation would also be very easy to set up without relying on any specific vendors.

If we keep up this proposal, I believe we should also add to the compiled list of clients how they handle HTTP-specific workflows. Not all clients nowadays are able to handle transfer encodings, protocol upgrades, cookie persistence, file uploads, keep alives and many other core HTTP features.

Maybe the clean request() method should even be clear about *not* treat all those workflows, to keep things uniform without having to implement a large subset of the spec. If we consider another RFCs besides 2616 there is a *lot* of things to specify. Subsequent revisions of this possible PSR should address more complex workflows as clients adopt the basic interface.

From the spec point of view, there is no valid Request without a Response and vice-versa. Browser-to-server implementations tend to separate these concepts mainly because of their "phisical" separation, but as an HTTP client self-contained this seems to be unecessary. The Request/Response pair is defined by the spec as an HTTP Message, and makes a lot of sense to me that a client could need only a Message interface to be clean and consistent.

--
You received this message because you are subscribed to the Google Groups "PHP Standards Working Group" group.
To post to this group, send email to php-st...@googlegroups.com.
To unsubscribe from this group, send email to php-standard...@googlegroups.com.

Alexandre Gaigalas

unread,
Mar 25, 2012, 8:27:43 PM3/25/12
to php-st...@googlegroups.com
Oops. Small correction: The spec defines an HTTP Message as an abstract format for Request and Response formats. They differ mainly on the request/status line and the related workflows.

Suissa

unread,
Mar 26, 2012, 10:56:02 PM3/26/12
to php-st...@googlegroups.com
I agree.

2012/3/25 Alexandre Gaigalas <alex...@gaigalas.net>



--
Jean C. Nascimento aka Suissa
WebDeveloper Sênior - SP



Beau Simensen

unread,
Mar 27, 2012, 12:06:32 AM3/27/12
to php-st...@googlegroups.com
2. API: I didn't include a Request object as i think its not really necessary. The proposal is expclicitly very simple.

Could we define standard HTTP Request and Response interfaces (and an HTTP Client that is just a pass-thru for Request -> Response) in one PSR and define a Simple Client that leverages the HTTP Response from the other PSR?


/* PSR-HTTP-Client */

use Whizbang\Client as PsrClient; // implements PsrClientInterface
use Whizbang\Request as PsrRequest; // implements PsrRequestInterface

$psrClient = new PsrClient;
$psrRequest = new PsrRequest();
(PsrResponseInterface) $psrResponse = $psrClient->makeRequest($psrRequest);


/* PSR-HTTP-SimpleClient */

use Whizbang\SimpleClient as PsrSimpleClient; // implements PsrSimpleClientInterface

$psrSimpleClient = new PsrSimpleClient;
(PsrResponseInterface) $psrReponse = $psrSimpleClient->makeSimpleRequest(/* specialized set of simple arguments */);


If so, vendors would be able to opt supporting either PSR-HTTP-Client or PSR-HTTP-SimpleClient (or both). Users would be able to decide which route they would need to go down for their particular application.

Beau Simensen

unread,
Mar 27, 2012, 12:22:58 AM3/27/12
to php-st...@googlegroups.com
Should the http client be bound to a host? so that $url is not containing a host? Or should the client wiggle this itself?

I think this would only be useful under very specific circumstances. In general I don't want to have to worry about the URL I am accessing being the same host as the last URL I accessed.

Being able to support this might be a good case for having a Request interface, though? It would provide flexibility to handle this kind of thing in the application's logic as opposed to offloading it to the HTTP Client magic.

    $apiRequestFactory = new MyApiRequestFactory('http://example.com/api/baseUrl');
    $psrResponse = $psrClient->makeRequest($apiRequestFactory->createPost('category/programming/tag/php'));
    $psrResponse = $psrClient->makeRequest($apiRequestFactory->createGet('products'));
    $psrResponse = $psrClient->makeRequest(new Request('GET', $product->getFavoritesUrl()));


It would be pretty trivial to provide a set of custom Request types and Request factories (GetRequest($url,...) , PostRequest($url,...), RestResourceRequestFactory($baseUrl)) as references. I'm not sure if they should be a part of the PSR, but it might not be a terrible idea.

Beau Simensen

unread,
Jun 4, 2012, 12:30:04 PM6/4/12
to php-st...@googlegroups.com
Does anyone else think this might be a good approach? If so I'd like to try and get the ball moving on this one again.

Alexandre Gaigalas

unread,
Jun 4, 2012, 12:44:15 PM6/4/12
to php-st...@googlegroups.com
I like it. Let me know if I get things right:

The final product of this recommendation would be a set of interfaces that compliant HTTP clients written in PHP must conform to. Some of them are optional based on the features that the client provides, so we can also use these interfaces to check if a specific client is compliant to something.

Right so far? Where do we go from here?

--
You received this message because you are subscribed to the Google Groups "PHP Standards Working Group" group.
To view this discussion on the web visit https://groups.google.com/d/msg/php-standards/-/CWSymx3hW2wJ.

Norv N.

unread,
Jun 7, 2012, 10:08:08 AM6/7/12
to php-st...@googlegroups.com
I think it's a good approach. Request and Response interfaces are expected and necessary. FWIW, I rather see a good standard proposal as one that covers first the essential interfaces of the domain.

The HTTP client interface can be defined in parallel or after these are established.

Dowling, Michael

unread,
Jun 7, 2012, 1:10:39 PM6/7/12
to php-st...@googlegroups.com
Assuming this proposal comes to fruition, what's the expectation on existing HTTP clients?  Are users expecting breaking changes, adapters, or something else?

-Michael

From: "Norv N." <no...@simplemachines.org>
Reply-To: <php-st...@googlegroups.com>
Date: Thu, 7 Jun 2012 07:08:08 -0700 (PDT)
To: <php-st...@googlegroups.com>
Subject: Re: A proposal for a HTTP Client

--
You received this message because you are subscribed to the Google Groups "PHP Standards Working Group" group.
To view this discussion on the web visit https://groups.google.com/d/msg/php-standards/-/W4twfs1ovXIJ.

Beau Simensen

unread,
Jun 7, 2012, 1:31:22 PM6/7/12
to php-st...@googlegroups.com
On Thursday, June 7, 2012 12:10:39 PM UTC-5, Michael Dowling wrote:
Assuming this proposal comes to fruition, what's the expectation on existing HTTP clients?  Are users expecting breaking changes, adapters, or something else?

I think that my expectation is that existing HTTP clients would not need to be changed. That is my expectation for the Cache proposals as well. New implementations would be welcome to implement the proposals directly but existing vendors can provide support for the interfaces by whatever means necessary. If that means BC breaks for their users, that is up to them.

People create different implementations for different reasons and people choose those implementations based on the differences. Cache and HTTP clients can be very specialized and a user can choose one that meets their specialized needs if they have specialized needs. I think it is important to not get in the way of the vendors in this case.

On the other hand, applications or libraries that just needs "a cache" or "an HTTP client" may not care about the details or specifics and can be written instead to accept a PSR cache or HTTP client implementation. A framework can be configured to use Buzz or Guzzle or XYZ as its "PSR HTTP client" and generic libraries and objects can consume it by way of dependency injection. Specialized domain objects can instead accept the actual implementation by way of dependency injection where access to functionality that isn't available by the PSR HTTP client is required.

Kris Wallsmith

unread,
Jun 7, 2012, 1:54:47 PM6/7/12
to php-st...@googlegroups.com
I do not agree with the Client/SimpleClient distinction. I've designed Buzz so the only thing a client needs to worry about is executing an HTTP request and populating the response. Since you pass the response object to the client, it isn't even expected to create that object.

interface ClientInterface
{
    function send(RequestInterface $request, MessageInterface $response);
}

If you want an easier interface you can wrap one around a client. In Buzz this is called a browser and has nice methods like…

public function get($url, $headers = array())
public function post($url, $headers = array(), $content = '')
public function call($url, $method, $headers = array(), $content = '')
public function submit($url, array $fields, $method = RequestInterface::METHOD_POST, $headers = array())
public function send(RequestInterface $request, MessageInterface $response = null)

It is important that these are two separate concepts.

However, I still think we should focus our current efforts on defining request and response interfaces. I've included Buzz's here for discussion…

interface MessageInterface
{
    function getHeader($name, $glue = "\r\n");
    function getHeaders();
    function setHeaders(array $headers);
    function addHeader($header);
    function getContent();
    function setContent($content);
}

interface RequestInterface extends MessageInterface
{
    const METHOD_OPTIONS = 'OPTIONS';
    const METHOD_GET     = 'GET';
    const METHOD_HEAD    = 'HEAD';
    const METHOD_POST    = 'POST';
    const METHOD_PUT     = 'PUT';
    const METHOD_DELETE  = 'DELETE';
    const METHOD_PATCH   = 'PATCH';

    function getMethod();
    function setMethod($method);
    function getResource();
    function setResource($resource);
    function getProtocolVersion();
    function getHost();
    function setHost($host);
    function isSecure();
}

Thanks,
Kris

Beau Simensen

unread,
Jun 7, 2012, 3:43:29 PM6/7/12
to php-st...@googlegroups.com
On Thursday, June 7, 2012 12:54:47 PM UTC-5, Kris Wallsmith wrote:
interface ClientInterface
{
    function send(RequestInterface $request, MessageInterface $response);
}

What are the benefits to passing in a $response vs having the object create it?


I do not agree with the Client/SimpleClient distinction.

What part do you disagree with? That there would be a SimpleClient at all?

Here is what I currently have in mind. I think that Client would be similar to Buzz Client interface and SimpleClient would be similar to Buzz Browser. Maybe it is the naming that is a bit off?


PSR-HTTP-Client:

namespace Fig\Psr\HttpClient;

interface RequestInterface
{
    /* TBD */
}

interface ResponseInterface
{
    /* TBD */
}

interface ClientInterface
{
    /**
* @return ResponseInterface
*/
    public function send(RequestInterface $request);
}


PSR-HTTP-SimpleClient (which would attempt to address Benjamin's "very simple HTTP client" use case leveraging the ResponseInterface from PSR-HTTP-Client):

namespace Fig\Psr\SimpleHttpClient;

interface SimpleClientInterface
{
    /**
* @return \Fig\Psr\HttpClient\ResponseInterface
*/
    public function request($method, $url, array $headers = array(), $content = null, array $options = array());
}


Lots of options here (should they actually be in separate PSR from each other? do they need to be in separate classes or can send and request live together? should we have request at all?), but I see these as the building blocks to try and flesh out.

Dowling, Michael

unread,
Jun 7, 2012, 3:44:19 PM6/7/12
to php-st...@googlegroups.com
I think we should work to create fairly generic interfaces that make no assumptions about the implementation details of an HTTP client vendor.  I think we should strive to ensure that a PSR HTTP client consumer knows that when it is dealing with a client, request, or response object that the object's dependencies are already associated with the object.  The long and short: a request represents a request, a response a response, and a client sends the requests and creates a response.  A consumer shouldn't have to care how any of this is done.

I don't see the need for a distinction between browsers and clients when we're talking about a PSR HTTP client.  I agree with what Beau said earlier: these HTTP client interfaces should be used to support dependency injection so that other libraries can utilize them in a generic way.  I don't think that a PSR should concern itself with the way in which a client is built or instantiated, a request is instantiated, what request or response factories it uses, or the specific way in which a client transfers data over the wire. I would rather not separate the transport layer from the client (aka browser in Kris's example).

If a client interface contained a createRequest() method or something similar, then the way in which a request is created could be irrelevant to a PSR consumer.  The underlying implementation is free to implement the the "createRequest()" method however deemed necessary -- implement factories, builders, or whatever.  However, these implementation details should not creep into a PSR.

Some parts of the interfaces Kris posted are pretty similar to Guzzle, so I think we've got a good spring board here for discussion.  (note: Guzzle's interfaces are a bit larger, but most of the differences are library requirement details specific to Guzzle).  Speaking purely in the context of a PSR proposal, here's some feedback that I think would would mold these interfaces into a more generic PSR:
  1. I would not want to include "setMethod($method)" in a PSR request interface.  This makes implementation assumptions and is something that could be handled in a createRequest() method of a client.  Guzzle uses a RequestInterface for GET, HEAD, DELETE, etc; and an EntityEnclosingRequestInterface for PUT, POST, PATCH, etc.
  2. Just brainstorming, but could the "isSecure()" method be replaced by a more generic "getScheme()" method that returned "http", "https", etc?
  3. I think a PSR client should deal with headers using keys and values rather than passing the full HTTP header line.  So getHeader($header), setHeader($header, $value), addHeader($header, $value), getHeaders(), setHeaders($headers),  etc.
  4. I think we should be clear that headers should be specific to HTTP headers and not affect the start line of an HTTP message (examples of start lines are "GET / HTTP/1.1" for requests and "HTTP/1.1 200 OK" for responses).  Allowing headers to change things like the request method is an implementation assumption that a HTTP vendor uses the same request classes for each type of method.
  5. Guzzle uses "addHeader($header, $value)" to add a header to a message.  If a header already exists, then the new value is appended to the existing header. If the header does not exist, then the value is added.  "setHeader($key, $value)" is used to add a header to a message and replace any existing headers by that name. Everything is handled case-insensitively.
  6. I think there should be a proper response interface that has methods like getStatusCode() and getReasonPhrase().  Both Guzzle and Buzz use these same method names.
  7. Can/should a PSR interface account for the ability to send HTTP requests in parallel?
-Michael

From: Beau Simensen <sime...@gmail.com>
Reply-To: <php-st...@googlegroups.com>
Date: Thu, 7 Jun 2012 12:43:29 -0700 (PDT)
To: <php-st...@googlegroups.com>
Subject: Re: A proposal for a HTTP Client

--
You received this message because you are subscribed to the Google Groups "PHP Standards Working Group" group.
To view this discussion on the web visit https://groups.google.com/d/msg/php-standards/-/1y22NG25EVgJ.

Paul M. Jones

unread,
Jun 7, 2012, 4:03:03 PM6/7/12
to php-st...@googlegroups.com

On Jun 7, 2012, at 2:43 PM, Beau Simensen wrote:

> On Thursday, June 7, 2012 12:54:47 PM UTC-5, Kris Wallsmith wrote:
>> interface ClientInterface
>> {
>> function send(RequestInterface $request, MessageInterface $response);
>> }
>>
> What are the benefits to passing in a $response vs having the object create it?

The benefits are subtle, sophisticated, and not immediately obvious. For one, you can pass in a mock response object at testing time. (While this is not strictly "dependency injection," it is similar.)



-- pmj

Dowling, Michael

unread,
Jun 7, 2012, 4:08:13 PM6/7/12
to php-st...@googlegroups.com
We would be providing an interface. Couldn't someone just implement a
mock client that returns a mock response?

This seems like an implementation detail to me. I think our PSR's should
be a black box.

-Michael
>--
>You received this message because you are subscribed to the Google Groups
>"PHP Standards Working Group" group.

Devon H. O'Dell

unread,
Jun 7, 2012, 4:11:43 PM6/7/12
to php-st...@googlegroups.com
Has anybody taken a look at the Go HTTP client interface[1]? I've used
it with much success and it has been a pleasure to work with. I think
modeling an interface after net/http would work well; I think it's at
least worth considering adopting ideas from an existing, successful
interface instead of trying to invent something entirely from the
ground up.

--dho

[1]: http://golang.org/pkg/net/http/#Client

2012/6/7 Dowling, Michael <mtdo...@gmail.com>:

Kris Wallsmith

unread,
Jun 7, 2012, 5:08:33 PM6/7/12
to php-st...@googlegroups.com
Hi Michael,

Thank you for your response. I look forward to discussing this with you.

On Thursday, June 7, 2012 12:44:19 PM UTC-7, Michael Dowling wrote:
I think we should work to create fairly generic interfaces that make no assumptions about the implementation details of an HTTP client vendor.  I think we should strive to ensure that a PSR HTTP client consumer knows that when it is dealing with a client, request, or response object that the object's dependencies are already associated with the object.  The long and short: a request represents a request, a response a response, and a client sends the requests and creates a response.  A consumer shouldn't have to care how any of this is done.

I agree with you 99% here except for the "client […] creates a response" bit.

I don't see the need for a distinction between browsers and clients when we're talking about a PSR HTTP client.  I agree with what Beau said earlier: these HTTP client interfaces should be used to support dependency injection so that other libraries can utilize them in a generic way.  I don't think that a PSR should concern itself with the way in which a client is built or instantiated, a request is instantiated, what request or response factories it uses, or the specific way in which a client transfers data over the wire. I would rather not separate the transport layer from the client (aka browser in Kris's example).

Why would you "rather not separate the transport layer from the client"? Buzz's client classes have a very specific responsibility: executing a request and populating a response. The client gets involved just-in-time and finishes as-soon-as-possible. The methods in Buzz's browser class are just sugar on top of an injected client.

If a client interface contained a createRequest() method or something similar, then the way in which a request is created could be irrelevant to a PSR consumer.  The underlying implementation is free to implement the the "createRequest()" method however deemed necessary -- implement factories, builders, or whatever.  However, these implementation details should not creep into a PSR.

I do not think the PSR should concern itself with how message objects are created.

Some parts of the interfaces Kris posted are pretty similar to Guzzle, so I think we've got a good spring board here for discussion.  (note: Guzzle's interfaces are a bit larger, but most of the differences are library requirement details specific to Guzzle).  Speaking purely in the context of a PSR proposal, here's some feedback that I think would would mold these interfaces into a more generic PSR:
  1. I would not want to include "setMethod($method)" in a PSR request interface.  This makes implementation assumptions and is something that could be handled in a createRequest() method of a client.  Guzzle uses a RequestInterface for GET, HEAD, DELETE, etc; and an EntityEnclosingRequestInterface for PUT, POST, PATCH, etc.
If setMethod() is not included, how do you set the method?
  1. Just brainstorming, but could the "isSecure()" method be replaced by a more generic "getScheme()" method that returned "http", "https", etc?
I've included this method in the Buzz interface so a cookie jar can determine whether to apply secure cookies to a particular request. 
  1. I think a PSR client should deal with headers using keys and values rather than passing the full HTTP header line.  So getHeader($header), setHeader($header, $value), addHeader($header, $value), getHeaders(), setHeaders($headers),  etc.
This is the main issue to be resolved before proposing request and response interfaces. I've designed Buzz to store header lines in a list in order to maintain the true order of headers. Maybe this is not important?
  1. I think we should be clear that headers should be specific to HTTP headers and not affect the start line of an HTTP message (examples of start lines are "GET / HTTP/1.1" for requests and "HTTP/1.1 200 OK" for responses).  Allowing headers to change things like the request method is an implementation assumption that a HTTP vendor uses the same request classes for each type of method.
That's a good point. I see now that the HTTP spec distinguishes between the request line and headers.

Thanks! 
Kris
To unsubscribe from this group, send email to php-standards+unsubscribe@googlegroups.com.

Kris Wallsmith

unread,
Jun 7, 2012, 5:15:00 PM6/7/12
to php-st...@googlegroups.com
Hi Beau,

Responses inline…


On Thursday, June 7, 2012 12:43:29 PM UTC-7, Beau Simensen wrote:
On Thursday, June 7, 2012 12:54:47 PM UTC-5, Kris Wallsmith wrote:
interface ClientInterface
{
    function send(RequestInterface $request, MessageInterface $response);
}

What are the benefits to passing in a $response vs having the object create it?

 
If you have many clients (i.e. cURL, stream, xyz…) you would be duplicating code to create a new response object in each one. You could abstract this out into a factory, but then you would be injecting the factory into each client. It's simpler to just expect the response object to be created upstream somewhere and only expect the client to populate it.


I do not agree with the Client/SimpleClient distinction.

What part do you disagree with? That there would be a SimpleClient at all?

Here is what I currently have in mind. I think that Client would be similar to Buzz Client interface and SimpleClient would be similar to Buzz Browser. Maybe it is the naming that is a bit off?


PSR-HTTP-Client:

namespace Fig\Psr\HttpClient;

interface RequestInterface
{
    /* TBD */
}

interface ResponseInterface
{
    /* TBD */
}

interface ClientInterface
{
    /**
* @return ResponseInterface
*/
    public function send(RequestInterface $request);
}


PSR-HTTP-SimpleClient (which would attempt to address Benjamin's "very simple HTTP client" use case leveraging the ResponseInterface from PSR-HTTP-Client):

namespace Fig\Psr\SimpleHttpClient;

interface SimpleClientInterface
{
    /**
* @return \Fig\Psr\HttpClient\ResponseInterface
*/
    public function request($method, $url, array $headers = array(), $content = null, array $options = array());
}


Lots of options here (should they actually be in separate PSR from each other? do they need to be in separate classes or can send and request live together? should we have request at all?), but I see these as the building blocks to try and flesh out.

Having two client interfaces means a vendor needs to implement them both to be compatible? Providing two interfaces that accomplish the same functionality is unnecessarily confusing.

Thanks,
Kris

Dowling, Michael

unread,
Jun 7, 2012, 5:53:49 PM6/7/12
to php-st...@googlegroups.com
Hi Kris,

> I agree with you 99% here except for the "client […] creates a response" bit.

The reason I say this is because I think a PSR client consumer should not be concerned with how a request or response is created. So, how can we achieve that?  We could go nuts and write up interfaces for request factories, but I think that's overkill.  We can't and should not specify the constructors of the request or response objects either.  An alternative would be to have a method on a client used to create and return a request.  Further, the client could execute the request and return a response interface object when a request is sent.

To address your concerns of single responsibility, when implementing the PSR interface, your client object could  internally delegate to a factory used for creating requests and responses.  All of these implementation details would be internal to your implementation and should not concern the consumer of the PSR client.

> Why would you "rather not separate the transport layer from the client"? Buzz's client classes have a very specific responsibility: executing a request and populating a response. The client gets involved just-in-time and finishes as-soon-as-possible. The methods in Buzz's browser class are just sugar on top of an injected client.

I think that works great for Buzz, but the transport of a client is an implementation detail of the client.  Why should we put implementation details into a PSR interface?  A PSR client consumer should not have to care at all how a request is being transferred over the wire, much like they shouldn't have to care if they're using Guzzle, Buzz, ZF, etc.  I think that's the whole point of this PSR.

So, to clarify and use Buzz as an example:

Someone makes a library that requires a PSR client and expects a client to be injected somewhere in their library.  Developer X wants to use Buzz, and Buzz supports different transports.  They would build the Buzz client using whatever transport they want, inject the client into the browser, then pass the browser or some other object implementing the PSR interface into the library that requires the client.  The client doesn't need to know anything other than the capabilities of the client, request, and responses.

> I do not think the PSR should concern itself with how message objects are created.

Agreed.

> If setMethod() is not included, how do you set the method?

In the constructor of a request object.  If we add something like a ``createRequest($method, …blah)`` method, then a PSR client consumer can create requests as needed.  Providing a way to create new request objects in this manner also allows for PSR specifications on sending requests in parallel (i.e. you grab 10 requests objects the send them at the same time using the client).

> I've included this method in the Buzz interface so a cookie jar can determine whether to apply secure cookies to a particular request.

Gotcha.  In the context of a PSR, could someone just say ``if ($request->getScheme() == 'https')``?  I'm fine with isSecure(), but a ``getScheme()`` method is still a good idea I think.  Simply sending request to port 443 does not automatically mean you'll use SSL encryption, whereas the scheme can tell you if the request should be sent using SSL.  It also helps creating URLs from a request object.

> I've designed Buzz to store header lines in a list in order to maintain the true order of headers. Maybe this is not important?

I have not seen a case where the order of HTTP headers is important, but there might be edge cases.  If this was truly a requirement, I think it could be implemented in other ways, so I think it's an implementation detail.

The only requirement I've seen is preserving the exact casing of headers.  That sucks because HTTP says headers should be case insensitive, but unfortunately, some servers require headers in a specific case.  I was able to handle this in Guzzle correctly, and it looks like what you've done would work for that as well.

Thanks,
Michael

To view this discussion on the web visit https://groups.google.com/d/msg/php-standards/-/Vhl2ZqaqNJIJ.

To post to this group, send email to php-st...@googlegroups.com.
To unsubscribe from this group, send email to php-standard...@googlegroups.com.

Beau Simensen

unread,
Jun 7, 2012, 6:12:59 PM6/7/12
to php-st...@googlegroups.com
Hi Kris,

Having two client interfaces means a vendor needs to implement them both to be compatible? Providing two interfaces that accomplish the same functionality is unnecessarily confusing.

That is why I first suggested the idea that there be two PSR's. By having both, a vendor could choose to be compatibile either with PSR-HTTP-Client, PSR-HTTP-SimpleClient, or both. Similar to PSR-1 and PSR-2...

The interfaces do accomplish the same functionality but in very different ways. I think that the focus should be on PSR-HTTP-Client and once that is squared away we can decide on trying to push something like Benjamin's initial proposal based on PSR-HTTP-Client's Response (what I refer to as PSR-HTTP-SimpleClient). It is possible that proposal will never get approval but I want to make sure it isn't ruled out at this point.

If it makes things easier to discuss I can stop talking about that as an option for the time being. :)



Kris Wallsmith

unread,
Jun 7, 2012, 6:39:36 PM6/7/12
to php-st...@googlegroups.com
Hi Michael!

On Thursday, June 7, 2012 2:53:49 PM UTC-7, Michael Dowling wrote:
Hi Kris,

> I agree with you 99% here except for the "client […] creates a response" bit.

The reason I say this is because I think a PSR client consumer should not be concerned with how a request or response is created.

Ah, so we disagree here. I think we should encourage consumers to create and work with request objects. The client convenience methods are nice, but they conceal too much.
 
So, how can we achieve that?  We could go nuts and write up interfaces for request factories, but I think that's overkill.  We can't and should not specify the constructors of the request or response objects either.  An alternative would be to have a method on a client used to create and return a request.  Further, the client could execute the request and return a response interface object when a request is sent.

To address your concerns of single responsibility, when implementing the PSR interface, your client object could  internally delegate to a factory used for creating requests and responses.  All of these implementation details would be internal to your implementation and should not concern the consumer of the PSR client.

I'm willing to go either way on this point. Does anyone else have an opinion?

> Why would you "rather not separate the transport layer from the client"? Buzz's client classes have a very specific responsibility: executing a request and populating a response. The client gets involved just-in-time and finishes as-soon-as-possible. The methods in Buzz's browser class are just sugar on top of an injected client.

I think that works great for Buzz, but the transport of a client is an implementation detail of the client.  Why should we put implementation details into a PSR interface?  A PSR client consumer should not have to care at all how a request is being transferred over the wire, much like they shouldn't have to care if they're using Guzzle, Buzz, ZF, etc.  I think that's the whole point of this PSR.

So, to clarify and use Buzz as an example:

Someone makes a library that requires a PSR client and expects a client to be injected somewhere in their library.  Developer X wants to use Buzz, and Buzz supports different transports.  They would build the Buzz client using whatever transport they want, inject the client into the browser, then pass the browser or some other object implementing the PSR interface into the library that requires the client.  The client doesn't need to know anything other than the capabilities of the client, request, and responses.

> I do not think the PSR should concern itself with how message objects are created.

Agreed.

> If setMethod() is not included, how do you set the method?

In the constructor of a request object.  If we add something like a ``createRequest($method, …blah)`` method, then a PSR client consumer can create requests as needed.  Providing a way to create new request objects in this manner also allows for PSR specifications on sending requests in parallel (i.e. you grab 10 requests objects the send them at the same time using the client).

> I've included this method in the Buzz interface so a cookie jar can determine whether to apply secure cookies to a particular request.

Gotcha.  In the context of a PSR, could someone just say ``if ($request->getScheme() == 'https')``?  I'm fine with isSecure(), but a ``getScheme()`` method is still a good idea I think.  Simply sending request to port 443 does not automatically mean you'll use SSL encryption, whereas the scheme can tell you if the request should be sent using SSL.  It also helps creating URLs from a request object.

The cookie specification does not mention a specific scheme, only that the client "secure means to contact the origin server."

> I've designed Buzz to store header lines in a list in order to maintain the true order of headers. Maybe this is not important?

I have not seen a case where the order of HTTP headers is important, but there might be edge cases.  If this was truly a requirement, I think it could be implemented in other ways, so I think it's an implementation detail.

I agree that this is an implementation detail. The question for this discussion is what method should be included in the message interface for working with headers. This is probably a topic that deserves its own thread.

Thanks,
Kris

The only requirement I've seen is preserving the exact casing of headers.  That sucks because HTTP says headers should be case insensitive, but unfortunately, some servers require headers in a specific case.  I was able to handle this in Guzzle correctly, and it looks like what you've done would work for that as well.

Thanks,
Michael


Date: Thu, 7 Jun 2012 14:08:33 -0700 (PDT)
To: <php-standards@googlegroups.com>
Subject: Re: A proposal for a HTTP Client
Hi Michael,

Thank you for your response. I look forward to discussing this with you.

On Thursday, June 7, 2012 12:44:19 PM UTC-7, Michael Dowling wrote:
I think we should work to create fairly generic interfaces that make no assumptions about the implementation details of an HTTP client vendor.  I think we should strive to ensure that a PSR HTTP client consumer knows that when it is dealing with a client, request, or response object that the object's dependencies are already associated with the object.  The long and short: a request represents a request, a response a response, and a client sends the requests and creates a response.  A consumer shouldn't have to care how any of this is done.

I agree with you 99% here except for the "client […] creates a response" bit.

I don't see the need for a distinction between browsers and clients when we're talking about a PSR HTTP client.  I agree with what Beau said earlier: these HTTP client interfaces should be used to support dependency injection so that other libraries can utilize them in a generic way.  I don't think that a PSR should concern itself with the way in which a client is built or instantiated, a request is instantiated, what request or response factories it uses, or the specific way in which a client transfers data over the wire. I would rather not separate the transport layer from the client (aka browser in Kris's example).

Why would you "rather not separate the transport layer from the client"? Buzz's client classes have a very specific responsibility: executing a request and populating a response. The client gets involved just-in-time and finishes as-soon-as-possible. The methods in Buzz's browser class are just sugar on top of an injected client.

If a client interface contained a createRequest() method or something similar, then the way in which a request is created could be irrelevant to a PSR consumer.  The underlying implementation is free to implement the the "createRequest()" method however deemed necessary -- implement factories, builders, or whatever.  However, these implementation details should not creep into a PSR.

I do not think the PSR should concern itself with how message objects are created.

Some parts of the interfaces Kris posted are pretty similar to Guzzle, so I think we've got a good spring board here for discussion.  (note: Guzzle's interfaces are a bit larger, but most of the differences are library requirement details specific to Guzzle).  Speaking purely in the context of a PSR proposal, here's some feedback that I think would would mold these interfaces into a more generic PSR:
  1. I would not want to include "setMethod($method)" in a PSR request interface.  This makes implementation assumptions and is something that could be handled in a createRequest() method of a client.  Guzzle uses a RequestInterface for GET, HEAD, DELETE, etc; and an EntityEnclosingRequestInterface for PUT, POST, PATCH, etc.
If setMethod() is not included, how do you set the method?
  1. Just brainstorming, but could the "isSecure()" method be replaced by a more generic "getScheme()" method that returned "http", "https", etc?
I've included this method in the Buzz interface so a cookie jar can determine whether to apply secure cookies to a particular request. 
  1. I think a PSR client should deal with headers using keys and values rather than passing the full HTTP header line.  So getHeader($header), setHeader($header, $value), addHeader($header, $value), getHeaders(), setHeaders($headers),  etc.
This is the main issue to be resolved before proposing request and response interfaces. I've designed Buzz to store header lines in a list in order to maintain the true order of headers. Maybe this is not important?
  1. I think we should be clear that headers should be specific to HTTP headers and not affect the start line of an HTTP message (examples of start lines are "GET / HTTP/1.1" for requests and "HTTP/1.1 200 OK" for responses).  Allowing headers to change things like the request method is an implementation assumption that a HTTP vendor uses the same request classes for each type of method.
That's a good point. I see now that the HTTP spec distinguishes between the request line and headers.

Thanks! 
Kris
  1. Guzzle uses "addHeader($header, $value)" to add a header to a message.  If a header already exists, then the new value is appended to the existing header. If the header does not exist, then the value is added.  "setHeader($key, $value)" is used to add a header to a message and replace any existing headers by that name. Everything is handled case-insensitively.
  2. I think there should be a proper response interface that has methods like getStatusCode() and getReasonPhrase().  Both Guzzle and Buzz use these same method names.
  3. Can/should a PSR interface account for the ability to send HTTP requests in parallel?
-Michael

Dowling, Michael

unread,
Jun 7, 2012, 7:07:20 PM6/7/12
to php-st...@googlegroups.com
> I think we should encourage consumers to create and work with request objects. 

Agreed!  However, I don't want to dictate the constructors of anything in our interfaces.  So that means we need either a separate factory interface or you can treat the createRequest() method on a client as a factory method that can delegate the responsibility of creating a request however it makes sense for that particular vendor. The latter would be a pretty lightweight approach to the problem, whereas providing something like a RequestFactoryInterface would be a more imposing PSR proposal.

I'm open to suggestions on this from others as well.

> The client convenience methods are nice, but they conceal too much.

I don't see that as a bad thing.  I see that as the point of an interface, and I think we should strive to conceal implementation details as much as possible.

We should not be designing a framework with this PSR, and we need to use the interfaces of particular framework implementations with a grain of salt.  The interfaces used by specific vendors have their own separate goals that might not make sense for a PSR.  The goal of HTTP client PSR should be to provide simple interfaces so that we can easily connect components together.  That's why I'm advocating this methodology.

> The cookie specification does not mention a specific scheme, only that the client "secure means to contact the origin server."

To clarify, I wasn't talking about cookies, but rather having a getScheme() method on a request interface.  So in the interfaces you provided, isSecure() tells you whether or not the request is being sent using SSL, right?  Providing a getScheme() method provides that same information, but in a more generic way, and allows you to build a URL string from a request object.  Maybe there's room for both of these methods on a request interface?

Anyone else have opinions on this stuff?

-Michael

From: Kris Wallsmith <kris.wa...@gmail.com>
Reply-To: <php-st...@googlegroups.com>
To view this discussion on the web visit https://groups.google.com/d/msg/php-standards/-/djhcmxb7Uo0J.

To post to this group, send email to php-st...@googlegroups.com.
To unsubscribe from this group, send email to php-standard...@googlegroups.com.

Beau Simensen

unread,
Jun 7, 2012, 11:02:48 PM6/7/12
to php-st...@googlegroups.com
So, how can we achieve that?  We could go nuts and write up interfaces for request factories, but I think that's overkill.  We can't and should not specify the constructors of the request or response objects either.  An alternative would be to have a method on a client used to create and return a request.  Further, the client could execute the request and return a response interface object when a request is sent.

To address your concerns of single responsibility, when implementing the PSR interface, your client object could  internally delegate to a factory used for creating requests and responses.  All of these implementation details would be internal to your implementation and should not concern the consumer of the PSR client.

I'm willing to go either way on this point. Does anyone else have an opinion?

When I asked for the use cases for the caller creating a Response object I did not really get anything back other than it might help with testing since it could be mocked and injected. Are there any use cases where it makes sense to create the Response before the Request is made by the Client?

As it stands right now I think that the Response object should be created and returned by Client::send rather than be sent to Client::send() as a parameter. I could probably be convinced otherwise if there are solid use cases floating around out there.

The Request is a bit trickier. I can see why a Client::createRequest method might be desirable since it would standardize the way that Request instances are created.

I played around a bit with ClientInterface based on some of these discussions:

namespace Fig\Psr\HttpClient;

interface ClientInterface
{
    /**
* @return RequestInterface
*/
    public function createRequest($method, $url, array $headers = array(), $content = null);

    /**
* @return ResponseInterface
*/
    public function sendRequest(RequestInterface $request);

    /**
* @return ResponseInterface
*/
    public function request($method, $url, array $headers = array(), $content = null);
}


All three methods return a ResponseInterface instance. We can decide on changing sendRequest to accept ResponseInterface as a param instead.

The request method I'm a bit on the fence about. I think it is fair game if we are providing something like createRequest that is going to accept (most) everything to make a Request instance, though.

In any event I see the request method as more or less boilerplate code for PSR-HTTP-Client implementations.

I also took a crack at what a very simple Buzz implementation might look like to see how it felt. I did it with Buzz mainly because I've used Buzz and have not actually ever done anything with Guzzle. :) As it is now I think it will work with any existing Buzz client backing it.

namespace Buzz\PsrHttpClient;

class Client implements \Fig\Psr\ClientInterface
{
    public function __construct(\Buzz\Client\ClientInterface $client)
    {
        $this->client = $client;
    }

    public function createRequest($method, $url, array $headers = array(), $content = null)
    {
        // Construct the PSR Request however makes sense for Buzz
        // ... the set* methods may not be a part of PSR Request
        // but that does not mean Buzz implementation cannot use
        // them. Implementation detail.
        $request = new \Buzz\PsrHttpClient\Request;
        $request->setMethod($method);
        $request->setUrl($url);
        $request->setHeaders($headers);
        $request->setContent($content);
    }

    public function sendRequest(RequestInterface $request)
    {
        // Use util... or could be done inline here, brevity
//
// If Buzz Client ever accepts PSR Request natively
// this conversion can be skipped.
        $buzzRequest = \Buzz\PsrHttpClient\Util::convertPsrRequestToBuzzRequest($request);
        $buzzResponse = new \Buzz\Message\Response;

        $this->client->send($buzzRequest, $buzzResponse);

        // Use util... or could be done inline here, brevity
//
// If \Buzz\Message\Response ever implements PSR Response
// it can just be returned at this point.
//
        // @return \Buzz\PsrHttpClient\Response
        return \Buzz\PsrHttpClient\Util::convertBuzzResponseToPsrResponse($buzzResponse);
    }

    public function request($method, $url, array $headers = array(), $content = null)
    {
        // boilerplate — conveniently mimics Benjamin's original proposal
        return $this->sendRequest($this->createRequest($method, $url, $headers, $content, $options));
    }
}



Michael Dowling

unread,
Jun 7, 2012, 11:22:44 PM6/7/12
to php-st...@googlegroups.com, php-st...@googlegroups.com
Beau,

I'm pretty much on board with everything you wrote except that you've never used Guzzle :)

-Michael
--
You received this message because you are subscribed to the Google Groups "PHP Standards Working Group" group.
To view this discussion on the web visit https://groups.google.com/d/msg/php-standards/-/VIOGwnTDjqsJ.

To post to this group, send email to php-st...@googlegroups.com.
To unsubscribe from this group, send email to php-standard...@googlegroups.com.

Kris Wallsmith

unread,
Jun 17, 2012, 9:49:25 AM6/17/12
to php-st...@googlegroups.com
I'm coming around to Michael's point that what Buzz calls a client is more of an internal implementation detail than something that should be included in a standard.

So, what the PSR calls a client should look like this, in my mind:

interface Client
{
  /** @return Response */
  function send(Request $request);

  /** @return Response */
  function request($method, $url, $headers = array());
}

I don't see a place for a createRequest() method in the standard. A library can choose whether or not to provide factory functionality.

This interface maps roughly to what Buzz\Browser and Guzzle\HTTP\Client are doing right now. Maybe the folks on this thread can tentatively agree to this interface so we can move on to the message interfaces and put this thing to bed?

Thanks,
Kris
To unsubscribe from this group, send email to php-standards+unsubscribe@googlegroups.com.

Michael Dowling

unread,
Jun 17, 2012, 1:30:26 PM6/17/12
to php-st...@googlegroups.com
Hi Kris,

I like what you have so far.

> I don't see a place for a createRequest() method in the standard. A library can choose whether or not to provide factory functionality.

The request() method you included has the inherent responsibility of
creating HTTP requests using the supplied arguments, thus it is a
factory. My suggestion for a createRequest() method has this same
behavior except that it returns the created request rather than
sending it.

In my opinion, we can include both a createRequest() method and a
request() method in the proposal, and both would serve an important
purpose.

* createRequest() - Used to create HTTP request objects. Can be used
to modify requests before sending, aggregate requests and transfer
them more efficiently, filter/update requests using application
specific requirements.
* request() - Used to send requests via a simple interface.

> function request($method, $url, $headers = array());

I think you forgot to include an optional $body argument in the
request() method signature. Entity bodies are a whole new can of
worms... The lowest common denominator, a string, forces the entire
body to load in memory before sending (try uploading a 5GB file to
S3). What do you think about allowing strings or open PHP resources
to be passed? I don't think we need something as full-fledged as
Guzzle's, but Guzzle has a class for entity bodies that internally
stores them as PHP resources:
https://github.com/guzzle/guzzle/blob/master/src/Guzzle/Http/EntityBody.php.
If a vendor does not support entity bodies represented as streams,
then they can simply do a type check and call stream_get_contents().

Thanks,
Michael
>> php-standard...@googlegroups.com.
>> For more options, visit this group at
>> http://groups.google.com/group/php-standards?hl=en.
>
> --
> You received this message because you are subscribed to the Google Groups
> "PHP Standards Working Group" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/php-standards/-/iAoUulM7_4wJ.
>
> To post to this group, send email to php-st...@googlegroups.com.
> To unsubscribe from this group, send email to
> php-standard...@googlegroups.com.

Nils Adermann

unread,
Jun 17, 2012, 7:12:55 PM6/17/12
to php-st...@googlegroups.com
Hey Michael,

On 06/17/2012 07:30 PM, Michael Dowling wrote:
> Hi Kris,
>
> I like what you have so far.
>
>> I don't see a place for a createRequest() method in the standard. A library can choose whether or not to provide factory functionality.
> The request() method you included has the inherent responsibility of
> creating HTTP requests using the supplied arguments, thus it is a
> factory. My suggestion for a createRequest() method has this same
> behavior except that it returns the created request rather than
> sending it.
>
> In my opinion, we can include both a createRequest() method and a
> request() method in the proposal, and both would serve an important
> purpose.
>
> * createRequest() - Used to create HTTP request objects. Can be used
> to modify requests before sending, aggregate requests and transfer
> them more efficiently, filter/update requests using application
> specific requirements.
> * request() - Used to send requests via a simple interface.
>
I'm not sure why you think it would be a client's responsibility to
create requests. Semantically the purpose of the client is to send
requests, accept responses and make these available to a user.

Request objects should be created and manipulated outside of the client.
The client's task is to serialize them to HTTP and send them, and to
parse the HTTP response and make this available as a Response object to
the user of the client.

It seems to be that creating and manipulating requests is an entirely
separate problem from serializing and sending them.

Cheers,
Nils

Michael Dowling

unread,
Jun 17, 2012, 7:25:57 PM6/17/12