[REVIEW] PSR-7 HTTP Message Interface

668 views
Skip to first unread message

Beau Simensen

unread,
Apr 14, 2015, 12:52:27 PM4/14/15
to php...@googlegroups.com
We have completed addressing the issues for which we canceled the PSR-7 acceptance vote along with some additional issues that came up between then and now. I hereby announce that as of 4:45pm UTC on Tuesday, April 14th, 2015, PSR-7 HTTP Message Interface has once again entered the Review stage.

Now is the time for members to get involved to help determine if this proposal is ready for an acceptance vote. If we decide to move forward with an acceptance vote, we can do so as early as 4:45PM UTC on Tuesday, April 28th, 2015.

For the current version see:



We had a number of items that came up during voting that we felt needed to be addressed before the proposal was accepted. Those were the issues that we focused on after first moving back to Review and then finally back into Draft.

It was our goal to minimize the amount of changes we considered during Draft as we did not want to stray too far from what was originally put up for an acceptance vote. The reason for this was that the vote was looking to pass by reaching quorum with the majority voting +1.

The editor of PSR-7 was kind enough to put together a summary of the changes that have been made since the acceptance vote was canceled.


The editor also notes that his reference implementation of PSR-7, phly/http, has been updated to be current with the current version of PSR-7.

Alexander Makarov

unread,
Apr 15, 2015, 4:50:59 AM4/15/15
to php...@googlegroups.com
It was a good decision to cancel voting. Very good changes.

Larry Garfield

unread,
Apr 26, 2015, 8:40:07 PM4/26/15
to php...@googlegroups.com
I just read over the mega-diff for the latest changes. Overall, woo!
Great work, everyone!

----

I want to call attention to this recent comment from Liz Smith that may
have been missed:

https://github.com/php-fig/fig-standards/pull/490#issuecomment-94197428

The renaming of Stream[able]Interface may be problematic long-term. Yes
it would be in a different namespace but that's still potentially
confusing, especially if the interfaces are not identical. (They likely
would have subtle differences, at least.) I would recommend reverting
this rename and letting internals "own" the name "StreamInterface".

----

There's still a reference to "host segment" in the "Host header"
section, even though Matthew's writeup above says that it should have
been switched to "component".

----

The UploadedFileInterface::move() method feels poorly named. move()
from? to? The parameter is simply named $path, which is not very
descriptive. I would recommend simply renaming this method to moveTo(),
which would give it a really nice readable style:

$request->getUploadedFiles()['myfile']->moveTo($destination_path);

----

The spec talks about the "tree structure" of uploaded files. To be
honest, I have no idea what that means. I rarely deal with$_FILES
directly, much to my joy. :-) What tree is it? The example implies
it's a tree caused by PHP's stupid design of $_FILES, but the whole
point seems to be to not deal with that stupid design.

So the spec should either:

1) Clarify in much more detail what tree we're talking about, and how it
works.

2) Normalize the upload data to a non-tree list.

The most important part here, IMO, being that a consumer trying to read
uploaded files needs as few if-statements as possible in order to do
so. As is, I couldn't actually write a conforming implementation since
I don't know what I'm supposed to do.

----

The rewording to allow with*() methods to return $this is very subtle.
It should be called out explicitly in the prose text somewhere, as
otherwise I would have missed that it now allows return $this or in what
cases.

----

getHeaderLine() now returns string|null. I generally find |null returns
to be a huge PITA. At least it's a primitive and not an object so
there's no "call to method on non-object" issues, but I still worry that
it will force more "wrap stuff in if(is_null)) BS" cases on clients. Is
there a reason to not return an empty string? In this case the
distinction is very subtle, but the fewer nulls in my code the happier I
am. The UriInterface methods all specify empty string if not defined,
so we should do the same here.

----

Can we normalize all of the first-line descriptions of methods to a
single line? A few wrap in the middle, which screws with some
documentation standards and documentation parsers. (Eg, see
getReasonPhrase())

----

UriInterface::getPath() allowing a leading / or not = *cries*. The
description of where it's different talks only of the empty case, ie, ""
and "/". However, as written it would also allow returning both
"/some/page/here" and "some/page/here". Can we at least normalize the
non-empty case to a leading /? Otherwise, this is another case of
"every project now has another 19 if statements in it, increasing the
cyclomatic complexity of the PHP universe by several million". :-)

----

I recall there being some discussion about getQuery() returning a string
vs. an array, since the data structure is naturally an array. Why is it
still returning a string? The array seems far more useful to me, and it
just forces everyone to parse that themselves. Or am I missing some
other part of the spec now?


--Larry Garfield

Woody Gilk

unread,
Apr 26, 2015, 8:47:04 PM4/26/15
to php...@googlegroups.com
On Sun, Apr 26, 2015 at 7:40 PM, Larry Garfield <la...@garfieldtech.com> wrote:
> I recall there being some discussion about getQuery() returning a string vs.
> an array, since the data structure is naturally an array. Why is it still
> returning a string? The array seems far more useful to me, and it just
> forces everyone to parse that themselves


This is a key point to me. While HTTP clearly defines the query string
as, well, a string, we simply don't deal with it in that way within
application code. It makes way more sense to use an array when doing
get/set operations on the query string, and only representing the
query as a string when dealing with URIs.

In many ways, this clearly illustrates why I mentioned concerns with
PSR-7 not detailing how to add and remove query parameters by _name_
when building requests.
--
Woody Gilk
http://about.me/shadowhand

