RFC: Server-Side Request and Response Objects (v2)

  108436
February 10, 2020 16:18 pmjones@pmjones.io ("Paul M. Jones")
Hi Internals,

After a couple of years of incubation, I am happy to offer a second version of this RFC:

  https://wiki.php.net/rfc/request_response

It proposes an object-oriented approach around request and response functionality already existing in PHP, in order to reduce the global-state problems that come with superglobals and the various response-related functions.

The SQLite "about" page says, "Think of SQLite not as a replacement for Oracle but as a replacement for fopen()." <https://www.sqlite.org/about.html>  Likewise, think of this RFC not as a replacement for HttpFoundation or PSR-7, or as a model of HTTP messages, but as an object-oriented alternative to superglobals, header(), setcookie(), setrawcookie(), and so on.

Thanks in advance for your time and consideration while evaluating it.


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

Modernizing Legacy Applications in PHP
https://leanpub.com/mlaphp

Solving the N+1 Problem in PHP
https://leanpub.com/sn1php
  108437
February 10, 2020 16:46 michal.brzuchalski@gmail.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=)
Hi Paul,

thanks for bringing it back. Got some questions:

1. In your RFC $_ENV is bound to $request.

Looking at HttpFoundation can see it's build from $_GET, $_POST, $_FILES,
$_COOKIE, $_SESSION
and then looking at PSR ServerSideRequest it's build from $_COOKIE, $_GET,
$_POST, $_FILES, $_SERVER
but none of them bounds $_ENV.

Server environment variables basically don't change over time when server
process runs.
What is the specific use case that requires them to be coupled with
$request instance?

2. In your RFC all headers are mapped to $request properties

This behaviour is unclear and unusual to me, for eg. in summary can see
that if
server request has "Header-Name" it is found in
$_SERVER["HTTP_HEADER_NAME"] and
that one is bound to $request->headers["header-name"], ok but the next line
with example for
"Content-Type" shows that it's bound to $request->contentType - which means
probably that it can
be accessed in $request->headers["content-type"] as well, right? seems
logic.

Going further, for eg. $_SERVER["PHP_AUTH_PW"] is bound to $request->authPw
and then
what will happen if the request will receive "Auth-Pw" header?

a) It'll be bound to $request->headers["auth-pw"] ?

b) It'll be bound to $request->authPw ?

c) None of above.

With those above I would see a set of getters like getContentType() or
getAuthPassword() etc.
instead of ServerRequest instance properties and a single point to retrieve
header values just like
well known ParameterBag's from HttpFoundation to operate on get, post,
headers etc.

BR,
--
Michał Brzuchalski
  108479
February 11, 2020 13:14 pmjones@pmjones.io ("Paul M. Jones")
Hi Michał,

Good to hear from you!

(While writing this, I realized I made a couple of mistakes in the RFC examples related to CONTENT_LENGTH and CONTENT_TYPE that might have caused confusion; I have now fixed them.)


> On Feb 10, 2020, at 10:46, Michał Brzuchalski brzuchalski@gmail.com> wrote: > > Hi Paul, > > thanks for bringing it back. Got some questions: > > 1. In your RFC $_ENV is bound to $request. > > Looking at HttpFoundation can see it's build from $_GET, $_POST, $_FILES, $_COOKIE, $_SESSION and then looking at PSR ServerSideRequest it's build from $_COOKIE, $_GET, $_POST, $_FILES, $_SERVER but none of them bounds $_ENV. > > Server environment variables basically don't change over time when server process runs. What is the specific use case that requires them to be coupled with $request instance?
I have no specific use case; I recall that $_ENV showed up in more than a couple of reviewed implementations, and so included it here. If there is no objection from John Boehr (who did the majority of heavy lifting C), or from anyone else, then I would be fine with removing the ENV-related element. I have no strong feelings on $_ENV representation either way.
> 2. In your RFC all headers are mapped to $request properties > > This behaviour is unclear and unusual to me, for eg. in summary can see that if server request has "Header-Name" it is found in $_SERVER["HTTP_HEADER_NAME"] and that one is bound to $request->headers["header-name"], ok but the next line with example for "Content-Type" shows that it's bound to $request->contentType - which means > probably that it can be accessed in $request->headers["content-type"] as well, right? seems logic.
I'm not sure if that is a question/comment/criticism, or a statement of "OK so far, but ...". Please bear with me, so I can make sure I'm addressing your question properly. First: Not *all* headers are mapped to $request properties; the only ones that get special $request properties are the ones that showed up as frequently getting special treatment in other implementations. Next: the $header array is populated by all $_SERVER['HTTP_*'] values, plus $_SERVER['CONTENT_TYPE'] and $_SERVER['CONTENT_LENGTH']. The latter two do not follow the 'HTTP_*' pattern because of RFC 3875; they come in as (e.g.) `Content-Type: text/plain`, so you might expect $_SERVER['HTTP_CONTENT_TYPE'], but RFC 3875 dictates otherwise. Finally: yes, $request->header['content-type'] is available at the same time as $request->contentType, but they are not quite the same thing. The Content-Type header also allows you to specify a charset, which value gets populated into $request->contentCharset. So, you can read these values these ways: - $request->header['content-type'] => 'text/plain; charset=UTF-8' (the original header) - $request->contentType => 'text/plain' (parsed out by ServerRequest) - $request->contentCharset => 'UTF-8' (parsed out by ServerRequest) Likewise, PHP puts `Content-Length: 123' into $_SERVER['CONTENT_LENGTH']; ServerRequest additionally puts it into $request->headers['content-length'] as a string; and then further tries to represent the value as an integer in $request->contentLength. So: - $request->server['CONTENT_LENGTH'] => (string) '123' - $request->headers['content-length'] => (string) '123' - $request->contentLength => (int) 123 My apologies if all of that was stuff you already knew and understood. (And if you did not already know it, maybe that means I should write up a narrative of the logic being applied.)
> Going further, for eg. $_SERVER["PHP_AUTH_PW"] is bound to $request->authPw and then what will happen if the request will receive "Auth-Pw" header?
In that example, PHP would populate two $_SERVER elements: 'PHP_AUTH_PW' from the password, and 'HTTP_AUTH_PW' from the header. ServerRequest only populates PHP_AUTH_PW into $authPw; the HTTP_AUTH_PW value would (per the above description) go into $request->headers['auth-pw']. I hope that made sense; let me know if it did not.
> With those above I would see a set of getters like getContentType() or getAuthPassword() etc. instead of ServerRequest instance properties and a single point to retrieve header values just like well known ParameterBag's from HttpFoundation to operate on get, post, headers etc.
Does the above explanation help to modify your preferences here? If not, we can continue talking about it. -- Paul M. Jones pmjones@pmjones.io http://paul-m-jones.com Modernizing Legacy Applications in PHP https://leanpub.com/mlaphp Solving the N+1 Problem in PHP https://leanpub.com/sn1php
  108438
February 10, 2020 17:02 marandall@php.net (Mark Randall)
On 10/02/2020 16:18, Paul M. Jones wrote:
> Hi Internals, > Thanks in advance for your time and consideration while evaluating it.
I suspect this is a similar sentinment to the previous version, but I can personally see no major benefit to having this as a core extension. I think the reality is that discussing it "on its own merits" is to completely ignore the wider ecosystem that already performs these functions, with more capabilities, and with potentially hundreds of thousands of implementations already in place. Does it add capabilities which cannot exist in userland or cannot exist in a reasonably performant way? Doesn't seem so except for a readonly property. If not, leave it to userland. Mark Randall
  108439
February 10, 2020 17:32 mike@newclarity.net (Mike Schinkel)
> On Feb 10, 2020, at 11:18 AM, Paul M. Jones <pmjones@pmjones.io> wrote: > > Hi Internals, > > After a couple of years of incubation, I am happy to offer a second version of this RFC: > > https://wiki.php.net/rfc/request_response > > It proposes an object-oriented approach around request and response functionality already existing in PHP, in order to reduce the global-state problems that come with superglobals and the various response-related functions. > > The SQLite "about" page says, "Think of SQLite not as a replacement for Oracle but as a replacement for fopen()." <https://www.sqlite.org/about.html> Likewise, think of this RFC not as a replacement for HttpFoundation or PSR-7, or as a model of HTTP messages, but as an object-oriented alternative to superglobals, header(), setcookie(), setrawcookie(), and so on. > > Thanks in advance for your time and consideration while evaluating it.
I like this, a lot. Having a language _standard_ approach means that I could depend on it existing and that all (new) userland code I might want to incorporate into a project would be compatible rather than the current state of 100! different incompatible implementations across frameworks. I had numerous questions and concerns, but after reading through the RFC and the associated links I think you addressed most of them. I do have questions about ServerResponse. It is not clear how that interacts with existing echo, header(), setcookie(), etc? You say it creates a buffer; is $responseSender->send($response) effectively the same as having issued regular echo, header(), setcookie(), etc. all at once? And that there is no global buffer but instead the application would have to manage using a shared $response on its own because each instance of $response has it's own buffer and can be sent at any time into the existing output "stream?" Regarding ServerRequest I saw the comment about using a factory instead of creating an instance and your reason for not going that route. How about instead creating an empty mutable object with the constructor and then a method with an optional array parameter that adds the values and "locks" it to be mutable, i.e. $request = new ServerRequest(); $request->initialize(); // OR $request->initialize([ '_SERVER' => [ 'foo' => 'bar', ], ]); With this approach someone could create a class that contains the ServerRequest and build the object up to be anything they like which could be useful for testing, i.e. $request = new ServerRequest(); $request->get = [ 'foo' => 'bar' ]; $request->cookie = [ 'id' => 123 ]; $request->nv = $_ENV; $request->server = $_SERVER; $request->lock(); I would also suggest considering to add get(), post(), request(), cookie(), server() and end() methods to ServerRequest that would incorporate the functionality of filter_input(). Otherwise we have to bypass the objects to get filtering. In general I think this would definitely be a nice enhancement to PHP, IMO. It would be great to be able to deprecate use of $GLOBALS, etc. with a language _standard_ approach, and flag their use as problematic in any of our code. Would you not also add an option to generate a warning when using them for those who want to deprecate their use in their own code (deprecating across the board would be too extreme give how much CMS and framework code uses them intimately.) Thanks in advance for considering. -Mike
  108507
February 12, 2020 12:47 pmjones@pmjones.io ("Paul M. Jones")
Hi Mike,


> I like this, a lot. ... In general I think this would definitely be a nice enhancement to PHP, IMO.
Thanks for saying!
> I do have questions about ServerResponse. It is not clear how that interacts with existing echo, header(), setcookie(), etc? You say it creates a buffer; is $responseSender->send($response) effectively the same as having issued regular echo, header(), setcookie(), etc. all at once?
That's correct. ServerResponse stores the (mutable) state of the response; ServerResponseSender reads from the ServerResponse and calls header(), setcookie(), etc. with its values.
> each instance of $response has it's own buffer and can be sent at any time
That is also correct.
> Regarding ServerRequest ... How about instead creating an empty mutable object with the constructor and then a method with an optional array parameter that adds the values and "locks" it to be mutable, i.e. > > $request = new ServerRequest(); > $request->initialize(); > // OR > $request->initialize([ > '_SERVER' => [ > 'foo' => 'bar', > ], > ]); > > With this approach someone could create a class that contains the ServerRequest and build the object up to be anything they like which could be useful for testing, i.e. > > $request = new ServerRequest(); > $request->get = [ 'foo' => 'bar' ]; > $request->cookie = [ 'id' => 123 ]; > $request->nv = $_ENV; > $request->server = $_SERVER; > $request->lock();
That is essentially what it does now; the difference is that you mimic the $GLOBALS array at construction time, and the instance locks automatically: $request = new ServerRequest([ '_GET' => ['foo' => 'bar'], '_COOKIE' => ['id' => 123], '_SERVER' => $_SERVER, ]); // $request is now locked The class that contains ServerRequest would then build up that array for the constructor. Do you feel that's close enough to what you're asking for?
> I would also suggest considering to add get(), post(), request(), cookie(), server() and end() methods to ServerRequest
First, I'd have to decline adding request() (or $request) at all; my opinion is that one ought to be reading from $get, $post, $cookies, etc. specifically, not from a pool of those values. Second, if I understand you correctly, I much prefer the property access over the method getter; it just "looks and feels better": $request->get['foo'] vs $request->get()['foo']; Let me know if that makes sense to you or not.
> incorporate the functionality of filter_input(). Otherwise we have to bypass the objects to get filtering.
I don't think you'd have to bypass the objects to get filtering; unless I am missing something, this ... $foo = filter_input(INPUT_GET, 'foo', FILTER_SANITIZE_SPECIAL_CHARS); .... would easily become this: $foo = filter_var($request->get['foo'], FILTER_SANITIZE_SPECIAL_CHARS); There might be behavioral nuances between the two, but the point is that you can still do filtering.
> Would you not also add an option to generate a warning when using them for those who want to deprecate their use in their own code (deprecating across the board would be too extreme give how much CMS and framework code uses them intimately.)
That seems a bit much at this point. ;-) I hope that answers all your questions. -- Paul M. Jones pmjones@pmjones.io http://paul-m-jones.com Modernizing Legacy Applications in PHP https://leanpub.com/mlaphp Solving the N+1 Problem in PHP https://leanpub.com/sn1php
  108511
February 12, 2020 18:21 mike@newclarity.net (Mike Schinkel)
> On Feb 12, 2020, at 7:47 AM, Paul M. Jones <pmjones@pmjones.io> wrote: > > That is essentially what it does now; the difference is that you mimic the $GLOBALS array at construction time, and the instance locks automatically: > > $request = new ServerRequest([ > '_GET' => ['foo' => 'bar'], > '_COOKIE' => ['id' => 123], > '_SERVER' => $_SERVER, > ]); > // $request is now locked > > The class that contains ServerRequest would then build up that array for the constructor. > > Do you feel that's close enough to what you're asking for?
No. IMO it should not lock until explicitly locked. Why? Take a look at WordPress. It does a lot of "fixup" to $_SERVER` variables — to deal with badly implemented web servers — to ensure all known variables have a value and that the format of the value is consistent. If you always lock on instantiation then WordPress would have to default to continue using $_SERVER. Or subclass it, but that would generate copying overhead and it would be too easy for programmers to just call ServerRequest() again. Another option actually would be to allow changes to $_SERVER prior to instantiating ServerRequest() the first time. Frankly though, none of those options should ideal, include your current proposed solution. Basically making a copy of $_SERVER so ServerRequest() is not authoritative, but alternately your solution does not allow frameworks to fix the inconsistency across servers. Maybe what would be needed is a single ServerRequest::initialize( $closure ) where the ciosure can do the fixup. This would need to be called before new ServerRequest() is called after which ServerRequest would be locked for changes.
> First, I'd have to decline adding request() (or $request) at all; my opinion is that one ought to be reading from $get, $post, $cookies, etc. specifically, not from a pool of those values.
That's definitely fair. I almost did not include request() in my suggestion but did because PHP has $_REQUEST.
> Second, if I understand you correctly, I much prefer the property access over the method getter; it just "looks and feels better": > > $request->get['foo'] > vs > $request->get()['foo']; > > Let me know if that makes sense to you or not.
I was actually proposing a third option: $request->get('foo'); Or as I like to do in my own code (to indicate where it comes from): $request->GET('foo'); Why a method is important is to support filters (I guess I assumed that was obvious but realize now it was not.) For example: $request->get('redirect', FILTER_SANITIZE_URL); $request->get('product_id', FILTER_SANITIZE_NUMBER_INT); $request->get('email', FILTER_SANITIZE_EMAIL); You could potentially even have scoped, easier to remember constants that can work with autocomplete: $request->get('redirect', ServerRequest::URL); $request->get('product_id', ServerRequest::INT); $request->get('email', ServerRequest::EMAIL);
> incorporate the functionality of filter_input(). Otherwise we have to bypass the objects to get filtering. > > I don't think you'd have to bypass the objects to get filtering; unless I am missing something, this ... > > $foo = filter_input(INPUT_GET, 'foo', FILTER_SANITIZE_SPECIAL_CHARS); > > ... would easily become this: > > $foo = filter_var($request->get['foo'], FILTER_SANITIZE_SPECIAL_CHARS); > > There might be behavioral nuances between the two, but the point is that you can still do filtering.
But wouldn't it be 1000% easier to write and easier to read code like this? $foo = $request->get( 'foo', ServerRequest::SPECIAL_CHARS); Since filtering $_GET elements is 100% done when accessing them it makes sense to streamline the operation. But if a developer does not want to filter, they just do this: $foo = $request->get( 'foo' );
>> Would you not also add an option to generate a warning when using them for those who want to deprecate their use in their own code (deprecating across the board would be too extreme give how much CMS and framework code uses them intimately.) > > That seems a bit much at this point. ;-)
Really? Seems like this and some guard code is all it would take: ini_set( "disallow_superglobals", true); -Mike
  108548
