[Pre-Draft] HTTP Message Proposal

1,193 views
Skip to first unread message

Michael Dowling

unread,
Jan 8, 2014, 4:42:00 PM1/8/14
to php...@googlegroups.com
I formally propose that the FIG begins the process of creating an HTTP message proposal. This has been previously suggested, but not pursued formally until now.

Based on the bylaws, I must first announce this as a pre-draft to see if others are interested in an HTTP message proposal (see https://github.com/php-fig/fig-standards/blob/master/bylaws/004-psr-workflow.md#21-pre-draft).

Here is the proposal that I have put together: https://github.com/php-fig/fig-standards/pull/244

A team has been selected:

- Editor: Michael Dowling
- Coordinator/Sponsor: Phil Sturgeon
- Sponsor: Beau Simensen
- Contributor: Chris Wilkinson (some work is derived from his original proposal)

Please note: This email is meant only to gauge interest in such a proposal and not to determine if everyone agrees with the standard being presented.

Thanks,
Michael (AWS)

Phil Sturgeon

unread,
Jan 8, 2014, 4:48:12 PM1/8/14
to php...@googlegroups.com
Further information on why this proposal makes for a strong start.

1. Michael Dowling is the creator of Guzzle, so has a good base of experience with the many problems HTTP messages could bring up
2. It implements much of the advice from previous iterations, meaning we can theoretically get going quicker.
3. It avoids the "HTTP Client" stuff, just giving us the absolute basics of HTTP messaging.

If you gave specific issues then at this stage they can be taken up with Michael offline, via email, on GitHub, wherever but please leave them out of here. This is just a formality at this point, and we don't all need to agree on specific content.

If nobody has any major concerns then I'll put this in for a Entrance Vote a week from now. :)

Larry Garfield

unread,
Jan 8, 2014, 7:49:14 PM1/8/14
to php...@googlegroups.com
Color me very interested!

Out of curiosity, what happened to the idea of having a separate URI
spec that this builds on? Did that get folded back in, or punted as
over-engineering? (I'm still in favor either way, I'm just wondering
what happened to that.)

--Larry Garfield, Drupal

Michael Dowling

unread,
Jan 8, 2014, 8:28:57 PM1/8/14
to php...@googlegroups.com
While a URI spec would be interesting, I don't think it's required for the HTTP message PSR. For example, an HTTP message proposal can allow anything that can be cast to a string to be provided as the URL. This is what I present in the HTTP message PSR I've linked to.

-Michael

Phil Sturgeon

unread,
Jan 8, 2014, 9:30:40 PM1/8/14
to php...@googlegroups.com


On Wednesday, 8 January 2014 16:42:00 UTC-5, Michael Dowling wrote:
I was never really sure what a URI spec was supposed to be, and as Michael says a URI is just a string, so in regards to this PSR it shouldn't really matter. 

Also, generally trying to make PSRs not rely on other PSRs seems to be a good idea. 

Jason Judge

unread,
Jan 10, 2014, 5:41:41 AM1/10/14
to php...@googlegroups.com


On Thursday, 9 January 2014 02:30:40 UTC, Phil Sturgeon wrote: 
I was never really sure what a URI spec was supposed to be, and as Michael says a URI is just a string, so in regards to this PSR it shouldn't really matter. 

Yes, it is a string ultimately, when passed out to the web to make a request for a resource. However, when dealing with web services, the URI needs to be constructed, and that construction needs data to define it and rules/templates to follow. Depending on the scope of a HTTP spec, the construction of URIs may be relevant. A library such as Guzzle will be constructing URIs to access resources, but the part of that library that just deals with the HTTP communication will just be using the URL strings it is given from higher up.

In this case the scope is pretty much limited to the HTTP messaging part, so the URI is just a string. I can see how people may want to widen that to cover more of their use-cases, but scope creep has IMO always been the enemy of the PSR creation process. So yeah, it's just a string. Generating the string is what the URI spec would be about :-)

-- Jason

Kris Wallsmith

unread,
Jan 10, 2014, 4:16:39 PM1/10/14
to php...@googlegroups.com
I fully support this initiative and agree with the reasons behind focusing only on messages. Thanks for taking the lead, Michael!
Kris


--
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.
To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/da850064-3520-435b-8f9a-2735b1f9641a%40googlegroups.com.

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

Kris Wallsmith

unread,
Jan 10, 2014, 4:53:49 PM1/10/14
to php...@googlegroups.com
Also, I'd be happy to help as a sponsor if needed.

Larry Garfield

unread,
Jan 12, 2014, 12:51:04 PM1/12/14
to php...@googlegroups.com
For the time being, we can probably get away with "string or an object that implements __toString", which would let us or someone else later implement URI objects if needed without any hard dependencies.  Drupal is currently debating a URI object for our own use, although more coupled to the Symfony routing system we have, so there is demand for it.  But I'm fine with leaving it out of scope for now as it's trivially easy to introduce later without any new PSR dependencies.

I think we have general agreement that a vote to bring an HTTP message proposal into the pipeline as PSR-7 would pass.  I'd also be happy to help make this happen.

--Larry Garfield

Phil Sturgeon

unread,
Jan 12, 2014, 2:13:14 PM1/12/14
to php...@googlegroups.com

On Wednesday, 8 January 2014 16:42:00 UTC-5, Michael Dowling wrote:
Great. Thanks for the vote of confidence Larry!

It's only been two days but this was purely a formality. There is no time requirement, and as we've had enough time for people to see two email digests with subject, people don't have a huge amount of ability to complain.

Let's vote. 

Beau Simensen

unread,
Jan 13, 2014, 1:26:57 PM1/13/14
to php...@googlegroups.com
I haven't had a chance to weigh in on this yet but as I am listed as a sponsor I think it should be obvious I'm interested in seeing this topic explored. Thanks for picking this up, Michael, and thanks to everyone whose work this is based on. I know we've been batting this idea around in various forms for a very long time. It will be great to see where this goes once we have it added to the PSR workflow.

Robert Hafner

unread,
Jan 18, 2014, 8:48:43 PM1/18/14
to php...@googlegroups.com

This all seems very interesting, but I'm unclear on what exactly would be standardized by this and how it'll help with interoperability. This isn't to say that I don't think it'll help, just that I literally have not seen this explained anywhere. Is there any chance someone can comment on that a little bit, and speak to what the goals here would be?

Robert

Phil Sturgeon

unread,
Jan 20, 2014, 12:23:02 PM1/20/14
to php...@googlegroups.com

On Saturday, 18 January 2014 20:48:43 UTC-5, Robert Hafner wrote:

This all seems very interesting, but I'm unclear on what exactly would be standardized by this and how it'll help with interoperability. This isn't to say that I don't think it'll help, just that I literally have not seen this explained anywhere. Is there any chance someone can comment on that a little bit, and speak to what the goals here would be?

Robert

HTTP has been discussed here several times which I guess could be a reason why this time the introduction was a little short. That is my fault, I should make things more clear. 

A "HTTP" PSR has been on the cards for a really long time, but most felt that it was too much of a challenge to tackle in one go. Just like logging, people were fed up with writing adapter classes for interacting with multiple types of logger, so defining an interface helped people cut that down drastically. 

So a HTTP Client PSR seemed a logic move, letting packages like Geocoder avoid writing adapters for Zend, Buzz, Guzzle, Requests, etc. 

A HTTP Client however did seem like a lot of work to dive straight into, as there were SO MANY factors. To easy this somewhat, a HTTP Message PSR was proposed to tackle the Request/Response stuff first. By defining these, a client would much later be easier to work with. Some systems could easily utilize this PSR to cut their code down, as HttpKernel, Joomla, Drupal, Kohana, Guzzle, Buzz, etc all have similar-by-different Req/Resp classes and that is a little silly.

There were about 5 alternatives happening and nobody had the time/effort/energy to find one and run with it - especially before we had a workflow for handling that stuff. But now Michael is on the case, we lookl like we have a team who is willing and able to get the job done. Hopefully when complete we can keep on trucking with get the HTTP Client lib on the go, which is considerably more useful for the average developer - much like your cache PSR.

Phil Sturgeon

unread,
Jan 22, 2014, 1:45:57 AM1/22/14
to php...@googlegroups.com
Some of that made no sense. I think I must have edited it a few times and not done a very good job. I definitely don't think that "To easy this somewhat" is a valid sentence.

Regardless, it seems like most folks are content with this entering as a PSR, and the vote is going well. If anyone has questions then get in touch, otherwise we can start thinking about what needs to be done in draft.

Keven

unread,
Apr 16, 2014, 12:48:28 PM4/16/14
to php...@googlegroups.com
This draft seems to be adopted this your proposal but I struggle to understand something about the methods getUrl() and setUrl() in the RequestInterface interface: it is unclear to me if this represents the request URI as defined by the HTTP standard (http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2), or an absolute URL that one must split into a Host header and the request URI, or maybe a third signification I don't see.

In the first case, the user has to provide the Host header explicitly for every request since it cannot be deduced from the request URI when the request is made to a proxy, which is OK (but a bit weird for me) but then the methods should be named set/getUri() instead of set/getUrl(), don't you think?.

In the second case, there is a real problem because the Host header may different that the host of the request URI, and the request URI can sometimes be absolute (when a request is issued to a proxy) so we should not split the given URL.

What was your intention when designing this interface?

Thanks,
Keven

Michael Dowling

unread,
Apr 16, 2014, 4:44:50 PM4/16/14
to php...@googlegroups.com
The method signature needs a bit more clarification, but it accepts an absolute URL. I'll need to update the proposal to better clarify the intent.

> In the second case, there is a real problem because the Host header may different that the host of the request URI

Actually, this is something that's been requested several times from various users of Guzzle (e.g., https://twitter.com/mtougeron/status/322882012878876672https://github.com/guzzle/guzzle/issues/362). I think it would ultimately be up to implementations to decide whether or not they'll allow the Host to differ from what would be returned from getUrl().

> the request URI can sometimes be absolute (when a request is issued to a proxy) so we should not split the given URL.

So in Guzzle, I have additional methods like getPath(), getQuery(), etc. I chose to go with the simpler approach of just having getUrl() and setUrl() in this PSR proposal in hopes that people would favor the simpler option.

-Michael

Jamie Hannaford

unread,
Apr 30, 2014, 6:51:22 AM4/30/14
to php...@googlegroups.com
Although I like pretty much everything in this proposal, some of the docblocks concern me.

There seems to be a convention of returning "self" for setter methods, enforcing some kind of dynamic interface (probably for method chaining). I don't think enforcing this logic is appropriate because of the controversial nature of this pattern.

IMHO, method chaining like this breaks encapsulation. To quote Martin Fowler: "every method should either be a command that performs an action, or a query that returns data to the caller, but not both."

But apart from that, this is awesome - really hope it gets official approval soon.

Mike van Riel

unread,
Apr 30, 2014, 6:58:17 AM4/30/14
to php...@googlegroups.com
Hi Jamie,

Please read http://martinfowler.com/bliki/FluentInterface.html where Martin Fowler makes an exception to that rule for Fluent Interfaces.

Kind regards,

Mike van Riel
--
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.

Jamie Hannaford

unread,
Apr 30, 2014, 7:50:52 AM4/30/14
to php...@googlegroups.com
Thanks for the link. You're right, he does say that fluent interfaces follow a different set of rules - but he only seems to allow that rule breach for internal DSLs.

I guess the question is whether there is an overwhelming reason to use a fluent interface. I know they can be really helpful when defining verbose configurations or query building (to increase readability), but I'm not sure whether this applies to a simple process of setting a Request body, for example.

I guess it's down to personal preference: Uncle Bob says method chaining are "train wrecks" and should be avoided. Here's another blog post which disparages them. 

Jamie

Larry Garfield

unread,
Apr 30, 2014, 10:05:02 PM4/30/14
to php...@googlegroups.com
My general rule is that if a method has nothing useful to return (because it doesn't make sense in context), it should return $this.  Why?  Because returning null is totally and utterly useless 99% of the time.  Returning $this lets me chain if I want to, but I can safely ignore it if I don't.  Not returning $this means I can't chain even if it would make the code easier to read.

The Caching PSR has some return self moments in it, too.

--Larry Garfield

Marco Pivetta

unread,
May 19, 2014, 4:50:48 PM5/19/14
to php...@googlegroups.com
This just caught my eye and I'd encourage to read my rant on fluent interfaces at http://ocramius.github.io/blog/fluent-interfaces-are-evil/ (which I've already spammed everywhere)...

Unless there's a VERY strong reason to have a fluent interface (copy on write, for example), then I wouldn't include it at all.

Phil Sturgeon

unread,
May 23, 2014, 6:24:47 PM5/23/14
to php...@googlegroups.com


On Monday, 19 May 2014 21:50:48 UTC+1, Marco Pivetta wrote:
This just caught my eye and I'd encourage to read my rant on fluent interfaces at http://ocramius.github.io/blog/fluent-interfaces-are-evil/ (which I've already spammed everywhere)...