Matthew Weier O'Phinney

unread,
Apr 27, 2015, 10:28:35 AM4/27/15
to php...@googlegroups.com
On Sun, Apr 26, 2015 at 7:40 PM, Larry Garfield <la...@garfieldtech.com> wrote:
> I want to call attention to this recent comment from Liz Smith that may have
> been missed:
>
> https://github.com/php-fig/fig-standards/pull/490#issuecomment-94197428
>
> The renaming of Stream[able]Interface may be problematic long-term. Yes it
> would be in a different namespace but that's still potentially confusing,
> especially if the interfaces are not identical. (They likely would have
> subtle differences, at least.) I would recommend reverting this rename and
> letting internals "own" the name "StreamInterface".

I'm conflicted on this one.

I was against renaming in the first place, but several people were
adamant about the change, and I didn't have enough of an argument to
stand on. That said:

- As you and others already noted, it would be namespaced.
- What nobody else has brought up is that there are very few
interfaces(*) in core with the suffix "Interface", and I highly
suspect we won't see many, if any more. A more likely naming in Core
will be Streamable (ah, the irony!) or StreamWrapper (note: the class
in the documentation is an *example*, and not something defined in
core!).

* DateTimeInterface exists mainly because DateTime *already existed*;
the same is true of SessionSaveHandlerInterface.

Essentially, while I understand Liz's concern, I also think the
liklihood of an interface with the "Interface" extension in core is
unlikely, which means there won't really be a conflict.

> ----
>
> There's still a reference to "host segment" in the "Host header" section,
> even though Matthew's writeup above says that it should have been switched
> to "component".

Thanks! PR opened: https://github.com/php-fig/fig-standards/pull/536

> ----
>
> The UploadedFileInterface::move() method feels poorly named. move() from?
> to? The parameter is simply named $path, which is not very descriptive. I
> would recommend simply renaming this method to moveTo(), which would give it
> a really nice readable style:
>
> $request->getUploadedFiles()['myfile']->moveTo($destination_path);

How about "move($destination)" or "move($target)"?

> ----
>
> The spec talks about the "tree structure" of uploaded files. To be honest, I
> have no idea what that means. I rarely deal with$_FILES directly, much to my
> joy. :-) What tree is it? The example implies it's a tree caused by PHP's
> stupid design of $_FILES, but the whole point seems to be to not deal with
> that stupid design.
>
> So the spec should either:
>
> 1) Clarify in much more detail what tree we're talking about, and how it
> works.

I'm really not sure what more we can do to detail this. Section 1.6
(https://github.com/php-fig/fig-standards/blob/master/proposed/http-message.md#16-uploaded-files)
shows what $_FILES does for structure, vs what is expected (with the
expected being the normalized tree). (See the first two code examples
in that section.)

> 2) Normalize the upload data to a non-tree list.
>
> The most important part here, IMO, being that a consumer trying to read
> uploaded files needs as few if-statements as possible in order to do so. As
> is, I couldn't actually write a conforming implementation since I don't know
> what I'm supposed to do.

The idea for a consumer is that they can do this:

$file = $request->getUploadedFiles()['avatars']['logo'][1];

and they'll get an UploadedFileInterface instance.

For a conforming implementation, you would need to traverse the
$_FILES array and create UploadedFileInterface instances; in the case
of an array of files (again, see section 1.6), you would need to
aggregate the data appropriately.

I'll find you in IRC to see if we can hash out more verbiage to clarify this.

> ----
>
> The rewording to allow with*() methods to return $this is very subtle. It
> should be called out explicitly in the prose text somewhere, as otherwise I
> would have missed that it now allows return $this or in what cases.

https://github.com/php-fig/fig-standards/blob/master/proposed/http-message-meta.md#new-instances-vs-returning-this

Do you have any suggestions on making that more clear?

> ----
>
> getHeaderLine() now returns string|null. I generally find |null returns to
> be a huge PITA. At least it's a primitive and not an object so there's no
> "call to method on non-object" issues, but I still worry that it will force
> more "wrap stuff in if(is_null)) BS" cases on clients. Is there a reason to
> not return an empty string? In this case the distinction is very subtle, but
> the fewer nulls in my code the happier I am. The UriInterface methods all
> specify empty string if not defined, so we should do the same here.

I'd be fine with that change; it would still make conditionals work as expected:

if (! $message->getHeaderLine('X-Foo')) {
}

though, if empty strings are the return value, I'd likely want to
rewrite that to:

if (empty($message->getHeaderLine('X-Foo')) {
}

I've created a PR: https://github.com/php-fig/fig-standards/pull/537

> ----
>
> Can we normalize all of the first-line descriptions of methods to a single
> line? A few wrap in the middle, which screws with some documentation
> standards and documentation parsers. (Eg, see getReasonPhrase())

I've created a PR for this: https://github.com/php-fig/fig-standards/pull/538

> ----
>
> UriInterface::getPath() allowing a leading / or not = *cries*. The
> description of where it's different talks only of the empty case, ie, "" and
> "/". However, as written it would also allow returning both
> "/some/page/here" and "some/page/here". Can we at least normalize the
> non-empty case to a leading /? Otherwise, this is another case of "every
> project now has another 19 if statements in it, increasing the cyclomatic
> complexity of the PHP universe by several million". :-)

@Tobion and a few others made strong cases for this. I'm not terribly
happy with it, but if some member projects have demonstrated needs, I
think it makes sense to keep it in.

Side note: When integrating this change into Conduit, I discovered
that I only needed to change 4 lines of code to accommodate it. The
implication is that with good test cases, projects can likely find
expedient solutions.

> ----
>
> I recall there being some discussion about getQuery() returning a string vs.
> an array, since the data structure is naturally an array. Why is it still
> returning a string? The array seems far more useful to me, and it just
> forces everyone to parse that themselves. Or am I missing some other part
> of the spec now?

There are two related functionalities:

- UriInterface::getQuery()
- ServerRequestInterface::getQueryParams()

For server-side applications, you'll be using the second, as it's a
direct member of the request, and gives you the array structure you're
expecting. This is what you want to use server-side.

UriInterface::getQuery() is for returning the query string *for the
URI*. Having this return an array may not be a good idea; as Michael
Dowling has noted elsewhere, while the structure we have in PHP
typically can map to an associative array, query strings used for
other servers *may not*. As such, you need access to the string itself
so you can parse it somehow. This method is one you'll typically only
use for client applications.

--
Matthew Weier O'Phinney
mweiero...@gmail.com
https://mwop.net/

Larry Garfield

unread,
Apr 27, 2015, 12:17:52 PM4/27/15
to php...@googlegroups.com
I don't have a strong feeling about the name either way, other than
playing nice with internals. I know there are people working on OOP
streams for 7.x, and we want to be working with them, not against.

Personally I think playing nice with Internals on naming, and therefore
easing the upgrade process for everyone in about 3 years who want to be
using PHP 7, is the most important criteria here around naming. I know
internals (like FIG) is not structured in such a way that we can
formally agree on who gets what name, but can we at least try to get an
explicit informal agreement?

>> ----
>>
>> The UploadedFileInterface::move() method feels poorly named. move() from?
>> to? The parameter is simply named $path, which is not very descriptive. I
>> would recommend simply renaming this method to moveTo(), which would give it
>> a really nice readable style:
>>
>> $request->getUploadedFiles()['myfile']->moveTo($destination_path);
>
> How about "move($destination)" or "move($target)"?

As discussed in IRC, moveTo($destination) is the most self-documenting
option so let's do that.

>> ----
>>
>> The spec talks about the "tree structure" of uploaded files. To be honest, I
>> have no idea what that means. I rarely deal with$_FILES directly, much to my
>> joy. :-) What tree is it? The example implies it's a tree caused by PHP's
>> stupid design of $_FILES, but the whole point seems to be to not deal with
>> that stupid design.
>>
>> So the spec should either:
>>
>> 1) Clarify in much more detail what tree we're talking about, and how it
>> works.
>
> I'm really not sure what more we can do to detail this. Section 1.6
> (https://github.com/php-fig/fig-standards/blob/master/proposed/http-message.md#16-uploaded-files)
> shows what $_FILES does for structure, vs what is expected (with the
> expected being the normalized tree). (See the first two code examples
> in that section.)

Exactly. That section shows what $_FILES does and explains what's stupid
about it, then the spec says "a tree structure", implying that it should
be the same structure as $_FILES, which we just said is dumb. WAT?

>> 2) Normalize the upload data to a non-tree list.
>>
>> The most important part here, IMO, being that a consumer trying to read
>> uploaded files needs as few if-statements as possible in order to do so. As
>> is, I couldn't actually write a conforming implementation since I don't know
>> what I'm supposed to do.
>
> The idea for a consumer is that they can do this:
>
> $file = $request->getUploadedFiles()['avatars']['logo'][1];
>
> and they'll get an UploadedFileInterface instance.
>
> For a conforming implementation, you would need to traverse the
> $_FILES array and create UploadedFileInterface instances; in the case
> of an array of files (again, see section 1.6), you would need to
> aggregate the data appropriately.
>
> I'll find you in IRC to see if we can hash out more verbiage to clarify this.

IMO, the spec itself needs to define what the array structure needs to
be in case of uniquely named files, array named files, nested array
named files, etc.

And if that gets hard to do, it is an indication that we're on the wrong
track. :-)

>> ----
>>
>> The rewording to allow with*() methods to return $this is very subtle. It
>> should be called out explicitly in the prose text somewhere, as otherwise I
>> would have missed that it now allows return $this or in what cases.
>
> https://github.com/php-fig/fig-standards/blob/master/proposed/http-message-meta.md#new-instances-vs-returning-this
>
> Do you have any suggestions on making that more clear?

Mm, that wasn't visible in the diff I was reading. :-) The metadoc is
sufficient, I think.

>> ----
>>
>> UriInterface::getPath() allowing a leading / or not = *cries*. The
>> description of where it's different talks only of the empty case, ie, "" and
>> "/". However, as written it would also allow returning both
>> "/some/page/here" and "some/page/here". Can we at least normalize the
>> non-empty case to a leading /? Otherwise, this is another case of "every
>> project now has another 19 if statements in it, increasing the cyclomatic
>> complexity of the PHP universe by several million". :-)
>
> @Tobion and a few others made strong cases for this. I'm not terribly
> happy with it, but if some member projects have demonstrated needs, I
> think it makes sense to keep it in.
>
> Side note: When integrating this change into Conduit, I discovered
> that I only needed to change 4 lines of code to accommodate it. The
> implication is that with good test cases, projects can likely find
> expedient solutions.