February 13, 2020 18:34 pmjones@pmjones.io ("Paul M. Jones")
Hi Mike,

Thanks for your continued evaluation of the RFC.

> Take a look at WordPress. It does a lot of "fixup" to $_SERVER` variables — to deal with badly implemented web servers — to ensure all known variables have a value and that the format of the value is consistent. > > ... > > Another option actually would be to allow changes to $_SERVER prior to instantiating ServerRequest() the first time.
Believe it or not, this RFC does make allowance for what you're describing, as a result of requiring a $globals array as the first parameter. An application-specific builder or factory can modify those $globals values as desired, before instantiating ServerRequest with the modified values. For example: namespace App; use ServerRequest; class ServerRequestFactory { public function new(array $globals, ?string $content = null) : ServerRequest { // do fixup to $globals['_SERVER'] ... // ... then: return new ServerRequest($globals, $content); } } $request = (new \App\ServerRequestFactory())->new($GLOBALS); I can easily imagine many ways to achieve the same end (i.e., modification of the constructor arguments before instantiating ServerRequest). Do you get where I'm coming from?
>> First, I'd have to decline adding request() (or $request) at all; my opinion is that one ought to be reading from $get, $post, $cookies, etc. specifically, not from a pool of those values. > > That's definitely fair. I almost did not include request() in my suggestion but did because PHP has $_REQUEST.
Good enough. :-)
> Why a method is important is to support filters (I guess I assumed that was obvious but realize now it was not.) For example: > > $request->get('redirect', FILTER_SANITIZE_URL); > $request->get('product_id', FILTER_SANITIZE_NUMBER_INT); > $request->get('email', FILTER_SANITIZE_EMAIL); > > You could potentially even have scoped, easier to remember constants that can work with autocomplete: > > $request->get('redirect', ServerRequest::URL); > $request->get('product_id', ServerRequest::INT); > $request->get('email', ServerRequest::EMAIL);
[snip] I do see what you mean. Even so, I think filtering is out-of-scope for this RFC. Certainly I want to avoid adding methods to the ServerRequest class, which as envisioned is a bag of values much the same way $_GET, $_POST, $_SERVER, etc. are bags of values.
>>> Would you not also add an option to generate a warning when using them for those who want to deprecate their use in their own code (deprecating across the board would be too extreme give how much CMS and framework code uses them intimately.) >> >> That seems a bit much at this point. ;-) > > Really? Seems like this and some guard code is all it would take: > > ini_set( "disallow_superglobals", true);
Even if that's true (and I think that example masks some complexity) I must maintain that it's out of scope for this RFC. -- Paul M. Jones pmjones@pmjones.io http://paul-m-jones.com Modernizing Legacy Applications in PHP https://leanpub.com/mlaphp Solving the N+1 Problem in PHP https://leanpub.com/sn1php
  108551
February 13, 2020 20:31 mike@newclarity.net (Mike Schinkel)
> On Feb 13, 2020, at 1:34 PM, Paul M. Jones <pmjones@pmjones.io> wrote: > > Hi Mike, > > Thanks for your continued evaluation of the RFC. > >> Take a look at WordPress. It does a lot of "fixup" to $_SERVER` variables — to deal with badly implemented web servers — to ensure all known variables have a value and that the format of the value is consistent. >> >> ... >> >> Another option actually would be to allow changes to $_SERVER prior to instantiating ServerRequest() the first time. > > Believe it or not, this RFC does make allowance for what you're describing, as a result of requiring a $globals array as the first parameter. An application-specific builder or factory can modify those $globals values as desired, before instantiating ServerRequest with the modified values. > > For example: > > namespace App; > > use ServerRequest; > > class ServerRequestFactory > { > public function new(array $globals, ?string $content = null) : ServerRequest > { > // do fixup to $globals['_SERVER'] ... > > // ... then: > return new ServerRequest($globals, $content); > } > } > > $request = (new \App\ServerRequestFactory())->new($GLOBALS); > > I can easily imagine many ways to achieve the same end (i.e., modification of the constructor arguments before instantiating ServerRequest). > > Do you get where I'm coming from?
Ah, I see now. That does allow for changing. OTOH, what it does not do is ensure that everyone looking at the values are looking at the fixed-up values, nor does it offer any way to cache the original value except to do so at the top of every PHP file that is a URL endpoint. I was hoping that your proposal was having a way to capture the original values before any PHP code could change them AND also then allow for fixed-up values to be provided for _every_ instantiation of ServerRequest(). This in case a library were to use it, I would not want the library to be getting the non-fixed up values. So in that respect, this is worse than what we have now.
>> Why a method is important is to support filters (I guess I assumed that was obvious but realize now it was not.) For example: >> >> $request->get('redirect', FILTER_SANITIZE_URL); >> $request->get('product_id', FILTER_SANITIZE_NUMBER_INT); >> $request->get('email', FILTER_SANITIZE_EMAIL); >> >> You could potentially even have scoped, easier to remember constants that can work with autocomplete: >> >> $request->get('redirect', ServerRequest::URL); >> $request->get('product_id', ServerRequest::INT); >> $request->get('email', ServerRequest::EMAIL); > > [snip] > > I do see what you mean. Even so, I think filtering is out-of-scope for this RFC.
I would implore you to reconsider. Without incorporating the functionality of filter_input() you have effectively neutered much of the value I envision your RFC having. In the USA we might say "Besides that Mrs. Lincoln, how was the play?" If I had a vote I would vote I would enthusiastically vote for the RFC if it includes filter_input() functionality. But I would vote against this RFC if it omits filter_input() functionality because it would mean another subsystem in core that does not actually address day-to-day concerns.
> Certainly I want to avoid adding methods to the ServerRequest class, which as envisioned is a bag of values much the same way $_GET, $_POST, $_SERVER, etc. are bags of values.
Unless I misunderstand avoiding methods in ServerRequest is an arbitrary constraint which has no real-world requirement. And if your motivation is to only mirror what exists then filter_input() is a function so an equivalent in ServerRequest should be a method and thus this in not inconsistent with your mirroring aspect. -Mike
  108627
February 16, 2020 23:34 rowan.collins@gmail.com (Rowan Tommins)
On 13/02/2020 20:31, Mike Schinkel wrote:
> If I had a vote I would vote I would enthusiastically vote for the RFC if it includes filter_input() functionality. But I would vote against this RFC if it omits filter_input() functionality because it would mean another subsystem in core that does not actually address day-to-day concerns.
I think you are over-estimating how central the filter API is to most people's workflow with requests. I think that's partly because designing a good validation API is hard, but also because filter_input in particular is a combination of three different concerns: 1) Fetching the raw information about the incoming HTTP request from the web server (the "SAPI") 2) Parsing that raw information into individual fields 3) Validating those fields against expected type constraints The superglobals already combine concerns 1 and 2, and the filter API adds concern 3; but to do so they all assume that the user is basically writing a CGI wrapper for some HTML forms. The modern reality is rather different, and step 2 in particular is much more variable: - Rather than query string parameters, it might involve extracting parameters from an SEO URL like "/products/123-acme-thingummy" or a RESTish URL like "/products/123/description/en-GB" - Rather than submitted form data, it might involve parsing JSON from an AJAX request or API call I would love to see new APIs that take a step back from the legacy, and tackle each of these concerns separately, based on modern requirements. For concern 1, getting data out of the web server, I'd love to see: - A more intuitive way to get the raw request body than file_get_contents('php://input') - A more reliable way to get the URL the user requested than checking 5 different variables in $_SERVER to handle different deployment methods (see e.g. [1] and [2] for the lengths libraries go to for this) - A proper distinction between HTTP headers, server status variables, and environment variables, because CGI name-mangling is legacy cruft that users shouldn't need to learn For concern 2, parsing that data, I'd love to see: - A better API than parse_str for parsing arbitrary strings in application/x-www-form-urlencoded format - A way to parse data in multipart/form-data format decoupled from the current HTTP request - Tools for working with Content-Type strings, such as a function for correctly parsing things like "text/html;charset=UTF-8", and constants for common MIME types Concern 3, filtering / sanitising / validating, I think is a really hard problem space, and I don't think there will ever be one implementation that suits all cases. A similar "shopping list" could probably be made for responses, but if we decoupled the pieces, we wouldn't have to perfect them all at once; instead, we could provide building blocks that make userland implementations easier. [1] https://github.com/symfony/symfony/blob/9acb06041cc88b5c14d40f8cd9a74dd14d7ac786/src/Symfony/Component/HttpFoundation/Request.php#L1741 [2] https://github.com/laminas/laminas-diactoros/blob/b36d6bf376b03dfc3190b1065630090e57f2e20d/src/functions/marshal_uri_from_sapi.php Regards, -- Rowan Tommins (né Collins) [IMSoP]
  108628