Unless there's a VERY strong reason to have a fluent interface (copy on write, for example), then I wouldn't include it at all.

That whole article seems to be "Fluent is bad if you require fluent methods to be called in a certain order. I have never seen an implementation where that mattered. Ever.  

The only thing that does come in order is "fluent stuff and then a get". It's usually pretty obvious because get is in the name. Setting is fluent, getting is not. 

I am not scared of them and we don't need to be for this spec.

Marco Pivetta

unread,
May 23, 2014, 6:36:27 PM5/23/14
to php...@googlegroups.com
I actually think that interfacing the fluent interface in those request/response objects will be useless in this case, and will also likely cause some confusion and bugs in implementations, as well as reduce the flexibility of the API.

An HTTP request/response is not an object builder, it is pretty much a VO in most cases, and a VO doesn't return itself when performing operations on it (unless we're doing CoW), and no code should be written (and especially interfaced) if there's no reason to do so.

If the point is saving a couple of keystrokes, then that's not a valid use case for fluent interfaces in first place :)

If you never saw a bad use-case for a fluent interface, or a bug because of its misuse, well then lucky you!

I would accept a fluent interface if there was a strong use-case for it.

--
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.

Larry Garfield

unread,
May 24, 2014, 10:22:00 PM5/24/14
to php...@googlegroups.com
Forking the thread title...