It's been a big knotted ball of fun (for sufficiently masochistic
definitions of fun) transitioning Drupal from no-/-prefix to /-prefix as
a result of adopting HttpFoundation. Those path strings can get into a
lot of places you wouldn't expect. I think we still have a few

'/' . trim($path, '/')

lines floating around somewhere. (If you aren't face-palming on that,
you should.) As I said, I can accept "/" vs "" being meaningful but is
it really a meaningful distinction between "/about" and "about"? I can
easily see people forgetting about that somewhere and getting a lot of
string or regex false negatives.

>> ----
>>
>> I recall there being some discussion about getQuery() returning a string vs.
>> an array, since the data structure is naturally an array. Why is it still
>> returning a string? The array seems far more useful to me, and it just
>> forces everyone to parse that themselves. Or am I missing some other part
>> of the spec now?
>
> There are two related functionalities:
>
> - UriInterface::getQuery()
> - ServerRequestInterface::getQueryParams()
>
> For server-side applications, you'll be using the second, as it's a
> direct member of the request, and gives you the array structure you're
> expecting. This is what you want to use server-side.
>
> UriInterface::getQuery() is for returning the query string *for the
> URI*. Having this return an array may not be a good idea; as Michael
> Dowling has noted elsewhere, while the structure we have in PHP
> typically can map to an associative array, query strings used for
> other servers *may not*. As such, you need access to the string itself
> so you can parse it somehow. This method is one you'll typically only
> use for client applications.

Hm, OK. So the idea is that you would pass the array to some message
builder provided by your library (eg, Guzzle, React), and it would
stringify it for you? That's better than ServerRequest also doing that,
but it's still not as good as being able to build the request object
myself and just handing it to Guzzle (or retrieving a Response from
Guzzle). I'll still vote for PSR-7 as is but I think it would be highly
useful to abstract that into the message object. (Well, URI object in
this case.)

--Larry Garfield

Matthew Weier O'Phinney

unread,
Apr 27, 2015, 12:40:07 PM4/27/15
to php...@googlegroups.com
I'll reach out to Liz today. Our own decision may inform any made by
internals, too.

>>> ----
>>>
>>> The UploadedFileInterface::move() method feels poorly named. move()
>>> from?
>>> to? The parameter is simply named $path, which is not very descriptive.
>>> I
>>> would recommend simply renaming this method to moveTo(), which would give
>>> it
>>> a really nice readable style:
>>>
>>> $request->getUploadedFiles()['myfile']->moveTo($destination_path);
>>
>>
>> How about "move($destination)" or "move($target)"?
>
>
> As discussed in IRC, moveTo($destination) is the most self-documenting
> option so let's do that.

PR is here: https://github.com/php-fig/fig-standards/pull/539
I'll get something in place today.


<snip>
The person pushing for this specifically is Tobias Schultze (@Tobion
on GitHub), who, IIRC, is leading symfony's HttpKernel development.

I'd personally prefer absolute path only (i.e., empty is represented
as "/", and anything else is *always* absolute), as it simplifies
logic in routers and URI generators. However, Tobias indicated that it
would break a lot of stuff in Symfony's router, though I was never
able to fully understand exactly how.
Correct. This could be as simple as a call to http_build_query(), or
usage of a class encapsulating that behavior, similar to what Beau
Simensen is doing with cookies.

> That's better than ServerRequest also doing that, but it's
> still not as good as being able to build the request object myself and just
> handing it to Guzzle (or retrieving a Response from Guzzle). I'll still
> vote for PSR-7 as is but I think it would be highly useful to abstract that
> into the message object. (Well, URI object in this case.)

The question then becomes: where do we stop? Do we do that for the
body parameters, too? What about mixed bodies that contain both
parameters and files? etc. etc.

The point is: we're modeling the components at the minimal viable
level currently; we can build abstractions on top, and if any of those
gain traction, look at a superceding PSR in the future.

Larry Garfield

unread,
Apr 27, 2015, 3:01:41 PM4/27/15
to php...@googlegroups.com, tob...@gmail.com
That's fascinating, since Drupal is using the Symfony Router (well, CMF
Router) and I don't understand either.

Tobias, can you explain here? Because as is this looks like a landmine
for all implementers, including Symfony.

(Copying Tobias in specifically to make sure he sees this...)

--Larry Garfield

Tobion

unread,
Apr 27, 2015, 3:36:41 PM4/27/15
to php...@googlegroups.com, tob...@gmail.com
See https://github.com/php-fig/fig-standards/pull/487 and https://github.com/php-fig/fig-standards/pull/503

One thing most people don't see is that there is a difference between an UriInterface and application code.
The UriInterface is just a representation of a URI. And a URI can have rootless paths which are different from absolute paths.
The UriInterface cannot know what the user wants to achieve and thus CANNOT do magic normalization. This normalization can only be provided by libraries that know the context. For example the symfony routing component only supports absolute paths. As such the routing component can normalize a user provided path of `foo/bar` to `/foo/bar` automatically to be fault-tolerant as this is what the user most likely tried to express. This is done in https://github.com/symfony/symfony/blob/2.7/src/Symfony/Component/Routing/Route.php#L196

But that cannot be done for every URI, i.e. in UriInterface directly, because under different contexts, this is not what the user expected.
One example where it doesn't make sense is expressing paths in guzzle relative to a "base url", i.e. resolve a path relative to another one. Pseudo code: new Guzzle('https://example.org/api/')->get('articles') would retrieve resource https://example.org/api/articles. I.e. one Uri with https://example.org/api/ and another one with just withPath('articles').
withPath('/articles') would mean something completely different, namely https://example.org/articles. This is according to RFC 3986. Please inform yourself about resolving URIs.