February 17, 2020 00:45 mike@newclarity.net (Mike Schinkel)
> On Feb 16, 2020, at 6:34 PM, Rowan Tommins collins@gmail.com> wrote: > > On 13/02/2020 20:31, Mike Schinkel wrote: >> If I had a vote I would vote I would enthusiastically vote for the RFC if it includes filter_input() functionality. But I would vote against this RFC if it omits filter_input() functionality because it would mean another subsystem in core that does not actually address day-to-day concerns. > > I think you are over-estimating how central the filter API is to most people's workflow with requests. I think that's partly because designing a good validation API is hard, but also because filter_input in particular is a combination of three different concerns:
I think your latter points are orthogonal to this. And that you are taking my advocacy for adding filtering to apply too literally to only the specific implementations in filter_input(). I can see addressing your comments below *and* having a filtering method built into these objects. Possibly even with applicable method names as opposed to a 2nd type parameter, like: $request->getInt('db_id'); $request->getJson('package'); $request->getUrl('return_url');
> 1) Fetching the raw information about the incoming HTTP request from the web server (the "SAPI") > 2) Parsing that raw information into individual fields > 3) Validating those fields against expected type constraints > > The superglobals already combine concerns 1 and 2, and the filter API adds concern 3; but to do so they all assume that the user is basically writing a CGI wrapper for some HTML forms. > > The modern reality is rather different, and step 2 in particular is much more variable: > > - Rather than query string parameters, it might involve extracting parameters from an SEO URL like "/products/123-acme-thingummy" or a RESTish URL like "/products/123/description/en-GB" > - Rather than submitted form data, it might involve parsing JSON from an AJAX request or API call > > > I would love to see new APIs that take a step back from the legacy, and tackle each of these concerns separately, based on modern requirements. > > For concern 1, getting data out of the web server, I'd love to see: > > - A more intuitive way to get the raw request body than file_get_contents('php://input') > - A more reliable way to get the URL the user requested than checking 5 different variables in $_SERVER to handle different deployment methods (see e.g. [1] and [2] for the lengths libraries go to for this) > - A proper distinction between HTTP headers, server status variables, and environment variables, because CGI name-mangling is legacy cruft that users shouldn't need to learn > > For concern 2, parsing that data, I'd love to see: > > - A better API than parse_str for parsing arbitrary strings in application/x-www-form-urlencoded format > - A way to parse data in multipart/form-data format decoupled from the current HTTP request > - Tools for working with Content-Type strings, such as a function for correctly parsing things like "text/html;charset=UTF-8", and constants for common MIME types > > Concern 3, filtering / sanitising / validating, I think is a really hard problem space, and I don't think there will ever be one implementation that suits all cases. > > A similar "shopping list" could probably be made for responses, but if we decoupled the pieces, we wouldn't have to perfect them all at once; instead, we could provide building blocks that make userland implementations easier.
Decoupling is a valid approach. But given how much work it is get to an RFC over the line, it feels like decoupling would end up with a lot more work, lengthen the timeline to achieve base level functionality, and add uncertainty to whether it will even happen whereas handling the 20% now that we need 80% of the time would mean the API would be mostly fully usable out of the gate. -Mike
  108635
February 17, 2020 07:51 rowan.collins@gmail.com (Rowan Tommins)
On 17 February 2020 00:45:26 GMT+00:00, Mike Schinkel <mike@newclarity.net> wrote:
>I think your latter points are orthogonal to this. And that you are >taking my advocacy for adding filtering to apply too literally to only >the specific implementations in filter_input().
Firstly, I deliberately didn't say "the filter API isn't well designed", I said "designing a good validation API is hard". In particular, finding the balance between flexibility and simplicity is key. Including a single blessed validation API in something as fundamental as a request object should take a lot of careful design, not be an afterthought to something like the current RFC. I also talked specifically about moving away from the old assumptions of CGI. What does it mean to "filter" a JSON body? We could check it's valid JSON, but parsing it will reveal that anyway. We could automatically parse it in the request object, and have "filters" apply to individual elements; but where would the user supply parser options, and how would you specify nested elements? Or we could keep it as a dumb string, and leave the validation to other systems, like a JSON Schema validator. Even with a plain HTML form, you might be using a form builder and want to associate your validation with the form definition rather than having it baked into the request object.
>But given how much work it is get to an RFC over the line, it feels >like decoupling would end up with a lot more work, lengthen the >timeline to achieve base level functionality, and add uncertainty to >whether it will even happen whereas handling the 20% now that we need >80% of the time would mean the API would be mostly fully usable out of >the gate.
Funny, I see the exact opposite: trying to build a single set of classes which include a system for getting global state AND a system for parsing it in different ways AND an in-built validation API seems like a mammoth task. And if you keep it monolithic, any feature you miss or make a mistake on is much harder to fix later. Regards, -- Rowan Tommins [IMSoP]
  108440
February 10, 2020 20:44 johannes@schlueters.de (Johannes =?ISO-8859-1?Q?Schl=FCter?=)
Hi,

On Mon, 2020-02-10 at 10:18 -0600, Paul M. Jones wrote:
> After a couple of years of incubation, I am happy to offer a second > version of this RFC: > > https://wiki.php.net/rfc/request_response
What indication is there that this will be more successfull than the filter API? The filter API was our previous attempt to improve upon input handling. With this new API we have three major ways (superglobals, filter, new API) Historically PHP isn't really successful in providing higher level APIs to things and I think leaving this to userland is very viable, since composer and performance improvements in later PHP 5 and PHP 7. Also use of $_* is fast to grep for and gives me directly in the grep an idea about the mess-factor of a code base, tracing all calls to a member of an instance of a class is harder. (and yes, references etc. to super globals aren't trivial to trace, but also rare) Let PHP provide the access to the data and promote the API library of the year. johannes
  108510
February 12, 2020 16:54 pmjones@pmjones.io ("Paul M. Jones")
Hi Johannes,