On 05/23/2014 05:36 PM, Marco Pivetta wrote:
> I actually think that interfacing the fluent interface in those
> request/response objects will be useless in this case, and will also
> likely cause some confusion and bugs in implementations, as well as
> reduce the flexibility of the API.
>
> An HTTP request/response is not an object builder, it is pretty much a
> VO in most cases, and a VO doesn't return itself when performing
> operations on it (unless we're doing CoW), and no code should be
> written (and especially interfaced) if there's no reason to do so.
>
> If the point is saving a couple of keystrokes, then that's not a valid
> use case for fluent interfaces in first place :)
>
> If you never saw a bad use-case for a fluent interface, or a bug
> because of its misuse, well then lucky you!
>
> I would accept a fluent interface if there was a strong use-case for it.
>
>
> Marco Pivetta

Sure, I've seen ugly fluent interfaces. There is no coding technique
ever created that cannot be used to create an incomprehensible crime
against nature itself. That's not the point.

My general guideline is this: If a method has something
useful/meaningful to return, do so, whatever that is. If it has nothing
meaningful to return, return $this so that one *could* chain method
calls if one were so inclined. Given the option between returning NULL
(utterly useless) and $this (optionally saves some typing and can make
code easier to read if not used stupidly), always return $this. Because
being utterly useless is for suckers.