Matthew Weier O'Phinney

unread,
Apr 27, 2015, 3:54:40 PM4/27/15
to php...@googlegroups.com, tob...@gmail.com
On Mon, Apr 27, 2015 at 2:36 PM, Tobion <tob...@gmail.com> wrote:
> See https://github.com/php-fig/fig-standards/pull/487 and
> https://github.com/php-fig/fig-standards/pull/503
>
> One thing most people don't see is that there is a difference between an
> UriInterface and application code.
> The UriInterface is just a representation of a URI. And a URI can have
> rootless paths which are different from absolute paths.
> The UriInterface cannot know what the user wants to achieve and thus CANNOT
> do magic normalization. This normalization can only be provided by libraries
> that know the context. For example the symfony routing component only
> supports absolute paths. As such the routing component can normalize a user
> provided path of `foo/bar` to `/foo/bar` automatically to be fault-tolerant
> as this is what the user most likely tried to express. This is done in
> https://github.com/symfony/symfony/blob/2.7/src/Symfony/Component/Routing/Route.php#L196
>
> But that cannot be done for every URI, i.e. in UriInterface directly,
> because under different contexts, this is not what the user expected.
> One example where it doesn't make sense is expressing paths in guzzle
> relative to a "base url", i.e. resolve a path relative to another one.
> Pseudo code: new Guzzle('https://example.org/api/')->get('articles') would
> retrieve resource https://example.org/api/articles. I.e. one Uri with
> https://example.org/api/ and another one with just withPath('articles').
> withPath('/articles') would mean something completely different, namely
> https://example.org/articles. This is according to RFC 3986.

Now I understand where you're coming from. However, I think we have a
disconnect.

We have made zero mention of doing path resolution when invoking
withPath(); in fact, it's currently written such that it should return
an instance with the _specified_ path (i.e., replace the path). As
such, withPath('articles') will not append at this time.

I left path resolution out of the original interface entirely, because
it's tricky, and, generally speaking, can be done outside the
specification.

Chris Wilkinson proposed a separate "resolve()" method for doing this
sort of thing when he originally proposed a separate PSR for URIs in
https://groups.google.com/d/msg/php-fig/06BSXGqPRiU/EXcGDR0G3iwJ — I
think this makes a ton of sense, and is the sort of utility that HTTP
clients and URI generators could provide. As an example:

return $request->withUri(resolve($request->getUri(), 'articles'));

Considering that path resolution is not intended, would you agree to
the previous behavior? (e.g., empty path = "/", all paths are prefixed
with "/"?)

> Please inform yourself about resolving URIs.

Comments like the above are incredibly divisive and inflammatory, and
make a huge assumption, usually incorrect, about the amount of
research others have performed.
> --
> 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/6f2598a0-d69c-4645-b705-bbf43be3f478%40googlegroups.com.
>
> For more options, visit https://groups.google.com/d/optout.

Dowling, Michael

unread,
Apr 27, 2015, 4:08:11 PM4/27/15
to php...@googlegroups.com
## Regarding relative paths in UriInterface:

To follow up on what Tobian said: There’s a big difference between “foo” and “/foo” when combining a relative URI with an absolute URI. By allowing relative paths in the URI, Guzzle could use it when resolving against a client’s base URI. Here’s a related comment that I give more detail: https://github.com/php-fig/fig-standards/pull/487#issuecomment-90677300

## Regarding query string parsing:

I don’t like this idea for the PSR as it would cause one of two things: we would couple the query string parsing to PHP conventions, limiting the ability of a client to interact with non-php servers; or we would add a lot of complexity to allow clients to somehow serialize duplicate query string values in different ways based on the server you’re interacting with. I think there’s a big difference between the needs of a client-side message and a server-side message: the client’s need much more flexibility which we don’t necessarily need to define in PSR-7. Related issue: https://github.com/php-fig/fig-standards/issues/515

## Regarding StreamableInterface vs StreamInterface:

Can we please not change this…again? Namespaces are a thing in PHP, and that a will protect us from any naming collision with a future OO PHP stream implementation.