> What indication is there that this will be more successfull than the > filter API?
Fair question. While I can't say how successful (or not) ext/filter has been, I *can* say that the proposal does not present extraordinary or dramatically different ways of working than PHP does currently. The extent of the RFC is only to provide behaviors similar to what PHP already does, in object-oriented dress. That is, whereas ext/filter might have been been described as "a new and different way of working", the concepts and functions in this RFC (even down to the method and property names!) should be immediately familiar even to junior PHP developers.
> Historically PHP isn't really successful in providing higher level APIs > to things and I think leaving this to userland is very viable, since > composer and performance improvements in later PHP 5 and PHP 7.
I addressed the "userland" argument in my response to Mark Randall; please forgive me for not repeating it here.
> Also use of $_* is fast to grep for and gives me directly in the grep > an idea about the mess-factor of a code base, tracing all calls to a > member of an instance of a class is harder. (and yes, references etc. > to super globals aren't trivial to trace, but also rare)
I feel your pain! I do a lot of legacy work too. The `$_` signature makes grepping easy, so that I can find places where there is spooky-action-at-a-distace from globally mutable state. However, ServerRequest is intended to reduce or remove that globally mutable state. The $request->get property is readonly, and neither global nor superglobal. So, while it is tougher to grep for `->get` and know that you have found a ServerRequest property, the *need* to do so should be much less even in legacy codebases.
> Let PHP provide the access to the data and promote the API library of the year.
"API library of the year" -- heh. To be fair, though, the API presented by ServerRequest and ServerResponse is highly similar to that presented by PHP itself; far from being "API of the year" it honors existing PHP functionality quite closely. -- Paul M. Jones pmjones@pmjones.io http://paul-m-jones.com Modernizing Legacy Applications in PHP https://leanpub.com/mlaphp Solving the N+1 Problem in PHP https://leanpub.com/sn1php
  108453
February 11, 2020 09:33 jan@horde.org (Jan Schneider)
Zitat von Paul M. Jones <pmjones@pmjones.io>:

> Hi Internals, > > After a couple of years of incubation, I am happy to offer a second > version of this RFC: > > https://wiki.php.net/rfc/request_response > > It proposes an object-oriented approach around request and response > functionality already existing in PHP, in order to reduce the > global-state problems that come with superglobals and the various > response-related functions.
I like this proposal a lot, since it provides a clear, concise interface to these commonly uses, yet inconveniant to use, existing functions and variables without having to always use a full-featured userland library. Speaking of interfaces: since you suggest using decoration and composition over extension for ServerResponse, I am missing a ServerResponseInterface, so that you can easily typehint such userland decorators. -- Jan Schneider The Horde Project https://www.horde.org/
  108471
February 11, 2020 12:42 albertcasademont@gmail.com (Albert Casademont)
This is very interesting, thanks!

Would it make sense to also add an INI setting to disable superglobals and
response functions?


On Tue, Feb 11, 2020 at 10:34 AM Jan Schneider <jan@horde.org> wrote:

> > Zitat von Paul M. Jones <pmjones@pmjones.io>: > > > Hi Internals, > > > > After a couple of years of incubation, I am happy to offer a second > > version of this RFC: > > > > https://wiki.php.net/rfc/request_response > > > > It proposes an object-oriented approach around request and response > > functionality already existing in PHP, in order to reduce the > > global-state problems that come with superglobals and the various > > response-related functions. > > I like this proposal a lot, since it provides a clear, concise > interface to these commonly uses, yet inconveniant to use, existing > functions and variables without having to always use a full-featured > userland library. > Speaking of interfaces: since you suggest using decoration and > composition over extension for ServerResponse, I am missing a > ServerResponseInterface, so that you can easily typehint such userland > decorators. > > -- > Jan Schneider > The Horde Project > https://www.horde.org/ > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
  108476
February 11, 2020 12:59 harry@rhsoft.net ("Reindl Harald (privat)")
Am 11.02.20 um 13:42 schrieb Albert Casademont:
> This is very interesting, thanks! > > Would it make sense to also add an INI setting to disable superglobals and > response functions?
no because changing basic language behavior that way is not helpful for code meant to run everywhere and not stop working just because tomorrow someone changed a random ini setting
  108491
February 11, 2020 17:22 albertcasademont@gmail.com (Albert Casademont)
On Tue, Feb 11, 2020 at 1:59 PM Reindl Harald (privat) <harry@rhsoft.net>
wrote:

> > > Am 11.02.20 um 13:42 schrieb Albert Casademont: > > This is very interesting, thanks! > > > > Would it make sense to also add an INI setting to disable superglobals > and > > response functions? > > no because changing basic language behavior that way is not helpful for > code meant to run everywhere and not stop working just because tomorrow > someone changed a random ini setting >
That could be said for all INI settings: yes they can break things if you touch them without knowing what they do. I think it might make sense to be able to disable superglobals and response functions if your codebase is committed to using the new classes, much like the old "register_globals" did. Why pollute the global namespace if you don't need them?
  108523
February 12, 2020 22:09 pmjones@pmjones.io ("Paul M. Jones")
Hi Albert,

> On Feb 11, 2020, at 11:22, Albert Casademont <albertcasademont@gmail.com> wrote: > >> Am 11.02.20 um 13:42 schrieb Albert Casademont: >> >>> This is very interesting, thanks! >>> >>> Would it make sense to also add an INI setting to disable superglobals >>> and response functions? >> >> no because changing basic language behavior that way is not helpful for >> code meant to run everywhere and not stop working just because tomorrow >> someone changed a random ini setting > > That could be said for all INI settings: yes they can break things if you > touch them without knowing what they do. > > I think it might make sense to be able to disable superglobals and response > functions if your codebase is committed to using the new classes, much like > the old "register_globals" did. Why pollute the global namespace if you > don't need them?
I share Harald's opinion here. I think a .ini setting to disable superglobals and the response-related functions is out-of-scope for this RFC. -- Paul M. Jones pmjones@pmjones.io http://paul-m-jones.com Modernizing Legacy Applications in PHP https://leanpub.com/mlaphp Solving the N+1 Problem in PHP https://leanpub.com/sn1php
  108516
February 12, 2020 19:44 pmjones@pmjones.io (Paul M . Jones)
Hi Jan,


> I like this proposal a lot, since it provides a clear, concise interface to these commonly uses, yet inconveniant to use, existing functions and variables without having to always use a full-featured userland library.
Glad to hear the RFC is hitting the right notes!
> Speaking of interfaces: since you suggest using decoration and composition over extension for ServerResponse, I am missing a ServerResponseInterface, so that you can easily typehint such userland decorators.
I am not opposed to a ServerResponseInterface, if John Boehr is not -- though at the same time, I am wary of expanding the number of new declarations too much. -- Paul M. Jones pmjones@pmjones.io http://paul-m-jones.com Modernizing Legacy Applications in PHP https://leanpub.com/mlaphp Solving the N+1 Problem in PHP https://leanpub.com/sn1php
  108489
February 11, 2020 16:58 ben@benramsey.com (Ben Ramsey)
> On Feb 10, 2020, at 10:18, Paul M. Jones <pmjones@pmjones.io> wrote: > > Hi Internals, > > After a couple of years of incubation, I am happy to offer a second version of this RFC: > > https://wiki.php.net/rfc/request_response > > It proposes an object-oriented approach around request and response functionality already existing in PHP, in order to reduce the global-state problems that come with superglobals and the various response-related functions. > > The SQLite "about" page says, "Think of SQLite not as a replacement for Oracle but as a replacement for fopen()." <https://www.sqlite.org/about.html> Likewise, think of this RFC not as a replacement for HttpFoundation or PSR-7, or as a model of HTTP messages, but as an object-oriented alternative to superglobals, header(), setcookie(), setrawcookie(), and so on. > > Thanks in advance for your time and consideration while evaluating it.
Regarding the array of arrays for $accept* and $forwarded, what are your thoughts on using value objects with properties, rather than arrays with keys? AcceptValue * string $value * string $quality * array $params * ?string $type * ?string $subtype ForwardedValue * string $by * string $for * string $host * string $proto Cheers, Ben
  108527
February 13, 2020 02:08 pmjones@pmjones.io (Paul M . Jones)
Hi Ben,


> On Feb 11, 2020, at 10:58, Ben Ramsey <ben@benramsey.com> wrote: > > Regarding the array of arrays for $accept* and $forwarded, what are your thoughts on using value objects with properties, rather than arrays with keys?
It's a fair suggestion, but I am not keen to expand the number of new declarations any more than necessary. To play with your idea a little bit, let's say we start with ... - ServerRequest - ServerResponse - ServerResponseSender .... then to bring in Jan Schneider's suggestion, we add ServerResponseInterface (4 classes). Then we add the value objects themselves: ServerRequestAcceptValue and ServerRequestForwardValue. That's 6 classes. That's maybe not so bad. But, once we start down this path, it's easy for me to imagine what comes after. For example, since we're trying to do "the right thing," someone will suggest to typehint the properties "correctly." Instead of arrays of value objects, they probably ought to be collection objects. That gets us ServerRequestAcceptValueCollection and ServerRequestForwardValueCollection, and we're at 8 classes. Then someone is likely to say that there ought to be interfaces for all those so their implementations can get swapped out. That's ... - ServerRequest - ServerResponse - ServerResponseInterface - ServerResponseSender - ServerRequestAcceptValue - ServerRequestAcceptValueInterface - ServerRequestAcceptValueCollection - ServerRequestAcceptValueCollectionInterface - ServerRequestForwardValue - ServerRequestForwardValueInterface - ServerRequestForwardValueCollection - ServerRequestForwardValueCollectionInterface .... 12 classes and interfaces. And probably not done yet, once others really sink their teeth into it. Now, that's not a *wrong* approach -- but it does seem like overkill to me. Or, we could avoid starting down that path and stick with arrays, which in ServerRequest are read-only and immutable and get us all the things we actually need for our daily work here. Of those two, I prefer the minimalist approach of arrays for this RFC; the "effort-to-benefit" ratio is much better. -- Paul M. Jones pmjones@pmjones.io http://paul-m-jones.com Modernizing Legacy Applications in PHP https://leanpub.com/mlaphp Solving the N+1 Problem in PHP https://leanpub.com/sn1php
  108493
February 11, 2020 18:30 pmjones@pmjones.io (Paul M . Jones)
Hi Mark,

> On Feb 10, 2020, at 11:02, Mark Randall <marandall@php.net> wrote: > > I suspect this is a similar sentinment to the previous version, but I can personally see no major benefit to having this as a core extension. > > I think the reality is that discussing it "on its own merits" is to completely ignore the wider ecosystem that already performs these functions, with more capabilities, and with potentially hundreds of thousands of implementations already in place. > > Does it add capabilities which cannot exist in userland or cannot exist in a reasonably performant way? Doesn't seem so except for a readonly property. > > If not, leave it to userland.
I completely understand that sentiment; I recognize that it is shared by others on this list and elsewhere. But if you'll allow me, I'd like to present a counterargument in relation to previous PHP extensions. When ext/pdo was added to core, there was already a "wider ecosystem that already performs these functions, with more capabilities, and with potentially hundreds of thousands of implementations already in place." Some of those implementations at the time included (I'm working from memory here) AdoDB, Metabase, MDB, PEAR_DB, and many more that I cannot recall. PDO did not (to my knowledge) "add capabilities which cannot exist in userland or cannot exist in a reasonably performant way". (I'll grant that FETCH_INTO_OBJECT setting properties directly without using the constructor was not possible in userland, but that's an exception that tests the rule.) Indeed, PDO had a relatively reduced feature set in comparison to some of those userland libraries, especially AdoDB. And yet, PDO has turned out to be of great benefit, because it brought together features into core that (figuratively speaking) everybody needed and was rewriting in userland over and over. PDO is the strongest example here, but depending on how you count, there are 2-3 other extensions that also serve: ext/date, ext/phar, and (reaching back to antiquity) ext/session. So, there is a long history of widely-needed userland functionality being brought into core. I would say this RFC is a pretty tame example of doing so; the proposal presented here is very similar to the way PHP itself already does things, just wrapped in object properties and methods, and is very similar to how things are being done across a wide swath of userland. Now, it is possible that the objections you raise *should* have prevented PDO (et al.) from going into core. If that is the case, and (in hindsight) we think it was a mistake to allow them, then consistency alone makes your objections valid here as well. However, if (in hindsight) it was *not* a mistake to allow those extensions, then the raised objections are not especially strong arguments against this RFC. That's not to say "because PDO was allowed into core, this RFC must therefore be allowed into core" but to say "those objections alone were not a barrier to PDO, so they alone should not be a barrier to this RFC". I hope that's an understandable counterargument, even if you don't agree with it. -- Paul M. Jones pmjones@pmjones.io http://paul-m-jones.com Modernizing Legacy Applications in PHP https://leanpub.com/mlaphp Solving the N+1 Problem in PHP https://leanpub.com/sn1php
  108512
February 12, 2020 18:20 me@kelunik.com (Niklas Keller)
> Hi Internals, > > After a couple of years of incubation, I am happy to offer a second version of this RFC: > > https://wiki.php.net/rfc/request_response > > It proposes an object-oriented approach around request and response functionality already existing in PHP, in order to reduce the global-state problems that come with superglobals and the various response-related functions. > > The SQLite "about" page says, "Think of SQLite not as a replacement for Oracle but as a replacement for fopen()." <https://www.sqlite.org/about.html> Likewise, think of this RFC not as a replacement for HttpFoundation or PSR-7, or as a model of HTTP messages, but as an object-oriented alternative to superglobals, header(), setcookie(), setrawcookie(), and so on. > > Thanks in advance for your time and consideration while evaluating it.
Hey Paul, I think the request / response API is entirely fine being solved in userland instead of in php-src. However, since you already made such a proposal, I want to give some feedback: Naming I think we shouldn't take over the naming of the super globals, e.g. $_GET really contains the query parameters and has nothing to do with GET or POST, so $request->getQueryParameter(...) would be a better name. Type Safety I think the API should be type safe. Currently $request->get['key'] can be null | string | string[] | ... Most parameters only appear a single time, so a method returning the first parameter value or null could be used instead. API Issues I don't see any reason to keep specials such as $_SERVER['HTTP_X_REQUESTED_WITH'] vs. $request->requestedWith, which is just another HTTP header. If there's $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] => $request->method and $_SERVER['REQUEST_METHOD'] => $request->method, how can I get the original (actual) method? Given 'echo $content; => $response->setContent($content);', shouldn't this rather be something like `addContent()`? How does streaming responses work? There's ServerResponseSender, but I think this should be part of the Response API. The Request object should be mutable, e.g. you might want to change the client IP to be based on a x-forwarded-header instead of the TCP source address. Other Commentary
> A: It supports async exactly as much as PHP itself does.
Not really. PHP has built-in stream_select / non-blocking streams, so supports the tools required for async out of the box. Regards, Niklas
  108513
February 12, 2020 18:39 mike@newclarity.net (Mike Schinkel)
> On Feb 12, 2020, at 1:20 PM, Niklas Keller <me@kelunik.com> wrote: > I think the request / response API is entirely fine being solved in > userland instead of in php-src.
I want to disagree in support of this RFC. While is *has* been solved in userland, but it has been solved 25+ times in 25+ different incompatible ways. This RFC would allow for a *single* solution that is compatible across all frameworks, and frameworks could create a shim to support their existing implementations. And given that it would be in core vs. userland, this would *not* apply: https://xkcd.com/927/ The one concern should be if there is anything about this RFC that would make writing such a shim impossible for any framework vendor?
> I think we shouldn't take over the naming of the super globals, e.g. > $_GET really contains the query parameters and has nothing to do with > GET or POST, so $request->getQueryParameter(...) would be a better > name.
Please no. That is all too Java-esque verbosity. Very commonly used functionality can afford to be concise like $request->get() because it would be well-known and not have to compete with lots of other functionality for mindshare. Thus no need for this to be so precise and have such long names when everyone writing PHP would soon lean what it means.
> I don't see any reason to keep specials such as > $_SERVER['HTTP_X_REQUESTED_WITH'] vs. $request->requestedWith, which > is just another HTTP header.
Reason: $request->requestedWith can be type-safe, something you asked for in another context. $_SERVER['HTTP_X_REQUESTED_WITH'] cannot be type safe without special cases in the language.
> The Request object should be mutable, e.g. you might want to change > the client IP to be based on a x-forwarded-header instead of the TCP > source address.
I totally agree it needs to be mutable, but only prior to first use. After the first use having it be locked would gain us the benefits of robustness that immutability can provide. -Mike
  108520
February 12, 2020 21:40 php.lists@allenjb.me.uk (AllenJB)
On 12/02/2020 18:20, Niklas Keller wrote:
> Type Safety > > I think the API should be type safe. Currently $request->get['key'] > can be null | string | string[] | ... Most parameters only appear a > single time, so a method returning the first parameter value or null > could be used instead.
I may be misunderstanding what you're suggesting here, but it seems you're proposing to have ->get['key'] only return null|string, getting rid of the arrays of parameter values ( ?key[]=value1&key[]=value2 ) functionality here. (And I assume you intend the same for ->post['key'] ) If so, I would say this is a bad idea. I and a lot of other devs use this functionality frequently (more often in POST, but often enough in GET for purposes such as search pages). And how would multiple select inputs be handled? AllenJB
  108534
February 13, 2020 10:52 come.chilliet@fusiondirectory.org (=?ISO-8859-1?Q?C=F4me?= Chilliet)
Le mercredi 12 février 2020, 19:20:56 CET Niklas Keller a écrit :
> Naming > > I think we shouldn't take over the naming of the super globals, e.g. > $_GET really contains the query parameters and has nothing to do with > GET or POST, so $request->getQueryParameter(...) would be a better > name.
I think this remark is really on point. GET and POST are HTTP methods and not ways of passing data. You can have query parameters on any request, and you can have POST data with a lot of other HTTP methods, as is commonly used in REST APIs. I do not like the name getQueryParameter at first sight, but I do not like get() and post() for getting values from $_GET and $_POST. I would expect a method named post to actually post something, not just get a value. -- Côme Chilliet FusionDirectory - https://www.fusiondirectory.org
  108535
February 13, 2020 11:23 me@ekins.space ("Ekin B.")
Hi all,

> On February 13, 2020 1:52 PM Côme Chilliet chilliet@fusiondirectory.org> wrote: > > > Le mercredi 12 février 2020, 19:20:56 CET Niklas Keller a écrit : > > Naming > > > > I think we shouldn't take over the naming of the super globals, e.g. > > $_GET really contains the query parameters and has nothing to do with > > GET or POST, so $request->getQueryParameter(...) would be a better > > name. > > I think this remark is really on point. > GET and POST are HTTP methods and not ways of passing data. You can have query parameters on any request, and you can have POST data with a lot of other HTTP methods, as is commonly used in REST APIs. > > I do not like the name getQueryParameter at first sight, but I do not like get() and post() for getting values from $_GET and $_POST. > I would expect a method named post to actually post something, not just get a value. > > -- > Côme Chilliet > FusionDirectory - https://www.fusiondirectory.org > > --
I agree that this is indeed on point. Personally I would like $request->getQueryParameter() and $request->getBody() over $request->get() and $request->post(). In the former case you are asking for the parameters in the query string (query component of the URI) and the latter is to get the request body. These method names make it very clear what you're asking for. What kind of bothers me in general about this RFC is that... we are talking about Request and Response and these are standard HTTP components, yet the RFC is about providing wrappers for existing global PHP variables and functions. It does not try to model these components as they are in their standard, nor aim to do so. This is why I believe it's better to leave it to userland implementations. Besides the fact that they are widely adopted, they attempt to follow the specification they are providing. Regards, Ekin H. Bayar https://ekins.space https://heap.space
  108581
February 14, 2020 15:47 pmjones@pmjones.io ("Paul M. Jones")
Hi Côme & Niklas,

> On Feb 13, 2020, at 04:52, Côme Chilliet chilliet@fusiondirectory.org> wrote: > > Le mercredi 12 février 2020, 19:20:56 CET Niklas Keller a écrit : > >> Naming >> >> I think we shouldn't take over the naming of the super globals, e.g. >> $_GET really contains the query parameters and has nothing to do with >> GET or POST, so $request->getQueryParameter(...) would be a better >> name. > > I think this remark is really on point. > GET and POST are HTTP methods and not ways of passing data. You can have query parameters on any request, and you can have POST data with a lot of other HTTP methods, as is commonly used in REST APIs.
Your comments on naming are well-made. While working on the implementation, we tried out $query instead of $get, on exactly the premise that you state: i.e., that `$_GET` holds the query parameters, and has nothing to do with the GET method. But in the end, we settled on mapping more directly from `$_GET` => `$get`, and `$_POST => $post`. Having said that, we are willing to revisit that naming decision if there's support for doing so. Perhaps: - rename $get to $query, populating it from `$globals['_GET']`, on the basis stated above - rename $post to $input, populating it from `$globals['_POST']`, on the basis that it typically relates to the parsed form of php://input Your (and/or anyone else's) thoughts on that? -- Paul M. Jones pmjones@pmjones.io http://paul-m-jones.com Modernizing Legacy Applications in PHP https://leanpub.com/mlaphp Solving the N+1 Problem in PHP https://leanpub.com/sn1php
  108582
February 14, 2020 16:03 benjamin.morel@gmail.com (Benjamin Morel)
> > Having said that, we are willing to revisit that naming decision if > there's support for doing so. Perhaps: > - rename $get to $query, populating it from `$globals['_GET']`, on the > basis stated above > - rename $post to $input, populating it from `$globals['_POST']`, on the > basis that it typically relates to the parsed form of php://input > Your (and/or anyone else's) thoughts on that?
Symfony uses $request->query and $request->request for $_GET and $_POST, respectively. I like $request->query for query params, but always found $request->request for body parameters confusing. I like $request->input a bit better, although I'd be interested to hear more ideas on this one. — Benjamin On Fri, 14 Feb 2020 at 16:47, Paul M. Jones <pmjones@pmjones.io> wrote:
> Hi Côme & Niklas, > > > On Feb 13, 2020, at 04:52, Côme Chilliet < > come.chilliet@fusiondirectory.org> wrote: > > > > Le mercredi 12 février 2020, 19:20:56 CET Niklas Keller a écrit : > > > >> Naming > >> > >> I think we shouldn't take over the naming of the super globals, e.g. > >> $_GET really contains the query parameters and has nothing to do with > >> GET or POST, so $request->getQueryParameter(...) would be a better > >> name. > > > > I think this remark is really on point. > > GET and POST are HTTP methods and not ways of passing data. You can have > query parameters on any request, and you can have POST data with a lot of > other HTTP methods, as is commonly used in REST APIs. > > Your comments on naming are well-made. > > While working on the implementation, we tried out $query instead of $get, > on exactly the premise that you state: i.e., that `$_GET` holds the query > parameters, and has nothing to do with the GET method. But in the end, we > settled on mapping more directly from `$_GET` => `$get`, and `$_POST => > $post`. > > Having said that, we are willing to revisit that naming decision if > there's support for doing so. Perhaps: > > - rename $get to $query, populating it from `$globals['_GET']`, on the > basis stated above > - rename $post to $input, populating it from `$globals['_POST']`, on the > basis that it typically relates to the parsed form of php://input > > Your (and/or anyone else's) thoughts on that? > > > -- > Paul M. Jones > pmjones@pmjones.io > http://paul-m-jones.com > > Modernizing Legacy Applications in PHP > https://leanpub.com/mlaphp > > Solving the N+1 Problem in PHP > https://leanpub.com/sn1php > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
  108584