For *most* setter methods (by no means all), there's rarely anything
useful/meaningful to return. So return $this so that you can chain if
you feel like it. If you don't feel like it, cool, no harm done.

An "undefined" return type is a dead end. I don't like dead ends.

--Larry Garfield

Marco Pivetta

unread,
May 25, 2014, 10:30:01 AM5/25/14
to php...@googlegroups.com
Hi Larry,

Answers inline:

On 25 May 2014 04:21, Larry Garfield <la...@garfieldtech.com> wrote:
Sure, I've seen ugly fluent interfaces.  There is no coding technique ever created that cannot be used to create an incomprehensible crime against nature itself.  That's not the point.

My general guideline is this: If a method has something useful/meaningful to return, do so, whatever that is.

The point is exactly this: returing `self` for no reason carries no semantical meaning, and instead drags some problems with itself (linking the rant about that as a cross-reference, in case anyone missed it: http://ocramius.github.io/blog/fluent-interfaces-are-evil/ ).
`self` is not the "result" of an operation, therefore it should not be returned unless really required.
Your setter methods are accessors for a property, they does not produce any result.
 
 If it has nothing meaningful to return, return $this so that one *could* chain method calls if one were so inclined.  

I've just said what "meaningful" means here, and `@return self` is not "meaningful".
This is not the point of an interface. An interface is a contract, and the contract states what goes in and what comes out, and that is not to ease development, but to set strict constraints on what is allowed and what isn't
 Chaining operations is not something that makes your interface safer or more useful.

Given the option between returning NULL (utterly useless)

We're not returning `NULL`, the method is simply `void`. There is a difference between `@return null` and `@return void`, which is that `@return null` basically "allows" for consumers to use the result of the operation (though indeed useless), while `@return void` defines that consumers SHOULD NOT use the result of the operation.
PHP is not strictly typed, but assigning the result of a `void` expression to a variable in strictly typed languages is a compiler error (as it should be).
 
and $this (optionally saves some typing and can make code easier to read if not used stupidly), always return $this.

Saving keystrokes is not a good reason to assign different (IMO wrong) semantic meaning to our methods.
If the problem is saving keystrokes, then start abbreviating variables (you will lose readability/explicitness in both cases):

$myAwesomeObject->setBar(1)->setBaz(2)->setTar(3)->setTaz(4);

or

$f->setBar(1);
$f->setBaz(2);
$f->setTar(3);
$f->setTaz(4);

better:

$myAwesomeObject->setBar(1);
$myAwesomeObject->setBaz(2);
$myAwesomeObject->setTar(3);
$myAwesomeObject->setTaz(4);
 
 Because being utterly useless is for suckers.

There's nothing useless in a strict interface. The interface is here to avoid having you write wrong implementations, and removing `@return self` on setters makes that simpler.
Also, there is seriously nothing useful in chaining methods unless you are in need for a readable DSL structure, such as (for example) Symfony's DIC config extensions (using FOSUserBundle as an example):

        $rootNode
            ->children()
                ->scalarNode('db_driver')
                    ->validate()
                        ->ifNotInArray($supportedDrivers)
                        ->thenInvalid('The driver %s is not supported. Please choose one of '.json_encode($supportedDrivers))
                    ->end()
                    ->cannotBeOverwritten()
                    ->isRequired()
                    ->cannotBeEmpty()
                ->end()
                ->scalarNode('user_class')->isRequired()->cannotBeEmpty()->end()
                ->scalarNode('firewall_name')->isRequired()->cannotBeEmpty()->end()
                ->scalarNode('model_manager_name')->defaultNull()->end()
                ->booleanNode('use_listener')->defaultTrue()->end()
                ->booleanNode('use_flash_notifications')->defaultTrue()->end()
                ->booleanNode('use_username_form_type')->defaultTrue()->end()
                ->arrayNode('from_email')
                    ->addDefaultsIfNotSet()
                    ->children()
                        ->scalarNode('address')->defaultValue('webm...@example.com')->cannotBeEmpty()->end()
                        ->scalarNode('sender_name')->defaultValue('webmaster')->cannotBeEmpty()->end()
                    ->end()
                ->end()
            ->end()
            // Using the custom driver requires changing the manager services
            ->validate()
                ->ifTrue(function($v){return 'custom' === $v['db_driver'] && 'fos_user.user_manager.default' === $v['service']['user_manager'];})
                ->thenInvalid('You need to specify your own user manager service when using the "custom" driver.')
            ->end()
            ->validate()
                ->ifTrue(function($v){return 'custom' === $v['db_driver'] && !empty($v['group']) && 'fos_user.group_manager.default' === $v['group']['group_manager'];})
                ->thenInvalid('You need to specify your own group manager service when using the "custom" driver.')
            ->end();

Now in this case, it makes sense, because if I zoom out and look at my screen, I have a clear overview of the inheritance between the nodes in this Config DSL.

For *most* setter methods (by no means all), there's rarely anything useful/meaningful to return.  

Correct, so don't do it :P
 
So return $this so that you can chain if you feel like it.  If you don't feel like it, cool, no harm done.

I've described the harm very well in that blogpost that I've linked.
 
An "undefined" return type is a dead end.  I don't like dead ends.

It doesn't stop execution of your program, and it behaves as it should: you can call the setter, values get (supposedly) set.
It's by far a dead end.

I still don't see any *STRONG* reason for having a fluent interface.

Phil Sturgeon

unread,
May 25, 2014, 10:20:34 PM5/25/14
to php...@googlegroups.com


On Sunday, 25 May 2014 15:30:01 UTC+1, Marco Pivetta wrote:
Hi Larry,

Answers inline:

On 25 May 2014 04:21, Larry Garfield <la...@garfieldtech.com> wrote:
Sure, I've seen ugly fluent interfaces.  There is no coding technique ever created that cannot be used to create an incomprehensible crime against nature itself.  That's not the point.

My general guideline is this: If a method has something useful/meaningful to return, do so, whatever that is.

The point is exactly this: returing `self` for no reason carries no semantical meaning, and instead drags some problems with itself (linking the rant about that as a cross-reference, in case anyone missed it: http://ocramius.github.io/blog/fluent-interfaces-are-evil/ ).
`self` is not the "result" of an operation, therefore it should not be returned unless really required.
Your setter methods are accessors for a property, they does not produce any result.
 
 If it has nothing meaningful to return, return $this so that one *could* chain method calls if one were so inclined.  

I've just said what "meaningful" means here, and `@return self` is not "meaningful".
This is not the point of an interface. An interface is a contract, and the contract states what goes in and what comes out, and that is not to ease development, but to set strict constraints on what is allowed and what isn't
 Chaining operations is not something that makes your interface safer or more useful.

It does not make it less safe or less useful.
 

Given the option between returning NULL (utterly useless)

We're not returning `NULL`, the method is simply `void`. There is a difference between `@return null` and `@return void`, which is that `@return null` basically "allows" for consumers to use the result of the operation (though indeed useless), while `@return void` defines that consumers SHOULD NOT use the result of the operation.
PHP is not strictly typed, but assigning the result of a `void` expression to a variable in strictly typed languages is a compiler error (as it should be).

This is purely academic and more irrelevant to the conversation than the other talk of fluent stuff was. There is no difference between null and void, other than docblocks, so... whatever. Just pretend Larry said "void" instead of "null" and his point stands.
 
[snip]

It's not your place to suggest that fluent is a good way to do things or a bad way to do things. At best this interface would be indifferent to them as PHP interfaces do not currently support return types, so we can say do whatever you like, or we can accept that getters get things and setters are fluent - like every single PHP class and package I can remember using in the last few years.

Marco Pivetta

unread,
May 25, 2014, 11:04:07 PM5/25/14
to php...@googlegroups.com
On 26 May 2014 04:20, Phil Sturgeon <em...@philsturgeon.co.uk> wrote:
This is purely academic and more irrelevant to the conversation than the other talk of fluent stuff was.

Please don't be the DHH of the situation. Academic knowledge works, otherwise it wouldn't be passed on. I just felt something was wrong and told why it is so. If it is irrelevant to you, then keep it that way, but `null` and `void` are different, and PHP turns out to be always returning `null` when a method is `void`. Yes, that's an engine thing, still different for the annotations.

It's not your place to suggest that fluent is a good way to do things or a bad way to do things.

Pardon me, this may be a language barrier issue, but I think there are more explicit ways to say "shut your mouth".
If feedback is not welcome on this mailing list, then just say so and I'll keep lurking instead of trying to be helpful.
Isn't this a discussion list? Or maybe is it just for the votes? Did I miss something? Anyone?
 
At best this interface would be indifferent to them as PHP interfaces do not currently support return types, so we can say do whatever you like,

Wrong. We trust the return types, even if they aren't enforced by the engine.
We've been doing that for years and we test our classes in order to respect our docblocks, and that's because PHP has always messed with us when we didn't.
Ultimately, type-hints are (hopefully) landing in PHP 5.7 (or whatever comes next) and we already have HackLang because we want them so badly.
So yes, even if it is a docblock, it still defines a contract, and people should implement every bit of it, even if just by using static introspection tools that use these annotations to tell us that we're doing it wrong, while at runtime everything works (woah, HackLang even does exactly this!).

Heck, we are even having something like a PSR just about annotations!
 
or we can accept that getters get things and setters are fluent - like every single PHP class and package I can remember using in the last few years.

Trends change, and TBH, I didn't find anything concrete (written/blogged/published) about why fluent interfaces are a smell, so that's why I felt the need to write it down.
This doesn't mean that what I write should be taken for granted, but that little thought has been put in fluent interface smells in the past (I think there's something about it in "Effective Java", but I'm not really sure).
That's why you probably have that many fluent interfaces coded without previous thoughts put in "why" making them fluent.
Just like Larry said above, we've been (are) doing it blindly for no proper good reason.
Yes, there are bigger problems, but this is still a problem, and it can be solved immediately.

By the way, yesterday I opened a pull request at https://github.com/php-fig/fig-standards/pull/286 about this.

guilher...@gmail.com

unread,
May 25, 2014, 11:44:54 PM5/25/14
to php...@googlegroups.com
I second Ocramius' opinion and it may also save us on a possible expansion of PSRs PHPCS later.
It avoids WTF/min by "disallowing" context switching is the root cause of many already known NullPointerException in Java.
Example:

$user->getLocation()->getCity()->getName();

What happens if getLocation() returns null? In Java, NullPointerException.
Bringing same concept of context switching to setters for the sake of fewer keystrokes is really bad. Look again at Symfony's DIC config extension and ask yourselves how many context switches happened there.
Now, try to write a PHPCS rule that prevents context switching (to avoid possible NullPointerExceptions, or method call on a non-object in PHP)... easy? No.

That brings the exact reason why "One dot per line" is an Object Calisthenics rule. It provides you a tougher decision that addresses the problem that you guys tend to bypass by adopting fluent interfaces.
It also prevents anyone to write a standardized PHPCS rule to look for that.


Cheers,



--
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.

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



--
Guilherme Blanco
MSN: guilher...@hotmail.com
GTalk: guilhermeblanco
Toronto - ON/Canada

Sebastian Krebs

unread,
May 26, 2014, 9:46:07 PM5/26/14
to php...@googlegroups.com

I always propagate to avoid fluent interfaces as long as one don't want to introduce a DSL. It only leads to bad habits. And that only to save some characters...

- What is the definition of "When a method has something useful to return"? Every method, that returns 'void' should return the current insurance instead? What happens, when the method can return something "useful" in the future? This is impossible and anyway would be confusing with other fluent methods.
- what if the context changes? One method returns a different instance? Now in a long chain of method calls we have a context change everybody needs to be aware of.
- who defined, what a "useful return value" is? A setter can always the old value instead.

I can find more, but at the end: all this only to save some characters?!

So as a non-voter I really prefer to rather discourage this, than recommend it. It only leads to bad behaviors.

Regards,
Sebastian

>
> --Larry Garfield


>
> --
> 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.

> To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/538153C3.1090404%40garfieldtech.com.

Chuck Burgess

unread,
May 27, 2014, 10:43:50 AM5/27/14
to php...@googlegroups.com
I started to say that a compromise here would be to allow for including a FluentDooblyGooblyInterface alongside any DooblyGooblyInterface, where the difference is "return void => becomes => return self".  However, the proliferation of additional interface definitions to just allow this one behavioral difference feels like a code smell to me.  Thinking further, would a FluentAwareInterface of just isFluent(boolean $flag) be a useful structure, where the PSR requirement for using that interface is that any "return void" behavior in an implementer is required to check that flag and "return self"?  By moving the behavior distinction into an interface definition, then an implementer of some other interface can just list itself as also implementing FluentAwareInterface.

I'm not fully convinced here just yet, as this either requires a further documentation requirement for the implementer of "@return void|self", or it means a documented "return void" allows for a semi-undocumented "return self".  Thus, consider this a brainstorming thought that I'm throwing against the wall ;-)

Phil Sturgeon

unread,
May 29, 2014, 8:16:27 AM5/29/14
to php...@googlegroups.com


On Monday, 26 May 2014 04:04:07 UTC+1, Marco Pivetta wrote:
On 26 May 2014 04:20, Phil Sturgeon <em...@philsturgeon.co.uk> wrote:
This is purely academic and more irrelevant to the conversation than the other talk of fluent stuff was.

Please don't be the DHH of the situation. Academic knowledge works, otherwise it wouldn't be passed on. I just felt something was wrong and told why it is so. If it is irrelevant to you, then keep it that way, but `null` and `void` are different, and PHP turns out to be always returning `null` when a method is `void`. Yes, that's an engine thing, still different for the annotations.

I'm sorry, I didn't meant that it was irrelevant to me, I meant that it was irrelevant to what we had been talking about. It was like picking a hole in something Larry said for no reason. He said NULL, you wanted to explain that really it was Void, being represented as a Null. That has no benefit to anyone, not me, not larry or anyone else in the conversation. It was a "well actually" that didn't need to be said. :)


It's not your place to suggest that fluent is a good way to do things or a bad way to do things.

Pardon me, this may be a language barrier issue, but I think there are more explicit ways to say "shut your mouth".
If feedback is not welcome on this mailing list, then just say so and I'll keep lurking instead of trying to be helpful.
Isn't this a discussion list? Or maybe is it just for the votes? Did I miss something? Anyone?

This has nothing to do with memebership status and feedback is always welcome, of course! 

One person can't just say "This design pattern is bad and shouldnt be used.", especially when that opinion can translate to forcing the hand of implementors. Writing off a whole design pattern because you've seen some bad uses of it is extreme. So lets just make sure we implement it logically - if we implement it.

 
At best this interface would be indifferent to them as PHP interfaces do not currently support return types, so we can say do whatever you like,

Wrong. We trust the return types, even if they aren't enforced by the engine.
[snip]

So, your PR here:


When I mean "do whatever you like" I mean, we can just not specify that you MUST use the fluent pattern. Your PR does that, by removing the return type. Not specifying a return type means that implementors can do whatever they like.

 So, im not against that PR. I would be against us specifically adding @return void or @return null, because that would be forcing the hand of implementers on something that is their decision - not yours. :)
 
