rfc:request_response
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision | ||
rfc:request_response [2016/12/21 16:26] – pmjones | rfc:request_response [2020/02/28 15:20] – add rejected feature pmjones | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== PHP RFC: Server-Side Request and Response Objects ====== | ====== PHP RFC: Server-Side Request and Response Objects ====== | ||
- | * Version: | + | * Version: |
- | * Date: 2016-09-27 | + | * Date: 2020-02-19 |
- | * Author: Paul M. Jones, | + | * Author: Paul M. Jones, |
- | * Status: | + | * Status: |
- | * First Published at: http:// | + | * First Published at: [[http:// |
- | Quoting [[http:// | + | ===== Introduction ===== |
- | > PHP is and should remain: | + | This RFC proposes an object-oriented approach around request and response functionality already existing in PHP, in order to reduce |
- | > 1) a pragmatic web-focused language | + | |
- | > 2) a loosely typed language | + | |
- | > 3) a language which caters | + | |
- | This RFC should move PHP forward following his vision. As [[http://news.php.net/php.internals/ | + | The SQLite " |
- | every new feature, and the scope of the goodness that those new features bring." | + | |
- | ===== Introduction ===== | + | 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, |
- | From time to time we've all heard the complaint that PHP has no built-in request object to represent the execution environment. Userland ends up writing these themselves, and those are usually tied to a specific library collection or framework. The same is true for a response object, to handle the output going back to the web client. I've written them myself more than once, as have others here. | + | ===== Proposal ===== |
- | After doing some library | + | This RFC proposes an extension to declare three new classes |
- | ===== Proposal ===== | + | * ServerRequest, |
- | I wrote up a userland implementation for that limited subset of functionality, and John Boehr then used that as a reference point for the C version. It is PHP 7.x only, and you can see the result at: | + | * ServerResponse and ServerResponseInterface, a buffer |
- | | + | * ServerResponseSender, |
- | (The userland reference implementation is at https://gitlab.com/ | + | The full README, working code, and all tests are available |
- | This extension | + | An earlier version of the extension |
- | This extension defines two classes in the global namespace: | + | ==== Summary ==== |
- | * ServerRequest, | + | < |
- | * ServerResponse, | + | Instead of the superglobal ... ... use ServerRequest: |
+ | --------------------------------------- --------------------------------------- | ||
+ | $_COOKIE | ||
+ | $_GET | ||
+ | $_GET[' | ||
+ | $_FILES | ||
+ | $_POST | ||
+ | $_SERVER | ||
+ | $_SERVER[' | ||
+ | $_SERVER[' | ||
+ | $_SERVER[' | ||
+ | $_SERVER[' | ||
+ | $_SERVER[' | ||
+ | $_SERVER[' | ||
+ | $_SERVER[' | ||
+ | |||
+ | Instead of parsing ... ... use ServerRequest: | ||
+ | --------------------------------------- --------------------------------------- | ||
+ | $_FILES to look more like $_POST | ||
+ | $_SERVER[' | ||
+ | and | ||
+ | $request-> | ||
+ | $_SERVER[' | ||
+ | $_SERVER[' | ||
+ | $_SERVER[' | ||
+ | $_SERVER[' | ||
+ | $_SERVER[' | ||
+ | $_SERVER[' | ||
+ | |||
+ | Instead of emitting ... ... buffer with ServerResponse: | ||
+ | --------------------------------------- --------------------------------------- | ||
+ | header(' | ||
+ | $response-> | ||
+ | header(' | ||
+ | header(' | ||
+ | setcookie(' | ||
+ | setrawcookie(' | ||
+ | echo $content; | ||
+ | |||
+ | Instead of sending with ... ... send with ServerResponseSender: | ||
+ | --------------------------------------- --------------------------------------- | ||
+ | echo, header(), setcookie(), | ||
+ | |||
+ | </ | ||
+ | |||
+ | There is more: please see the docs at [[https:// | ||
+ | |||
+ | |||
+ | ==== Criticism and Objections ==== | ||
+ | |||
+ | === Why Do This In PHP Itself? === | ||
+ | |||
+ | For a language as closely related to the web as it is, PHP has lacked core server-side request and response objects for its entire existence. It has date objects, database objects, XML objects, and other object-oriented extensions, but none for server-side request and response objects. This proposal fills that gap, covering a set of common functionality that has until now been available only in userland. | ||
+ | |||
+ | Further, truly read-only objects in PHP userland are difficult if not impossible to achieve, especially when you take immutability of values into account. Working within an extension is the surest way of doing it, a la the request object offered here. | ||
+ | |||
+ | === What About Existing Userland Projects? === | ||
+ | |||
+ | I would prefer to discuss this proposal on its own merits, and thereby stay away from what might appear to be negative commentary on other projects. Nonetheless, | ||
+ | |||
+ | With all that in mind, I will present limited comparisons to two other major projects, hopefully hitting the high points without sounding overly-negative. If further comparison is desired, I will attempt to provide it. | ||
+ | |||
+ | == Symfony HttpFoundation == | ||
+ | |||
+ | HttpFoundation provides a very wide range of functionality, | ||
+ | |||
+ | As it happens, this proposal turns out to mimic a reduced subset of HttpFoundation functionality. The same subset is common to many userland implementations: | ||
+ | |||
+ | * a way to read the request-related superglobals such as $_GET, $_POST, etc. from an object; | ||
+ | |||
+ | * a way to set headers and content into an object so they can be inspected and modified before sending. | ||
+ | |||
+ | So, this proposal is in some ways a distillation and summary of widely desired functionality in userland. | ||
+ | |||
+ | Back to HttpFoundation specifically, | ||
+ | |||
+ | <code php> | ||
+ | use Symfony\Component\HttpFoundation\Cookie; | ||
+ | |||
+ | $response-> | ||
+ | </ | ||
+ | |||
+ | This is not so terrible, though it does additionally involve the HeaderBag and Cookie classes. | ||
+ | |||
+ | <code php> | ||
+ | $response-> | ||
+ | </ | ||
+ | |||
+ | HttpFoundation request and response objects are both fully mutable. In contrast, this proposal offers a request object with read-only properties; the class can be extended to add mutable or immutable properties. The response object offered here is fully mutable, though its methods are marked as final; this leaves the response object open for extension by frameworks and libraries, but closed for modification to its core functions. | ||
+ | |||
+ | == PSR-7 == | ||
+ | |||
+ | (Full disclosure: I was one of the sponsors on PSR-7.) | ||
+ | |||
+ | PSR-7 is a newer competitor to Symfony HttpFoundation. The PSR-7 interoperability interfaces, and their various competing implemementations (each with their own idiosyncrasies and additions), attempt to model HTTP messages, both for use by an HTTP client (as in Guzzle) and by server-side applications (as in Laminas). | ||
+ | |||
+ | Using PSR-7 for server-side requests and responses can be challenging. For good or bad, the specification defines a way of working that is very different from implementations pre-existing it. A number of followup PSRs have been created to relieve these issues, as have other userland helper packages. | ||
+ | |||
+ | Using the same example as with HttpFoundation above, setting a cookie in PSR-7 is no simple task. PSR-7 only provides a way to set headers, which generally means a helper library is necessary to set cookies properly. For examples of such libraries, see [[https:// | ||
+ | |||
+ | Using the FigCookies project, this is how to set a cookie into a PSR-7 Response: | ||
+ | |||
+ | <code php> | ||
+ | use Dflydev\FigCookies\SetCookies; | ||
+ | use Dflydev\FigCookies\SetCookie; | ||
+ | |||
+ | $response = $response-> | ||
+ | SetCookies:: | ||
+ | SetCookie:: | ||
+ | ); | ||
+ | </ | ||
+ | |||
+ | As such, PSR-7 is not " | ||
+ | |||
+ | In contrast, this proposal offers something much more straightforward: | ||
+ | |||
+ | <code php> | ||
+ | $response-> | ||
+ | </ | ||
+ | |||
+ | In a polar opposite of HttpFoundation, | ||
+ | |||
+ | To reiterate, this proposal offers read-only properties on the request with consistent and reliable immutability of those values. The response object remains mutable. | ||
+ | |||
+ | == Userland Availability, | ||
+ | |||
+ | (Copied, with light editing, from [[https:// | ||
+ | |||
+ | One common objection, with variations, has been: "There is a wider userland ecosystem that already performs the proposed | ||
+ | functions, with more capabilities, | ||
+ | |||
+ | The proposal authors recognize and understand the sentiment. The following counterargument, | ||
+ | |||
+ | When ext/pdo was added to core, there was already a "wider ecosystem that already performs these functions, with more capabilities, | ||
+ | |||
+ | PDO did not "add capabilities which do not or cannot exist in userland" | ||
+ | |||
+ | 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/ | ||
+ | |||
+ | So, there is a long history of widely-needed userland functionality being brought into core. This proposal is a pretty tame example of doing so; as presented, it 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 above objection should have prevented PDO (et al.) from going into core. If that is the case, and (in hindsight) it was a mistake to allow them, then consistency alone makes the objection valid here as well. | ||
+ | |||
+ | However, if (in hindsight) it was not a mistake to allow those extensions, then the objection is not an especially strong argument against this RFC. That's not to say " | ||
+ | |||
+ | === Other Questions And Comments === | ||
+ | |||
+ | Q: Does ServerRequest hold references to the superglobals, | ||
+ | |||
+ | A: Copies, made at instantiation time. Changes to `$_GET` after the ServerRequest is instantiated will not be reflected in the existing instance. | ||
+ | |||
+ | Q: Since the $get, $post etc. properties are the same as $_GET and $_POST, does that mean they retain the same name mangling scheme? | ||
+ | |||
+ | A: They do; that is, ServerRequest uses whatever is passed into it at construction time. If PHP changes its name mangling, or if different array values are passed in, ServerRequest will use those instead. | ||
+ | |||
+ | Q: Readonly properties are unusual for PHP. | ||
+ | |||
+ | A: Granted, though not unheard of. PdoStatement:: | ||
+ | |||
+ | Q: Does this has any performance impact? | ||
+ | |||
+ | A: Compared to userland, probably greater performance, | ||
+ | |||
+ | Q: Why is ServerRequest readonly, and ServerResponse mutable? | ||
+ | |||
+ | A: It makes sense that you would not want to change what you have received as a request; however, as you are in charge of creating the response, modifying it as needed seems reasonable. | ||
+ | |||
+ | Q: Why is ServerRequest composed only of properties, and ServerResponse composed only of methods? | ||
+ | |||
+ | A: It's an outgrowth of an asymmetry that already exists in PHP: $_GET, $_POST, et al. are properties representing the request, whereas header(), setcookie(), | ||
+ | |||
+ | Q: Why not write (PSR-7|HttpFoundation|OtherImplementation) in C, instead of your own version? | ||
+ | |||
+ | A: This is not "my own version." | ||
+ | |||
+ | Q: Does it support HTTP/2? | ||
+ | |||
+ | A: It supports HTTP/2 exactly as much as PHP itself does. | ||
+ | |||
+ | Q: Does it support async? | ||
+ | |||
+ | A: Async is not in scope for the proposed API. | ||
+ | |||
+ | ==== Changes From The 1.x Version ==== | ||
+ | |||
+ | Based on user feedback over the past couple of years, this proposal differs from the earlier 1.x version in the following substantial ways: | ||
+ | |||
+ | * Some users objected on principle to the ServerRequest constructing itself using the superglobals internally. As a result, the constructor now requires a single array parameter; the corresponding argument is typically $GLOBALS but can be any array that mimics the $GLOBALS structure. | ||
+ | |||
+ | * The ServerRequest object no longer has the immutable application-related functionality represented by withInput(), | ||
+ | |||
+ | * The ServerResponse object no longer has setContent() | ||
+ | |||
+ | * ServerResponse no longer has a self-sending capability. | ||
+ | |||
+ | * To address some concerns from an earlier round of discussion, all ServerResponse properties are now private, and all its methods are now final, though the class itself is not. This keeps the class open for extension but closed for modification. | ||
+ | |||
+ | * ServerResponse:: | ||
+ | |||
+ | In all, these removals and changes bring the proposal much closer to PHP as-it-is. | ||
+ | |||
+ | ==== Open Questions ==== | ||
+ | |||
+ | 1. Are the more appropriate names other than ServerRequest, | ||
+ | |||
+ | 2. Should these classes go into an existing extension, rather than one of their own? | ||
- | The full extent of all functionality is described at https:// | ||
===== Backward Incompatible Changes ===== | ===== Backward Incompatible Changes ===== | ||
- | None expected. | + | Userland code that declares classes named ServerRequest, |
===== Proposed PHP Version(s) ===== | ===== Proposed PHP Version(s) ===== | ||
- | Next PHP 7.x. | + | Next PHP 7.x or 8.0. |
===== RFC Impact ===== | ===== RFC Impact ===== | ||
Line 53: | Line 255: | ||
==== To SAPIs ==== | ==== To SAPIs ==== | ||
- | None expected. | + | None. |
==== To Existing Extensions ==== | ==== To Existing Extensions ==== | ||
- | None expected. | + | It would be convenient if the php_head_parse_cookie_options_array() function in ext/ |
==== To Opcache ==== | ==== To Opcache ==== | ||
- | None known. | + | None. |
==== New Constants ==== | ==== New Constants ==== | ||
Line 73: | Line 275: | ||
===== Open Issues ===== | ===== Open Issues ===== | ||
- | Make sure there are no open issues when the vote starts! | + | None at this time. |
===== Unaffected PHP Functionality ===== | ===== Unaffected PHP Functionality ===== | ||
The remainder of PHP should remain unaffected. | The remainder of PHP should remain unaffected. | ||
- | |||
===== Future Scope ===== | ===== Future Scope ===== | ||
- | Possible support for HTTP/ | + | This extension acts as an object-oriented wrapper around existing PHP request |
===== Proposed Voting Choices ===== | ===== Proposed Voting Choices ===== | ||
- | This is not a language change, so only 50%+1 should be needed. | + | For or against the proposal. |
===== Patches and Tests ===== | ===== Patches and Tests ===== | ||
- | The C code for the extension and tests are at https://gitlab.com/ | + | The C code for the extension and tests are at [[https://github.com/ |
===== Implementation ===== | ===== Implementation ===== | ||
- | After the project is implemented, | + | After the project is implemented, |
- the version(s) it was merged to | - the version(s) it was merged to | ||
Line 102: | Line 303: | ||
===== References ===== | ===== References ===== | ||
- | Links to external references, | + | 1.x discussions: |
+ | |||
+ | [[https:// | ||
+ | |||
+ | [[https:// | ||
+ | |||
+ | [[https:// | ||
+ | |||
+ | [[http:// | ||
+ | |||
+ | [[https:// | ||
+ | |||
+ | [[http:// | ||
+ | |||
+ | [[https:// | ||
+ | |||
+ | 2.x discussions: | ||
+ | |||
+ | [[https:// | ||
+ | |||
+ | [[https:// | ||
===== Rejected Features ===== | ===== Rejected Features ===== | ||
- | Keep this updated with features that were discussed | + | Add filter_input integration to ServerRequest. |
+ | |||
+ | Add .ini setting(s) to disable superglobals, | ||
+ | |||
+ | Add .ini setting(s) to disable response-related functions, and/or warn on their use. | ||
+ | |||
+ | Expand the number of classes provided, to allow for various ServerRequest-related value objects. | ||
+ | |||
+ | Provide builder and locking methods for ServerRequest. | ||
+ | |||
+ | Make the ServerRequest properties mutable. | ||
+ | |||
+ | Add a ServerResponse:: | ||
+ | |||
+ | Embed the PHP multipart/ | ||
rfc/request_response.txt · Last modified: 2020/04/08 12:47 by pmjones