February 14, 2020 16:44 weirdan@gmail.com (Bruce Weirdan)
On Fri, Feb 14, 2020 at 5:47 PM Paul M. Jones <pmjones@pmjones.io> wrote:
>
> - rename $get to $query, populating it from `$globals['_GET']`, on the basis stated above > - rename $post to $input, populating it from `$globals['_POST']`, on the basis that it typically relates to the parsed form of php://input
What about $query and $body? That would be closer to the terminology used in HTTP RFCs. -- Best regards, Bruce Weirdan mailto:weirdan@gmail.com
  108585
February 14, 2020 16:47 benjamin.morel@gmail.com (Benjamin Morel)
> > What about $query and $body? That would be closer to the terminology > used in HTTP RFCs.
The problem is that $body is typically used to get the raw message body as a string or stream. I was thinking more something along the lines of $bodyParams, which is more verbose but leaves no ambiguity: *$queryParams* and *$bodyParams*. — Benjamin
  108589
February 14, 2020 22:12 pmjones@pmjones.io ("Paul M. Jones")
Hi all,

> On Feb 14, 2020, at 10:47, Benjamin Morel morel@gmail.com> wrote: > >> >> What about $query and $body? That would be closer to the terminology >> used in HTTP RFCs. > > > The problem is that $body is typically used to get the raw message body as > a string or stream. > > I was thinking more something along the lines of $bodyParams, which is more > verbose but leaves no ambiguity: *$queryParams* and *$bodyParams*.
I get the desire to disambiguate. But as an added consideration, there's a desire for consistency; when adding a -Params suffix to those names, it might then make sense to have $serverParams, $cookieParams, etc. Looking at it that way, I don't think a -Params suffix is necessary. I would think $query would be enough. As for the other name, the one for the $_POST equivalent, $body doesn't seem quite right to me; it seems a little close to $content. I've also been thinking about $values, $params, $parsedContent, $contentValues, $bodyValues, $contentArray, and other variations with and without prefixes and suffixes, but $input is the one that feels like the least-terrible alternative to $post for me, esp. given the connection to php://input. -- Paul M. Jones pmjones@pmjones.io http://paul-m-jones.com Modernizing Legacy Applications in PHP https://leanpub.com/mlaphp Solving the N+1 Problem in PHP https://leanpub.com/sn1php
  108591
February 15, 2020 08:01 larry@garfieldtech.com ("Larry Garfield")
On Fri, Feb 14, 2020, at 10:47 AM, Benjamin Morel wrote:
> > > > What about $query and $body? That would be closer to the terminology > > used in HTTP RFCs. > > > > The problem is that $body is typically used to get the raw message body as > a string or stream. > I was thinking more something along the lines of $bodyParams, which is more > verbose but leaves no ambiguity: *$queryParams* and *$bodyParams*. > > — Benjamin
Data point: In PSR-7, the names used are: - queryParams: The query string values. - parsedBody: The body of the message, converted to a meaningful value. If the request type is a form, then it MUST be equivalent to $_POST. If not, it's up to the particular implementation to determine what "parsed" means. (Eg, parsing a JSON body of a POST into some domain object, or whatever.) - The raw body is a stream called "body", or rather an OOP wrapper around a stream since PHP's native stream interface is fugly. - There's specific handling for uploadedFiles, too. cf: https://www.php-fig.org/psr/psr-7/ To the earlier point about existing implementations, while there are a myriad of older, custom implementations of abstractions around superglobals, there's only two that are not decade-old proprietary implementations: HttpFoundation and PSR-7. Those are, realistically, the only implementations that matter. Anything else would be on the same order of magnitude effort to port to one of those as to port to this proposal. In a meaningful sense, those are the only "existing competition". Both also have robust ecosystems that make leveraging them in an entirely custom app pretty straightforward. (Whatever your feelings of the technical merits of either design, that's the current state-of-the-market.) Which therefore begs the question, is this proposal intended to supplant HttpFoundation and PSR-7, or to become a common underpinning that both of them wrap, or to be a third cohabitating implementation in the ecosystem? It doesn't seem robust enough to supplant both of them entirely, there's little value to either HttpFoundation or PSR-7 to rewrite their guts to wrap this object (though it's easier for PSR-7, as an interface, for someone to write a new implementation of it than for HttpFoundation), which would mean we'd end up with a 3rd in-the-wild implementation for user space to keep track of. I am unclear how that is a market win. PDO was mentioned previously as a model. Yes, there were many user-space implementations prior to PDO. PDO effectively supplanted and obsoleted them. However... PDO was also never thought-through enough or robust enough to be used directly, spawning a whole new ecosystem of PDO++ libraries that people actually use (Doctrine, Eloquent, Drupal's DBTNG, Aura..sql, to name but a few). So, not the full win people were hoping for. If that pattern holds, we'd end up with... a new generation of this-RFC++ wrappers that still abstract it away yet aren't compatible with each other. That said, PDO did have the advantage of at least partially unifying disparate SQL APIs. There really aren't multiple incompatible HTTPs to abstract over the way there is for SQL backends, so the analogy is somewhat weak either way. --Larry Garfield
  108607
February 15, 2020 20:10 pmjones@pmjones.io ("Paul M. Jones")
Hi all,

> On Feb 15, 2020, at 02:01, Larry Garfield <larry@garfieldtech.com> wrote: > > ... is this proposal intended to supplant HttpFoundation and PSR-7 ... ?
This is question is answered in the RFC introduction; quoting from there: The SQLite “about” page says, “Think of SQLite not as a replacement for Oracle but as a replacement for fopen().” https://www.sqlite.org/about.html Likewise, think of this RFC not as a replacement for HttpFoundation or PSR-7, or as a model of HTTP messages, but as an object-oriented alternative to superglobals, header(), setcookie(), setrawcookie(), and so on.
> PDO was mentioned previously as a model.
I did not mention PDO as "a model". I mentioned PDO (along with other extensions) to illustrate a counter-argument to objections based on the availability and comparability of userland implementations. The counter-argument summary was: That's not to say "because PDO was allowed into core, this RFC must therefore be allowed into core" but to say "those objections alone were not a barrier to PDO, so they alone should not be a barrier to this RFC". The argument, and my counter-argument, are here: <https://externals.io/message/108436#108493> -- Paul M. Jones pmjones@pmjones.io http://paul-m-jones.com Modernizing Legacy Applications in PHP https://leanpub.com/mlaphp Solving the N+1 Problem in PHP https://leanpub.com/sn1php
  108613