or we can accept that getters get things and setters are fluent - like every single PHP class and package I can remember using in the last few years.

Trends change, and TBH, I didn't find anything concrete (written/blogged/published) about why fluent interfaces are a smell, so that's why I felt the need to write it down.
This doesn't mean that what I write should be taken for granted, but that little thought has been put in fluent interface smells in the past (I think there's something about it in "Effective Java", but I'm not really sure).
That's why you probably have that many fluent interfaces coded without previous thoughts put in "why" making them fluent.
Just like Larry said above, we've been (are) doing it blindly for no proper good reason.
Yes, there are bigger problems, but this is still a problem, and it can be solved immediately.
 
It's a problem to you. I read your whole blog and disagree that its a problem. People using fluent pattern to do stupid things (requiring action A happens before action B but using fluent stuff to do it) is a problem, and I've not ever seen anyone doing that. If they did do that, I'd tell them to stop doing that.

My personal opinion is that it is not a problem, but I don't mind if we let this PR be vague enough to let people use fluent if they want, or not use it if they dont want. 

Fair enough? :)

Marco Pivetta

unread,
May 29, 2014, 1:18:15 PM5/29/14
to php...@googlegroups.com
Hey Phil,

On 29 May 2014 14:16, Phil Sturgeon <em...@philsturgeon.co.uk> wrote:
Fair enough? :)