> Chris Wilkinson proposed a separate "resolve()" method for doing this sort of thing when he originally proposed a separate PSR for URIs in https://groups.google.com/d/msg/php-fig/06BSXGqPRiU/EXcGDR0G3iwJ — I think this makes a ton of sense, and is the sort of utility that HTTP clients and URI generators could provide. As an example: return $request->withUri(resolve($request->getUri(), 'articles')); Considering that path resolution is not intended, would you agree to
the previous behavior? (e.g., empty path = "/", all paths are prefixed with "/“?)

I’m not against this, but I don’t think this is necessary in the interface for a minimum viable product. This could be a function that acts on URIs rather than a requirement of the interface. For example: https://github.com/guzzle/psr7/blob/master/src/Uri.php#L120

--Michael

Tobion

unread,
Apr 27, 2015, 4:15:08 PM4/27/15
to php...@googlegroups.com, tob...@gmail.com
withPath DOES NOT DO path resolution! The UriInterface simulates A SINGLE URI.
You need TWO URIs to resolve them against each other. Where did I say withPath does path resolution?
That doesn't make sense at all.

All I said, is that if withPath only supports absolute paths, then THERE COULD BE NO LIBRARY BASED ON PSR-7 to support path resolution for two URIs and PSR-7 would be unusable for many use-cases.

We had many discussions on this topic already. And it's not like I'm suggesting something completely new or sophisticated. All I'm trying to do is make PSR-7 useable and according to existing standards. So you might understand my frustration when I see that PSR-7 completely violated RFC 3987 at the first voting stage (and would have been accepted like this) and I need to keep explaining the most basic RFC that PSR-7 is build upon to the editor.

Matthew Weier O'Phinney

unread,
Apr 27, 2015, 4:45:47 PM4/27/15
to php...@googlegroups.com
On Mon, Apr 27, 2015 at 3:08 PM, Dowling, Michael <mtdo...@gmail.com> wrote:
> ## Regarding relative paths in UriInterface:
>
> To follow up on what Tobian said: There’s a big difference between “foo” and
> “/foo” when combining a relative URI with an absolute URI. By allowing
> relative paths in the URI, Guzzle could use it when resolving against a
> client’s base URI. Here’s a related comment that I give more detail:
> https://github.com/php-fig/fig-standards/pull/487#issuecomment-90677300

To clarify, this would enable the following:

$uri1 = new Uri('https://example.com/resources?page=1');
$uri2 = (new Uri())
->withPath('foo/bar');
$uri = resolve($uri1, $uri2);
echo($uri); // https://example.com/resources/foo/bar

where both arguments to resolve() are UriInterface instances.

Correct?

<snip>

> ## Regarding StreamableInterface vs StreamInterface:
>
> Can we please not change this…again? Namespaces are a thing in PHP, and that
> a will protect us from any naming collision with a future OO PHP stream
> implementation.

I've spoken with Liz Smith today, and it looks like the streams
implementation she is working on for core will likely drop the
Interface suffix. As such, I'm not planning on changing the name at
this time.

>> Chris Wilkinson proposed a separate "resolve()" method for doing this sort
>> of thing when he originally proposed a separate PSR for URIs in
>> https://groups.google.com/d/msg/php-fig/06BSXGqPRiU/EXcGDR0G3iwJ — I think
>> this makes a ton of sense, and is the sort of utility that HTTP clients and
>> URI generators could provide. As an example: return
>> $request->withUri(resolve($request->getUri(), 'articles')); Considering that
>> path resolution is not intended, would you agree to
> the previous behavior? (e.g., empty path = "/", all paths are prefixed with
> "/“?)
>
> I’m not against this, but I don’t think this is necessary in the interface
> for a minimum viable product.

You misunderstood: I'm not recommending we add it to the spec! I was
simply indicating that in the proposed URI PSR, the method was
present. I fully support NOT having it in PSR-7; it can be something
in a later URI interface and/or, as you note, in utility
classes/functions/etc. that can be required and consumed where
necessary. Not every library will have a need for resolving two URIs.
> https://groups.google.com/d/msgid/php-fig/D163E3D0.931C2%25mtdowling%40gmail.com.

Tobion

unread,
Apr 27, 2015, 5:01:23 PM4/27/15
to php...@googlegroups.com

Matthew Weier O'Phinney

unread,
Apr 27, 2015, 5:03:11 PM4/27/15
to php...@googlegroups.com, tob...@gmail.com
On Mon, Apr 27, 2015 at 3:15 PM, Tobion <tob...@gmail.com> wrote:
> withPath DOES NOT DO path resolution! The UriInterface simulates A SINGLE
> URI.
> You need TWO URIs to resolve them against each other. Where did I say
> withPath does path resolution?
> That doesn't make sense at all.

It comes from my interpretation of this paragraph:

> Pseudo code: new Guzzle('https://example.org/api/')->get('articles') would retrieve resource https://example.org/api/articles. I.e. one Uri with https://example.org/api/ and another one with just withPath('articles').
withPath('/articles') would mean something completely different,
namely https://example.org/articles.

I read the above as:

$baseUri = new Uri('https://example.com/api/');
$relative = $baseUri->withPath('articles'); // yields path /api/articles
$absolute = $baseUri->withPath('/articles'); // yields path /articles

Clearly I was wrong, but perhaps you can see where I drew my
conclusions. Lack of a concrete example means that the reader fills in
the blanks.

> All I said, is that if withPath only supports absolute paths, then THERE
> COULD BE NO LIBRARY BASED ON PSR-7 to support path resolution for two URIs
> and PSR-7 would be unusable for many use-cases.

Based on Michael's feedback, I see the point at this time.

> We had many discussions on this topic already. And it's not like I'm
> suggesting something completely new or sophisticated. All I'm trying to do
> is make PSR-7 useable and according to existing standards. So you might
> understand my frustration when I see that PSR-7 completely violated RFC 3987
> at the first voting stage (and would have been accepted like this) and I
> need to keep explaining the most basic RFC that PSR-7 is build upon to the
> editor.

Again, please stop with the ad hominem attacks; they're making a ton
of assumptions that are simply untrue.

My misinterpretation was not of the RFC here; it was of the intent
behind why omission of relative paths in the UriInterface instances
would inhibit path resolution. When looking at a resolve() function
implementation, I was considering path resolution only, in which case,
*string* arguments would suffice. The question I had was whether the
_relative_ path to resolve needed to be in the UriInterface instance
*at all*; I was looking at something like this:

$newPath = resolve($uri->getPath(), 'articles');

In other words, why create a URI instance if all you want to set is
the path, which you're going to use to resolve to a fully-qualified
path anyways?

However, it's clear that both you and Michael were positing a function
that would accept the URI *instances*. This of course makes sense,
when you consider resolution could also take into account scheme,
user-info, subdomains, and query string arguments. This aspect was
*not* clear from previous questioning (concrete examples go a *long*
way; a big thank you to Michael for linking me to some), which was why
I was still uncertain about the changes we made. I no longer have any
uncertainty, and can articulate the design to others.

I have the information I need to make a decision at this point, and
will be leaving this aspect of the spec as it currently is.
> https://groups.google.com/d/msgid/php-fig/89882a19-444f-4e08-9bb5-0adf0d05400d%40googlegroups.com.

Matthew Weier O'Phinney

unread,
Apr 27, 2015, 5:04:03 PM4/27/15
to php...@googlegroups.com
On Mon, Apr 27, 2015 at 4:01 PM, Tobion <tob...@gmail.com> wrote:
> Almost correct.
>
> https://example.com/resources?page=1 + foo/bar = https://example.com/foo/bar
> https://example.com/resources/?page=1 + foo/bar =
> https://example.com/resources/foo/bar

Yeah - I hit send and realized I'd forgotten to remove the query
string. :facepalm:
> https://groups.google.com/d/msgid/php-fig/a9067456-3e80-4e99-8078-e180cdacf69e%40googlegroups.com.

Tobion

unread,
Apr 27, 2015, 5:53:03 PM4/27/15
to php...@googlegroups.com
> Yeah - I hit send and realized I'd forgotten to remove the query 
string. :facepalm: 

It's not about the query string. The error is the trailing slash.

> $newPath = resolve($uri->getPath(), 'articles'); 

URI resolving is not only about the path. It can also be relative to the scheme, e.g. `//example.org` or just the query string, e.g. `?foo=bar` or just the hash `#hash`. So your example doesn't work. Or you have to create a method for every permuation of all URI components. This would stand in constrast to why we have UriInterface at all: To have one object to represent a Uri with all components in a standarized way.

Matthew Weier O'Phinney

unread,
Apr 27, 2015, 6:03:22 PM4/27/15
to php...@googlegroups.com
On Mon, Apr 27, 2015 at 4:53 PM, Tobion <tob...@gmail.com> wrote:
>> $newPath = resolve($uri->getPath(), 'articles');
>
> URI resolving is not only about the path. It can also be relative to the
> scheme, e.g. `//example.org` or just the query string, e.g. `?foo=bar` or
> just the hash `#hash`. So your example doesn't work. Or you have to create a
> method for every permuation of all URI components. This would stand in
> constrast to why we have UriInterface at all: To have one object to
> represent a Uri with all components in a standarized way.

I get it - that was the point I got at in my previous post: if you
want to do a general-purpose URI resolver that takes into account all
components, the only way this will work is if we also allow the path
to be relative.
> https://groups.google.com/d/msgid/php-fig/a94914b6-8f23-4253-94a3-460479715d7d%40googlegroups.com.

Larry Garfield

unread,
Apr 29, 2015, 12:13:05 PM4/29/15
to php...@googlegroups.com
On 4/27/15 5:03 PM, Matthew Weier O'Phinney wrote:
> On Mon, Apr 27, 2015 at 4:53 PM, Tobion <tob...@gmail.com> wrote:
>>> $newPath = resolve($uri->getPath(), 'articles');
>>
>> URI resolving is not only about the path. It can also be relative to the
>> scheme, e.g. `//example.org` or just the query string, e.g. `?foo=bar` or
>> just the hash `#hash`. So your example doesn't work. Or you have to create a
>> method for every permuation of all URI components. This would stand in
>> constrast to why we have UriInterface at all: To have one object to
>> represent a Uri with all components in a standarized way.
>
> I get it - that was the point I got at in my previous post: if you
> want to do a general-purpose URI resolver that takes into account all
> components, the only way this will work is if we also allow the path
> to be relative.

All of this makes sense if we're using Uri objects on their own, as a
value object that supports "URI Math". That is, similar to
DateTime::modify() or DateTime::add(DateTimeInterval). That's a
somewhat broader scope than we originally intended here, but I am fine
with covering that use case.

What still doesn't quite sit right with me is this excerpt of
UriInterface::getPath() (NOT withPath()):

***
Normally, the empty path "" and absolute path "/" are considered equal
as defined in RFC 7230 Section 2.7.3. But this method MUST NOT
automatically do this normalization because in contexts with a trimmed
base path, e.g. the front controller, this difference becomes
significant. It's the task of the user to handle both "" and "/".
***

This is the part I don't get. The example called out here isn't an HTTP
client that's doing relative URL math (which mtdowling gave a good
argument for). The example called out is a front-controller. That
seems like the place LEAST likely to be using a relative path. IE, it
would be using a ServerRequest and representing the message as received
from Apache. In that case, if I read that paragraph correctly it means
that I as a routing system must do the following:

$request_path = $request->getUri()->getPath();
$request_path = '/' . ltrim(trim($path), '/');
do_whatever_path_matching_logic_here($path);

Because the docblock above says that the URI for a front controller may
have a leading / or not. In context I don't know why I'd ever not have
one, unless a particular implementation decided that it didn't feel like
it, but then that's legal and I have to account for it. That second
line is what strikes me as pointless and unnecessary in the context of a
front controller/router. In fact, that's the only case where it seems
mtdowling's use case doesn't apply, yet it's the one that's called out
as the quintessential example!

If you feel that strongly about it, what am I missing?

--Larry Garfield

Evert Pot

unread,
Apr 29, 2015, 12:29:53 PM4/29/15
to php...@googlegroups.com
I think that the issue is that people want to abuse the URI object for
relative URIs.

The URI object should imho *never* contain a relative or incomplete value.

Evert

Dracony

unread,
Apr 30, 2015, 10:43:46 AM4/30/15
to php...@googlegroups.com
 A more likely naming in Core 
will be Streamable (ah, the irony!) or StreamWrapper (note: the class 
in the documentation is an *example*, and not something defined in 
core!). 

I think this is a huge concern actually. Getting this PSR into core at some point would be awesome. The 'Interface' suffix is an unneeded addition in my opinion since all OOP languages consider Interfaces and Classes to be "Types" having virtually no difference between the two. The theory then states that each instance may belong to one or many of these "Types". This drives things like multiple inheritance for example. The 'Interface' suffix doesn't bring any information about the actual Type rather about its implementation.

C++ diesnt use the suffix (well its mostly because of multiple inhertiance and interfaces just being pure virtual classes): http://accu.org/index.php/journals/233
As was already noted PHP itself doesn't use the suffix, e.g. Traversable interface.

The suffix idea came into PHP world because of some popular ( but far from all ) frameworks using that. When FIG was making decision on camelCase vs snake_case it went with camelCase because PHP core classes already used it. In the same way I believe PSR-7 must also comply with how PHP does things and have becoming part of PHP core as its goal.

Matthew Weier O'Phinney

unread,
Apr 30, 2015, 10:59:15 AM4/30/15
to php...@googlegroups.com
On Thu, Apr 30, 2015 at 9:43 AM, Dracony <draco...@gmail.com> wrote:
>> A more likely naming in Core
>> will be Streamable (ah, the irony!) or StreamWrapper (note: the class
>> in the documentation is an *example*, and not something defined in
>> core!).
>
>
> I think this is a huge concern actually. Getting this PSR into core at some
> point would be awesome.

It's unlikely, unnecessary, and generally unwanted for PSR-7 to go into core.

Here's why: different implementations will focus on different aspects,
such as performance, SAPI vs non-SAPI support, etc. Additionally, some
may or may not implement additional features, and we'll want to
evaluate if some of those features should *also* be standardized at
some point in a subsequent PSR. Things like the stream implementation,
in particular, will likely see many different implementations tuned to
different use cases: wrapping php://input, wrapping stream contexts,
wrapping generators, wrapping callables, etc. These are userland
concerns.

I discussed ideas with a number of core contributors at SunshinePHP,
and they suggested that what would make the most sense is for the
language to provide features that will assist with and complement
PSR-7. Examples of some of those:

- functions for aggregating incoming headers
- functions for providing a normalized upload file structure
- functions for aggregating the request URI
- functions for parsing upload files from non-POST bodies

etc.

The point is: with functions like this in place, many of the hardest
operations can be handed off to C code, giving performance boosts and
simplifying implementations, allowing them to focus on
differentiation.

> The 'Interface' suffix is an unneeded addition in my
> opinion since all OOP languages consider Interfaces and Classes to be
> "Types" having virtually no difference between the two. The theory then
> states that each instance may belong to one or many of these "Types". This
> drives things like multiple inheritance for example. The 'Interface' suffix
> doesn't bring any information about the actual Type rather about its
> implementation.
>
> Java doesn't use the suffix:
> https://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html
> C++ diesnt use the suffix (well its mostly because of multiple inhertiance
> and interfaces just being pure virtual classes):
> http://accu.org/index.php/journals/233
> As was already noted PHP itself doesn't use the suffix, e.g. Traversable
> interface.
>
> The suffix idea came into PHP world because of some popular ( but far from
> all ) frameworks using that. When FIG was making decision on camelCase vs
> snake_case it went with camelCase because PHP core classes already used it.
> In the same way I believe PSR-7 must also comply with how PHP does things
> and have becoming part of PHP core as its goal.

The Interface suffix is part of PSR-2, and exists for a number of
reasons. This is not the time or place to discuss it.

I've worked with Elizabeth Smith to ensure we will not conflict, and
that was the primary objection raised. Now that that is resolved, we
can move forward with the proposal.
> --
> 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/93f7cc3e-2b13-4810-861a-98b6f5a1404b%40googlegroups.com.

Roman Tsjupa

unread,
Apr 30, 2015, 11:11:45 AM4/30/15
to php...@googlegroups.com
Ah yes, alright, the suffix is there to stay then. But I don't see why PSR-7 shouldn't come into core. Sure there may be non-SAPI implementations and they can still exist with the SAPI Implementation available in the core. Not all classes need implementing though, just the SAPIServerRequest would be  enough for 99% cases


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

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

John Nickell

unread,
May 2, 2015, 2:59:00 AM5/2/15
to php...@googlegroups.com


On Thursday, April 30, 2015 at 9:59:15 AM UTC-5, Matthew Weier O'Phinney wrote:

The Interface suffix is part of PSR-2, and exists for a number of
reasons. This is not the time or place to discuss it.


The only thing I found in PSR-2 regarding the Interface suffix:

http://www.php-fig.org/psr/psr-2/#7.-conclusion

There are many elements of style and practice intentionally omitted by this guide. These include but are not limited to:
  • Class name prefixes and suffixes

Reply all
Reply to author
Forward
0 new messages