February 16, 2020 01:37 rowan.collins@gmail.com (Rowan Tommins)
On 15 February 2020 20:10:30 GMT+00:00, "Paul M. Jones" <pmjones@pmjones.io> wrote:
>Hi all, > >> On Feb 15, 2020, at 02:01, Larry Garfield <larry@garfieldtech.com> >wrote: >> >> ... is this proposal intended to supplant HttpFoundation and PSR-7 >... ? > >This is question is answered in the RFC introduction
You've cut Larry's question in half there, and in doing so made it seem like a repeat, when it is not. The second half of the sentence is this:
> ...or to become a common underpinning that both of them wrap, or to be a third cohabitating implementation in the ecosystem?
I haven't seen you answer that part yet: do you expect existing userland libraries to migrate from wrapping $_GET etc to using these built-in wrappers. If so, what benefit does it bring those libraries? If not, who is its intended audience? You said previously:
> PDO did not (to my knowledge) "add capabilities which cannot exist in userland or cannot exist in a reasonably performant way".
I think this is a misjudgement, and a relevant one. PDO didn't take functionality that existed purely in userland and migrate it to an extension; it took functionality that was scattered across a dozen different vendor-specific extensions with different naming and calling conventions, and centralised it into one more-or-less consistent interface. In doing so, it made (or at least tried to make) life easier both for database vendors, who can provide a PDO driver and fit into the ecosystem, and library developers, who can use PDO as a base and have less vendor-specific code. Your other examples - date, phar, and session - took common problems that were possible to solve in userland but tricky to solve well, and provided a standard out-of-the-box implementation. We already have a unified out-of-the-box implementation for the problem "get data out of HTTP request", in the form of superglobals, so neither comparison seems apt. A better comparison might be to features which have been reimplemented multiple times, to fix fundamental problems. A recent example is __serialize, but interestingly $_GET et al are themselves the third implementation of the feature, after "register globals" and the $HTTP_* arrays. As far as I can see, the RFC mentions two things it fixes about the current implementation: - The current implementation is not OO. That's not really surprising, since PHP is not a purely OO language, and treats OO as a matter of style - to the extent of providing hybrid object-procedural APIs like date and mysqli. - The current implementation is based on global state. This is definitely something that would be good to fix, but you can do almost as much in that direction as the RFC by writing "$get=$_GET; unset($_GET);" The hard problem is that the entry point for a request is in global scope, not a main() or handleRequest() function. Introducing these objects as part of a new calling convention for PHP scripts would definitely add value, and make them a true replacement for the superglobals, but that would be a very different RFC. However well designed this extension is within itself, I think it needs a stronger description of who should use it and why. Regards, -- Rowan Tommins [IMSoP]
  108650
February 17, 2020 15:55 pmjones@pmjones.io ("Paul M. Jones")
Hi Rowan,

I apologize in advance for the length of this email. I hate reading walls-of-text, but the answers are necessarily long. I have tried to break it up into bullets where possible for easier reading.


> On Feb 15, 2020, at 19:37, Rowan Tommins collins@gmail.com> wrote: > > On 15 February 2020 20:10:30 GMT+00:00, "Paul M. Jones" <pmjones@pmjones.io> wrote: > >> Hi all, >> >>> On Feb 15, 2020, at 02:01, Larry Garfield <larry@garfieldtech.com> >>> wrote: >>> >>> ... is this proposal intended to supplant HttpFoundation and PSR-7 >>> ... ? >> >> This is question is answered in the RFC introduction > > You've cut Larry's question in half there, and in doing so made it seem like a repeat, when it is not. The second half of the sentence is this: > >> ...or to become a common underpinning that both of them wrap, or to be a third cohabitating implementation in the ecosystem? > > I haven't seen you answer that part yet: do you expect existing userland libraries to migrate from wrapping $_GET etc to using these built-in wrappers. If so, what benefit does it bring those libraries? If not, who is its intended audience?
I really did think the answers to these were obvious, or easily-inferred, but obviously I was wrong. I will attempt to expand. Q: "Do you expect existing userland libraries to migrate ... ?" A: To be clear, I don't *expect* anything from existing published library authors; however ... - I *suspect* that some will choose to ignore this extension, - that others will decorate or extend it, - and that still others may find their own work so close to this extension that they migrate over to it entirely. Further ... - I suspect that developers of in-house unpublished request/response objects may find this useful for their own purposes, - and that developers who are using $_GET, header(), etc. will be pleased to find an OO-ish system that operates much like PHP itself already does, easing their transition away from global state. Finally ... - I suspect that some *consumers* of existing libraries will feel this extension is not their preferred way of working, and continue on with whatever libraries they already use, - while other consumers of those libraries will prefer this extension in their place. On reading over this, I suppose I do have an "expectation" of library authors and their consumers: that as good engineers they will evaluate this extension in reference to their own circumstances, and choose to use it (or not use it) based on the tradeoffs they find for their situation. Q: "What benefit does it bring those libraries?" A: Whatever benefits their authors happen to see in it. For myself, and as noted by Jan Schneider and others, those benefits center around having a built-in OO-ish request/response object set that does pretty much just what PHP itself already does, and that is well-suited to our daily work, without needing to incorporate comparatively large libraries into our projects for what we consider to be relatively simple purposes -- those purposes being "reading the request inputs" and "sending the response outputs". Q: "Who is its intended audience?" A: I always thought of the "intended audience" as the much the same as for any RFC: that is, developers working on a website who want PHP to provide a reasonable set of functionality for doing so, request/response objects being part of that set in a language as closely related to the web as PHP is. (As a fun side note: for all its apparent simplicity, this is an extraordinary question. Unless I have missed something, I don't recall it being asked directly of any RFC before. A Google search against wiki.php.net and externals.io reveals a double-handful of results on the terms "intended audience" and "target audience", but the only even indirect question about it on an RFC is in relation to the JIT, here: <https://externals.io/message/103903#103995> -- and it's by you. :-) * * * Rowan, you quoted Larry asking if this extension was ...
>> to be a third cohabitating implementation in the ecosystem?
.... that is, third after HttpFoundation and PSR-7. (By way of preamble, and to reiterate what I've said before: I would prefer to discuss this RFC on its own terms. But, as you and others want some discussion in context of other projects, I will do so. My apologies if I sound negative or critical toward those projects.) It turns out the question is kind of a funny way of describing the situation, in that PSR-7 is not an implementation of anything. (Full disclosure: I was a sponsor on the PSR-7 proposal.) PSR-7 is instead a set of interfaces; that is, they "are abstractions around HTTP messages and the elements composing them." <https://www.php-fig.org/psr/psr-7/> The meta document notes, "the goal of this proposal is not to obsolete the current interfaces utilized by existing PHP libraries. This proposal is aimed at interoperability between PHP packages for the purpose of describing HTTP messages." <https://www.php-fig.org/psr/psr-7/meta/> So, to refer to PSR-7 as a "cohabiting implementation" is a bit mistaken (but a very easy mistake to make -- I imagine I have made it myself occasionally). If we are going to talk about implementations in an ecosystem, then, there are at least four for PSR-7: - Cake - Guzzle - Slim - Zend/Laminas There are others as well, with varying ranges of adoption; and, as PSR-7 is an interoperability specification, I predict there will be more implementations later. The above implementations are partially but not always fully interchangeable; while they adhere to the PSR-7 method interfaces, they each have their own behavioral idiosyncrasies. In some cases they offer additional methods and behaviors that the others do not have. (This is not a criticism, merely an observation.) Thus, if one wishes to speak in terms of "cohabiting implementations", ext/request would have to be counted as one of at least six (i.e., itself, HttpFoundation, and the above four). I don't know if that strengthens or weakens the ext/request case, but it does help to clarify the world in which it exists. * * * Rowan, you wrote:
> You said previously: > >> PDO did not (to my knowledge) "add capabilities which cannot exist in userland or cannot exist in a reasonably performant way". > > I think this is a misjudgement, and a relevant one. PDO didn't take functionality that existed purely in userland and migrate it to an extension; it took functionality that was scattered across a dozen different vendor-specific extensions with different naming and calling conventions, and centralised it into one more-or-less consistent interface. In doing so, it made (or at least tried to make) life easier both for database vendors, who can provide a PDO driver and fit into the ecosystem, and library developers, who can use PDO as a base and have less vendor-specific code.
That is one valid way of looking at it; but then, on exactly the same terms, it is likewise valid to say that AdoDB "took functionality that was scattered across a dozen different vendor-specific extensions with different naming and calling conventions, and centralised it into one more-or-less consistent interface." So did PEAR DB, Metabase, MDB, and so on. PDO did it as an extension, instead of in userland, but the goals and outcomes noted were identical. I find this more confirmatory than not regarding my earlier counter-argument; i.e., that (in hindsight) objections based on the existence and availability of userland projects would not themselves have been a barrier to PDO, and so should not themselves be a barrier to this RFC.
> Your other examples - date, phar, and session - took common problems that were possible to solve in userland but tricky to solve well, and provided a standard out-of-the-box implementation. > > We already have a unified out-of-the-box implementation for the problem "get data out of HTTP request", in the form of superglobals, so neither comparison seems apt.
Perhaps; working by analogy is always a difficult and imperfect task.
> A better comparison might be to features which have been reimplemented multiple times, to fix fundamental problems. A recent example is __serialize, but interestingly $_GET et al are themselves the third implementation of the feature, after "register globals" and the $HTTP_* arrays. > > As far as I can see, the RFC mentions two things it fixes about the current implementation: > > - The current implementation is not OO. That's not really surprising, since PHP is not a purely OO language, and treats OO as a matter of style - to the extent of providing hybrid object-procedural APIs like date and mysqli.
"As a matter of style" is a good line! In support of that line, this RFC does not seek to remove $_GET and the response-related functions, but rather to provide an additional OO-ish approach that honors the existing (as you say, the third-time's-the-charm) language-level approaches. FWIW, I had considered mentioning the dual OO+procedural APIs of mysqli, date, etc. but it seemed too much in an already-long RFC; I'm glad you brought it up, as I did have it in mind.
> - The current implementation is based on global state. This is definitely something that would be good to fix, but you can do almost as much in that direction as the RFC by writing "$get=$_GET; unset($_GET);" The hard problem is that the entry point for a request is in global scope, not a main() or handleRequest() function.
Another "hard" problem is carrying those values around in the system once they are decoupled from global state; the objects in this RFC purport to do so.
> Introducing these objects as part of a new calling convention for PHP scripts would definitely add value, and make them a true replacement for the superglobals, but that would be a very different RFC.
That does seem rather ambitious. If you feel that's a valuable thing to add to PHP, perhaps it could be part of a future RFC, maybe even one that uses the objects in this RFC as a starting point. * * * Again, my apologies for the wall-of-text. Please let me know if you find those answers are sufficient or not. -- Paul M. Jones pmjones@pmjones.io http://paul-m-jones.com Modernizing Legacy Applications in PHP https://leanpub.com/mlaphp Solving the N+1 Problem in PHP https://leanpub.com/sn1php
  108661
February 18, 2020 20:21 rowan.collins@gmail.com (Rowan Tommins)
On 17/02/2020 15:55, Paul M. Jones wrote:
> I apologize in advance for the length of this email. I hate reading walls-of-text, but the answers are necessarily long. I have tried to break it up into bullets where possible for easier reading.
No, thank you for taking the time to respond. I've cherry-picked rearranged the quotes in this reply a bit so I can try not to repeat myself too much; let me know if I've taken your words too far out of context.
> ...it is likewise valid to say that AdoDB "took functionality that was scattered across a dozen different vendor-specific extensions with different naming and calling conventions, and centralised it into one more-or-less consistent interface." So did PEAR DB, Metabase, MDB, and so on. PDO did it as an extension, instead of in userland, but the goals and outcomes noted were identical.
That's a fair point, everything PDO does could be and has been done in userland. There is one thing that couldn't be done outside of an extension, though, which is that PDO doesn't rely on the legacy extensions, it (in theory at least) replaces them. What I think is slightly more relevant, is that PDO makes writing wrappers like ADODB easier, because it contains non-trivial functionality that those wrappers would otherwise have to write.
> I don't *expect* anything from existing published library authors > ... > I always thought of the "intended audience" as the much the same as > for any RFC
I think perhaps my choice of words has caused a bit of confusion. Perhaps "hope" would have been better than "expect", and "use cases" better than "intended audience". I was trying to draw out two pieces of information: - What's the sales pitch - what do YOU think is great about these classes? - When working out the details, what code should we be picturing using the new classes? I wasn't around when PDO was proposed, but an imaginary "sales pitch" might have gone something like this: - Developers should be able to implement multiple database systems without learning the quirks of each vendor's extension - Library authors shouldn't need to implement the basic functionality of every driver from scratch when the vendor could do it for them - Vendors shouldn't need to decide which API to follow, when we can normalise everything internally - We can offer different result formats and error-handling models out of the box ....and so on
> - some [library authors] may find their own work so close to this extension that they migrate over to it entirely.
So, there is at least some hope that this will entirely replace some people's existing libraries, even if it doesn't replace the more powerful ones like HttpFoundation. That's probably reasonable. (Note that I've been thinking of "library" fairly loosely - a single class used in a completely private monolithic repo, but which is written to be generic functionality, has much the same role as a public composer package in this case.)
> For myself, and as noted by Jan Schneider and others, those benefits center around...
This is what I was looking for. Sell it to me! :)
> ...a built-in OO-ish request/response object set...
"OO-ish" is a wise choice of words here. One of the reasons I'm not terribly keen on this proposal - particularly the request half - is that I'm a bit of an OO purist. By that I mean that I prefer objects that have a strong responsibility, and encapsulate useful behaviour, rather than just spitting out what went in. The response part of the proposal is closer to "real" OO, IMO, because it includes behaviour like manipulating individual headers and cookies. The lack of behaviour also makes it less useful to people writing their own request and response objects: if I have a copy of $_POST and want to put it in my own object property, why would I first pass it to a ServerRequest object, and then get it straight back out again, if the object isn't helping me do anything with that data? That said, I know some people are happy with OO-ish code, some even to the extent of preferring stdClass to an array. So it's not unreasonable to say that this will appeal to developers who are less puritanical about objects than me.
> ...that does pretty much just what PHP itself already does...
As you say elsewhere, this is useful for helping people migrate to it. The flipside of that is that it ties us into past decisions, rather than evaluating whether those decisions are still what we want. The approach in providing both $files and $uploads arrays is a good compromise - provide the new better way, but also an easy-to-migrate way. I'd love to see them named more distinctly, though, maybe even calling one of them "legacy". I'd probably also make them methods so that the data can be stored once (in the new format) and re-formatted on demand (again, objects as behaviour rather than data).
> ...easing their transition away from global state
This I find less convincing. To quote from further down the e-mail, I wrote:
>> ...you can do almost as much in that direction as the RFC by writing "$get=$_GET; unset($_GET);" The hard problem is that the entry point for a request is in global scope, not a main() or handleRequest() function.
and you replied:
> Another "hard" problem is carrying those values around in the system once they are decoupled from global state; the objects in this RFC purport to do so.
If all the object does is "carry around" a copy of the superglobal arrays, I can write it in about a dozen lines of code. Admittedly, the implementation wouldn't match anyone else's, but if I wanted interoperability, I would need to follow a standard like PSR-7, or match a popular existing library; this does neither. Again, the response object is more powerful in this regard, because we can't currently pass around a list of prepared cookies and trivially output it to the client. Along the lines of my previous message about decoupling concerns, I would personally like the response parts of the proposal split out and re-cast as a kind of "response buffer". That could be useful directly, and it could provide useful functionality to people implementing a full PSR-7 or similar Response object.
> I had considered mentioning the dual OO+procedural APIs of mysqli, date, etc. but it seemed too much in an already-long RFC; I'm glad you brought it up, as I did have it in mind.
This is an interesting comparison to consider. Just as those extensions have procedural options for people who can't stand objects, the current proposal has objects for people who like "OO-ish".
>> Introducing these objects as part of a new calling convention for PHP scripts would definitely add value, and make them a true replacement for the superglobals, but that would be a very different RFC. > That does seem rather ambitious. If you feel that's a valuable thing to add to PHP, perhaps it could be part of a future RFC, maybe even one that uses the objects in this RFC as a starting point.
One of the things I don't like about the current proposal is that it's modelled so closely around the current superglobals that I fear it would actually be *harder* to replace them. I find this constraint particularly awkward:
> N.b.: It is up to you to make sure the various content-related header > values in |$GLOBALS| match the custom |$content| string.
Parsing a request body from an arbitrary source into arrays that match the structure of $_POST and $_FILES would be a really useful feature. It would be something userland libraries could make use of right now, and would fit in nicely with a future where those arrays aren't populated by default. I also really want to see the day when I never have to interact with $_SERVER again. Other than renaming it, this object's design makes that less likely, not more. Imagine if in future we're able to redesign the internals so that there is a dedicated and reliable field for the requested URL; the current object doesn't have anywhere we can put that. If we add it later, it will be too late, code will be written that passes around ServerRequest objects, but relies on the full array of CGI variables to reconstruct the URL. If this object took a more opinionated view of what behaviour to encapsulate, we could simply hide the "server" array completely. Common use cases would be exposed via methods, and rarer use cases would have to be added in userland with their own copy of $_SERVER. Then my dream of deprecating $_SERVER could mean something other than moving it into an object. I hope I haven't left any unfinished sentences as I went back and forth through this! And thanks again for taking the time to engage with me. Regards, -- Rowan Tommins (né Collins) [IMSoP]
  108618