Fair enough :-)

Jason Judge

unread,
Jun 17, 2014, 11:32:09 AM6/17/14
to php...@googlegroups.com, Demon...@gmail.com
On Tuesday, 27 May 2014 15:43:50 UTC+1, Chuck Burgess wrote:
I started to say that a compromise here would be to allow for including a FluentDooblyGooblyInterface alongside any DooblyGooblyInterface, where the difference is "return void => becomes => return self".  However, the proliferation of additional interface definitions to just allow this one behavioral difference feels like a code smell to me.  Thinking further, would a FluentAwareInterface of just isFluent(boolean $flag) be a useful structure, where the PSR requirement for using that interface is that any "return void" behavior in an implementer is required to check that flag and "return self"?  By moving the behavior distinction into an interface definition, then an implementer of some other interface can just list itself as also implementing FluentAwareInterface.

I'm not fully convinced here just yet, as this either requires a further documentation requirement for the implementer of "@return void|self", or it means a documented "return void" allows for a semi-undocumented "return self".  Thus, consider this a brainstorming thought that I'm throwing against the wall ;-)

I came to a similar conclusion after following this enormous thread over the last few moths, and posted it on the PR (having overlooked your post here). It's been suggested it may be worthwhile repeating it here:-


I like fluent interfaces. I love fluent interfaces. It is not about being able to type fewer characters, but about visualising the code. I look at a fluent structure, coming back to it months after coding it, and it just feels and looks right. That's not to say it cannot be abused, especially when chains of references hop between different objects at each stage. But it is the responsibility of the programmer to behave in these instances.