February 16, 2020 11:49 mike@newclarity.net (Mike Schinkel)
> On Feb 15, 2020, at 3:01 AM, Larry Garfield <larry@garfieldtech.com> wrote: > > Data point: > > In PSR-7, the names used are: > > - queryParams: The query string values. > - parsedBody: The body of the message, converted to a meaningful value. If the request type is a form, then it MUST be equivalent to $_POST. If not, it's up to the particular implementation to determine what "parsed" means. (Eg, parsing a JSON body of a POST into some domain object, or whatever.) > - The raw body is a stream called "body", or rather an OOP wrapper around a stream since PHP's native stream interface is fugly. > - There's specific handling for uploadedFiles, too. > > cf: https://www.php-fig.org/psr/psr-7/ > > To the earlier point about existing implementations, while there are a myriad of older, custom implementations of abstractions around superglobals, there's only two that are not decade-old proprietary implementations: HttpFoundation and PSR-7. Those are, realistically, the only implementations that matter. Anything else would be on the same order of magnitude effort to port to one of those as to port to this proposal. In a meaningful sense, those are the only "existing competition". Both also have robust ecosystems that make leveraging them in an entirely custom app pretty straightforward. > > (Whatever your feelings of the technical merits of either design, that's the current state-of-the-market.) > > Which therefore begs the question, is this proposal intended to supplant HttpFoundation and PSR-7, or to become a common underpinning that both of them wrap, or to be a third cohabitating implementation in the ecosystem? > > It doesn't seem robust enough to supplant both of them entirely, there's little value to either HttpFoundation or PSR-7 to rewrite their guts to wrap this object (though it's easier for PSR-7, as an interface, for someone to write a new implementation of it than for HttpFoundation), which would mean we'd end up with a 3rd in-the-wild implementation for user space to keep track of. > > I am unclear how that is a market win.
The win is it allows developer to provide a simple to use object oriented interface *in core*. IOW, without having to bring in an external PHP-code implementation that may or may not break in the future. Another big win would be to allow developers to deprecate use of superglobals in their own apps. However Paul do not want to add an ini setting to disable the use of superglobals. But how could disabling superglobals be done in HttpFoundation or PSR-7? Maybe Paul will reconsider adding such an capability to his RFC because it is something we cannot get with HttpFoundation and PSR-7. Or maybe we should be looking at is a core implementation of PSR-7 instead; one that would allow us to disable access to the superglobals? One that people could subclass, of course. If we did that, we might hash out why some developers do not use PSR-7 and possibly fix its (perceived?) faults in a new PSR to amend PSR-7. -Mike
  108540
February 13, 2020 15:16 pmjones@pmjones.io ("Paul M. Jones")
Hi Niklas,

> On Feb 12, 2020, at 12:20, Niklas Keller <me@kelunik.com> wrote: > > I think the request / response API is entirely fine being solved in > userland instead of in php-src. However, since you already made such a > proposal, I want to give some feedback:
Re: userland, I gave a counterargument in my reply to Mark Randall. Even so, thanks for providing feedback in spite of that objection; I appreciate your time and effort.
> Naming > > I think we shouldn't take over the naming of the super globals, e.g. > $_GET really contains the query parameters and has nothing to do with > GET or POST, so $request->getQueryParameter(...) would be a better > name.
/me nods Yeah, naming is one of the hard problems. I considered $query as an alternative property name for $get, but in the end, the `$_GET => $get` symmetry was too great to ignore. If others here feel that $query is a better name for `$_GET` than $get, I will submit to consensus on that point. Using a getQueryParameter() method strikes a little too close to PSR-7, and thereby to charges of favoritism. But let's say we do use a method instead of a property here, call it getQuery(); then, of the following .... $foo = $request->getQuery()['foo']; // vs $foo = $request->query['foo']; .... the latter (using a property) looks and feels more appropriate to me. Thus, the RFC specifies properties over methods for ServerRequest.
> Type Safety > > I think the API should be type safe. Currently $request->get['key'] > can be null | string | string[] | ... Most parameters only appear a > single time, so a method returning the first parameter value or null > could be used instead.
This sounds a little like using `$_REQUEST` instead of `$_GET`, `$_POST`, and `$_COOKIES`. Using the "first" parameter would then depend on the order in which the superglobals get entered into the ServerRequest object, and/or we're in the business of reading and honoring the `variables_order` INI setting, at which point the logic starts sounding rather involved. So while I do get the desire for type-safety, I'd prefer to avoid all that intricacy, and go with something much closer to what PHP itself already does. That is, read $get for $_GET, $post for $_POST, etc., and just go with what is stored there.
> API Issues > > I don't see any reason to keep specials such as > $_SERVER['HTTP_X_REQUESTED_WITH'] vs. $request->requestedWith, which > is just another HTTP header.
I get that; $requestedWith in 1.x was a boolean $xhr, to let you know if `$_SERVER['HTTP_X_REQUESTED_WITH'] === 'XmlHttpRequest'`. It struck me that there might be different values in the future, and so moved to just $requestedWith instead. I am not attached to that particular property; if others agree, I am OK with removing it.
> If there's $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] => $request->method > and $_SERVER['REQUEST_METHOD'] => $request->method, how can I get the > original (actual) method?
The $method property is a calculated value: if there is a method override on a POST, $method is the override; otherwise, it is the "actual" method. So: - $request->server['REQUEST_METHOD'] is the original (actual) method, - $request->server['HTTP_X_METHOD_OVERRIDE'] is the override method, and - $request->method is the "calculated" value between them. You can see the code here: https://github.com/pmjones/ext-request/blob/2.x/php_request.c#L147-L175
> Given 'echo $content; => $response->setContent($content);', shouldn't > this rather be something like `addContent()`?
That looks like poor describing on my part in the RFC. It is more true to say that these are equivalent: echo $content; // => $response->setContent($content); $responseSender->send($response); I will try to make that more apparent in the RFC.
> How does streaming responses work? There's ServerResponseSender, but I think this should > be part of the Response API.
Here's an example: $fh = fopen('/path/to/file.ext', 'rb'); $response->setContent($fh); // ... $responseSender->send($response); When the ServerResponseSender calls $response->getContent() and detects a resource, it calls rewind() on that resource, then fpassthru(). That should stream through nicely. For more information, please see the ServerResponseSender methods at <https://github.com/pmjones/ext-request#methods-2> under the sendContent() bullet: • If the content is a resource, it is sent using rewind() and then fpassthru(). • If the content is a callable object or closure, it is invoked, and then its return value (if any) is echoed as a string; note that object returns will be cast to string at this point, invoking the __toString() method if present. • Otherwise, the content is echoed as a string; note that objects will be cast to string at this point, invoking the __toString() method if present. There are limitations to that approach, but experience has shown that it covers the vast majority of common requirements.
> The Request object should be mutable, e.g. you might want to change > the client IP to be based on a x-forwarded-header instead of the TCP > source address.
That's a great example. First, note that ServerRequest avoids setting a $clientIp property. Further, *extensions* to the ServerRequest object might well be mutable. So, to go with your example, you would be well within bounds to do something like this: class MyServerRequest extends ServerRequest { private $clientIp; public function __construct(array $globals, ?string $content = null) { parent::__construct($globals, $content); if ($this->forwarded) { $this->clientIp = // ... } } public function getClientIp() { return $this->clientIp; } } You could do that for all your custom calculations on a ServerRequest object.
> Other Commentary > >> A: It supports async exactly as much as PHP itself does. > > Not really. PHP has built-in stream_select / non-blocking streams, so > supports the tools required for async out of the box.
I will address this separately, per the resolution of our private email conversation. -- Paul M. Jones pmjones@pmjones.io http://paul-m-jones.com Modernizing Legacy Applications in PHP https://leanpub.com/mlaphp Solving the N+1 Problem in PHP https://leanpub.com/sn1php
  108652