However, as a PSR recommendation I am not sure it should be mandated. The PSR needs to be explicit - to be compliant with this PSR, the methods must return void. If people are free to put in their their own return values (which will happen if the PSR does not say otherwise - what it not said, being open to interpretation), then developers relying on the return values of some implementations will have a problem switching to other implementations. As others have pointed out, it makes for less portable code.

There is no reason why a library could not provide a fluent version of its API though. There would be the PSR layer that does not make any assumptions about how code is going to be chained together. Then there can be the fluent layer that adds `return $this` to methods where relevant. That serves to keep the fluent users happy (inc. me), and also provides a source of documentation in one place showing which methods support fluent.

When fluent falls out of favour, and we all move to FluentPlus®, new layers can be added without affecting the underlying PSR compliant library.

If I want to swap out a library that has a fluent layer built-in, with a library that does not, then I port those fluent override classes to my own project, knowing that it will work with my code. And by "port", I mean just change the `namespace` and `use` clauses, since everything else in that layer would remain pretty much the same.

    namespace MyLib\Fluent;
   
    use My\Stuff as NonFluentStuff;
   
    class Stuff extends NonFluentStuff
    {
        public function setFoo($foo)
        {
            parent::setFoo($foo);
            return $this;
        }
    }

So I use `MyLib\Fluent\Stuff` instead of `MyLib\Stuff` to get at the fluent API.