February 18, 2020 09:24 come.chilliet@fusiondirectory.org (=?ISO-8859-1?Q?C=F4me?= Chilliet)
Le jeudi 13 février 2020, 09:16:49 CET Paul M. Jones a écrit :
> Yeah, naming is one of the hard problems. I considered $query as an alternative property name for $get, but in the end, the `$_GET => $get` symmetry was too great to ignore. If others here feel that $query is a better name for `$_GET` than $get, I will submit to consensus on that point.
query is definitely better than get. Regarding post, I’m fine with body, parsedBody and input. I get the idea of input to mimic php://input, but if I understand things correctly, php://input is raw body, while $request->post is parsed body, so naming them alike might actually cause confusion?
> > Given 'echo $content; => $response->setContent($content);', shouldn't > > this rather be something like `addContent()`? > > That looks like poor describing on my part in the RFC. It is more true to say that these are equivalent: > > echo $content; > > // => > > $response->setContent($content); > $responseSender->send($response); > > I will try to make that more apparent in the RFC.
I still do not understand this. echo adds content to the response, it does not replace it. So the equivalent function should be $response->addContent. I would expect $response->setContent to replace the content. Can you explicit behavior for this: $response->setContent("a\n"); $response->setContent("b\n"); $responseSender->send($response); Compared to echo "a\n"; echo "b\n"; -- Côme Chilliet FusionDirectory - https://www.fusiondirectory.org
  108657
February 18, 2020 13:33 pmjones@pmjones.io ("Paul M. Jones")
Hi Côme,

> On Feb 18, 2020, at 03:24, Côme Chilliet chilliet@fusiondirectory.org> wrote: > > Le jeudi 13 février 2020, 09:16:49 CET Paul M. Jones a écrit : > >> Yeah, naming is one of the hard problems. I considered $query as an alternative property name for $get, but in the end, the `$_GET => $get` symmetry was too great to ignore. If others here feel that $query is a better name for `$_GET` than $get, I will submit to consensus on that point. > > query is definitely better than get.
Excellent.
> Regarding post, I’m fine with body, parsedBody and input. > > I get the idea of input to mimic php://input, but if I understand things correctly, php://input is raw body, while $request->post is parsed body, so naming them alike might actually cause confusion?
Might, might not. I don't think there is any "good" name here, only names that are less-bad than others.
> I still do not understand this. > echo adds content to the response, it does not replace it. > So the equivalent function should be $response->addContent. > > I would expect $response->setContent to replace the content.
Ah, I see what you are getting at now ...
> Can you explicit behavior for this: > > $response->setContent("a\n"); > $response->setContent("b\n"); > $responseSender->send($response); > > Compared to > > echo "a\n"; > echo "b\n";
.... the output would be "b\n". As you say, setContent() replaces whatever content is already in the ServerResponse. While the comparison for a single echo is accurate, the comparison for multiple echoes would be: $content = "a\n"; $content .= "b\n"; $response->setContent($content); $responseSender->send($content); Does that help to clarify? -- Paul M. Jones pmjones@pmjones.io http://paul-m-jones.com Modernizing Legacy Applications in PHP https://leanpub.com/mlaphp Solving the N+1 Problem in PHP https://leanpub.com/sn1php
  108659
February 18, 2020 14:10 come.chilliet@fusiondirectory.org (=?ISO-8859-1?Q?C=F4me?= Chilliet)
Le mardi 18 février 2020, 07:33:37 CET Paul M. Jones a écrit :
> ... the output would be "b\n". As you say, setContent() replaces whatever content is already in the ServerResponse. While the comparison for a single echo is accurate, the comparison for multiple echoes would be: > > $content = "a\n"; > $content .= "b\n"; > $response->setContent($content); > $responseSender->send($content); > > Does that help to clarify?
Yes, but to me that means we also need an addContent method. Otherwise people will have to carry a global $content along side $response, or use setContent(getContent().$additionalContent). -- Côme Chilliet FusionDirectory - https://www.fusiondirectory.org
  108660
February 18, 2020 14:56 pmjones@pmjones.io ("Paul M. Jones")
> On Feb 18, 2020, at 08:10, Côme Chilliet chilliet@fusiondirectory.org> wrote: > > to me that means we also need an addContent method.
I can see why we'd think that; it's symmetrical, if nothing else. Even so, none of the researched implementations have a method like that. As far as I can recall, they call have setContent() and getContent() equivalents, but no addContent() equivalent. They all work much like you point out here ...
> Otherwise people will have to carry a global $content along side $response, or use setContent(getContent().$additionalContent).
.... although usually it's not a global $content. Instead, the $content is built up from a template or other subsystem of some sort, and then assigned to the response when complete. For example: $content = $template->render(); $response->setContent($content); So, I am reluctant to add something that no other implementations, across many years and many authors, have actually found a need for. Any further thoughts on this? -- Paul M. Jones pmjones@pmjones.io http://paul-m-jones.com Modernizing Legacy Applications in PHP https://leanpub.com/mlaphp Solving the N+1 Problem in PHP https://leanpub.com/sn1php
  108518
February 12, 2020 21:27 php.lists@allenjb.me.uk (AllenJB)
This might be more a "future scope" thing, but I would like to see a way 
to access the raw request body as part of this. Currently (as far as I 
know) the only way is to open/read php://input, which isn't particularly 
intuitive in my opinion.

AllenJB

On 10/02/2020 16:18, Paul M. Jones wrote:
> Hi Internals, > > After a couple of years of incubation, I am happy to offer a second version of this RFC: > > https://wiki.php.net/rfc/request_response > > It proposes an object-oriented approach around request and response functionality already existing in PHP, in order to reduce the global-state problems that come with superglobals and the various response-related functions. > > The SQLite "about" page says, "Think of SQLite not as a replacement for Oracle but as a replacement for fopen()." <https://www.sqlite.org/about.html> Likewise, think of this RFC not as a replacement for HttpFoundation or PSR-7, or as a model of HTTP messages, but as an object-oriented alternative to superglobals, header(), setcookie(), setrawcookie(), and so on. > > Thanks in advance for your time and consideration while evaluating it. > >
  108524
February 12, 2020 22:46 pmjones@pmjones.io ("Paul M. Jones")
Hi Allen & Robin,

Allen, you asked:

> On Feb 12, 2020, at 15:27, AllenJB lists@allenjb.me.uk> wrote: > > This might be more a "future scope" thing, but I would like to see a way to access the raw request body as part of this. Currently (as far as I know) the only way is to open/read php://input, which isn't particularly intuitive in my opinion.
Robin asked the same thing earlier:
> On Feb 11, 2020, at 08:24, Kingsquare.nl - Robin Speekenbrink <robin@kingsquare.nl> wrote: > > What i haven't clearly seen in the RFC nor the interfaces of the proposed objects: how will this handle PUT / php:://input / raw posted data? Or am i missing something?
Answer: the php://input stream is accessible via the ServerRequest $content property; see <https://github.com/pmjones/ext-request#content-related>. -- Paul M. Jones pmjones@pmjones.io http://paul-m-jones.com Modernizing Legacy Applications in PHP https://leanpub.com/mlaphp Solving the N+1 Problem in PHP https://leanpub.com/sn1php
  108529
February 13, 2020 06:45 ruudboon@php.net (Ruud Boon)
--Apple-Mail=_9C16F5E4-0018-4038-86FB-47C751F8EEE0
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain;
	charset=us-ascii

Hi!

I really like this RFC.
> On 12 Feb 2020, at 23:46, Paul M. Jones <pmjones@pmjones.io> wrote: >=20 > Hi Allen & Robin, >=20 > Allen, you asked: >=20 >> On Feb 12, 2020, at 15:27, AllenJB lists@allenjb.me.uk> wrote: >>=20 >> This might be more a "future scope" thing, but I would like to see a = way to access the raw request body as part of this. Currently (as far as =
I know) the only way is to open/read php://input, which isn't = particularly intuitive in my opinion.
>=20 >=20 > Robin asked the same thing earlier: >=20 >> On Feb 11, 2020, at 08:24, Kingsquare.nl - Robin Speekenbrink = <robin@kingsquare.nl> wrote:
>>=20 >> What i haven't clearly seen in the RFC nor the interfaces of the = proposed objects: how will this handle PUT / php:://input / raw posted =
data? Or am i missing something?
>=20 > Answer: the php://input stream is accessible via the ServerRequest = $content property; see =
<https://github.com/pmjones/ext-request#content-related>. In the the RFC I see that $content will return php://input = <php://input> using file_get_contents on the fly. Can we add the = possibility to get a reference to the stream as well? This allows the = use of stream_copy_to_stream, mime_content_type etc without reading the = fill input to memory.
>=20 >=20 > -- > Paul M. Jones > pmjones@pmjones.io > http://paul-m-jones.com >=20 > Modernizing Legacy Applications in PHP > https://leanpub.com/mlaphp >=20 > Solving the N+1 Problem in PHP > https://leanpub.com/sn1php >=20 > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php Cheers,
Ruud --Apple-Mail=_9C16F5E4-0018-4038-86FB-47C751F8EEE0 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=us-ascii Hi!

I really = like this RFC. 
On 12 Feb 2020, at 23:46, Paul M. Jones = <mailto:pmjones@pmjones.io" = class=3D"">pmjones@pmjones.io> wrote:

Hi = Allen & Robin,

Allen, you asked:

On Feb = 12, 2020, at 15:27, AllenJB <mailto:php.lists@allenjb.me.uk"= class=3D"">php.lists@allenjb.me.uk> wrote:

This might be more a "future scope" thing, but I would like = to see a way to access the raw request body as part of this. Currently = (as far as I know) the only way is to open/read php://input" = class=3D"">php://input, which isn't particularly intuitive in my = opinion.


Robin = asked the same thing earlier:

On Feb 11, 2020, at 08:24, http://Kingsquare.nl" class=3D"">Kingsquare.nl - Robin = Speekenbrink <mailto:robin@kingsquare.nl" = class=3D"">robin@kingsquare.nl> wrote:

What i haven't clearly seen in the RFC nor the interfaces of = the proposed objects: how will this handle PUT / php:://input / raw = posted data? Or am i missing something?

Answer: the php://input" class=3D"">php://input = stream is accessible via the ServerRequest $content property; see <https://github.com/pmjones/ext-request#content-related" = class=3D"">https://github.com/pmjones/ext-request#content-related>.=
In the the RFC I see that = $content will return  php://input" = class=3D"">php://input using file_get_contents on the fly. = Can we add the possibility to get a reference to the stream as well? = This allows the use of stream_copy_to_stream, mime_content_type etc = without reading the fill input to memory.



--
Paul M. Jones
mailto:pmjones@pmjones.io" class=3D"">pmjones@pmjones.io
http://paul-m-jones.com

Modernizing Legacy Applications in PHP
https://leanpub.com/mlaphp

Solving= the N+1 Problem in PHP
https://leanpub.com/sn1php

--
PHP Internals - PHP Runtime = Development Mailing List
To unsubscribe, visit: = http://www.php.net/unsub.php
Cheers,
Ruud
= --Apple-Mail=_9C16F5E4-0018-4038-86FB-47C751F8EEE0--
  108541
February 13, 2020 16:08 pmjones@pmjones.io ("Paul M. Jones")
Hi Ruud,


> On Feb 13, 2020, at 00:45, Ruud Boon <ruudboon@php.net> wrote: > > Hi! > > I really like this RFC.
Glad to hear it!
> In the the RFC I see that $content will return php://input using file_get_contents on the fly. Can we add the possibility to get a reference to the stream as well?
By "get a reference" I presume you mean "an open file handle" (a la `return fopen('php://input', 'rb')`). In which case: maybe? I can see where that would be useful for extremely large request bodies in a memory-constrained environment. But the question becomes: is that needed frequently-enough across a wide-enough range of PHP developers to warrant inclusion in this RFC? I don't recall seeing any userland implementation from my research provide such a thing. That makes me think it's a comparatively infrequent need. If many others disagree with that assessment, I'm happy to entertain the possibility of adding something like it to ServerRequest. -- Paul M. Jones pmjones@pmjones.io http://paul-m-jones.com Modernizing Legacy Applications in PHP https://leanpub.com/mlaphp Solving the N+1 Problem in PHP https://leanpub.com/sn1php