Anyway, too much detail. The point is, fluent APIs can be layered over any library that wishes to support it. Not mandating it in the PSR - and being explicit that it should not be used for the PSR layers of a library - will mean more flexibility in the long run in what can be implemented *on top* of the PSR part of the library.

-- Jason

Filipe Guerra

unread,
Jun 19, 2014, 11:45:57 AM6/19/14
to php...@googlegroups.com, Demon...@gmail.com
I would like to add that we can use magic methods like `__call()` to provide that fluent abstract class (that would extend from this PSR interface). That way we can do stuff like:

    abstract class Stuff implements NonFluentInterface
    {
        //...
        // all interface methods would be here with new doc blocks for the chainable methods

        //...
        public function __call($method, $args)
        {
            $return = $this->$method($args);
            if (in_array($method, self::$chainableMethods) {
                return $this;
            }
            return $return;
        }
        //...
    }


Another way is someone that likes fluent interfaces to just extend from this PSR interface (extending it) and just change the docblocks to "return $this" (most probably easier)...

What am I missing?

Evert Pot

unread,
Jun 19, 2014, 11:50:40 AM6/19/14
to php...@googlegroups.com
On Thu Jun 19 11:45:57 2014, Filipe Guerra wrote:
> I would like to add that we can use magic methods like `__call()` to
> provide that fluent abstract class (that would extend from this PSR
> interface). That way we can do stuff like:
>
> abstract class Stuff implements NonFluentInterface
> {
> //...
> // all interface methods would be here with new doc blocks for
> the chainable methods
>
> //...
> public function __call($method, $args)
> {
> $return = $this->$method($args);
> if (in_array($method, self::$chainableMethods) {
> return $this;
> }
> return $return;
> }
> //...
> }
>
>
> Another way is someone that likes fluent interfaces to just extend
> from this PSR interface (extending it) and just change the docblocks
> to "return $this" (most probably easier)...
>
> What am I missing?

It's probably better to proxy/decorate as opposed to subclass in this
case, but this definitely demonstrates that for the folks that want a
fluid interface, it's still very easy to achieve this, without forcing
it upon people that don't want it.

Evert

Chuck Burgess

unread,
Jun 19, 2014, 11:53:09 AM6/19/14
to php...@googlegroups.com
Would __call() get executed if the named method exists?  I thought it was only used in the case of a non-existing method name being called at runtime.

Evert

--
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.
To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/53A30669.1%40gmail.com.

Marco Pivetta

unread,
Jun 19, 2014, 12:01:39 PM6/19/14
to php...@googlegroups.com
On 19 June 2014 17:52, Chuck Burgess <demon...@gmail.com> wrote:
Would __call() get executed if the named method exists?  I thought it was only used in the case of a non-existing method name being called at runtime.

I just patched up an example to show how this would work: http://3v4l.org/Xj719

Beau Simensen

unread,
Jun 19, 2014, 12:26:44 PM6/19/14
to php...@googlegroups.com
On Thursday, June 19, 2014 11:01:39 AM UTC-5, Marco Pivetta wrote:
On 19 June 2014 17:52, Chuck Burgess <demon...@gmail.com> wrote:
Would __call() get executed if the named method exists?  I thought it was only used in the case of a non-existing method name being called at runtime.

I just patched up an example to show how this would work: http://3v4l.org/Xj719

Nice start! I tweaked it a bit to account for the fluent wrapper to be able to know which methods should return self as some methods may intentionally return null and null being a valid response. You can argue whether this actually makes sense to do or not, but that is outside of the scope of this discussion.

Reply all
Reply to author
Forward
0 new messages